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 HTTP
verb. Now Rails knows that browsers don’t support the HTTP 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 CRUD actions in a controller, you need 4 unique routes. To me, the routes for #new and #edit are ugly. If we just had more HTTP 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 HTTP 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 HTTP
verbs via the new :conditions
parameter:
- new
- 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:
- users
- 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 URL 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