RESTful Rails for the restless

November 24th, 2008

QuickStarts-R-Us

As one of the most active Rails trainers on the circuit, I come up a lot against the challenge of introducing RESTful Rails to relative newcomers. It’s a challenge because the REST support in Rails is very high-level and, even for the diligent, basically impossible to understand deeply without a knowledge of the subsystems—in particular, the routing system—on which it is built.

I believe it’s possible, nonetheless, to understand up front how the RESTful support in Rails fits into the subsystems that support it; and I believe that it’s beneficial to gain such an understanding. My purpose is thus to provide a “QuickStart” introduction, not to the practice of writing RESTful Rails applications but to the way the REST support in Rails fits into what’s around and beneath it. If you want to do RESTful Rails but either find it too magical or don’t quite understand how it relates to the framework overall (does it add? supersede? enhance?), then this article may be of interest to you.

You may wonder why I’m not making use of the Rails scaffolding. That is, as they say, “a whole nother” story. Short answer: the scaffolding gives you a quick start, but also a quick end. It explains nothing and leaves you with a lot of work to do to reverse the ill effects of having a lot of “one-size-fits-none” code lying around your application directory.

So no scaffolding. Also, no REST theory—but by all means have a look at the theory once you get into the practice. It’s just not my focus here.

In what follows, I’ve tried to be concise—minimalist, almost. I’d advise not skimming over anything, even if you think you already know it. I’m chosing the path carefully. If you don’t trust me as a guide, that’s another matter entirely :-) If you do, welcome.

What a (non-RESTful) Rails application does

The job of a Rails application is to provide responses to requests. Responses are generated by controller actions, which are (in Ruby terms) instance methods of controller classes.

When your application receives a request, the first order of business is to figure out which action to execute. The subsystem that does this is the routing system. It’s the routing system’s job, for every request, to determine two things:

1. controller
2. action

If it cannot determine those two things, it has failed, and you get a routing error. If it can, the routing has succeeded. End of story. (You might get a “No such action” error, but that’s not the routing system’s problem. The routing system has done its job if it comes up with an action, whether the action exists or not.)

The main information that the routing system uses to determine which controller and action you want for a given request is the request URL. By definition, every URL that’s meaningful to your application can be resolved to a controller/action pair. If the URL contains information beyond that which is needed to determine a controller and action, that information gets stored in the params hash, to which the controller action has access. (That’s how you get params[:id], for example.)

The routing system uses a rule-based approach to resolving URLs into controller/action pairs. The rules are stored in routes.rb. A rule might say, for example (paraphrased here in English), “A URL with (1) a string, (2) a slash, (3) a string, (4) a slash, and (5) an integer means: execute action (3) of controller (1) with params[:id] set to (5)” (and indeed the default routing rule says exactly that). Rules can be specific, to the point of silliness. It’s perfectly possible to program the routing system so that “/blah” means: “the show action of the students controller with params[:id] set to 1010.” There’s almost certainly no point in such a mapping, but the point is that you can program the routing system in a fine-grained way.

In the non-RESTful case, the URL is all that the routing system needs to do its job of performing a rule-based determination of a controller and an action.

In the RESTful case, it isn’t.

Enter the verbs

This is the crux of RESTful routing in Rails. Everything else flows directly from this, so make sure you understand it.

Instead of routing based solely on rule-driven mapping of each URL to a controller/action pair, RESTful Rails adds another decision gate to the chain: the HTTP request method of the incoming request. That method will be one of GET, POST, PUT, or DELETE. It’s the combined information—URL plus request method—that the RESTful routing uses to determine the controller and the action.

That means that for every incoming request, the correct controller/action pair is determined not per URL, but per URL per request method. That, in turn, means that a given URL, such as this:

  http://blah.blah/houses/14

might map to two or more different controller/action pairs. It all depends on the HTTP request method.

In theory, any one URL can be routed to as many as four controller/action pairs, because any one URL can be used in a GET, PUT, POST, or DELETE request. In practice there aren’t that many permutations, because some combinations of request method and URL semantics are not meaningful. But the principle is what matters: a single URL no longer has an unambiguous meaning, but must be interpreted in conjunction with the request method.

Furthermore, these conjoined interpretations are hard-coded to a pre-determined set of seven actions: index, show, delete, edit, update, new, and create. (You can add custom ones, but those are the canonical ones.) For example, the “houses” URL above, if requested as a GET, automatically routes to the show action of the houses controller, with params[:id] set to 14. If submitted with a PUT, it goes to the update action. A URL with no id field (/houses) goes either to index or to create, depending on the request method. And so forth.

That, as I say, is the crux of the matter: routing based on URL plus request method. Keep this in mind as you get into the details and bells and whistles of RESTful Rails.

Interpreting requests, though, is only half of the job of the routing system. The other half is the generation of strings.

RESTful URL generation

When you write this in your view:

  <%= link_to "Click here for help", :controller => "users", :action => "help" %>

your view ends up containing this:

  <a href="/users/help">Click here for help</a>

It’s the routing system that does the job of processing the link_to arguments and figuring out what the URL (or, in this case, the relative path) in your tag should consist of.

The same thing happens with RESTful routing, except that you never have to spell out the controller and action. Instead, you call yet more helper methods. Compare this:

  <%= link_to "User profile for #{user.name}",
               :controller => "users", 
               :action => "show",
               :id => user.id %>

with this:

  <%= link_to "User profile for #{user.name}", user_path(user) %>

You don’t have to define the method user_path. It comes into being automatically, when you write:

  map.resources :users

in routes.rb. And it has a simple job: return the right string, in this case the string ”/users/14” (assuming that user.id is 14).

For every resource you route, you get a fistful of such methods: user_path(user), users_path, new_user_path, and edit_user_path (plus all of these with _url instead of _path). These methods do nothing but generate strings. They have no knowledge of request methods or REST. In fact, they’re just examples of named routes—methods that generate the right strings for specific routing rules—and you can use named routes in routes.rb even without REST. The only REST-related special treatment is that map.resources automatically writes a bunch of these methods for you. You can think of map.resources as, primarily, a macro that writes named route methods, much as attr_accessor automatically writes getter and setter methods.

The specifics of what the various RESTful named route methods do is for future study. The point here is to see the roadmap. You do map.resources :users, and from that point on, you can use methods in your views to create URL strings, rather than having to spoonfeed the information about which controller, action, and id are involved.

But that still leaves the question of the request method. How does ”/users/14” know which action to trigger when clicked?

Specifying request methods

When you write view code that generates path strings (with link_to, form_for, link_to_remote, etc.), you want the right string, obviously, but you also need the link, when clicked, to use a particular HTTP request method for the request. Otherwise the RESTful routing system won’t have enough information to make sense of the URL.

The helper methods that generate hyperlinks all have sensible HTTP request method defaults (which you can override if needed). link_to generates a link that will submit a GET request. form_for generates a POST form tag (method=“post”), unless you tell it to use PUT (which is conventional for update operations, as opposed to new record creation operations), and so forth.

Again, the named route methods don’t have request method intelligence. The enclosing hyperlink-writing methods (link_to and friends) do. They just used the named route methods as lower-level helpers for the specific purpose of generating the right strings.

Invisible ink

One of the challenges of using RESTful routing in Rails is that you end up with not very much information available to you visually. When you write a RESTful form in your view, let’s say for an update:

  <% form_for :house, :url => house_path(@house.id),
                     :html => { :method => :put } do |f| %>
  <% end %>

you never see the word “update” in routes.rb, nor in the URL, nor in the view templates, nor in the HTML source of your rendered views. You just have to know that a thing_path-style named route, coupled with a request method override to PUT (override of the default POST for form_for, that is), will result in a form that, when submitted, will send a PUT request to the update action of the houses controller. And you have to trust that the routing system will succeed in so routing it.

RESTful routing pushes most of the routing intelligence—which, as you now know, means the determination of a controller/action pair from an incoming request—under the surface. You have to learn how the REST-ified routing system thinks. The early phases of learning RESTful routing tend to involve memorizing the combinations of named routes and request methods, and which action they point to. The good news is that there’s a finite number of them, and they make sense. If it seems like routing soup, hang in there and look closely at the logic. It will come clear.

The rest…

That’s the basics. There’s a lot more to it, including (but not limited to) more “magic” shortcuts. But if you get the basic ideas you’ll be in good shape.

  • The basic routing system resolves a URL to a controller/action pair.
  • RESTful routing resolves a URL/request-method combination to a controller/action pair.
  • map.resources :things generates a bunch of named routes (things_path, etc.) for you automatically.
  • You don’t see as much visual evidence of the routing logic with RESTful routing as with non-RESTful routing, so you have to learn exactly what it’s thinking, especially the seven hard-coded action names.

Now go forth and REST. Oh, one more thing. Here’s a chart I once made, showing how the named routes map through the request methods to the seven canonical actions. The chart uses the _url methods (which give you the whole thing, including http://), but the _path versions would exist too.

RESTful routing chart

Sorry, comments are closed for this article.