simply_restful in Rails Core

posted by David Goodlad

Now that Rick Olson has integrated the functionality of the simply_restful plugin with Rails Core, the interest in RESTful design in Rails is bound to increase. I’ve found that most of the documentation is rather out-dated, and that it takes a bit of digging to figure out exactly what syntax you should be using for your named routes, url generation, etc. Differences between the simply_restful README, DHH’s presentation from RailsConf, and various random blog posts, it can get rather confusing! Let’s clear that up, shall we?

URL Structure

First of all, something that many of you are familiar with: the URL structure. There are 3 basic URLs, but each can respond to the various HTTP verbs in different ways. Using a Order model:

Named Route URL HTTP Verb Controller Action
orders_url/postsGETindex
orders_url/postsPOSTcreate
order_url/posts/:idGETshow
order_url/posts/:idPUTupdate
order_url/posts/:idDELETEdestroy
new_order_url/posts/newGETnew
edit_order_url/posts/id;editGETedit

The plural form of the named route can be thought of as the ‘collection’ URL. It allows operations on the collection as a whole:

  1. Get a list of all the entities, in this case orders (GET)
  2. Create a new entity (POST)

The singular form of the named route, on the other hand, is used to refer to a specific entity in the collection: in this case, a post. By using the GET, PUT, and DELETE verbs, you can operate on this entity:

  1. Show the attributes of a specific entity (GET)
  2. Update the attributes of a specific entity (PUT)
  3. Destroy a specific entity (DELETE)

There are two ‘oddball’ URLs: ‘new’ and ‘edit’. They are used to show forms for submission to the ‘create’ and ‘update’ URLs (via the appropriate verb).

Setting up routes.rb

To map a model as a resource, an entity that can be operated upon in the manner described above, you add a line to your config/routes.rb file:

ActionController::Routing::Routes.draw do |map|
  map.resources :orders
end

Multiple resources can be specified on the same line, reducing clutter:

map.resources :orders, :invoices, :customers

Resources can also be nested, to produce URLS like /orders/1/invoices/3:

map.resources :orders do |map|
  map.resources :invoices
end

There are a number of optional parameters to the #resource method, but I’ll leave these alone for now…

Controller

There are seven standard controller actions. Following the Order model example:

class OrdersController < ApplicationController
  def index
  end

  def show
  end

  def new
  end

  def create
  end

  def edit
  end

  def update
  end

  def destroy
  end
end

This boilerplate code is enough to respond to all of the default URLs defined by the map.resources call.

Parameters

When the singular entity URL is requested, you’ll have access to the ID requested in the params[:id] variable.

A create or update request will give you the new attributes in a hash accessed by params[:entity_name]. For example, params[:order].

When a nested URL is requested (described above), you’ll have access to all the ‘parent’ IDs based on the name of the model. For example, if you had invoices nested in orders, like the example above, you would receive params[:order_id] in your controller. Not surprising, is it?

Named Routes

Using the named routes is remarkably easy. Assuming the Order model, like before, and that for the singular urls you have an instance of an order available in the @order variable, you can link to the various controller actions:

Controller Action Method to call
indexlink_to orders_url
showlink_to order_url(@order)
newlink_to new_order_url
createform_for :order, :url => orders_url, :html => { :method => :post }
editlink_to edit_order_url(@order)
updateform_for :order, :url => order_url(@order), :html => { :method => :put }
destroylink_to order_url(@order), :method => :delete

Note that you can pass the instance you want directly to the singular route. Also note that the :method parameter is not given to the named route, rather it’s passed to either link_to or form_for (or their variants). This last one has frustrated me before.

The named routes for nested resources operate in the same way; you just need to pass the ‘parent’ resource(s) as the first parameter(s):

invoices_url(@order)
invoice_url(@order, @invoice)

The Basics: Check!

Those are the basics needed to get started using the RESTful functionality, correct as of the date of this post. There are many options and fancy methods of extending this functionality, but I’ll leave those for another article. The defaults seem to cover a huge range of possible uses, so make a good place to start.