Part 2

About this series

This is a series for beginners wanting to learn about routing in Rails 2.0. This first post is aimed at exploring the behaviour of routing in Rails. It examines what happens when the routing system is given a certain URL. Future posts will examine the ‘hows’ and ‘whys’ of this behaviour. The plan is to start with the basics and move towards the advanced topics. There is only a brief mention of RESTful routing in this post. I plan to delve deeper into that topic at a later stage.

Introduction

The routing subsystem is at the heart of a Rails application. By looking at the following image, we can see the role of the routing system.

The diagram is a modified version of a diagram found in Flexible Rails.

As the picture depicts, in a Rails application the process begins with a request from the browser. This request is passed into the Rails routing system. The routing system examines the request and is able to determine the following information:

Which controller to instantiate

Which action (method) to invoke on that controller

The parameters, if any, to pass into the action

When receiving a correctly formatted request URL the routing system will instantiate the specified controller, call the appropriate action and pass in the supplied method parameters, if required. The controller will then perform the necessary action and Rails will render the relevant view. This will result in the browser receiving a response and the round trip is complete. This is oversimplified, but it does show the importance of the routing system.

If the request is not in the correct format the routing system may not be able to infer the correct action to take. Therefore it is important that the format is correct. We are in luck as the Rails routing system also helps us to generate requests in the correct format.

In short, the routing system has two main functions:

Interpreting a request URL

Generating a request URL

The routing system performs these functions by using routing rules, defined in Ruby code as opposed to XML. The rules can be found in the config/routes.rb Ruby file. Each rule is used to both interpret and to generate a related request URL.

I could go into more theory here but, as with many things, the best way to get to grips with routing is to do it!

The Practical Part…at last

When I wrote this post, I was using Instant Rails as per my suggestion in a previous post.

Preparation

Issue the create command to create a rails application:

C:\InstantRails-2.0-win\rails_apps>rails -d mysql music_store

Rails does its thing…

Then we create the database…

C:\InstantRails-2.0-win\rails_apps>cd music_store C:\InstantRails-2.0-win\rails_apps\music_store>rake db:create:all (in C:/InstantRails-2.0-win/rails_apps/music_store)

Do a quick test to make sure things are in order.

C:\InstantRails-2.0-win\rails_apps\music_store>ruby script\server => Booting Mongrel (use 'script/server webrick' to force WEBrick) => Rails application starting on http://0.0.0.0:3000 => Call with -d to detach ... output ... ** Mongrel 1.1.2 available at 0.0.0.0:3000 ** Use CTRL-C to stop.

Start a browser and navigate to: http://localhost:3000/

Make sure you expand the ‘About your application’s environment’

You should see the standard welcome screen:

If you are experiencing problems, make sure your proxy is turned off.

Perform some scaffolding to create an ‘Album’.

ruby script\generate scaffold Album title:string review:text

Rails goes again…



In the last line of the output we see:

route map.resources :albums

Rails has actually created a route for us, it happens to be a RESTful route! We will get to that later. We are just noting it for now, Rails has jumped the gun. RESTful routes are what we want in our application, but first we want to have a good understanding of routes. The best way to understand routes is to experiment with them. Later we will remove that RESTful route, which rails created, and play around with customised routes. We will then explore named routes and move back to using RESTful routing.

Before doing all of that, lets get our Albums section up and running.

Run the rake migrate task

C:\InstantRails-2.0-win\rails_apps\music_store>rake db:migrate

Browse to this URL: http://localhost:3000/albums

You should see:

Let’s use our shiny new Rails application to add a few albums. Create three albums and do not delete these first three. Later on we will assume that you have three albums having database IDs of 1, 2 and 3. Create and delete a forth one if you like, but don’t delete the first three Albums. You can enter any data, try to show more taste than I did…

We are not going to use any CSS or partials at this point, that is not the point of this tutorial. OK, let’s get back to routing!

Experimenting 1 – View the generated routes

The objective of this experiment is to view the generated Ruby code which defines the generated routes. We then explore the behaviour of these routes. This will help us gain some insight into how the routing works. We will use the IRB to perform these actions.

Open up the config\routes.rb file to see the actual code that defines the routes. You should see code as depicted below, with additional comments. You may want to leave the comments so that you can read them as we move on.

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

Rails has generated three routes for us, these are the routes our application is currently using. Let’s examine the behaviour of these routes. A good way to do this is by using the IRB.

C:\InstantRails-2.0-win\rails_apps\music_store>ruby script/console Loading development environment (Rails 2.0.2) >> rts = ActionController::Routing::Routes

Expect a console output splat! Let’s get some output in a nice format.

>> puts rts.routes 1 GET /albums/ {:action=>"index", :controller=>"albums"} 2 GET /albums.:format/ {:action=>"index", :controller=>"albums"} 3 POST /albums/ {:action=>"create", :controller=>"albums"} 4 POST /albums.:format/ {:action=>"create", :controller=>"albums"} 5 GET /albums/new/ {:action=>"new", :controller=>"albums"} 6 GET /albums/new.:format/ {:action=>"new", :controller=>"albums"} 7 GET /albums/:id/edit/ {:action=>"edit", :controller=>"albums"} 8 GET /albums/:id/edit.:format/ {:action=>"edit", :controller=>"albums"} 9 GET /albums/:id/ {:action=>"show", :controller=>"albums"} 10 GET /albums/:id.:format/ {:action=>"show", :controller=>"albums"} 11 PUT /albums/:id/ {:action=>"update", :controller=>"albums"} 12 PUT /albums/:id.:format/ {:action=>"update", :controller=>"albums"} 13 DELETE /albums/:id/ {:action=>"destroy", :controller=>"albums"} 14 DELETE /albums/:id.:format/ {:action=>"destroy", :controller=>"albums"} 15 ANY /:controller/:action/:id/ {} 16 ANY /:controller/:action/:id.:format/ {}

The middle column is commonly referred to as the path (also called the relative path). On our local development machine the fully qualified URL of /albums/ is localhost:3000/albums.

Line 1 shows us that if we do a GET using /albums/ the action index will be called on the albums controller. Try it in your browser, use http://localhost:3000/albums/.

Line 7, here the routing system will behave in a similar way as before. To invoke this rule we use a URL replacing the :ID symbol with an existing album ID from the database e.g. /albums/2/edit/. This will result in the edit action being called on the albums controller. The 2 will be made available to the album controllers edit action through the params hash. We can see this if we open the albums controller and look at the edit method:

# GET /albums/1/edit def edit @album = Album.find(params[:id]) end end

Using :id is a convention. We could use another symbol but then we would have to remember to use that in the controller. As you know, it is always better to stick to convention when working with Rails – DHH knows best.You may have noticed some paths ending with format. This can be used to pass a format though to a controller in the params hash, this is similar to the id field. Do not change this symbol, it is a special symbol and must be called :format.One example of using :format would be if we wanted to retrieve XML. Using http://localhost:3000/albums.xml will render xml in the browser, try it. Looking at the albums controller we can see the params[:format] parameter being used.

# GET /albums # GET /albums.xml def index @albums = Album.find(:all) respond_to do |format| format.html # index.html.erb format.xml { render :xml => @albums } end end

As can be seen in the code above, it is the respond_to method that uses the format value to determine the format of the response. At this stage you should be gaining some sort of intuition for Rails routing, right? Now it is time to break our application…

Experimenting 2 – Removing all routes

Open up the config\routes.rb file and delete (or comment out) all the code. Now you should have removed the RESTful route as well as the default route. I will explain the default route (as well as RESTful routes) in future posts. As for now, we are left with an empty file. Behold the naked routes.rb!

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

Run your application through the browser and you will find that our application is completely broken! It should show the error below.

You can aslo use the IRB to check the status of the routes. I did have some problems seeing the updated routes within the IRB. I found that I needed to exit the IRB, then do the ‘load and instantiate’ dance again…

>> exitC:\InstantRails-2.0-win\rails_apps\music_store>ruby script\console Loading development environment (Rails 2.0.2) >> rts = ActionController::Routing::Routes => #<ActionController::Routing::RouteSet:0x3ed12e8 ... more output ... >> puts rts.routes => nil

Perfect, no routes as we expected…

If someone can help with this issue of needing to exit the IRB, I am all ears? If needing to exit the IRB unavoidable, a small script may be handy…

At this stage we have a broken Rails application. In the next post we will set about fixing the music_store application, while doing that we will learn different aspects of routing in Rails.

I realise this is poor to leave you with a broken application, I will attempt to make up for it in the next post…

The End (of part 1)

Well, that is it for the first post. Please let me know if you found this useful. As I said before, I will only continue with this series if there is a demand. Please suggest topics you would like me to cover.

I have a general plan, future posts will cover:

The anatomy of routing rules

The default route

Static routes

Wild card\Receptors

RESTful routing

If people are finding this pace too slow, I might skip directly yo RESTful routing, but this all depends on your feeback.

Thanks, I hope this was useful to you…

Part 2







