---
title: simplier restful
teaser:
tags: web,rails
author: Jared Carroll
published_on: 2007-07-26
---

One of the biggest features in the latest version of Rails is RESTful routes.

By writing the following in your _routes.rb_ file (assuming you have a
UsersController):

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

You get the following named routes for free (excluding named routes that include
a :format extension):

    GET    /users         #index
    POST   /users         #create
    GET    /users/new     #new
    GET    /users/1       #show
    PUT    /users/1       #update
    GET    /users/1;edit  #edit
    DELETE /users/1       #destroy

The first column is the <abbr title="HyperText Transfer Protocol">HTTP</abbr>
verb.  Now Rails knows that browsers don't support the <abbr title="HyperText
Transfer Protocol">HTTP</abbr> verbs PUT and DELETE, so it fakes it by doing
POSTs instead, and passing along a hidden field parameter named `_method` whose
value is either 'put' or 'delete'.

So in order to support the basic <abbr title="Create Read Update
Delete">CRUD</abbr> actions in a controller, you need 4 unique routes.  To me,
the routes for #new and #edit are ugly.  If we just had more <abbr
title="HyperText Transfer Protocol">HTTP</abbr> verbs, we could eliminate both
of those routes and reduce the total number of unique routes to 2 (actually, we
could go all the way to just 1 route).  Since Rails just uses POSTs and hidden
parameters for PUT and DELETE, we can easily add our own <abbr title="HyperText
Transfer Protocol">HTTP</abbr> verbs.

However, we can't use #resources anymore in our _routes.rb_ file though, instead
we'll have to create named routes for each of our 7 actions.

    ActionController::Routing::Routes.draw do |map|
      # Create
      map.new_user 'users',
      :conditions => { :method => :new },
      :controller => 'users',
      :action => 'new'

      map.users 'users',
      :conditions => { :method => :post },
      :controller => 'users',
      :action => 'create'

      # Read
      map.users 'users',
      :conditions => { :method => :get },
      :controller => 'users'

      map.user 'users/:id',
      :conditions => { :method => :get },
      :controller => 'users',
      :action => 'show'

      # Update
      map.edit_user 'users/:id',
      :conditions => { :method => :edit },
      :controller => 'users',
      :action => 'edit'

      map.user 'users/:id',
      :conditions => { :method => :put },
      :controller => 'users',
      :action => 'update'

      # Delete
      map.user 'users/:id',
      :conditions => { :method => :delete },
      :controller => 'users',
      :action => 'destroy'
    end

The above code added 2 new <abbr title="HyperText Transfer Protocol">HTTP</abbr>
verbs via the new `:conditions` parameter:

1. new
1. edit

Those will handle the form display for creating a new user and editing an
existing user.  Now our app only needs 2 unique routes:

1. users
1. users/:id

We can use the normal `#link_to` to create links to our `#new` and `#edit`
actions by using the `:method` parameter:

    link_to 'new', user_url(@user), :method => :new
    link_to 'edit', user_url(@user), :method => :edit

This really is going to create a handler for this link's `onclick` event that
creates a form to the specified <abbr title="Uniform Resource
Locator">URL</abbr> and POSTs to it with a hidden parameter value for `_method`
of either 'new' or 'edit' (i.e. this won't degrade gracefully with javascript
disabled, if you want that you'll need to create forms that contain nothing but
a button - unfortunately `#button_to` doesn't support the `:method` parameter).

Of course, the major downside of all this is that users can't bookmark links to
the #new and #edit pages.  But I like the simplicity of only having 2 routes.
You could take this all the way down to 1 route, but I think 2 is better:

* one for the collection of users
* one for an individual user
