3. REST, Resources, and Rails -
3.9 RESTful Route Customisations
The techniques for doing this are useful when, for example, you’ve got more than one way of viewing a resource that might be described as showing. You can’t (or shouldn’t) use the show action itself for more than one such view. Instead, you need to think in terms of different perspectives on a resource, and create URLs for each one.
3.9.1 Etra Member Routes
For example, let’s say we want to make it possible to retract a bid. The basic nested route for bids looks like this:
resources :auctions do
resources :bids
end
We’d like to have a retract action that shows a form (and perhaps does some screening for retractability). The retract isn’t the same as destroy; it’s more like a portal to destroy. It’s similar to edit, which serves as a form portal to update. Following the parallel with edit/update, we want a URL that looks like:
/auctions/3/bids/5/retract
and a helper method called retract_auction_bid_url
. The way you achieve this is by specifying an extra member route for the bids.
resources :auctions do
resources :bids do
member do
get :retract
post :retract
# match :retract, via: [:get, :post]
end
end
end
resources: auctions do
resources :bids do
match :retract, via: [:get, :post], on: :member
end
end
3.9.2 Extra Collection Routes
You can use the same routing technique to add routes that conceptually apply to an entire collection of resources:
resources :auctions do
collection do
match :terminate, via: [:get, :post]
end
end
In its shorter form:
resources :auctions do
match :terminate, via: [:get, :post], on: :collection
end
This example will give you a terminate_auctions_path
method, which will produce a URL mapping to the terminate action of the auctions controller. (A slightly bizarre example, perhaps, but the idea is that it would enable you to end all auctions at once.)
Thus you can fine-tune the routing behavior—even the RESTful routing behavior—of your application, so that you can arrange for special and specialized cases while still thinking in terms of resources.
3.9.3 Custom Action Names
Occasionally, you might want to deviate from the default naming convention for Rails RESTful routes. The :path_names
option allows you to specify alternate name mappings. The example code shown changes the new and edit actions to Spanish-language equivalents.
resources :projects, path_names: {new: 'nuevo', edit: 'cambiar'}
3,9,4 Mapping to a Different Controller
You may use the :controller
option to map a resource to a different controller than the one it would do so by default. This feature is occasionally useful for aliasing resources to a more natural controller name.
resources :photos, controller :"images"
3.9.5 Routes for New Resources
The routing system has a neat syntax for specifying routes that only apply to new resources, ones that haven’tbeen saved yet. You declare extra routes inside of a nested new block, like this:
resources :reports do
new do
post :preview
end
end
The declaration above would result in the following route being defined.
preview_new_report POST /reports/new/preview(.:format) reports#preview
3.9.6 Considerations foro Extra Routes
Referring to extra member and collection actions, David has been quoted as saying, “If you’re writing so many additional methods that the repetition is beginning to bug you, you should revisit your intentions. You’re probably not being as RESTful as you could be.”
The last sentence is key. Adding extra actions corrupts the elegance of your overall RESTful application design, because it leads you away from finding all of the resources lurking in your domain.
Keeping in mind that real applications are more complicated than code examples in a reference book, let’s see what would happen if we had to model retractions strictly using resources. Rather than tacking a retract action onto the BidsController, we might feel compelled to introduce a retraction resource, associated with bids, and write a RetractionController
to handle it.
resources :bids do
resource :retraction
end
`RetractionController` could now be in charge of everything having to do with retraction activities, rather than having that functionality mixed into `BidsController`. And if you think about it, something as weighty as bid retraction would eventually accumulate quite a bit of logic. Some would call breaking it out into its own controller proper separation of concerns or even just good object-orientation.