Controller methods and how I learned to love 'rake routes'

Face it: to the newcomer, Rails routing is strange and mysterious. It
took me a while get the hang of it, so I thought I’d share some info
(and a couple of handy techniques) for those who are new to Rails.

Here, assume we have Users and Reports, and that one User “has many”
Reports. Normally, that will be expressed in your model files (but
that’s another topic!) – but right now we’re focused on making an entry
in config/routes.rb to express how incoming URLs map to your

In this example, we’re using RESTful routing to create “shallow nested
routes” to express that a User :has_many Reports. So we create an entry
in config/routes.rb like this:

====== config/routes.rb:
map.resources :users, :has_many => :reports, :shallow => true

Assuming we already have a controller for User, we still need a
controller for Reports. Go ahead and type “rake routes” in a shell
window to see what the routing system generated. Here are the relevant
lines for the Reports controller:

% rake routes

user_reports GET /users/:user_id/reports
{:controller=>“reports”, :action=>“index”}
POST /users/:user_id/reports
{:controller=>“reports”, :action=>“create”}
new_user_report GET /users/:user_id/reports/new
{:controller=>“reports”, :action=>“new”}
edit_report GET /reports/:id/edit
{:controller=>“reports”, :action=>“edit”}
report GET /reports/:id
{:controller=>“reports”, :action=>“show”}
PUT /reports/:id
{:controller=>“reports”, :action=>“update”}
DELETE /reports/:id
{:controller=>“reports”, :action=>“destroy”}

Though it may not be immediately obvious, each line tells us two things:
which URL will be matched to method in the Reports controller, and a
‘helper function’ you can use to generate that URL.

The leftmost column is the name of the helper functions that have been
created for you, but stripped of _url or _path, so ‘user_reports’
signifies ‘user_reports_url(user_id)’ (the fully qualified url) or
‘user_repors_path(user_id)’ (the abbreviated path).

The next column is the HTTP method (GET, POST, PUT, DELETE).

The third column is the path template that the routing system matches
against: the incoming URL must have this form to be a match.

And the last column is the controller and method within the controller
that gets called for that method/template combination.

Handy technique 1:

To better understand those generated helper functions, launch a console.
Within the console, you can test the helper functions using
‘app.<helper_fn>’ like this:

bash-3.2$ script/console
Loading development environment (Rails 2.3.5)

=> “/users/10/reports”
=> “
=> “/users/10/reports/new”
=> “
=> “/reports/33”

Handy technique 2:

Since I haven’t been writing Rails code very long I can’t keep all these
routes, parameters and helper functions straight in my mind. So the
first thing I do is stub out a skeleton ReportsController and annotate
with comments lifted from ‘rake routes’.

Here’s what my skeleton ReportsController might look like – the comment
before each method documents the helper function that will generate the
HTTP call to get you to that method, and what to look for in your
params[] hash.

If you look at the comments, the relationship to the output of rake
routes should be obvious. (It would be great if ‘script/generate
controller’ added these comments for your automatically! That will be a
future project unless someone beats me to it…)

====== file: app/controllers/reports_controller.rb
class ReportsController < ApplicationController

arrive here via ‘GET user_reports_{url|path}(user_id)’

url has the form ‘/users/:user_id/reports’

def index

arrive here via ‘POST user_reports_{url|path}(user_id)’

url has the form ‘/users/:user_id/reports’

def create

arrive here via ‘GET new_user_report_{url|path}(user_id)’

url has the form ‘/users/:user_id/reports/new’

def new

arrive here via ‘GET edit_report_{url|path}(id)’

url has the form ‘/reports/:id/edit’

def edit

arrive here via ‘GET report_{url|path}(id)’

url has the form ‘/reports/:id’

def show

arrive here via ‘PUT report_{url|path}(id)’

url has the form ‘/reports/:id’

def update

arrive here via ‘DELETE report_{url|path}(id)’

url has the form ‘/reports/:id’

def destroy

So now, you can fill out your code knowing what you’ll find in the
params hash when a method is called, and which helper function will
generate an HTTP call to each method within your controller.

Hope this helps.

  • ff