We have 7 rules and only one database table. Does this mean we need to add 7 more rules for every table we add? No, there are ways to reduce the amount of explicitly defined rules.

When Rails receives a URL it tries to find a matching routing rule. It does this by comparing the URL to the pattern string in the rule.
The pattern string for map.connect ‘music/create’, :controller => “albums”, :action => “create” Is: music/create

The :id is a Ruby symbol and is referred to as a receptor. If you are new to Ruby you may not be too familiar with symbols. I once came across a short and sweet description of a symbol “A symbol is the name badge, not the person”. I may have read that in here.

A receptor is used as a wildcard. It matches anything that it is compared against. The wildcard receptor gets passed into the controller action within the params hash e.g.

def edit
@album = Album.find(params[:id])
end

You can change :id to almost anything as long as you remember to change it in the controller and in the views where the URL is generated e.g. when using link_to. I recommend you use :id as that is the convention.

Rails will get the first rule defined in routes.rb and look if the URL matches the pattern string. The first rule in our case is:
map.connect ‘music’, :controller => “albums”, :action => “index”

Rails performs the test to see if there is a match:
URL: music/edit/1
Rule: music

This test fails, no match because the rules pattern string does not contain edit or a receptor (:id).

Rails tests the URL against the pattern string defined in the second rule:
URL: music/edit/1
Rule: music/show/:id

Again, this is not a match. Rails will then test the next rule. This process continues until Rails finds a match. In our case, this will be on this rule:
map.connect ‘music/edit/:id’, :controller => “albums”, :action => “edit”

The test:
URL: music/edit/1
Rule: music/edit/:id

The match is made! Rails will then follow the rule and invoke the edit action on the albums controller. Passing through the :id (as the key) with the value of 1 into the edit action. This is done through the params hash.

Take note of these two points:
1. If the pattern string does not specify a receptor and the URL contains a value to be used, then the route will not match.
We can see this by using this URL: http://localhost:3000/music/new/42

2. If the pattern string does contain a receptor and the URL does not supply a value for it, the route will still match – although an error may occur.
We can see this by using this URL: http://localhost:3000/music/edit/

When using that URL, the match is still made and the edit action is still invoked. Rails will simply pass through nil to the action. This will result in an error as the find method can not find an album with no ID.

We could add a receptor to the ‘new’ pattern string e.g. ‘music/new/:id’
Now you can invoke the new action with http://localhost:3000/music/new/42 – if you really wanted to.

The albums_controller.new action does not use the :id from the params hash. Therefore you can safely omit the receptor value and call it by using http://localhost:3000/music/new. As we can see, this request works even when Rails used nil as the value of :id.

Putting things back into perspective, the goal here is to reduce the amount of explicit routing rules. Let’s have a look at some of the pattern strings and resulting actions invoked:

‘music/new‘ results in the new action being invoked
‘music/create‘ results in the create action being invoked
‘music/edit/:id’ results in the edit action being invoked
Etc…

We see that the URL contains the name of the action. We could write a more general rule if we could extract the action name from the URL. Rails provides us with an easy way of doing this. We can use the ‘Magic Receptors’.

Experiment 3.1 Applying a ‘Magic’ Receptor

Rails has two ‘magic’ receptors, these are :controller and :action. These are really just receptors, however they are special. These receptors form a part of the Rails magic.

As previously stated, a receptor is a wildcard. Therefore :action will match anything that it is compared against. There we can get this match occurring.

URL: music/show/1
Rule: music/:action/:id

This results with a new entry into the params table, with a key of :action and a value of “show”. Now Rails can use this entry to determine the controller action to call. Now ‘show’ rule can be reduced to:
map.connect ‘music/:action/:id’, :controller => “albums”

New and Create appear to be different as they do not require the album ID.
Therefore we might decide to change the new rule from
map.connect ‘music/new’, :controller => “albums”, :action => “new”
to
map.connect ‘music/:action’, :controller => “albums”

And the same for the create rule. This will allow us to end up with a routing file like this:

As stated above:
If the pattern string does contain a receptor and the URL does not supply a value for it, the route will still match – although an error may occur. Rails uses nil if the URL does not supply a value for the receptor.

The only times we don’t supply a value for id is in the create and new actions. These actions do not use the id parameter. Therefore the site still works without a hitch.

Experiment 3.2 Applying the other ‘Magic’ Receptor

As I said before, Rails has two ‘magic’ receptors (:controller and :action). We have already used :action and now we are going to use :controller.

One of the reasons I did this was to show you that the routes are independent of the projects folder and file structure. Now we are going to move back towards a Rails convention. Our controller is named albums therefore (according to convention) our URL should be of the form http://localhost:3000/albums.

If we used the :controller receptor, we would have a more generic rule, one that would aslo work with other controllers.

Make your routes.rb look like this:

ActionController::Routing::Routes.draw do |map|
map.connect ':controller/:action/:id'
end

Now we have a very generic routing rule. When we add a new controller, this rule may be perfect for it. We only need to add rules if we require them, this rule can be used as the default rule. In fact, this is known as the default route. And now you know…

The End of Part 3

And so ends this long awaited third edition. I hope this series continues to help those of you who are new to Rails.

The only topics left in the series are Named Routes and then RESTfull routing.

When I do a “rake routes” with these, they randomly map GET, POST, ANY, etc.

What, exactly is the grammar/syntax for writing these routing rules? Everywhere I look, there are lots of examples, and magic functions such as map.resources, map.home, map.namespace, etc, but no general grammar that shows how to write routes to get the routing table as described in “rake routes”.

Firstly there is a lot of magic going on. It is probably more important to understand how to use the magic, then to understand the magic.

The good news is that there is not random mapping going on. That is RESTful routing, I would say it is the opposite of random mapping. Too many ‘spaghetti’ custom routes lead to random mapping.

You are right, your questions are fodder for the next articles.

I recommend you that you go through Part 1 and 2 (and 3 again if required) of this series. I will do my best to whip up part 4 and 5 as soon as possible. I don’t want to commit to a deadline, after all my ‘real’ work comes first.

When you get to RESTful routing I would like to see an example of how to add a SaveAs function. This would allow the user to derive a new record in a table from an existing record. So it would start as an edit function but end as a create action instead of an update action

As far as I’m concerned, you performed a great service to the Rails community. I’ve been trying to get a grip on RESTful routing without much success. Part 3 was great! That’s my starting point for really for “owning” the new routing concept. Thank for your generous and informative series.

man u r simply amazing!!!!
God bless u!!
i was stuck on this routing stuff and although
i read some book on the matter(written by “important” authors -lol-) , none of them explain it
clearly like u did!!!

Good tutorial. I am new to rails routing and I don’t understand in example 3.1, when you use the magic routing, why you need to modify edit.html.erb to add :id explicitly. Why if you keep your own update routing rule you don’t need to set :id explicitly? Thank you very much for your answer.

Thank you very much….great content and nicely done! Very easy to understand it.
When i first read about rails and just try to do an example everything was so confused so i got to remember, to learn it by memory the controller instructions…..now verything has come to sense.
Thanks Daryn.

The tutorial is really awesome man. I was having sooo much problem in understanding how this routes basically works, but this tutorial really makes it clear whats happens inside rails routes. Thanks a lot .