- 3.1 REST in a Rather Small Nutshell
- 3.2 Resources and Representations
- 3.3 REST in Rails
- 3.4 Routing and CRUD
- 3.5 The Standard RESTful Controller Actions
- 3.6 Singular Resource Routes
- 3.7 Nested Resources
- 3.8 RESTful Route Customizations
- 3.9 Controller-Only Resources
- 3.10 Different Representations of Resources
- 3.11 The RESTful Rails Action Set
- 3.12 Conclusion
3.4 Routing and CRUD
The acronym CRUD (Create Read Update Delete) is the classic summary of the spectrum of database operations. It's also a kind of rallying cry for Rails practitioners. Because we address our databases through abstractions, we're prone to forget how simple it all is. This manifests itself mainly in excessively creative names for controller actions. There's a temptation to call your actions add_item and replace_email_address and things like that. But we needn't, and usually shouldn't, do this. True, the controller does not map to the database, the way the model does. But things get simpler when you name your actions after CRUD operations, or as close to the names of those operations as you can get.
The routing system does not force you to implement your app's CRUD functionality in any consistent manner. You can create a route that maps to any action, whatever the action's name. Choosing CRUD names is a matter of discipline. Except... when you use the REST facilities offered by Rails, it happens automatically.
REST in Rails involves standardization of action names. In fact, the heart of the Rails's REST support is a technique for creating bundles of named routes automatically—named routes that are bundled together to point to a specific, predetermined set of actions.
Here's the logic. It's good to give CRUD-based names to your actions. It's convenient and elegant to use named routes. The REST support in Rails gives you named routes that point to CRUD-based action names. Therefore, using the REST facilities gives you a shortcut to some best practices.
Shortcut hardly describes how little work you have to do to get a big payoff. If you put
resources :auctions
into your config/routes.rb file, you will have created four named routes, which, in a manner to be described in this chapter, connect to seven controller actions. And those actions have nice CRUD-like names, as you will see.
3.4.1 REST Resources and Rails
Like most of Rails, support for RESTful applications is "opinionated"; that is, it offers a particular way of designing a REST interface, and the more you play along, the more convenience you reap from it. Most Rails applications are database-backed, and the Rails take on REST tends to associate a resource very closely with an Active Record model, or a model/controller stack.
In fact, you'll hear people using the terminology fairly loosely. For instance, they'll say that they have created a Book resource. What they mean, in most cases, is that they have created a Book model, a book controller with a set of CRUD actions, and some named routes pertaining to that controller (courtesy of resources :books). You can have a Book model and controller, but what you actually present to the world as your resources, in the REST sense, exists at a higher level of abstraction: The Little Prince, borrowing history, and so on.
The best way to get a handle on the REST support in Rails is by going from the known to the unknown. In this case, from the topic of named routes to the more specialized topic of REST.
3.4.2 From Named Routes to REST Support
When we first looked at named routes, we saw examples where we consolidated things into a route name. By creating a route like
match 'auctions/:id' => "auction#show", :as => 'auction'
you gain the ability to use nice helper methods in situations like
link_to item.description, auction_path(item.auction)
The route ensures that a path will be generated that will trigger the show action of the auctions controller. The attraction of this kind of named route is that it's concise and readable.
Now, think in terms of CRUD. The named route auction_path is a nice fit for a show (the R in CRUD) action. What if we wanted similarly nicely named routes for the create, update, and delete actions?
Well, we've used up the route name auction_path on the show action. We could make up names like auction_delete_path and auction_create_path but those are cumbersome. We really want to be able to make a call to auction_path and have it mean different things, depending on which action we want the URL to point to.
We could differentiate between the singular (auction_path) and the plural (auctions_path). A singular URL makes sense, semantically, when you're doing something with a single, existing auction object. If you're doing something with auctions in general, the plural makes more sense.
The kinds of things you do with auctions in general include creating. The create action will normally occur in a form:
form_tag auctions_path
It's plural because we're not saying "perform an action with respect to a particular auction," but rather "with respect to the collection of auctions, perform the action of creation." Yes, we're creating one auction, not many. But at the time we make the call to our named route, auctions_path, we're addressing auctions in general.
Another case where you might want a plural named route is when you want an overview of all of the objects of a particular kind, or at least, some kind of general view, rather than a display of a particular object. This kind of general view is usually handled with an index action. These index actions typically load a lot of data into one or more variables, and the corresponding view displays it as a list or table (possibly more than one).
Here again, we'd like to be able to say:
link_to "Click here to view all auctions", auctions_path
Already, though, the strategy of breaking auction_path out into singular and plural has hit the wall: We've got two places where we want to use the plural named route. One is create; the other is index. But they're both going to look like
/auctions
How is the routing system going to know that when we use auctions_path as a link versus using it in a form that we mean the create action and not index? We need another qualifier, another flag, another variable on which to branch.
Luckily, we've got one.
3.4.3 Reenter the HTTP Verb
Form submissions are POSTs by default. Index actions are GETs. That means that we need to get the routing system to realize that
/auctions submitted in a GET request!
versus
/auctions submitted in a POST request!
are two different things. We also have to get the routing system to generate the same URL—/auctions—but with a different HTTP request method, depending on the circumstances.
This is what the REST facility of Rails routing does for you. It allows you to stipulate that you want /auctions routed differently, depending on the HTTP request method. It lets you define named routes with the same name, but with intelligence about their HTTP verbs. In short, it uses HTTP verbs to provide that extra data slot necessary to achieve everything you want to achieve in a concise way.
The way you do this is by using a special routing method: resources. Here's what it would look like for auctions:
resources :auctions
That's it. Making this one call inside routes.rb is the equivalent of defining four named routes. And if you mix and match those four named routes with a variety of HTTP request methods, you end up with seven useful—very useful—permutations.