Rails uncountable model name, no route matches get name_index - ruby-on-rails

I have a model with uncountable name - class Equipment and in this article (https://markembling.info/2011/06/uncountable-nouns-rails-3-resource-routing) I found that in such cases we get into problems while trying to get model's index path. So article provides tips how to use inflection rules. However, I believe word 'Equipment', just like 'person' is already understood by Rails and I dont even need to define inflection rule, since I still get this path:
equipment_index GET /equipment(.:format) equipment#index
But, for some reason, after I navigate to localhost:3000/equipment_index, I get
No route matches [GET] "/equipment_index"
All other paths works (like localhost:3000/equipment).
Any ideas whats going on..?
p.s. please do not write how to add a custom path. I hope to solve this in the Rails way - convention over configuration. Thanks.
routes:
equipment_index GET /equipment(.:format) equipment#index
POST /equipment(.:format) equipment#create
new_equipment GET /equipment/new(.:format) equipment#new
edit_equipment GET /equipment/:id/edit(.:format) equipment#edit
equipment GET /equipment/:id(.:format) equipment#show
PATCH /equipment/:id(.:format) equipment#update
PUT /equipment/:id(.:format) equipment#update
DELETE /equipment/:id(.:format) equipment#destroy
routes.rb:
resources :users do
member do
get 'generate_raport'
end
end
resources :client_users
resources :clients
devise_for :users, skip: [:registrations]
resources :equipment
root to: 'static#homepage'

equipment_index is a named route, not a url string. The url string that corresponds to this named route is in this part:
GET /equipment(.:format)
When you say:
equipment_index GET /equipment(.:format) equipment#index
you are really saying that equipment_index is a named route (an alias so to say) for the actual url route localhost:3000/equipment. The last part that says:
equipment#index
just says that your request will be routed through the equipment controller and the corresponding index action.
Solution
You can simply navigate to localhost:3000/equipment to get to the index page for your equipment controller.
For example, you would link to this page using a rails link_to helper and the named route discussed above like this:
link_to "My index path", equipment_index_path
Follow up on comments
change add the following line to your routes.rb file directly after the line that contains resources :equipment. It would now look like:
resources :equipment
get 'equipment', to: 'equipment#index', as: 'equipment'

This is convention over configuration!
You're simply reading the output of rake routes wrong or have the wrong expectations about how its supposed to work. The first column is just the name of the route which is primarily used for creating path helpers. The actual paths are in the third column*.
equipment_index_path() # /equipment
equipment_path(1) # /equipment/1
equipment_path() # error due to missing id param
Since equipment is an uncountable noun Rails cleverly avoids an issue where the generated path helpers would be ambiguous - equipment_path could potentially lead to either the index action or the show action. Regular countable nouns don't have this issue so the _index postfix is not usually needed.
# no ambiguity
cats_path() # /cats
cat_path(1) # /cats/1
While you could argue that rails in that case should use the presence of the id param to differentiate that is not how its built and could mask bugs where you pass nil instead of a record.

Related

What's the "Rails way" to route a "diff" between two instances of the same model?

I'm building a "Brand personality" tool that gives you a report based on the text you share on social media.
I have a model PersonalityReport and in routes I have resources :personality_reports.
A new feature is to offer a "diff" between two reports and I'm trying to work out the most "guessable" way to model this in routes.
Ideally I'd like GET /personality_reports/:personality_report_id/diff/:id or something along those lines, and while I could simply put that into routes as a GET route, is there a more Railsy way of specifying a route using the resources / collections style so that my routes.rb file is more easy to understand?
The 'neatest' way can think of is:
resources :personality_reports, param: 'personality_report' do
member do
get 'diff/:id', to: 'personality_reports#action', as: 'diff_route'
end
end
Where obviously to: is your controller#action, and as: is the name of your route. After running rake routes you will see this generates:
diff_route_personality_report GET /personality_reports/:personality_report_id/diff/:id(.:format) personality_reports#action
I think whatever you mentioned is good enough,
resources : personality_reports do
resources :diffs, only: [:show]
end
So, routes like below,
personality_report_diff GET /personality_reports/:personality_report_id/diffs/:id(.:format) diffs#show
NOTE: You can also make diff route in singular resource :diff if you want to make it as singular resource.

Rails root permalink routing

In Rails, the standard routing to objects is nested to a Model's name, example.com/model/object_id.
Is it anyhow possible to access objects without the Model part, so example.com/object_id shadowly accesses example.com/model/object_id?
Rails includes routes like you said. You can add constraints to determine object_id is integer or string.
get '/:id', to: 'articles#show', constraints: { id: /^\d/ }
This is for more information about routes constraint.
What you are first describing are the RESTful routes provided by the resources template in the rails router.
You can define different routes in the config/routes.rb file.
And for resources, you can provide a path option, where you can define a path.
resources :models, path: "/"
Will provide models resources at the route path. So a GET request to "/" would fire the "models#index" action and "/1/edit" would delegate to "models#edit"

Including attributes in custom Rails routes

I hope the title is not to misleading, as I don't know a better title for the problem I'm working on:
I have a doctor which belongs to location and specialty. I'd like to route to show action of the doc controller like this:
/dentist/berlin/7
I defined my routes like this:
get ':specialty/:location/:id', to: 'docs#show'
And in my views create the following url to link to the show action of the doc controller:
<%= link_to doc.name, "#{doc.specialty.name}/#{doc.location.name}/#{doc.id}" %>
Is this a good solution to the problem? If not, is there a cleaner way to construct urls like this possibly using resources? What the heck is the name for a this problem?
Thank your very much for your help in advance.
For references, you should have a look at this page (especially the end of section 2.6)
If it is only for a single route, it's okay as you did. But then if you want to have more than one route (like /dentist/berlin/7, /dentist/berlin/7/make_appointment, etc.) you might want to structure a bit more your routes so as to take advantage of rails resources.
For example, instead of
get ':specialty/:location/:id', to: 'doctors#show'
get ':specialty/:location/:id/appointment', to: 'doctors#new_appointment'
post ':specialty/:location/:id/appointment', to: 'doctors#post_appointment'
You could have something like this (the code is almost equivalent, see explanation below)
resources :doctors, path: '/:specialty/:location', only: [:show] do
member do
get 'new_appointment'
post 'create_appointment'
end
end
Explanation
resources will generate the RESTful routes (index, show, edit, new, create, destroy) for the specified controller (doctors_controller I assume)
The 'only' means you don't want to add all the RESTful routes, just the ones specified
Then you want to add member actions, ie. actions that can be executed on a particular item of the collection. You can chose different syntaxes
resources :doctors do
member do
# Everything here will have the prefix /:id so the action applies to a particular item
end
end
# OR
resources :doctors do
get 'new_appointement', on: :member
end
By default, the controller action is the same as the path name you give, but you can also override it
member do
get 'appointment', action: 'new_appointment'
post 'appointment', action: 'post_appointment'
end
Rails has some wonderful helpers when it comes to routing !
The correct approach is to give your route a name, like this:
get ':specialty/:location/:id', to: 'docs#show', as: 'docs_show'
Then you can use it like this:
<%= link_to doc.name, docs_show_path(doc.specialty.name, doc.location.name, doc.id) %>
Note 1:
Rails appends _path at the end of the route names you define.
Note 2:
You can see all the available named routes by executing rake routes.

Ruby on Rails 3: Change default controller and parameter order in routing

I have a Rails app that has a controller called domain which has a nested controller called subdomain and stats. I have defined them in routes.rb:
resources :domains do
resources :subdomains, :stats
end
I have changed the to_param of the domain and subdomain models to use the name of the domain, e.g.: the routing I get is http://site/domains/foo/subdomains/bar.
I would like to tidy it up to so that instead of using http://site/domains/foo/subdomains/bar I could access it with just http://site/foo/subdomains/bar. I have tried the following in routes.rb:
match "/:id/" => "domains#show", :as => :domain
Which works fine, but it only gives me the ability to use the path http://site/foo but for example http://site/foo/subdomains/bar doesn't. I could create match lines for every respective model and nested model but that does nothing to other helpers besides domain_url - i.e. edit_domain_url points to /domains/foo/edit/ instead of /foo/edit.
Is there a way to change the routing so that the resources generates helpers that point to the root url without the 'domains' part?
The single match in your routes creates only one route. Resource helpers create many routes at once. Luckily there are a lot of options for customisation. If you want to omit /domains/ from your paths, it's as simple as:
resources :domains, :path => "/" do
resources :subdomains, :stats
end
With the above in config/routes.rb, running rake routes says the following:
domain_subdomains GET /:domain_id/subdomains(.:format)
domain_subdomains POST /:domain_id/subdomains(.:format)
new_domain_subdomain GET /:domain_id/subdomains/new(.:format)
edit_domain_subdomain GET /:domain_id/subdomains/:id/edit(.:format)
domain_subdomain GET /:domain_id/subdomains/:id(.:format)
domain_subdomain PUT /:domain_id/subdomains/:id(.:format)
domain_subdomain DELETE /:domain_id/subdomains/:id(.:format)
domain_stats GET /:domain_id/stats(.:format)
domain_stats POST /:domain_id/stats(.:format)
new_domain_stat GET /:domain_id/stats/new(.:format)
edit_domain_stat GET /:domain_id/stats/:id/edit(.:format)
domain_stat GET /:domain_id/stats/:id(.:format)
domain_stat PUT /:domain_id/stats/:id(.:format)
domain_stat DELETE /:domain_id/stats/:id(.:format)
domains GET /(.:format)
domains POST /(.:format)
new_domain GET /new(.:format)
edit_domain GET /:id/edit(.:format)
domain GET /:id(.:format)
domain PUT /:id(.:format)
domain DELETE /:id(.:format)
Looks like all the routes you need!

How to rename the default identifier param "id" in Rails' map.resources()?

I like all the default routes that are generated by Rail's map.resources. But, there are cases where I would like to use a non-numeric identifier in my routes. For example, If have a nested route consist of users and their articles, a standard route could be written as such:
map.resources :users, :has_many => [:articles] # => e.g. '/users/:id/articles/:id'
However, there are many advantages / reasons not to use the default numerical identifier generated by Rails. Is there a way to replace the default :id params to another canonical identifier of my choice without resulting to writing custom routes for every standard action? Say if I want a route in the following format:
'/users/:login/articles/:id'
Is this kind of routes achievable using map.resources?
As of Rails 2.3, it's not possible to change the parameter name and still use the automatic routing that #resources provides.
As a workaround, you can map articles with a :path_prefix and :name_prefix:
map.resources :articles, :path_prefix => "/users/:login",
:name_prefix => "user_"
The :path_prefix affects the URL, and the :name_prefix affects the generated named routes, so you'll end up with these routes:
user_articles GET /users/:login/articles(.:format) {:controller=>"articles", :action=>"index"}
POST /users/:login/articles(.:format) {:controller=>"articles", :action=>"create"}
new_user_article GET /users/:login/articles/new(.:format) {:controller=>"articles", :action=>"new"}
edit_user_article GET /users/:login/articles/:id/edit(.:format) {:controller=>"articles", :action=>"edit"}
user_article GET /users/:login/articles/:id(.:format) {:controller=>"articles", :action=>"show"}
PUT /users/:login/articles/:id(.:format) {:controller=>"articles", :action=>"update"}
DELETE /users/:login/articles/:id(.:format) {:controller=>"articles", :action=>"destroy"}
As a general rule-of-thumb, though, I'd stick with the Rails default convention of :user_id, with the routing you posted in your question. It's generally understood that :id and :user_id don't necessarily imply "numeric identifier" — they imply "resource identifier," whatever that might be. And by sticking to the default convention, your code will be easier to understand for anyone who's used resource routes in Rails.
To use a non-numeric identifier for a resource, just redefine #to_param in your model. Then, make sure to use a finder in your controller that will find by this identifier (rather than the numeric ID), such as User#find_by_login!.
You can change the default of using the ID in URLs by overriding to_param in your model. e.g.
class User < ActiveRecord::Base
def to_param
login
end
end
user_articles_path(#user) => "/users/:login/articles"
The only other change you'll need to make is to find users by login rather than by ID in your controllers.

Resources