Saturday, January 12, 2008

Rails Routing

When a user clicks on a link the url eg (http://www.mysite.com/users/show/12) is sent by the browser to the rails server (eg. webrick/mongrel). The server then looks at then looks at the routes file to work out which controller to send it to. The request is then dispatched to the controller which manages requesting the data from the model and sending it to the view to display. For a much better explanation (with picture) see BetterExplained (a brilliant little site well worth investigating). So, in the routes file (/config/routes.rb) are instructions that tell the server how to turn urls into controllers and actions. The server will start at the top of the file and work down until it finds an instruction that works. If you look in the file you will find the instructions look a bit like
map.connect ':controller/:action/:id'
What will this do? The argument after the map.connect tells the server a string to match and what to call the bits it matches. Here when the server receives "http://www.mysite.com/user/show/12" it first strips off the web address and so is left with "user/show/12". It will then look at this rule and match the form and asign the string 'user' to :controller, 'new' to :action and '12' to :id. It will then call the 'user' controller with the 'new' action and id = 12. This is a pretty basic rule. How about this one:
 map.connect 'date/:year/:month/:day', :controller => 'blog', :action => 'display_date' 
this would match "www.mysite.com/date/2008/1/1" to the 'display_date' method of the 'blog' controller with :year => 2008, :month => 1, :day => 1. To specify that some of the match pattern is optional (to match www.mysite.com/date/2008) say, you can add
 :month =>nil, :day=>nil 
to the rule. To stop too much matching (eg to stop the above matching www.mysite.com/date/latest ) you can add
 :requirements => {:year => /\d{4}/, :month => /\d{1,2}/, :day => /\d{1,2}/} 
This allows you to use the full power of regular expressions when matching. To add a default eg
 map.connect 'posts/:category', :controller => 'blog', :action => 'posts', :category => :all 
At first glance there doesn't seem much point to doing this as :category could default to :all in the controller. However the payoff comes when 'going backwards' using link_to and url_for. If you didn't have the default set you would have to write
 link_to 'Posts', :controller => 'blog', :action => 'posts', :category => :all 
and also "all" would be displayed at the end of the url in the browser. In the link_to syntax the first argument is the name to call the link (ie the link as shown to the user) and the rest of the arguments the stuff necessary to match to a url by looking in the routes.rb file. For more explanation of the above (where I copied the examples from :p ) see the rubyonrails manual Remember that the server starts at the top and works down so that rules at the top have higher precedence. See here for a deeper look at what's going on. Note "map.root" is equivalent to "map.connect ' ', :controller => 'index', :action => 'index' " (I think).

No comments: