Home > Articles > Programming > Ruby

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

3.5 The Standard RESTful Controller Actions

Calling 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 3.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 lists _path methods rather than _url ones, but you get both.)

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

Helper Method

GET

POST

PUT

DELETE

client_path(client)

/clients/1 show

/clients/1 update

/clients/1 destroy

clients_path

/clients index

/clients create

edit_client_path(client)

/clients/1/edit edit

new_client_path

/clients/new new

(The edit and new actions have unique named routes, and their URLs have a special syntax.)

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", auction_path(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_path(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 when submitting the form (as per row 2, column 4 of Table 3.1). You don't normally have to program this functionality specifically, because as we'll see later in the book, Rails automatically figures out whether you need a POST or PUT if you pass an object to form helpers.

3.5.1 Singular and Plural RESTful Routes

As you may have noticed, 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 be able to figure out the id of the member of the collection referenced.

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

You don't have to call the id method on item. Rails will figure it out (by calling to_param on the object passed to it).

3.5.2 The Special Pairs: new/create and edit/update

As Table 3.1 shows, new and edit obey somewhat special RESTful naming conventions. The reason for this 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!

That line of reasoning might be a little too philosophical to be useful. The bottom line, as implemented in RESTful Rails, is the following: 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

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.

3.5.3 The PUT and DELETE Cheat

We have just seen how Rails routes PUT and DELETE requests. Some HTTP clients are able to use said verbs, but forms in web browsers can't be submitted using anything other than a POST. Rails provides a hack that is nothing to worry about, other than being aware of what's going on.

A PUT or DELETE request originating in a browser, 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. (HTML5 in particular adds PUT and DELETE as valid method attributes for forms.)

3.5.4 Limiting Routes Generated

It's possible to add :except and :only options to the call to resources in order to limit the routes generated.

resources :clients, :except => [:index]
resources :clients, :only => [:new, :create]
  • + Share This
  • 🔖 Save To Your Account