It is hard to overestimate the usefulness of returning to your routes constantly while you are working on an application. An overview of how routing works
can be found in the Rails Guide, but I hope here to offer a quick summation of using the
rake routes tool. Knowing what to look for can ease both the development process AND debugging greatly. That's why the default Rails error page has a routes print out on it!
Routing in a Nutshell
Resources (Rails Default Routes) with resources and resource
Naming models as "resources" in the
routes.rb file, as opposed to creating a series of "custom" routes, essentially allows the user to declare, all at once, a series of default routes for a model that follow a Rails default conception of how CRUD (create, read, update, delete) works in a RESTful manner. This also allows us to use built-in Rails helpers (
form_for(),
modelname_path(), etc.) to go to the most commonly used actions (index, show; new, create; edit, update; and delete).
If the model is inherently singular, either by itself or in terms of how it shall be accessed relative to any resources it belongs to (stay with me here), you can define it as a
resource instead of as
resources. The only difference, in terms of default actions, is that "index" is not created (since it lists multiples of a resource). It is worth noting that the path helpers also work differently, in that all of them other than "new" would normally need an instance of the model passed as a parameter (to know which of the resource's members we would be referring to, usually by its
id) , but since there is only one resource now, this can be assumed.
Members and Collections
If we define a model as a
resource /
resources, and we get the whole suite of default routes and (controller#)actions built, we still may need to add more routes and their requisite actions to our
routes.rb file. We can do this by declaring the routes as either members or collections, in order to plug in to the Rails-y conventions above, according to whether or not the resultant action needs to be aware of a specific "member" of the resource, or refers to the resource in its entirety (a "collection," as with search, etc.).
resources :projects do
member do
get 'status' # get and render a project's status in projects#status
end
collection do
get 'search' # search ALL the projects and render result projects#search
end
end
Note: if you have a similar route on a number of resources (such as "search", for example) you can declare it as a concern and
Nesting
In order to use the Rails-y conventions and their helpers, we should also make sure that we "nest" resources that have a
has_one or
has_many relationship on one end, and
belongs_to on the other. That is, since each member of a collection that "belongs to" a member of another collection, ie each "project" belongs to a "student," whenever we reference a particular project, we are also referencing it in the context of a student. Thus we write the routes:
resources :students do
resources :projects do
resources :dependencies do
resources :links
end
end
end
Note: in order to keep the URLs short and sensible, we can request that the resource paths be "shallow" using
shallow: true with our resource.
Reading rake routes from Left to Right
We can use
rake routes to see all of the information defined above, and reference the correct helper-method-names, controllers and actions that we created in our
routes.rb file (or see that we forgot to create)!
From top to bottom are listed our routes. They are all of your routes. Hurrah! For each route, from left to right, we can see:
Helpers
The prefix for our path- and url-helper-methods comes first. Appending "_path" to the prefix (ie
ga_classes ->
ga_classes_path) will give us a method that returns the internal path (ie the RESTful path from our application's root) for this route.
ga_classes_path() == "/classes"
Appending "_url" to the prefix (ie
ga_classes ->
ga_classes_url) will give us a method that returns the full URL for this route.
ga_classes_url() == "http://localhost:3000/classes"
Importantly! The helper methods need to be passed instances representing all of the "members" (ie singular instances of a model) of the referenced model, and any models it is nested beneath. A simple mnemonic is to look at the URL to the right of the prefix and find all of the "
id" (or "...
_id") references in the path. Eg:
dependency_link_path() gives us the path to a single link (necessitating a link object), which belongs to a single dependency (necessitating another object). This looks like:
# if @dependency.id = 5 and @link.id = 12
dependency_link_path( @dependency, @link ) == "/dependencies/5/links/12"
Verbs & URL Formats
In the center you can find the full URL and HTTP verb that are expected by
routes.rb. Remember that the same URL can route to multiple actions based on the verb that the request was sent with. Also remember that Rails helpers like
link_to(),
button_to(),
form_for(), and
form_tag(), etc. have default verbs associated with them that may need to be overwritten.
Controllers and Actions
Finally, on the right, is the name of the controller followed by the name of the action inside that controller that is called when the verb/path combo we just saw receives an HTTP request. Thus, a GET request to "
/students/2/projects/13/new" runs the action described here:
# controller#action
# projects#new --> file: projects_controller.rb, which contains, at least:
class ProjectsController < ApplicationController
def new
# do this!
end
end
Bringing it All Together
You can use rake routes as a concordance: consult it whenever you only have part of the story! If you have created routes in
routes.rb, and now need to build a controller, then get the action list from
rake routes. If you are trying to link to an action and can't make it work, find the correct path or helper based on that action. Check pluralizations, and make sure that the actions are sensible for what you want to do! If they are incorrect, then you know you need to make changes to your
routes.rb file.
All in all, it's a great resource to look to as you build and debug your app!