ruby on rails routes - ruby-on-rails

I am having a hard time understanding routes in rails 3. I created two scaffolds: Users and magazines. The users are able to login, but I am unable to link to the magazine page. I know it has to do with creating a route. If I navigate via the URL to localhost:3000/magazines, I can see the multiple magazines I created and each user associated with each magazine. I just can't seem to connect the dots. I want to create a link from the user page to the magazine page. I know this is basic, but all the routes documentation just are not making sense to me. Thanks so much for your time.

Resource pointed out in previous answers is awesome and that is where I got started. I still refer that in case I am stuck somewhere. One thing I find missing in the recourse is that it doesn't include the explanation of reading the routes table i.e. output of command rake routes and it takes time to fit the pieces together. Although if you read through the whole guide patiently, you can fit the pieces together.
On my system 'rake routes' gives the following output (excerpt relevant to resources :messages)
messages GET /messages(.:format) {:action=>"index", :controller=>"messages"}
POST /messages(.:format) {:action=>"create", :controller=>"messages"}
new_message GET /messages/new(.:format) {:action=>"new", :controller=>"messages"}
edit_message GET /messages/:id/edit(.:format) {:action=>"edit", :controller=>"messages"}
message GET /messages/:id(.:format) {:action=>"show", :controller=>"messages"}
PUT /messages/:id(.:format) {:action=>"update", :controller=>"messages"}
DELETE /messages/:id(.:format) {:action=>"destroy", :controller=>"messages"}
All the columns in this table give very important information:
Route Name(1st Column): This gives the name of the route, to which you can append "_url" or "_path" to derive the helper name for the route. For example, first one is the "messages", so you can use messages_path and messages_url in your views and controllers as a helper method. Looking at the table you can tell messages_path will generate a path of form "/messages(.:format)". Similarly, other route names generated are "new_message", "edit_message" and "message". You can also control the naming of routes.
HTTP Verb(2nd Column): This gives the information about the http verb which this route will respond to. If it is not present, then it means this route will respond to all http verbs. Generally browsers only support, "GET" and "POST" verbs. Rails simulate "PUT" and "DELETE" by passing a parameter "_method" with verb name as value to simulate "PUT" and "DELETE". Links by default result in a "GET" verb and form submissions in "POST". In conjunction with the first column, if you use messages_path with http "GET" it would match first route and if you use it with "POST" it will match second route. This is very important to note, same url with different http verbs can map to different routes.
URL Pattern(3rd Column): Its like a limited featured regular expression with syntax of its own. ":id" behaves like (.+) and captures the match in parameter "id", so that you can do something like params[:id] and get the captured string. Braces () represent that this parameter is optional. You can also pass these parameters in helpers to generate the corresponding route. For example if you used message_path(:id => 123) is will generate the output "/messages/123".
Where this routes(4th Column): This column generally tells the controller and the corresponding action which will handle requests matching this route. There can be additional information here like constraints if you defined any.
So if "localhost:3000/magazines" is the page you want, you should check the routes table with url pattern as "/magazines(.:format)" and disect it yourself to find out what you need. I would recommend you read the whole guide from top to bottom if you are just starting rails.
(This might be just an overkill to write all this here, but I faced many problems due to this info not being available in a consolidated manner. Always wanted to write it out and finally did. I wish it was available on http://edgeguides.rubyonrails.org/routing.html in a separate section.)

This is a really nice summary of the routes: Rails Routing from the Outside In.

What about:
<%= link_to "magazines", magazines_path %>
You should be aware of all routes created by the mere scaffold. It's quite easy and explained in Rails guides.
Here are the details: http://guides.rubyonrails.org/routing.html#paths-and-urls

You also might want to check these RailsCasts:
http://railscasts.com/episodes/203-routing-in-rails-3 ,
http://railscasts.com/episodes/231-routing-walkthrough ,
http://railscasts.com/episodes/232-routing-walkthrough-part-2
and these pages:
http://edgeguides.rubyonrails.org/routing.html
http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/
http://markconnell.co.uk/posts/2010/02/rails-3-routing-examples

A few things, in addition to what others have already said:
magazines_path is the most likely name of the link to the index page.
<%= link_to "Magazines", magazines_path %>
so that should do the trick. But if you want to see routes, I'd recommend you just run rake routes, which will list whatever Rails is considering a valid route name. If you want to see how they're used, check out the view pages for your scaffold. app/views/magazines/show.html.erb, for example, might have something like this at the bottom:
<%= link_to 'Edit', edit_magazine_path(#magazine) %> |
<%= link_to 'Back', magazines_path %>
The edit link goes to the edit page (/magazines/[ID]/edit) for the magazine stored in #magazine and the back link goes to the index page (/magazines/). The show page for an individual magazine would be magazine_path(#magazine) and the new path would be new_magazine_path(#magazine).
You should definitely check out the resources others have posted -- Rails routing is flexible but very "magical" -- but in any case, that should help give you some context.
Also, this should be automatically generated, but I think most people are assuming your config/routes.rb contains something like the following:
My::Application.routes.draw do
resources :magazines
resources :users
# or the above combined as resources :magazines, :users
end
This is what tells rails to build out the basic routes for index, new, edit, show, create, update, destroy for a particular resource.

By far, the best explanation of routes I've found is in The Rails 3 Way by Obie Fernandez (founder of Hash Rocket).

Related

Understanding Rails singular and plural paths

I'm very new to using Rails and at the moment am building an Instagram clone as a project to help me understand Rails a bit better. I am following this very helpful tutorial on how to implement the likes/unlikes feature:
https://medium.com/full-taxx/how-to-add-likes-to-posts-in-rails-e81430101bc2
However, I don't fully understand the Rails paths - please could someone explain the difference between:
post_like_path and post_likes_path as mentioned in the tutorial. I cannot see why one is like and one is likes? :(
Really trying to get my head around this so would be so grateful for any insight!
Thanks :)
In Rails as per the REST -
If trying to refer to the single resource then use post_like_path.
If trying to refer to a collection of resources then use post_likes_path
When you want to show or delete a particular resource then you will have to provide an :id for the resource so that the target resource can be found.
[/posts/1/likes/1] - A single record of "like" is being referred here.
While in case of all records plural path is formed to refer to all like records -
[/posts/1/likes] - All records of "like" are being referred here.
post model can have multiple likes. But when we do undo like it will be singular right. so post_like_path will handle the single like and it's going to trigger "delete" action in controller.
post_likes_path will trigger the new action for for creating new like.
Please routes resources concept then you come to know more about it.
post_like_path is used for show page, update and destroy path. The post_likes_path will give you the path for the index and create actions.
This link is the ROR guide and has quite a simple explanation on it:
https://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use
I'd suggest to always run this command from terminal:
rake routes
Or just for LikesController:
rake routes -c likes
This shows all the routes related to likes controllers. That's the output, it tells a lot of things. You can see where the plural and singular is used.
# Prefix Verb URI Pattern Controller#Action
# post_likes GET /posts/:post_id/likes(.:format) likes#index
# POST /posts/:post_id/likes(.:format) likes#create
# new_post_like GET /posts/:post_id/likes/new(.:format) likes#new
# edit_post_like GET /posts/:post_id/likes/:id/edit(.:format) likes#edit
# post_like GET /posts/:post_id/likes/:id(.:format) likes#show
# PATCH /posts/:post_id/likes/:id(.:format) likes#update
# PUT /posts/:post_id/likes/:id(.:format) likes#update
# DELETE /posts/:post_id/likes/:id(.:format) likes#destroy
First column shows you the path, the second the pattern (with required parameters) and the third the controller with action (related to the view).
So, for example, take
# new_post_like GET /posts/:post_id/likes/new(.:format) likes#new
This says that the form for a new like can be placed to a page linked by this URL:
new_post_like_path(post_id: #post) note the parameter required. The page is views/likes/new.html.erb.
the controller is LikesController and the action is def new; end where you need to instantiate the objects to be used in that page: #like = Like.new and #post = Post.find(params[:post_id]).
The form is then submitted by POST action, so the line to check is the following:
# post_likes POST /posts/:post_id/likes(.:format) likes#create
As before, the page where the form is located is views/likes/new.html.erb, the url to submit the form is post_likes_path(post.id). The POST action when submitting the form is processed by the controller LikesController and the action is def create; end
Other example:
# post_like GET /posts/:post_id/likes/:id(.:format) likes#show
It tells that to show the Like object with a certain id, you need to visit this link_to: post_like_path(post.id, like.id), the controller is LikesController, the action is def show; end and the view is in views/likes/show.html.erb.
And so on..

how to give names to rails generated routes?

when using the following on routes.rb: resource :my_model
I get a few automatically generated routes. Some of them have a name (just like when manually defining them with 'as' keyword) but some of them don't.. so how can I give them names?
Or maybe it's a 'hint' given to me by rails that I'm not supposed to use these routes?
What do you refer to when you say "name", the Prefix when you run rake routes? Many of the HTTP requests (i.e. patch, put, delete) are handled by the controllers and are intended to then either redirect to another path or alter the DOM of the current page if you're using javascript, so they wouldn't have a prefix associated with them as those requests don't have an associated view.
When using a singular resource, all CRUD routes will be generated with the exception of an index route. You do not need names for these paths if you are looking to use them as intended. What is your intent for using these routes?
As per the docs:
A singular resourceful route generates these helpers:
new_resourceName_path returns /re/new
edit_geocoder_path returns /geocoder/edit
geocoder_path returns /geocoder
Please post your output via: bundle exec rake routes
You'll notice the show and create controller actions share the same path with the only difference being one expects a POST request and the other a GET request. This is some of the magic provided by Rails allowing you to use similarly named routes that map to different actions.
Try resources instead of resource. If you want a helper for PATCH, PUT or DELETE, just use the helper for the show action (that you'll get from resources) and specify the HTTP method with method:.
The second answer here has a decent explanation about your resource call.
These routes expect a different request method (this implies additional data in request), and do not need separate name, just use generic global_preferences_path and for example - set method for unobtrusive js in link:
<%= link_to 'Set preference', global_preferences_path(some_parameter:'foo'),
confirm:'Are you sure?', method: :put %>
or use html form or so

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

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

What is the difference between a restful route method for getting an index vs. creating a new object?

According to rake routes, there's the same path for getting an index of objects as there is for creating a new object:
cars GET /cars(.:format) {:controller=>"plugs", :what=>"car", :action=>"index"}
POST /cars(.:format) {:controller=>"plugs", :what=>"car", :action=>"create"}
Obviously, the HTTP verb is what distinguishes between them. I want the "create" version of the cars_path method, not the "index" version. My question is what route method do you invoke to choose the one you want? I'm telling cucumber what path to generate with this:
when /the car plug preview page for "(.+)"/
cars_path(:action => :create, :method => :post)
...but it always chooses the "index" action, not "create". I've tried lots of combinations for the hash argument following cars_path and nothing changes it from choosing "index" instead of "create".
I'll get an error like this:
cars_url failed to generate from {:controller=>"plugs", :method=>:post,
:what=>"car", :action=>"create"}, expected: {:controller=>"plugs", :what=>"car",
:action=>"index"}, diff: {:method=>:post, :action=>"index"}
(ActionController::RoutingError)
This seems like a very simple question but I've had no luck googling for it, so could use some advice. Thanks.
Since the URL is the same for both actions, you can use cars_path (without arguments) in both cases. You just simply have to make sure that the form's method-parameter is set to :post. You can not set the method via the URL, you need to set it for the form (and you can't reach the create action by using a link, you need to use a form).
The difference is that one is accessed when a POST is performed, the other is accessed when a GET is performed. Typing a URL into the browser or (typically) clicking a link is the equivalent of a GET action. POST actions are typically performed by form submissions.

Resources