— Dec 09, 2014
Recently I had a conversation with a coworker about the routes in their app. It turns out that they (an experienced Rails dev) had not seen nested resources on collections rather than members. (If you don’t know what that means, stay tuned.) During our discussion I quickly realized I had some serious gaps in my knowledge of Rails routing as well!.. TO THE INTERNET
One of the simplest routes in a Rails app is a resource. A plural resource generates 8 routes.
You can restrict the generated routes by :only
defining particular actions.
Let’s use that option for the following examples to keep outputs to a minimum.
Depending on how familiar you are with Rails, you may not know what “Prefix” means in the context of routing. These names correspond to Rails generated path helpers. So what if you want to customize the name of these helper methods?
You’ll notice that the prefix is now named after “bar” rather than “foo”. One tricky little detail is that the “index” path is no longer pluralized. Instead it has an “_index” suffix.
What if instead you want to change the URI for a particular resource without affecting the generated path helpers and associated controller? CAN DO!
Now the resource is accessed from “outside” the app by the URI /bar
, but
internally it is still referred to as foo
. Don’t miss that the value of
:path
is used exactly as the resource’s path. It isn’t pluralized for a
plural route.
You can also customize the name the controller name of your resource.
You can see that this defines a route which expects a BarController
to be
defined. It’s also worth nothing that while the controller is (as always)
suffixed with “Controller”, it does not pluralize the given name for
plural resources.
Ruby programmers commonly use Ruby Module
to namespace objects. By default all
resources are defined at the top level. You may still want to take advantage of
code organization by modules for your objects.
If you’re not familiar with the output of the Rails routes
rake task, it may
not be immediately obvious what’s going on here. But the short of it is that
the controller must now we “namespaced” by a Bar
Module. So the relevant
controller would be Bar::FoosController
. Conveniently the URI pattern and
“prefix” (which corresponds to path helpers)
are not affected by this option. Sweet.
Commonly enough it is desirable to “prefix” path helpers, URI, and the controller with an identifier. One example of such might be “nesting” a number of resources in an “admin” namespace.
This is the first “block style” routing directive that you’re seeing. Route
namespaces are specified by calling the namespace
method with a block
defining the namespaced resources. If you only wish to namespace a single
resource, it might be tempting to pass a :namespace
option to your resource.
Be warned! It doesn’t work as you might expect.
It is often desirable to design routes based on the relations that exist
in your model. For example, if you wanted to get all comments made by a user
you might expect the route for this resource to exist at /users/jay/comments
.
As a baseline here is the most basic nested resource. Much like the basic resource, a suite of routes is defined for each resource.
That’s a ton of routes for 3 lines. You will notice that the “nested” resource
establishes routes “beneath” individual “parent” resources. Also as you might
have expected, you get 16 routes. 8 for each resource. In effort to keep noise
to a minimum, you will use the only: :index
option from here on.
You may not have caught the detail that “bars”, while indeed nested in the path
helper and URI, were not namespaced at the controller. Grab the familiar
module
option to nest the controller.
Perhaps the contrived nature of the example doesn’t do the behavior justice, but you may run into a case for this eventually.
Above I mentioned that the nested resource is under an individual “member” (instance) of the parent resource. So what if we wanted to nest under the “collection”?
Now the resource is nested beneath the Foos’ index action as opposed to an
individual Foo record. Similar to the namespace
method, collection
only
works as expected in the block form. Confusingly, you may specify an on:
:collection
option for the nested resource, but the behavior is not as
expected.
Additional resource options, such as :path
and :as
, also work with nested
resources. You should note, however, that Rails still prefixes paths and
helpers with the parent resource.
There’s always more. Routing can be very complicated. There are a lot of options, so covering all their possible combinations would be exhausting. We didn’t even talk about constraints. Take some time to explore the options and be creative when defining routes. You will likely run into cases where you want to model them in a specific way. Chances are you are able to accomplish what you want to. If you have to much difficulty your design may be smelling. Seek some advise from a peer!
Good luck routing! Let me know if the comments if anything is unclear or glaringly missing.