Chapter 4. REST, Resources, and Rails

 

Before REST came I (and pretty much everyone else) never really knew where to put stuff.

 
 --Jonas Nicklas on the Ruby on Rails mailing list

With version 1.2, Rails introduced support for designing APIs consistent with the REST style. Representational State Transfer (REST) is a complex topic in information theory, and a full exploration of it is well beyond the scope of this chapter.[1] We‘ll touch on some of the keystone concepts, however. And in any case, the REST facilities in Rails can prove useful to you even if you’re not a REST expert or devotee.

The main reason is that one of the inherent problems that all web developers face is deciding how to name and organize the resources and actions of their application. The most common actions of all database-backed applications happen to fit well into the REST paradigm—we’ll see what that means in a moment.

REST in a Rather Small Nutshell

REST is described by its creator, Roy T. Fielding, as a network “architectural style,” specifically the style manifested in the architecture of the World Wide Web. Indeed, Fielding is not only the creator of REST but also one of the authors of the HTTP protocol itself—REST and the web have a very close relationship.

Fielding defines REST as a series of constraints imposed upon the interaction between system components: Basically, you start with the general proposition of machines that can talk to each other, and you start ruling some practices in and others out by imposing constraints.

The REST constraints include (among others)

  • Use of a client-server architecture

  • Stateless communication

  • Explicit signaling of response cacheability

The World Wide Web allows for REST-compliant communication. It also allows for violations of REST principles; the constraints aren’t always all there unless you put them there. But Fielding is one of the authors of the HTTP protocol; and while he has some criticisms of the protocol from the REST point of view (as well as criticisms of widespread non-REST-compliant practices, such as the use of cookies), the overall fit between REST and the web is not a coincidence.

REST is designed to help you provide services, and to provide them using the native idioms and constructs of HTTP. You’ll find, if you look for it, lots of discussion comparing REST to, for example, SOAP—the thrust of the pro-REST argument being that HTTP already enables you to provide services, so you don’t need a semantic layer on top of it. Just use what HTTP already gives you.

One of the payoffs of REST is that it scales relatively well for big systems, like the web. Another is that it encourages—mandates, even—the use of stable, long-lived identifiers (URIs). Machines talk to each other by sending requests and responses labeled with these identifiers. Those requests and responses also contain representations (manifestations in text, XML, graphic format, and so on) of resources (high-level, conceptual descriptions of content). Ideally at least, when you ask a machine for an XML representation of a resource—say, Romeo and Juliet—you’ll use the same identifier every time and the same request metadata indicating that you want XML, and you’ll get the same response. And if it’s not the same response, there’s a reason—like, the resource you’re retrieving is a changeable one (“The current transcript for Student #3994,” for example).

We’ll look at resources and representations further a little later on. For now, though, let’s bring Rails back into the picture.

REST in Rails

The REST support in Rails consists of helper methods and enhancements to the routing system, designed to impose a particular style and order and logic on your controllers and, consequently, on the way the world sees your application. There’s more to it than just a set of naming conventions (though there’s that too). We’ll get to details shortly. In the large scheme of things, the benefits that accrue to you when you use Rails’ REST support fall into two categories:

  • Convenience and automatic best practices for you

  • A REST interface to your application’s services, for everyone else

You can reap the first benefit even if you’re not concerned with the second. In fact, that’s going to be our focus here: what the REST support in Rails can do for you in the realm of making your code nicer and your life as a Rails developer easier.

This isn’t meant to minimize the importance of REST itself, nor the seriousness of the endeavor of providing REST-based services. Rather, it’s an expedient; we can’t talk about everything, and this section of the book is primarily about routing and how to do it, so we’re going to favor looking at REST in Rails from that perspective.

Moreover, the relationship between Rails and REST, while a fruitful one, is not free of difficulties. Much Rails practice is noncompliant with the precepts of REST from the beginning. REST involves stateless communication; every request has to contain everything necessary for the recipient to generate the correct response. But pretty much every nontrivial Rails program in the world uses server state to track sessions. To the extent that they do, they are not adhering to the REST design. On the client side, cookies—also used by many Rails applications—are singled out by Fielding as a non-REST-compliant practice.

Untangling all the issues and dilemmas is beyond our scope here. Again, the focus will be on showing you how the REST support works, and opening the door to further study and practice—including the study of Fielding’s dissertation and the theoretical tenets of REST. We won’t cover everything here, but what we do cover will be “onward compatible” with the wider topic.

The story of REST and Rails starts with CRUD...

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 is not wired for CRUD. You can create a route that goes 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’ REST support is a technique for creating bundles of named routes automatically—named routes that are hard-programmed 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 this:

map.resources :auctions

into your routes.rb files, you will have created four named routes, which, in a manner to be described in this chapter, actually allow you to connect to seven controller actions. And those actions have nice CRUD-like names, as you will see.

The term “resources” in map.resources deserves some attention.

Resources and Representations

The REST style characterizes communication between system components (where a component is, say, a web browser or a server) as a series of requests to which the responses are representations of resources.

A resource, in this context, is a “conceptual mapping” (Fielding). Resources themselves are not tied to a database, a model, or a controller. Examples of resources include

  • The current time of day

  • A library book’s borrowing history

  • The entire text of Little Dorrit

  • A map of Austin

  • The inventory of a store

A resource may be singular or plural, changeable (like the time of day) or fixed (like the text of Little Dorrit). It’s basically a high-level description of the thing you’re trying to get hold of when you submit a request.

What you actually do get hold of is never the resource itself, but a representation of it. This is where REST unfolds onto the myriad content types and actual deliverables that are the stuff of the web. A resource may, at any given point, be available in any number of representations (including zero). Thus your site might offer a text version of Little Dorrit, but also an audio version. Those two versions would be understood as the same resource, and would be retrieved via the same identifier (URI). The difference in content type—one representation vs. another—would be negotiated separately in the request.

REST Resources and Rails

Like most of what’s in Rails, the Rails support for REST-compliant applications is “opinionated”; that is, it offers a particular way of designing a REST interface, and the more you play in its ballpark, the more convenience you reap from it. Rails applications are database-backed, and the Rails take on REST tends to associate a resource very closely with an ActiveRecord model, or a model/controller stack.

In fact, you’ll hear people using the terminology fairly loosely—for instance, saying that they have created “a Book resource.” What they really 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 map.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: Little Dorrit, 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.

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 this...

map.auction 'auctions/:id',
  :controller => "auction",
  :action     => "show"

you gain the ability to use nice helper methods in situations like this:

<%= link_to h(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.

By associating the auction_path method with the auction/show action, we’ve done ourselves a service in terms of standard database operations. Now, think in terms of CRUD. The named route auction_path is a nice fit for a show (the, um, 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.

So we need a way to differentiate one auction_path call from another. 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 do |f| %>

It’s plural because we’re not saying Perform an action with respect to a particular auction, but rather With respect to the whole world 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. 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 this:

http://localhost:3000/auctions

How is the routing system going to know that when we click on one, we mean the create action, and when we click on the other, we mean index? We need another data-slot, another flag, another variable on which to branch.

Luckily, we’ve got one.

Reenter the HTTP Verb

Form submissions are POSTs. 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 it to generate one and the same URL—/auctions—but with a different HTTP request method, depending on the circumstances.

This is what the REST facility in Rails does for you. It lets you 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 form of routing command: map.resources. Here’s what it would look like for auctions:

map.resources :auctions

That’s it. Making this one call inside routes.rb is the equivalent of defining four named routes (as you’ll see shortly). 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.

The Standard RESTful Controller Actions

Calling map.resources :auctions involves striking a kind of deal with the routing system. The system hands you four named routes. Between them, these four routes point to seven controller actions, depending on HTTP request method. In return, you agree to use very specific names for your controller actions: index, create, show, update, destroy, new, edit.

It’s not a bad bargain, since a lot of work is done for you and the action names you have to use are nicely CRUD-like.

Table 4.1 summarizes what happens. It’s a kind of “multiplication table” showing you what you get when you cross a given RESTful named route with a given HTTP request method. Each box (the nonempty ones, that is) shows you, first, the URL that the route generates and, second, the action that gets called when the route is recognized. (The table uses _url rather than _path, but you get both.)

Table 4.1. RESTful Routes Table Showing Helpers, Paths, and the Resulting Controller Action

Helper Method

GET

POST

PUT

DELETE

client_url(@client)

/clients/1 show

 

/clients/1 update

/clients/1 destroy

clients_url

/clients index

/clients create

  

edit_client_url(@client)

/clients/1/edit edit

   

new_client_url

/clients/new new

   

(The edit and new actions have unique named routes, and their URLs have a special syntax. We’ll come back to these special cases a little later.)

Since named routes are now being crossed with HTTP request methods, you’ll need to know how to specify the request method when you generate a URL, so that your GET’d clients_url and your POST’d clients_url don’t trigger the same controller action. Most of what you have to do in this regard can be summed up in a few rules:

  1. The default request method is GET.

  2. In a form_tag or form_for call, the POST method will be used automatically.

  3. When you need to (which is going to be mostly with PUT and DELETE operations), you can specify a request method along with the URL generated by the named route.

An example of needing to specify a DELETE operation is a situation when you want to trigger a destroy action with a link:

<%= link_to "Delete this auction",:url => auction(@auction),
        :method => :delete %>

Depending on the helper method you’re using (as in the case of form_for), you might have to put the method inside a nested hash:

<% form_for "auction", :url => auction(@auction),
       :html => { :method => :put } do |f| %>

That last example, which combined the singular named route with the PUT method, will result in a call to the update action (as per row 2, column 4 of the table).

The PUT and DELETE Cheat

Web browsers generally don’t handle request methods other than GET and POST. Therefore, in order to send them PUT and DELETE requests, it’s necessary for Rails to do a little sleight of hand. It’s not anything you need to worry about, other than to be aware of what’s going on.

A PUT or DELETE request, in the context of REST in Rails, is actually a POST request with a hidden field called _method set to either “put” or “delete”. The Rails application processing the request will pick up on this, and route the request appropriately to the update or destroy action.

You might say, then, that the REST support in Rails is ahead of its time. REST components using HTTP should understand all of the request methods. They don’t—so Rails forces the issue. As a developer trying to get the hang of how the named routes map to action names, you don’t have to worry about this little cheat. And hopefully some day it won’t be necessary any more.

Singular and Plural RESTful Routes

Some of the RESTful routes are singular; some are plural. The logic is as follows.

  1. The routes for show, new, edit, and destroy are singular, because they’re working on a particular resource.

  2. The rest of the routes are plural. They deal with collections of related resources.

The singular RESTful routes require an argument, because they need to know the id of the particular member of the collection that you’re operating on. You can use either a straightforward argument-list syntax:

item_url(@item)  # show, update, or destroy, depending on HTTP verb

or you can do it hash style:

item_url(:id => @item)

You don’t have to call the id method on @item (though you can), as Rails will figure out that that’s what you want.

The Special Pairs: new/create and edit/update

As Table 4.1 shows, new and edit obey somewhat special RESTful naming conventions. The reason for this actually has to do with create and update, and how new and edit relate to them.

Typically, create and update operations involve submitting a form. That means that they really involve two actions—two requests—each:

  1. The action that results in the display of the form

  2. The action that processes the form input when the form is submitted

The way this plays out with RESTful routing is that the create action is closely associated with a preliminary new action, and update is associated with edit. These two actions, new and edit, are really assistant actions: All they’re supposed to do is show the user a form, as part of the process of creating or updating a resource.

Fitting these special two-part scenarios into the landscape of resources is a little tricky. A form for editing a resource is not, itself, really a resource. It’s more like a pre-resource. A form for creating a new resource is sort of a resource, if you assume that “being new”—that is, nonexistent—is something that a resource can do, and still be a resource...

Yes, it gets a bit philosophical. But here’s the bottom line, as implemented in RESTful Rails.

The new action is understood to be giving you a new, single (as opposed to plural) resource. However, since the logical verb for this transaction is GET, and GETting a single resource is already spoken for by the show action, new needs a named route of its own.

That’s why you have to use

<%= link_to "Create a new item", new_item_path %>

to get a link to the items/new action.

The edit action is understood not to be giving you a full-fledged resource, exactly, but rather a kind of edit “flavor” of the show resource. So it uses the same URL as show, but with a kind of modifier, in the form of /edit, hanging off the end, which is consistent with the URL form for new:

/items/5/edit

It’s worth mentioning that prior to Rails 2.0, the edit action was set off by semicolons like this: /items/5;edit, a choice that may have had more to do with the limitations of the routing system than any other loftier motives. However, the semicolon scheme caused more problems than it solved,[2] and was scrapped in Edge Rails right after the release of Rails 1.2.3.

The corresponding named route is edit_item_url(@item). As with new, the named route for edit involves an extra bit of name information, to differentiate it from the implied show of the existing RESTful route for GETting a single resource.

Singular Resource Routes

In addition to map.resources, there’s also a singular (or “singleton”) form of resource routing: map.resource. It’s used to represent a resource that only exists once in its given context.

A singleton resource route at the top level of your routes can be appropriate when there’s only one resource of its type for the whole application, or perhaps per user session.

For instance, an address book application might give each logged-in user an address book, so you could write:

map.resource :address_book

You would get a subset of the full complement of resource routes, namely the singular ones: GET/PUT address_book_url, GET edit_address_book_url, and PUT update_address_book_url.

Note that the method name resource, the argument to that method, and all the named routes are in the singular. It’s assumed that you’re in a context where it’s meaningful to speak of “the address book”—the one and only—because there’s a user to which the address book is scoped. The scoping itself is not automatic; you have to authenticate the user and retrieve the address book from (and/or save it to) the database explicitly. There’s no real “magic” or mind-reading here; it’s just an additional routing technique at your disposal if you need it.

Nested Resources

Let’s say you want to perform operations on bids: create, edit, and so forth. You know that every bid is associated with a particular auction. That means that whenever you do anything to a bid, you’re really doing something to an auction/bid pair—or, to look at it another way, an auction/bid nest. Bids are at the bottom of a “drill-down” that always passes through an auction.

What you’re aiming for here is a URL that looks like this:

/auctions/3/bids/5

What it does depends on the HTTP verb it comes with, of course. But the semantics of the URL itself are: the resource that can be identified as bid 5, belonging to auction 3.

Why not just go for bids/5 and skip the auction? For a couple of reasons. First, the URL is more informative—longer, it’s true, but longer in the service of telling you something about the resource. Second, thanks to the way RESTful routes are engineered in Rails, this kind of URL gives you immediate access to the auction id, via params[:auction_id].

To created nested resource routes, put this in routes.rb:

map.resources :auctions do |auction|
  auction.resources :bids
end

Note that in the inner call to resources, the receiver of the call is auction, not map. That’s an easy thing to forget.

What that tells the mapper is that you want RESTful routes for auction resources; that is, you want auctions_url, edit_auction_url, and all the rest of it. You also want RESTful routes for bids: auction_bids_url, new_auction_bid_url, and so forth.

However, the nested resource command also involves you in making a promise: You’re promising that whenever you use the bid named routes, you will provide a auction resource in which they can be nested. In your application code, that translates into an argument to the named route method:

<%= link_to "See all bids", auction_bids_path(@auction) %>

When you make that call, you enable the routing system to add the /auctions/3 part before the /bids part. And, on the receiving end—in this case, in the action bids/index, which is where that URL points, you’ll find the id of @auction in params[:auction_id]. (It’s a plural RESTful route, using GET. See Table 4.1 again if you forgot.)

You can nest to any depth. Each level of nesting adds one to the number of arguments you have to supply to the nested routes. This means that for the singular routes (show, edit, destroy), you need at least two arguments, as in Listing 4.1.

Example 4.1. Passing Two Parameters to Identify a Nested Resource Using link_to

<%= link_to "Delete this bid",
    auction_bid_path(@auction, @bid), :method => :delete %>

This will enable the routing system to get the information it needs (essentially @auction.id and @bid.id) in order to generate the route.

If you prefer, you can also make the same call using hash-style method arguments, but most people don’t because it’s longer code:

auction_bid_path(:auction => @auction, :bid => @bid)

Setting :path_prefix Explicitly

You can also achieve the nested route effect by specifying the :path_prefix option to your resource mapping call explicitly. Here’s how you’d do this for the auctions/bids nest:

map.resources :auctions
map.resources :bids, :path_prefix => "auctions/:auction_id"

What you’re saying here is that you want all of the bids URLs to include the static string “auctions” and a value for auction_id—in other words, to contain the contextual information necessary to associate the bid collection or member with a particular auction.

The main difference between this technique and regular nesting of resources has to do with the naming of the helper methods that are generated. Nested resources automatically get a name prefix corresponding to their parent resource. (See auction_bid_path in Listing 4.1.)

You’re likely to see the nesting technique more often than the explicit setting of :path_prefix, because it’s usually easier just to let the routing system figure it out from the way you’ve nested your resources. Plus, as we’ll see in a moment, it’s easy to get rid of the extra prefixes if you want to do so.

Setting :name_prefix Explicitly

Sometimes you might want to nest a particular resource inside more than one other resource. Or you might want to access a resource through a nested route sometimes, and directly other times. You might even want your named route helpers to point to different resources depending on the context in which they are executed.[3] The :name_prefix makes it all possible, since it lets you control the way that named route helper methods are generated.

Let’s say you want to get at bids through auctions, as in the preceding examples, but also just by themselves. In other words, you want to be able to recognize and generate both:

/auctions/2/bids/5 and /bids/5

The first thought might be bid_path(@auction, @bid) for the first helper, and bid_path(@bid) for the second. It seems logical to assume that if you want a route to bid that doesn’t pass through the auction nest, you’d just leave out the auction parameter.

Given the automatic name-prefixing behavior of the routing system, you’d have to override the name_prefix of bids to make it all work as desired, as in Listing 4.2.

Example 4.2. Overriding name_prefix in a Nested Route

map.resources :auctions do |auction|
  auction.resources :bids, :name_prefix => nil
end

I will warn you, as someone who has used this technique extensively in real applications, that when you eliminate name prefixing, debugging route problems gets an order of magnitude harder. As they say, your mileage may vary.

As an example, what if we wanted a different way to access bids, via the person who made them, instead of in the context of auctions? See Listing 4.3.

Example 4.3. Overriding name_prefix in a Nested Route

map.resources :auctions do |auction|
  auction.resources :bids, :name_prefix => nil
end

map.resource :people do |people|
  people.resources :bids, :name_prefix => nil # are you sure?
end

Amazingly, the code in Listing 4.3 should[4] work just fine, and generate the following route helpers:

bid_path(@auction, @bid) # /auctions/1/bids/1
bid_path(@person, @bid)  # /people/1/bids/1

The thing is that your controller and view code might start getting a wee bit complex if you go down this route (pardon the pun).

First of all, your controller code would have to check for the presence of params[:auction_id] versus params[:person_id] and load the context accordingly. The view templates would probably need to do similar checking, in order to display correctly. At worst your code would end up with tons of if/else statements cluttering things up!

Whenever you’re programming dual functionality like that, you’re probably doing something wrong. Luckily, we can also specify which controller we would like to be involved in each of our routes explicitly.

Specifying RESTful Controllers Explicitly

Something we haven’t yet discussed is how RESTful routes are mapped to a given controller. It was just presented as something that happens automatically, which in fact it does, based on the name of the resource.

Going back to our recurring example, given the following nested route:

map.resources :auctions do |auction|
  auction.resources :bids
end

...there are two controllers that come into play, the AuctionsController and the BidsController.

You could explicitly specify which controller to use with the :controller_name option of the resources method. Having the option means you can name the (user-facing) resource whatever you want, and keep the name of your controller aligned with different naming standards, for example:

map.resources :my_auctions, :controller => :auctions do |auction|
  auction.resources :my_bids, :controller => :bids
end

All Together Now

Now that we know about the :name_prefix, :path_prefix, and :controller options, we can bring it all together to show why having such fine-grained control over RESTful routes is useful.

For example, we can improve what we were trying to do in Listing 4.3, by using the :controller option. See Listing 4.4.

Example 4.4. Multiple Nested Bids Resources, with Explicit Controller

map.resources :auctions do |auction|
  auction.resources :bids, :name_prefix => nil,
                           :controller => :auction_bids
end

map.resource :people do |people|
  people.resources :bids, :name_prefix => nil,
                          :controller => :person_bids
end

Realistically, the AuctionBidsController and PersonBidsController would extend the same parent class BidsController, as in Listing 4.5, and leverage before filters to load things correctly.

Example 4.5. Subclassing Controllers for Use with Nested Routes

class BidsController < ApplicationController
  before_filter :load_parent
  before_filter :load_bid

  protected

    def load_parent
      # overriden in subclasses
    end

    def load_bid
      @bids = @parent.bids
    end
end

class AuctionBidsController < BidsController

  protected

    def load_parent
      @parent = @auction = Auction.find(params[:auction_id])
    end

end

class PersonBidsController < BidsController

 protected

    def load_parent
      @parent = @person = Person.find(params[:person_id])
    end

end

Note that that although it is customary to provide name-style options as symbols, the :controller option does understand strings, as you would need to use if you were specifying a namespaced controller, like this example, which sets up an administrative route for auctions:

map.resources :auctions,
          :controller => 'admin/auctions', # Admin::AuctionsController
          :name_prefix => 'admin_',
          :path_prefix => 'admin'

Considerations

Is nesting worth it? For single routes, a nested route usually doesn’t tell you anything you wouldn’t be able to figure out anyway. After all, a bid belongs to an auction. That means you can access bid.auction_id just as easily as you can params[:auction_id], assuming you have a bid object already.

Furthermore, the bid object doesn’t depend on the nesting. You’ll get params[:id] set to 5, and you can dig that record out of the database directly. You don’t need to know what auction it belongs to.

Bid.find(params[:id])

A common rationale for judicious use of nested resources, and the one most often issued by David, is the ease with which you can enforce permissions and context-based constraints. Typically, a nested resource should only be accessible in the context of its parent resource, and it’s really easy to enforce that in your code based on the way that you load the nested resource using the parent’s ActiveRecord association (see Listing 4.6).

Example 4.6. Loading a Nested Resource Using the Parent’s has_many Association

@auction = Auction.find(params[:auction_id])
@bid = @auction.bids.find(params[:id]) # prevents auction/bid mismatch

If you want to add a bid to an auction, your nested resource URL would be:

http://localhost:3000/auctions/5/bids/new

The auction is identified in the URL rather than having to clutter your new bid form data with hidden fields, name your action add_bid and stash the user in :id, or any other non-RESTful practice.

Deep Nesting?

Jamis Buck is a very influential figure in the Rails community, almost as much as David himself. In February 2007, via his blog,[5] he basically told us that deep nesting was a bad thing, and proposed the following rule of thumb: Resources should never be nested more than one level deep.

That advice is based on experience and concerns about practicality. The helper methods for routes nested more than two levels deep become long and unwieldy. It’s easy to make mistakes with them and hard to figure out what’s wrong when they don’t work as expected.

Assume that in our application example, bids have multiple comments. We could nest comments under bids in the routing like this:

map.resources :auctions do |auctions|
  auctions.resources :bids do |bids|
    bids.resources :comments
  end
end

However, we’d have to start resorting to all sort of options to avoid having a auction_bid_comments_path helper to deal with. (Actually, that’s not too bad, but I’ve seen and written much worse.)

Instead, Jamis would have us do the following:

map.resources :auctions do |auctions|
  auctions.resources :bids
end

map.resources :bids do |bids|
  bids.resources :comments
end

map.resources :comments

Notice that each resource (except auction) is defined twice, once in the top-level namespace, and one in its context. The rationale? When it comes to parent-child scope, you really only need two levels to work with. The resulting URLs are shorter, and the helper methods are easier to work with.

auctions_path          # /auctions
auctions_path(1)       # /auctions/1
auction_bids_path(1)   # /auctions/1/bids
bid_path(2)            # /bids/2
bid_comments_path(3)   # /bids/3/comments
comment_path(4)        # /comments/4

I personally don’t follow Jamis’ guideline all the time in my projects, but I have noticed something about limiting the depth of your nested resources—it makes it all the more palatable to keep those handy-dandy name prefixes in place, instead of lopping them off with :name_prefix => nil. And trust me, those name prefixes do help with maintainability of your codebase in the long run.

RESTful Route Customizations

Rails’ RESTful routes give you a pretty nice package of named routes, hard-wired to call certain very useful and common controller actions—the CRUD superset you’ve already learned about. Sometimes, however, you want to customize things a little more, while still taking advantage of the RESTful route naming conventions and the “multiplication table” approach to mixing named routes and HTTP request methods.

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.

Extra 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:

map.resources :auctions do |a|
  a.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 this:

/auctions/3/bids/5/retract

and a helper method called retract_bid_url. The way you achieve this is by specifying an extra :member route for the bids, as in Listing 4.7.:

Example 4.7. Adding an Extra Member Route

map.resources :auctions do |a|
  a.resources :bids, :member => { :retract => :get }
end

Then you can add a retraction link to your view with the following code:

<%= link_to "Retract", retract_bid_path(auction, bid) %>

and the URL generated will include the /retract modifier. That said, you should probably let that link pull up a retraction form (and not trigger the retraction process itself!). The reason I say that is because, according to the tenets of HTTP, GET requests should not modify the state of the server—that’s what POST requests are for.

Is it enough to add a :method option to link_to?

<%= link_to "Retract", retract_bid_path(auction,bid), :method=>:post
%>

Not quite. Remember that in Listing 4.7 we defined the retract route as a :get, so a POST will not be recognized by the routing system. The solution is to define the extra member route as mapping to any HTTP verb, like this:

map.resources :auctions do |a|
  a.resources :bids, :member => { :retract => :any }
end

Extra Collection Routes

You can also use this routing technique to add routes that conceptually apply to an entire collection of resources:

map.resources :auctions, :collection => { :terminate => :any }

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.

Considerations

During a discussion of RESTful routing on the Rails mailing list,[6] Josh Susser proposed flipping the syntax for custom actions so that they would be keyed on the HTTP verb and accept an array of action names, like this:

map.resources :comments,
              :member => { :get => :reply,
                           :post => [:reply, :spawn, :split] }

Among other reasons, Josh cited how it would simplify the practice of writing so-called post-backs, dual-purpose controller actions that handle both GET and POST requests in one method.

The response from David was not a positive one. After expressing his position against post-backs, he said: “I’m starting to think that explicitly ignoring [post-backs] with map.resources is a feature.”

Later in the thread, continuing to defend the API, David added, “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.” (italics mine)

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.

map.resources :bids do |bids|
  bids.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.

I can’t help but continue the story of that fateful mailing list thread, because it led to a priceless moment in Rails community history, which added to our reputation as an opinionated bunch!

Josh replied, “Just checking... You think that code that is less readable and more tedious to write is an advantage? I guess from the perspective of macro-optimization versus micro-optimization I wouldn’t argue with you, but I think that’s a hell of a way to encourage people to do the right thing. If going RESTful is all that, you shouldn’t need to rely on syntactic vinegar to force people to do it the right way. Now, if you were to say that organizing the actions hash as {:action => method, ...} is desirable because it guarantees an action only is used once, then sure, that makes sense.” (italics mine)

David did indeed see the less readable and more tedious code as an advantage in this particular case, and he latched on to the syntactic vinegar term with enthusiasm. About two months later, he wrote one of his most famous blog entries about the concept (excerpted here):

Syntactic sugar has long hogged the spotlight in discussions of framework and language design. It holds the power to turn idioms into conventions, to promote a common style through beauty, brevity, and ease of use. We all love syntactic sugar—and we want it all: the terrifying lows, the dizzying highs, the creamy middles. It’s what makes languages such as Ruby taste ever so sweet in comparison to the plain alternatives.

But sugar is not all we need. Good design lies not only in emphasizing the proper, but de-emphasizing the improper too. Just as we can sprinkle syntactic sugar across a certain style or approach to promote its use, we can add syntactic vinegar to discourage it as well. It’s more sparingly used, but that makes it no less important. ... http://www.loudthinking.com/arc/2006_10.html

Controller-Only Resources

The word “resource” has a substantive, noun-like flavor that puts one in mind of database tables and records. However, a REST resource does not have to map directly to an ActiveRecord model. Resources are high-level abstractions of what’s available through your web application. Database operations just happen to be one of the ways that you store and retrieve the data you need to generate representations of resources.

A REST resource doesn’t necessarily have to map directly to a controller, either, at least not in theory. As we learned in relation to the :path_prefix and :controller options of map.resources, you could, if you wanted to, provide REST services whose public identifiers (URIs) did not match the names of your controllers at all.

What all of this adds up to is that you might have occasion to create a set of resource routes, and a matching controller, that don’t correspond to any model in your application at all. There’s nothing wrong with a full resource/controller/model stack where everything matches by name. But you may find cases where the resources you’re representing can be encapsulated in a controller but not a model.

An example in the auction application is the sessions controller. Assume a routes.rb file containing this line:

map.resource :session

It maps the URL /session to a SessionController as a singleton resource, yet there’s no Session model. (By the way, it’s properly defined as a singleton resource because from the user’s perspective there is only one session.)

Why go the RESTful style for authentication? If you think about it, user sessions can be created and destroyed. The creation of a session takes place when a user logs in; when the user logs out, the session is destroyed. The RESTful Rails practice of pairing a new action and view with a create action can be followed! The user login form can be the session-creating form, housed in the template file such as session/new.rhtml (see Listing 4.8).

Example 4.8. A RESTful Login Screen

<h1>Log in</h1>

<% form_for :user, :url => session_path do |f| %>
  <p>Login: <%= f.text_field :login %></p>
  <p>Password: <%= f.password_field :password %></p>
  <%= submit_tag "Log in" %>
<% end %>

When the form is submitted, the input is handled by the create method of the sessions controller in Listing 4.9.

Example 4.9. A RESTful Login Action

def create
  @user = User.find_by_login(params[:user][:login])
  if @user and @user.authorize(params[:user][:password])
    flash[:notice] = "Welcome, #{@user.first_name}!"
    redirect_to home_url
  else
    flash[:notice] = "Login invalid."
    redirect_to :action => "new"
  end
end

Nothing is written to any database table in this action, but it’s worthy of the name create by virtue of the fact that it creates a session. Furthermore, if you did at some point decide that sessions should be stored in the database, you’d already have a nicely abstracted handling layer.

It pays to remain open-minded, then, about the possibility that CRUD as an action-naming philosophy and CRUD as actual database operations may sometimes occur independently of each other—and the possibility that the resource-handling facilities in Rails might usefully be associated with a controller that has no corresponding model. Creating a session isn’t the most shining example of REST-compliant practices, since REST mandates stateless transfers of representations of resources... But it’s a good illustration of why, and how, you might make design decisions involving routes and resources that don’t implicate the whole application stack.

Sticking to CRUD-like action names is, in general, a good idea. As long as you’re doing lots of creating and destroying anyway, it’s easier to think of a user logging in as the creation of a session, than to come up with a whole new semantic category for it. Rather than the new concept of “user logs in,” just think of it as a new occurrence of the old concept, “session gets created.”

Different Representations of Resources

One of the precepts of REST is that the components in a REST-based system exchange representations of resources. The distinction between resources and their representations is vital.

As a client or consumer of REST services, you don’t actually retrieve a resource from a server; you retrieve representations of that resource. You also provide representations: A form submission, for example, sends the server a representation of a resource, together with a request—for example, PUT—that this representation be used as the basis for updating the resource. Representations are the exchange currency of resource management.

The respond_to Method

The ability to return different representations in RESTful Rails practice is based on the respond_to method in the controller, which, as you’ve seen, allows you to return different responses depending on what the client wants. Moreover, when you create resource routes you automatically get URL recognition for URLs ending with a dot and a :format parameter.

For example, assume that you have map.resources :auctions in your routes file and some respond_to logic in the AuctionsController like this:

def index
  @auctions = Auction.find(:all)
  respond_to do |format|
    format.html
    format.xml { render :xml => @auctions.to_xml }
  end
end

Now, you’ll now be able to connect to this URL: http://localhost:3000/auctions.xml

The resource routing will ensure that the index action gets executed. It will also recognize the .xml at the end of the route and interact with respond_to accordingly, returning the XML representation.

Of course, all of this is URL recognition. What if you want to generate a URL ending in .xml?

Formatted Named Routes

The resource routing facility also gives you .:format-flavored versions of its named routes. Let’s say you want a link to the XML representation of a resource. You can achieve this by using the formatted_ version of the RESTful named route:

<%= link_to "XML version of this auction",
       formatted_auction_path(@auction, "xml") %>

This will generate the following HTML:

<a href="/auctions/1.xml">XML version of this auction</a>

When followed, this link will trigger the XML clause of the respond_to block in the show action of the auctions controller. The resulting XML may not look like much in a browser, but the named route is there if you want it.

The circuit is now complete: You can generate URLs that point to a specific response type, and you can honor requests for different types by using respond_to. And if the request wants to specify its desired response by using the Accept header instead, it can do that too. All told, the routing system and the resource routing facilities built on top of it give you quite a set of powerful, concise tools for differentiating among requests and, therefore, being able to serve up different representations.

The RESTful Rails Action Set

Rails’ REST facilities, ultimately, are about named routes and the controller actions to which they point. The more you use RESTful Rails, the more you get to know each of the seven RESTful actions. How they work across different controllers (and different applications) is of course somewhat different. Still, perhaps because there’s a finite number of them and their roles are fairly well-delineated, each of the seven tends to have fairly consistent properties and a characteristic “feel” to it.

We’re going to take a look at each of the seven actions, with examples and comments. You’ve encountered all of them already, particularly in Chapter 2, “Working with Controllers,” but here you’ll get some “backstory” and start to get a sense of the characteristic usage of them and issues and choices associated with them.

Index

Typically, an index action provides a representation of a plural (or collection) resource. The index representation will usually be generic and public. The index action shows the world the most neutral representation possible.

A typical index action looks like this:

class AuctionsController < ApplicationController

  def index
    @auctions = Auction.find(:all)
  end

  ...


end

The view template will display public information about each auction, with links to specific information about each one, and to public profiles of the sellers.

Although index is best thought of as public, you’ll certainly encounter situations where you want to display a representation of a collection, but in a restricted way. For example, users should be able to see a listing of all their bids. But you don’t want everyone seeing everyone else’s lists.

The best strategy here is to slam down the gate at the latest possible point. You can use RESTful routing to help you.

Let’s say we want each user to see his or her bid history. We could decide that the index action of the bids controller will be filtered through the current user (@user). The problem with that, though, is that it rules out a more public use of that action. What if we want a public collection view that shows all the current highest bids? Maybe even a redirect to the auction index view. The point is to keep things as public as possible for as long as possible.

There are a couple of ways to do this. One way is to test for the presence of a logged-in user, and decide what to show based on that. But that’s not going to work here. For one thing, the logged-in user might want to see the more public view. For another, the more dependence on server-side state we can eliminate or consolidate, the better.

So let’s look at the two bid lists, not as a public and private version of the same resource, but as different resources. We can encapsulate the difference directly in the routing:

map.resources :auctions do |auctions|
  auctions.resources :bids, :collection  => { :manage => :get }
end

We can now organize the bids controller in such a way that access is nicely layered, using filters only where necessary and eliminating conditional branching in the actions themselves:

class BidsController < ApplicationController

  before_filter :load_auction
  before_filter :check_authorization, :only => :manage

  def index
    @bids = Bid.find(:all)
  end

  def manage
    @bids = @auction.bids
  end

  ...

  protected

    def load_auction
      @auction = Auction.find(params[:auction_id])
    end

    def check_authorization
      @auction.authorized?(current_user)
    end
end

There’s now a clear distinction between /bids and /bids/manage and the role that they play in your application.

On the named route side, we’ve now got bids_url and manage_bids_url. We’ve thus preserved the public, stateless face of the /bids resource, and quarantined as much stateful behavior as possible into a discrete subresource, /bids/manage. Don’t fret if this mentality doesn’t come to you naturally—it’s part of the REST learning curve.

If I were dogmatic about REST, I might find it ironic, even distasteful, to discuss REST-related techniques in the context of quarantining stateful behavior, since RESTful requests are not supposed to depend on session state to begin with. It goes to show, however, that the REST facilities available to you in Rails can, so to speak, gracefully degrade, in situations where you need to depart from a strictly REST-compliant interface.

Show

The RESTful show action is the singular flavor of a resource. That generally translates to a representation of information about one object, one member of a collection. Like index, show is triggered by a GET request.

A typical—one might say classic—show action looks like this:

class AuctionController < ApplicationController
  def show
    @auction = Auction.find(params[:id])
  end
end

Of course, the show action might depend on before_filters as a way of not having to load the shown resource explicitly in the show action. You might want to differentiate between publicly available profiles, perhaps based on a different route, and the profile of the current user, which might include modification rights and perhaps different information.

As with index actions, it’s good to make your show actions as public as possible, and offload the administrative and privileged views into either a different controller or a different action.

Destroy

Destroy actions are good candidates for administrative safeguarding, though of course it depends on what you’re destroying. You might want something like the code in Listing 4.10 to protect the destroy action.

Example 4.10. Safeguarding the destroy Action

class UsersController < ApplicationController
  before_filter :admin_required, :only => :destroy

A typical destroy action might look like this, assuming that @user was already loaded by a before filter:

def destroy
  @user.destroy
  flash[:notice] = "User deleted!"
  redirect_to users_url
end

This approach might be reflected in a simple administrative interface like this:

<h1>Users</h1>
<% @users.each do |user| %>
  <p><%= link_to h(user.whole_name), user_path(user) %>
  <%= link_to("delete", user_path(user), :method => :delete) if
   current_user.admin? %></p>
<% end %>

That delete link appears, depending on the whether current user is an admin.

In fact, the most striking thing about the RESTful destroy sequence in Rails is what happens in the view that contains the links to the action. Here’s the HTML from one time through the loop. Be warned: It’s longer than you might think.

<p><a href="http://localhost:3000/users/2">Emma Knight Peel</a>
 <a href="http://localhost:3000/users/2" onclick="var f =
document.createElement('form'), f.style.display = 'none';
this.parentNode.appendChild(f); f.method = 'POST'; f.action =
this.href;var m = document.createElement('input'),
m.setAttribute('type', 'hidden'), m.setAttribute('name', '_method'),
m.setAttribute('value', 'delete'), f.appendChild(m);f.submit();return
false;">Delete</a>)</p>

Why so much code—JavaScript, yet!—for two little links? The first link is handled quickly; it’s just a link to the show view for the user. The reason the second link is so long is this. DELETE submissions are dangerous. Rails wants to make them as hard as possible to spoof or trigger accidentally—for instance, by a crawler or bot sending requests to your site. So when you specify the DELETE method, a whole JavaScript script is generated inside your HTML document. This script actually wraps your link in a form. Since bots don’t submit forms, this gives a layer of protection to your code.

New and Create

As you’ve already seen, the new and create actions go together in RESTful Rails. A “new resource” is really just a virtual entity waiting to be created. Accordingly, the new action customarily presents a form, and create creates a new record, based on the form input.

Let’s say you want a user to be able to create (that is, start) an auction. You’re going to need

  1. A new action, which will display a form

  2. A create action, which will create a new Auction object based on the form input, and proceed to a view (show action) of that auction.

The new action doesn’t have to do much. In fact, it has to do nothing. Like any empty action, it can even be left out. Rails will still figure out that you want to render the new.erb.html view.

The new.erb.html template might look like Listing 4.11. Notice that some of the input fields are namespaced to :item (courtesy of the fields_for helper method) and some are namespaced to :auction (courtesy of form_for). That’s because an item and an auction really get created in tandem.

Example 4.11. A New Auction Form

<h1>Create a new auction</h1>
<%= error_messages_for :auction %>

<% form_for :auction, :url => auctions_path do |f| %>
  <% fields_for :item do |i| %>
    <p>Item description: <%= i.text_field "description" %></p>
    <p>Item maker: <%= i.text_field "maker" %></p>
    <p>Item medium: <%= i.text_field "medium" %></p>
    <p>Item year: <%= i.text_field "year" %></p>
  <% end %>
  <p>Reserve: <%= f.text_field "reserve" %></p>
  <p>Bid increment: <%= f.text_field "incr" %></p>
  <p>Starting bid: <%= f.text_field "starting_bid" %></p>
  <p>End time: <%= f.datetime_select "end_time" %>
  <%= submit_tag "Create" %>
<% end %>

The form action here is expressed by the named route auctions, coupled with the fact that this is a form and will therefore automatically generate a POST request.

Once the information is filled out, it’s time for the main event: the create action. Unlike new, this action has something to do.

def create
  @auction = current_user.auctions.build(params[:auction])
  @item = @auction.build_item(params[:item])

  if @auction.save
    flash[:notice] = "Auction started!"
    redirect_to auction_url(@auction)
  else
    render :action => "new"
  end
end

Having used both an "auction" namespace and an "item" namespace for our input fields, we can piggyback on both, via the params hash, to instantiate a new Auction object from the current user’s auctions association and hang an Item object off it with build_item. This is a convenient way to operate on two associated objects at once. If @auction.save fails for any reason, the associated item will not be created, so we don’t have to worry about cleaning up after a failure.

When the save succeeds, both auction and item will be created.

Edit and Update

Like new and create, the edit and update actions go together: edit provides a form, and update processes the form input.

The form for editing a record is very similar to the form for creating one. (In fact, you can put much of it in a partial template and use it for both; that’s left as an exercise for the reader.) Here’s what edit.html.erb might look like for editing an item:

<h1>Edit Item</h1>

<% form_for :item, :url => item_path(@item),
            :html => { :method => :put } do |item| %>

  <p>Description: <%= item.text_field "description" %></p>
  <p>Maker: <%= item.text_field "maker" %></p>
  <p>Medium: <%= item.text_field "medium" %></p>
  <p>Year: <%= item.text_field "year" %></p>
  <p><%= submit_tag "Save Changes" %></p>

<% end %>

The main difference between this form and the form for specifying a new item (Listing 4.11) is which named RESTful route you use, and the fact that for the update action, you have to specify the PUT request method. That will have the effect of steering the dispatcher to the update method.

Conclusion

In this chapter, we tackled the tough subject of using REST principles to guide the design of our Rails applications, mainly as they apply to the routing system and controller actions. We learned how the foundation of RESTful Rails is the map.resources method in your routes file, and how to use the numerous options available to make sure that you can structure your application exactly how it needs to be structured. We also learned how in some cases, David and Rails core team apply syntactic vinegar to keep us from straying down the wrong path.

One of the challenges of writing and maintaining serious Rails applications is the routing system, namely understanding it and being able to figure out mistakes that you will undoubtedly make during the course of day-to-day development. It’s such a crucial topic for the Rails developer that we have an entire chapter devoted to it.

Reference

1.

For those interested in REST, the canonical text is Roy Fielding’s dissertation, which you can find at http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm. In particular, you’ll probably want to focus on Chapters 5 and 6 of the dissertation, which cover REST and its relation to HTTP. You’ll also find an enormous amount of information, and links to more, on the REST wiki at http://rest.blueoxen.net/cgi-bin/wiki.pl.

2.

In addition to being weird, the semicolon had a number of significant problems. For instance, it wreaked havoc on caching. Safari users were not able to authenticate URLs with semicolons in them. Also, various web servers (most damningly Mongrel) correctly consider the semicolon to be part of the query string, since that character is reserved for delimiting the start of path parameters (specific to a path element in between slashes, as opposed to request parameters that come after a ‘?’ character).

3.

Trevor Squires has a great plugin called ResourceFu that makes this technique possible, which is available at http://agilewebdevelopment.com/plugins/resource_fu.

4.

I can only say should work, because routing code is historically some of the most volatile in the entire Rails codebase, so whether it works depends on your version of Rails. I know for a fact that it doesn’t work in Rails 1.2.3.

5.

http://weblog.jamisbuck.org/2007/2/5/nesting-resources

6.

Read the full thread at http://www.ruby-forum.com/topic/75356.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.138.104