index_ appended to path erroneously added to rails 4 route - ruby-on-rails

I added a controller named "Triage" to my application, and added a PUT route as follows:
resources :triage do
collection do
put :process_multiple
end
end
Instead of the expected process_multiple_triage_path route, it seems it is processed as process_multiple_triage_index_path:
process_multiple_triage_index_path PUT /triage/process_multiple(.:format) triage#process_multiple
triage_index_path GET /triage(.:format) triage#index
POST /triage(.:format) triage#create
Answer: Turns out it is because triage is seen as a singular resource by Rails, thus by way of convention, you'd request the "index" of the resource.

This was because of the singular form of triage. Rails noticed that triage was used, instead of triages, and thus as a result of convention, the request would be of the index. I've linked below another StackOverflow that explains this well.
You can use resource :triage instead of resources (which also won't create an index route automatically either)
More info here

It's on the collecion, so Rails appends an _index. If you want to overwrite it, just change to:
resources :triage do
collection do
put :process_multiple, as: :proccess_multiple_triage
end
end
And now you'll be able to use process_multiple_triage_path

Related

What is the logic behind Rails' default restful resource code?

I'm Learning Rails. Lots of the conventions make great sense. The convention for code that maps to controller actions is odd:
//code url controller action
tweets_path /tweets/ def index
tweet /tweet/ID def show
new_tweet_path /tweets/new def new
edit_tweet_path(tweet) /tweets/ID/edit def edit
Why aren't the automatically generated method helpers done in the same symmetrical way as the controller actions? eg:
//code
tweet_index
tweet_show(tweet)
tweet_new
tweet_edit(tweet)
I'm so new I'm sure there's a perfectly good reason, I just don't know it yet :)
There are two asymmetries here.
The first is plural vs. singular route helpers. Why are some helpers tweets_* helpers while others are tweet_* helpers?
The answer is that some resource routes are member routes and others are collection routes. Member routes have to do with an instance of a resource and collection routes have to do with all instances of a resource as a group (unless the resource is singular in which case there are no collection routes). The index action is a collection route and the show action is a member route.
You can declare your own member and collection routes like this:
# routes.rb
resources :tweets do
member do
get :duplicate
end
collection do
get :summarize
end
end
This will create two helpers in addition to the standard ones. Note that Rails will create route helpers that are appropriately singular or plural.
a summarize_tweets_path helper that does not take a parameter
a duplicate_tweet_path helper that does
Official docs are here.
The second asymmetry is that the action is left out of the helper for many of the built-in resource actions. I suppose this could have been for brevity, but I don't really know.
Edit
After thinking about it, the action name was dropped because there is path overloading in Rails and REST. The '/tweet/:id' path could be the show, update, or delete action depending on the HTTP verb. Basically, the path tells you what you are operating on but not what action to take.
The helper methods are generated in a way that makes them more readable by making them more like parts of sentences. Saying 'Create a link to a new tweet' sounds better than 'Create a link to a tweet new'. It helps to keep this in mind as well when naming any custom actions, using names that fit sentences makes it easier to comprehend and remember since this is how we learn to speak.
One reason for it not being symmetrical is that the mappings depends on the HTTP action. For example, a GET to /tweets map to the index action, but a POST to /tweets maps to the create action.

checking restful features of the action in controller

when drawing restful routes in ruby on rails you specify
if a controller action applies to a member or collection and
what http verb is used. http://guides.rubyonrails.org/routing.html
How can I check 1 from within the controller instance?
(The HTTP verb can be checked via request.method)
The ugly hack is to check if the named route exists with singular or plural, but the route can have alternative name.
Yet another is to check for the request param used for identifying the member, but collection routes could have the same (optional) param.
If the route is following restful conventions, then running rake routesshould give you the answers you are looking for.
You will see what http verb is expected (answer to 2) and the path should answer your 1) question. For a member action, you need the ID, so typically the path contains the id
This route configuration:
resources :photos do
member do
get 'preview'
end
end
will give you the path photos/:id/preview -> ment to preview a single photo
And the route configuration:
resources :photos do
collection do
get 'preview'
end
end
will give you the path photos/preview -> ment to preview a list of photos
If you need to get this information from within the code of an action:
Inspect the params[:id] to distinguish the intent. If it is for a member, that value will be not nil.
Have you looked at the Journey gem for such a method? Assuming 3.2, that would have it, if anywhere, rather than request param. By then it's too late isn't it?

rails_3_question :as => why is my /posts/new routing to posts/show after setting up a slug

I'm using Rails 3 and after setting up slugs, I found that posts/new no longer works.
posts/:id, posts/:id/edit and all the other CRUD operations work.
However /posts/new gives me a routing error
No route matches {:action=>"show", :controller=>"posts"}
Now for some reason posts/new is routing to posts#show. In my routes, its just
resources :posts
My theory is that since /posts/:slug now matches against things other than numbers ids, the show verb is being routed to first. However it doesn't make sense since posts/grr a nonexistent entry gives a different error than posts/new and posts/first comes out just fine with all its associated paths working fine as well.
Anyone know what might be going on?
I've uploaded the repo to https://github.com/cultofmetatron/cassowary/tree/photogallary
I know my code sucks, I'm still learning the ins and outs of the system and I'd appreciate any insight into whats going on.
In your comment the first part seems fine: add a column to the Post column called slug and so on, and the contents of that will become some or all of the URL used to display a specific post. (I'll assume the other CRUD operations should work as normal)
To find the URL, the router has to know how to know which controller and action will handle this URL (as compared to others). A normal resources :posts route will match all of the RESTful methods, e.g. mapping a GET request onto a path starting with the controller name, and if an id is specified (/posts/1) map to the posts#show controller method, if not, it will map to posts#index method. If the request is a PUT, or DELETE or POST, different actions around a standardized URL format will occur.
Two changes are needed:
URL with the post slug format needs to map to the posts#show method (which is modified accordingly), and
Any links to the show page that are generated on your site need to use the post slug instead of the id
I'll assume you're OK with URLs start with /posts (if not, you'll need to identify some other unique pattern).
The first change requires that you override the specific case of the show method using route globbing, my adding something like match 'posts/*slug before the standard resource route. Here's a link to the guide on route globbing: http://guides.rubyonrails.org/routing.html#route-globbing
The next change, modify the existing posts#show method so that it looks for slug instead of id, e.g.
def show
#post = Post.where("slug = ?", params[:slug])
...
end
Finally, change the way Rails handles the URL helper posts_path. Do this by overriding to_param in your Post model, e.g.
def to_param
"/posts/#{slug}"
end
And then you're done. Maybe.
After that, see how the friendly_id gem does the same thing :-) https://github.com/norman/friendly_id

Rails incorrectly generates route for a resource with :controller set

So here's the routes in question:
resources :subjects, path: 'library' do
resources :modules, controller: 'subject_modules'
end
When I write form_for [#subject, #subject_module] (with those two set to what you'd expect), Rails tries to generate "subject_subject_module_path".
When I remove the :path for the subjects resource, the generated helper remains the same (as expected).
What it should be is "subject_module_path"; I suspect the problem is that Rails looks at the controller for the modules resources and uses that instead of its actual path, i.e. it builds this helper:
subject_ + subject_module_ + path
From what I've gathered so far, it's pointing towards the possibility of a bug, but is it possible it's more something on my side or something intended by design?
For now, I suppose this is usable as a temporary fix:
form_for([#subject, #subject_module], url: subject_module_path(#subject, #subject_module))
I'm using Rails 3.1.3.
If this is a form (update or create) then your routes should be pointing to
subjects_module_path
which should generate the URL
/library/modules/
have you tried pointing your form at
form_for [:subjects, #subject_module]?
which will then end up at your subject_modules controller
To be honest, your naming is very confusing and probably not helping. Ideally subject_modules should just be modules.
This is something that occurs due to the design of Rails.
The following is what the Rails API docs say in relation to url_for:
Relying on named routes
Passing a record (like an Active Record or Active Resource) instead of
a Hash as the options parameter will trigger the named route for that
record. The lookup will happen on the name of the class. So passing a
Workshop object will attempt to use the workshop_path route. If you
have a nested route, such as admin_workshop_path you’ll have to call
that explicitly (it’s impossible for url_for to guess that route).
But saying explicitly declaring a helper is required when you have a "nested route" is in accurate. If the nesting occurs under a resource (without certain kinds of routing options, more on this later), Rails will be able to generate the path without any issues.
form_for appears to at some point, like url_for, call polymorphic_url in order to generate the URL to target, which in turn calls build_named_route_call.
You can see that build_named_route_call simply generates underscored_versions of the ModelNames passed in, and joins them together with underscores.
Bringing that back to my routing:
resources :subjects, path: 'library' do
resources :modules, controller: 'subject_modules'
end
Since the subjects resource is under /library/ by setting :path, its helpers remain as subjects_*, and thus Rails has no problems generating a URL for it when passed a Subject. The subject_modules resource (named as such since Rails reserves the name Module for models) however, has had its named helpers changed from its model name through the setting of :controller.
Mystery solved.

Why is the scaffold generating routes like this? Why do they work?

The book "Agile development with Rails" shows in the second chapter, that you can say:
<%= link_to "Goodbye",say_goodbye_path %>
Instead of hardcoding the path to "/say/goodbye". Makes sense, I thought to myself. Probably Ruby is splitting the say_goodbye_path by _, assigns the first part as the controller name, the second part as the action name. But, afterwards, I generated the following scaffold:
rails generate scaffold User name:string mail:string
And I noticed in the index.html.erb view, that it had methods like: edit_user_path(user). I tried to rewrite it to user_edit_path(user), but of course, it didn't work. My question is, why are the scaffold links the other way around? How would I know if I should write them in the way the author uses them in link_to, or in the way they are generated by the scaffold. Can you shed some light on this?
The helper functions like user_edit_path are automatically generated by rails to map operations on resources to the matching routes and thus HTTP paths and HTTP verbs. You have to understand that you are dealing with resources here, not necessarily with simple controllers.
While most of the time your resources can map to a single controller, it doesn't have to be that way. You can have nested or combined resources which can result in rather complex routing definitions.
Resources are typically defined by giving it a name (userin this case) and defining some allowed operations on them. Rails encourages to follow the REST pattern there, so you can have shortcuts to have some operations pre-defined. One of them is edit, which by default matches to a GET request to users_controller#edit. Default operations on RAILS resources are:
HTTP verb path matching controller action
===================================================
GET /users #=> index
GET /users/1 #=> show
GET /users/new #=> new
GET /users/1/edit #=> edit
PUT /users/1 #=> update
POST /users #=> create
DELETE /users/1 #=> destroy
These mappings can be customized on your routes.rb (changing methods, adding or removing operations, ...) Generally you are encouraged to use the default mappings as these are supported by standard helpers and make your app easier to understand.
Scaffolds are code generated by a template which is not related to the routing.
Routing is based on the route.rb in your config folder. All resources are routed by default (when generated by scaffolds) but there's a default rule /:controller/:action/:id that you can enable. Think of a "catch all" case.
One way to see what routes to have is to edit route.rb and run rake routes and see how they change. There's an official guide here too: http://guides.rubyonrails.org/routing.html

Resources