9780596521424
routing.html

Chapter 6. Routing

Every web application needs a way of mapping incoming HTTP request URIs to the application logic that will handle the request and return a response to the client. In Rails, this system is called routing. For every requested URI, such as http://example.com/orders/1, the Rails framework needs to know which controller action to execute in order to return a response to the client. HTML pages are full of hyperlinks to other pages, so the application must also be able to generate URIs that the client will use when navigating between the pages of the site in a web browser.

Aside from mapping request URIs to the application's controller actions, there are many other benefits to the Rails routing system. These benefits include: enhanced aesthetics and descriptiveness of URIs, more control over search engine optimization, and the abstraction of an application's URIs from the internal implementation of the application's behavior. Having the URI rewrite engine built-in also makes an application's routing configuration portable between web servers. This has many benefits including: only having to learn a single routing syntax, delayed decision making power of which web server to use, and easily switching from one web server to another.

The Rails routing engine is incredibly powerful, flexible, and easy to use. It is bidirectional, meaning that it can translate back and forth between URIs and their corresponding controller actions. An application's routes are defined using a simple Ruby Domain Specific Language (DSL) in the confg/routes.rb file of an application. The routing DSL gives you the power to:

  • Declare routes to RESTful resources that conform to Rails' RESTful conventions. See Chapter 12, Active Resource for more information.

  • Map arbitrary URI paths to any controller action you wish.

  • Issue redirects from with within the routes file to matching URIs.

  • Declare named routes, which allows you to generate URIs from within your application using a given name.

  • Route directly to a Rack application. See Chapter 10, Rack for more information.

Defining Routes

The first job of the routing system is to map the URI paths of incoming requests to a particular controller action in your application. It works by splitting the request URI into parameters based on rules defined in your application's routes file. The routing file is located in config/routes.rb and is defined using a simple Ruby DSL. The Ruby DSL makes defining complex routes both fast and concise.

Getting a Feel for the Routing DSL

Here's an example routes.rb file to give you an idea of how the DSL works:

Notes::Application.routes.draw do
  root :to => "notes#index"

  get "color/:hexcode" => "colors#show"

  resources "notes"

  controller "site_info" do
    get "faq"
    get "contact"
  end
end

This code shows a few different ways you can define routes in Rails. If multiple routes end up matching the same request, the one defined higher up in the file will be given priority. We can see a listing of the resulting routes by running rake routes:

     root        /                         notes#index
          GET    /color/:hexcode(.:format) colors#show
    notes GET    /notes(.:format)          notes#index
          POST   /notes(.:format)          notes#create
 new_note GET    /notes/new(.:format)      notes#new
edit_note GET    /notes/:id/edit(.:format) notes#edit
     note GET    /notes/:id(.:format)      notes#show
          PUT    /notes/:id(.:format)      notes#update
          DELETE /notes/:id(.:format)      notes#destroy
      faq GET    /faq(.:format)            site_info#faq
  contact GET    /contact(.:format)        site_info#contact

Note

You can also generate the routes just for a specific controller by passing along the CONTROLLER environment variable when invoking rake:

$ rake routes CONTROLLER=orders

The listing shows you the name of the route if it has one, followed by the HTTP verb and path pattern it's triggered by, and finally the controller action assigned to handle matching requests. There are a couple of new syntaxes to learn here: one to describe patterns of request paths, and one to specify a controller action.

  • The path pattern syntax uses colon-prefixed strings (":hexcode", ":id" and ":format" in these routes) to represent dynamic segments which may take arbitrary values, and parentheses (around ".:format" here) to denote optional segments which may be left out of the URI entirely. So for example, the edit_note route will match URIs like "/notes/5/edit" and "/notes/6/edit.html", but not "/notes/edit" (because the :id segment is not optional). The syntax also uses asterisk-prefixed strings for globs, which capture one or more segments into a single parameter.

  • The controller action syntax is just the controller name and the action name joined with a pound sign. The controller name used here is the lowercase, underscored version of the controller's class name, minus the "Controller" ending. So for the faq route above, "site_info#faq" means that it's using the faq method of SiteInfoController to handle requests. If the controller is namespaced in a module, then it shows up as a prefix with a forward slash like "module/controller#action.

You can see both of these syntaxes used in the example routes.rb above. The root method is using the string "notes#index" to route the application's root path ("/") to the index action of NotesController. The next line uses the path pattern "color/:hexcode" to match any two-segment path where the first segment is "color".

Notice that the humble resources :notes line is actually responsible for generating the majority of the routes here. Resources are powerful abstractions and they are the recommended way to do most of your routing in Rails.

The controller "site_info" block defines a couple of routes which are both handled by the same controller. Each route definition in there is relying on some syntax conventions in the DSL which mean that a static, single-segment path pattern (such as "faq" or "contact") is assumed to double as the name of the action being routed to, unless a different action is specified.

It may seem strange that the optional (.:format) segment is included on all the routes, even though it was never specified in routes.rb. Rails does this to provide a standardized way of accessing the same URIs in different formats (.html, .json, or whatever else your controller action is written to handle).

Resources

Note

The examples in this chapter assume that you've already generated a new Rails application. If you haven't, then take a look at Chapter 2, Rails in a Nutshell for information on getting started.

The conventional way of configuring routes for a Rails application is to build everything around resources. A resource is an entity that you expose to the outside world at a particular URI. If someone wants to access one of your application's resources, they send your application a request using one of the HTTP verbs. If they just want to see the resource, they use GET. If they want to update it, they use PUT. POST is for creating new resources, and DELETE is for destroying them.

In Rails, resources are declared in the routing system and implemented as actions in a controller whose job it is to handle that one kind of resource. Here's how you'd define routes for an orders resource:

You always need to define your routes within the routes.draw block, but for simplicity and succinctness we'll only be showing the contents of the draw block in the upcoming examples.

resources :orders

As you can see from the output of rake routes, the resources :orders definition generates seven routes for you:

$ rake routes

           GET    /orders(.:format)          {:controller=>"orders", :action=>"index"}
    orders POST   /orders(.:format)          {:controller=>"orders", :action=>"create"}
 new_order GET    /orders/new(.:format)      {:controller=>"orders", :action=>"new"}
           GET    /orders/:id(.:format)      {:controller=>"orders", :action=>"show"}
           PUT    /orders/:id(.:format)      {:controller=>"orders", :action=>"update"}
     order DELETE /orders/:id(.:format)      {:controller=>"orders", :action=>"destroy"}
edit_order GET    /orders/:id/edit(.:format) {:controller=>"orders", :action=>"edit"}

There are two main path patterns being matched here: /orders (the collection URI) and /orders/:id (the member or element URI). Each is routed to a different controller action depending on which HTTP verb is being used. In addition, the new_order and edit_order routes are mapped to special actions which have their own unique URIs relative to the collection and member paths, respectively.

Table 6.1. RESTful Rails HTTP to controller action mapping

HTTP VerbRequest URIController ActionHelpers
The :id in URI represents the ID of the resource in a request.
GET/ordersindexorders_path, orders_url
POST/orderscreateorders_path, orders_url
GET/orders/newnewnew_order_path, new_order_url
GET/orders/:id/editeditedit_order_path, edit_order_url
GET/orders/:idshoworder_path, order_url
PUT/orders/:idupdateorder_path, order_url
DELETE/orders/:iddestroyorder_path, order_url

So, what should all these controller actions do? It's up to you how you implement these behaviours in the controllers, but here's a rundown of the conventional approach:

index (GET /orders)

This action responds with a list of the resources in question. The params of the GET request (provided by the query string) are often used to constrain the results in one way or another, such as for controlling pagination or filtering based on matched attributes.

new (GET /orders/new)

For HTML requests, this action responds with a screen allowing the user to create a new instance of the resource, typically through a standard web form. For JSON or XML requests, you can use this action to respond with an example of a new (unsaved) instance of the resource. It is not uncommon to leave this action un-implemented if you provide ways of creating instances of the resource which don't require a separate screen (for example, a small form included on the index screen).

create (POST /orders)

This action uses the params provided in a POST request to create a new instance of the resource.

show (GET /orders/:id)

This action returns the single resource which has the specified ID.

edit (GET /orders/:id/edit)

This action is usually only used for HTML requests, and responds with a form to edit a single resource with the specified ID. You might leave out this action if your show view includes a way of editing the resource.

update (PUT /orders/:id)

This action uses the params provided in a PUT request to update a single resource with new attributes.

destroy (DELETE /orders/:id)

This action destroys a resource. Depending on how much you like keeping stuff around, this might not mean actually removing its record from your database. But whatever you do with it behind the scenes, a deleted resource should somehow be made invisible to future requests (the index action shouldn't include it in future results, the show action should respond with a 404 for its ID, etc.).

Note

The :id param usually holds the numeric, auto-incremented id of a record in the database, but you don't need to identify resources this way. For example, you might want your user resource URIs to look like /users/karen instead of /users/83629. To accomplish this, all you have to do is look them up by their nicknames instead of by numeric ID:

def show
  @user = User.find_by_nickname!(params[:id])
  respond_with(@user)
end

This looks a little weird because params[:id] implies that we're expecting a database ID, not a nickname. In Rails 4, you will be able to specify the name of the key passed in to the controller params like so:

resources "users", :param => :nickname   # get it with params[:nickname]

If you only want a resource to respond to some of the above actions but not others, you can use the :only option. For example, you might have a read-only resource:

resources "fibonacci_numbers", :only => ["index", "show"]

Custom Resource Actions

You can add custom actions to a resource, too. The routing DSL uses a nested block syntax for this:

resources "templates" do
  member do
    get "render"    # GET /templates/:id/render
  end
  collection do
    get "count"     # GET /templates/count
    put "reorder"   # PUT /templates/reorder
  end
end

The member block is for actions which work on a single instance of the resource, using the member path pattern as a base. So the get :render route defined above will route GET requests to paths matching /templates/:id/render. The collection block adds actions using the collection path as a base, so from the above example we can GET /templates/count and PUT /templates/reorder.

Instead of using the block methods, you can use an :on option in the route definitions to specify them as either member or collection routes. So the above could be rewritten like this:

resources "templates" do
  get "render",  :on => :member
  get "count",   :on => :collection
  put "reorder", :on => :collection
end

Custom resource routes aren't limited to fixed, single-segment paths like "render" and "count"; they can use the full routing pattern syntax described at the beginning of this chapter. Here's an articles resource which has an extra route to the index action:

resources "articles" do
  collection do
    get ":author", :action => "index"
  end
end

This would let you use paths like /articles/jenkins to show articles by a particular author; you just need to include code in the index action which uses params[:author] to scope your results with Active Record (or whichever persistence mechanism you might be using). More information on constructing individual route definitions can be found beginning on the section called “Routing Any Path You Like”.

Singleton Resources

A singleton or singular resource is like a regular one except it only has one base URI for its single member. A good example of a singleton resource would be the account of the currently logged-in user:

resource :account
$ rake routes

             POST   /account(.:format)      {:controller=>"accounts", :action=>"create"}
 new_account GET    /account/new(.:format)  {:controller=>"accounts", :action=>"new"}
             GET    /account(.:format)      {:controller=>"accounts", :action=>"show"}
             PUT    /account(.:format)      {:controller=>"accounts", :action=>"update"}
     account DELETE /account(.:format)      {:controller=>"accounts", :action=>"destroy"}
edit_account GET    /account/edit(.:format) {:controller=>"accounts", :action=>"edit"}

As you can see from the output of rake routes, the controller for the singleton account resource is still the AccountsController in plural. Singleton resources use the same controller naming scheme as regular resources.

Note

Always using plural resource controller names isn't just a nice way to keep things predictable: it also makes it really easy to set up both a singular and plural resource tied to the same controller:

resource  :account
resources :accounts

To make this work, you just need to write the actions of AccountsController so that the currently logged-in user is loaded by default in member actions when params[:id] is not present to specify one.

Nested Resources

You can nest resources inside one another, such that a member URI of one resource is used as the base for another. The following code lets you access members of the line_items resource at paths like /orders/5/line_items/6:

resources :orders do
  resources :line_items
end
$ rake routes

                     GET    /orders/:order_id/line_items(.:format)          {:controller=>"line_items", :action=>"index"}
    order_line_items POST   /orders/:order_id/line_items(.:format)          {:controller=>"line_items", :action=>"create"}
 new_order_line_item GET    /orders/:order_id/line_items/new(.:format)      {:controller=>"line_items", :action=>"new"}
                     GET    /orders/:order_id/line_items/:id(.:format)      {:controller=>"line_items", :action=>"show"}
                     PUT    /orders/:order_id/line_items/:id(.:format)      {:controller=>"line_items", :action=>"update"}
     order_line_item DELETE /orders/:order_id/line_items/:id(.:format)      {:controller=>"line_items", :action=>"destroy"}
edit_order_line_item GET    /orders/:order_id/line_items/:id/edit(.:format) {:controller=>"line_items", :action=>"edit"}
                     GET    /orders(.:format)                               {:controller=>"orders", :action=>"index"}
              orders POST   /orders(.:format)                               {:controller=>"orders", :action=>"create"}
           new_order GET    /orders/new(.:format)                           {:controller=>"orders", :action=>"new"}
                     GET    /orders/:id(.:format)                           {:controller=>"orders", :action=>"show"}
                     PUT    /orders/:id(.:format)                           {:controller=>"orders", :action=>"update"}
               order DELETE /orders/:id(.:format)                           {:controller=>"orders", :action=>"destroy"}
          edit_order GET    /orders/:id/edit(.:format)                      {:controller=>"orders", :action=>"edit"}

As you can see, the nested line_item routes provide :id as the param for the line item and :order_id as the param for the order. In LineItemsController, the controller actions and/or filters should somehow guard against accessing a particular line item through a mismatched :order_id. In other words, if the the line item with id 6 belongs to the order with id 5, then performing a GET on /orders/4/line_items/6 or /orders/99/line_items/6 should return a 404 not found response.

You can nest singleton resources too, and it probably works exactly how you expect:

resources :orders do
  resource :shipping_address
end
$ rake routes

                            POST   /orders/:order_id/shipping_address(.:format)      {:controller=>"shipping_addresses", :action=>"create"}
 new_order_shipping_address GET    /orders/:order_id/shipping_address/new(.:format)  {:controller=>"shipping_addresses", :action=>"new"}
                            GET    /orders/:order_id/shipping_address(.:format)      {:controller=>"shipping_addresses", :action=>"show"}
                            PUT    /orders/:order_id/shipping_address(.:format)      {:controller=>"shipping_addresses", :action=>"update"}
     order_shipping_address DELETE /orders/:order_id/shipping_address(.:format)      {:controller=>"shipping_addresses", :action=>"destroy"}
edit_order_shipping_address GET    /orders/:order_id/shipping_address/edit(.:format) {:controller=>"shipping_addresses", :action=>"edit"}
                            GET    /orders(.:format)                                 {:controller=>"orders", :action=>"index"}
                     orders POST   /orders(.:format)                                 {:controller=>"orders", :action=>"create"}
                  new_order GET    /orders/new(.:format)                             {:controller=>"orders", :action=>"new"}
                            GET    /orders/:id(.:format)                             {:controller=>"orders", :action=>"show"}
                            PUT    /orders/:id(.:format)                             {:controller=>"orders", :action=>"update"}
                      order DELETE /orders/:id(.:format)                             {:controller=>"orders", :action=>"destroy"}
                 edit_order GET    /orders/:id/edit(.:format)                        {:controller=>"orders", :action=>"edit"}

Routing Any Path You Like

The same methods used to make custom actions for resources can be used to make standalone routes that match whatever patterns you can dream up. This section describes in greater detail how to create create individual routes, whether or not they are scoped to a resource.

Maybe we just want an "about us" page with no fuss. Okay, assuming we have a PagesController with an about action:

get "about" => "pages#about"

We don't really need to specify the action in this case, because Rails makes the assumption that a fixed single-segment path pattern is the action name unless otherwise specified. On the other hand, there's definitely something to be said for being explicit. The following lines all define equivalent routes:

# all of these routes lead to PagesController#about:
get "about" => "pages#about"
get "about" => "pages"
get "about", :to => "pages#about"
get "about", :to => "pages"
get "about", :controller => "pages"

If you want your route to match some other HTTP method, that works too:

post "webhooks/github_post_receive"

In this case, because we've provided a fixed two-segment pattern, Rails will assume that the first segment is the controller name and the second segment is the action.

Tip

Whenever you want to expose some new part of your application to the outside world, Rails encourages you to do it with resources. Even if it isn't backed by an ActiveRecord model, even if it doesn't seem like a "thing" at all, you will likely find that describing the functionality strictly in terms of applying HTTP verbs to a resource makes everything a lot more straightforward.

Accepting More Than One HTTP Method

If you need a route to match multiple HTTP methods, you can use the match method with the :via option:

match "big-red-button", :to => "control_panel#button", :via => [:get, :post]

If you use match without the :via option, it will match requests that use any method. This is usually not the right thing to do, but it can be handy with redirects (see the section called “Redirects”).

Most of the time, you should stick to one method per controller action. This will usually keep your code cleaner and make it easier to know that you aren't allowing GET requests to trigger side effects, which is important in preventing security threats such as cross-site request forgery. If you want the same URI to respond to multiple methods, the alternative to one multi-verb action is to use two routes which each point to a separate action:

# but really, this should use resources:
get  "big-red-button" => "control_panel#show_button"
post "big-red-button" => "control_panel#push_button"

Dynamic Segments

Colons designate dynamic segments. Here's a route from the example at the beginning of this chapter:

get "color/:hexcode" => "colors#show"

This lets you access the second path segment as params[:hexcode] in the routed controller action. There are three segment names which have special meaning here:

  • :controller and :action segments act as if you'd provided the segment's value as the controller or action of the route, so a ":controller/:action" route would dispatch /foo/bar to FooController#bar

  • :format specifies the format of the request, and if you include it in a path pattern then it replaces the optional (.:format) segment that would otherwise be appended by default.

Tip

If you don't want a route to match a :format segment at all, you can pass :format => false as an option:

get "welcome" => "pages", :format => false
# /welcome.json or /welcome.html would not match this route

Segments are delimited by both forward slashes (/) and periods (.), so normally the value of a single dynamic segment never includes either character. If you want to get around this, you can either use globs (see below) or override the matched characters with a regular expression constraint (see

Optional Segments

Use parentheses for optional segments, which can be nested:

get "articles(/:author(/:topic))" => "articles#index"

The above route would match paths like /articles, /articles/jenkins, and /articles/jenkins/playground-gossip. For optional segments which are missing from a matching path, the corresponding params key simply isn't assigned. Your controllers must be written to accomodate whichever possible mix of params that you've designed your routes to throw at them.

Globs

If you want to match multiple segments of a path, you can use an asterisk to capture them all into a single param:

get "articles/*tags" => "articles#index"

This route would match on a path like "articles/silly/politics/facepalm" and in the controller you'd end up with params[:tags] equal to "silly/politics/facepalm". If you have a required segment defined after the glob, it'll get matched before the blob gobbles it up:

get "articles/*tags/:last_tag" => "articles#index"

# gives you the following params:
# {
#   "controller" => "articles",
#   "action" => "index",
#   "tags" => "silly/politics",
#   "last_tag" => "facepalm"
# }

However, a glob will roll right over an optional segment: "articles/*tags(/:last_tag)" would never end up filling params[:last_tag] with anything. An important exception to this is the ubiquitous (.:format) segment appended to all routes by default; unless you get rid of it with :format => false, it will match the last dot-delimited segment even if it's preceded by a glob.

Specifying Parameter Defaults

When defining routes that contain optional named parameters, it is useful to be able to define a default value for the optional parameter in the route for when it isn't provided. A hash of defaults can be passed to the route definition itself or you can specify routes within a defaults block.

Defaults are passed by name as additional options to the routing definition. For example, to set a default :handle parameter when none is provided, the route would look as follows:

get "pages(/:handle)", :to => "pages#show", :handle => "home"

With the default provided for :handle, whenever the path /pages is requested the :handle parameter will be set to home. To specify defaults for multiple routes you can pass the defaults to the defaults method and define all routes that you want the defaults to apply to within the block passed to the defaults method:

defaults :handle => "home" do
  get "pages(/:handle)"    => "pages#show"
  get "articles(/:handle)" => "articles#show"
end

Parameter defaults work for any route definition, not just ones with optional path segments:

get "apps", :to => "apps#index", :filter => "featured"

The :filter param can then be overridden in the query string, as in /apps?filter=all. In a case like this, though, you'd probably want to define the default value in the controller action code; that's the first place people will look if they're trying to figure out how the response is constructed.

Simple Constraints

If you want to limit a dynamic segment to a specific pattern of characters, you can use the :constraints option, which takes a hash of segment params mapped to regular expressions:

get "color/:hexcode" => "colors#show",
  :constraints => {:hexcode => /[A-Fa-f0-9]{6}/}

The regular expressions are automatically wrapped in beginning- and end-of-string anchors, so you should leave out any anchors of your own (^, $, \A, \Z, or \z). In fact if you try to include them, you'll get an ArgumentError.

Note

If your regular expression matches either of the segment delimiters ("/" or "."), then this overrides the normal behaviour and the segment value will include them. This means that if you end your regular expression in .*, the segment in question will dutifully gobble up all the characters it can, whether or not they would otherwise be matched by another segment.

You can also easily place constraints on attributes of the incoming request. You can use regular expressions just like segment constraints, but you'll often just want to match a particular string:

get "secrets" => "secrets#index",
  :constraints => {:subdomain => "admin"}

Any of the public methods on the request object which return strings are fair game here. For more information about request objects in Rails, see ???.

Advanced Constraints

If you want to go beyond regular expressions, you can. To bring out the big guns, you have to skip the regex hash and supply the :constraints option with one of two things: an object that implements a matches? method, or a callable object like a Proc.

The first way is probably the most common, and the most straightforward approach is to define a little class above your routes, like so:

class SpecialConstraint
  def matches?(request)
    request.headers.key?("X-Special")
  end
end

MyApp::Application.routes.draw do
  get "foo" => "special#foo", :constraints => SpecialConstraint.new
  get "foo" => "boring#foo"
end

The matches? method must take a single argument (the request object), and it should return true or false to indicate a matching or non-matching request. Here we have two routes matching the same path, but they get switched between based on HTTP request headers. Because the constrained route is defined first, the "boring" route ends up as the fallback.

The above logic is simple enough that you could also go the other route and use a Proc object (instantiated here with lambda):

get "foo" => "special#foo",
  :constraints => (lambda {|req| req.headers.key?("X-Special")})

Note

You can define constraints of any sort for multiple routes at a time with a constraints block:

constraints :subdomain => "begins-with-j", :name => /[Jj]\w*/ do
  get "people/:name" => "people#show"
  get "places/:name" => "places#show"
end

Named Routes

Named routes are essentially the same as routes defined using match in that they match incoming URIs to controller actions. The advantage of named routes is that they provide you with some additional helpers that can be used in your controllers and views. Each named route provides you with two helpers of the form name_url and name_path, where name is the name you've given the named route. For example, if we generate the following named route:

match 'orders' => 'orders#index', :as => 'orders'
$ rake routes

orders  /orders {:controller=>"orders", :action=>"index"}

You then have access to orders_url and orders_path helpers in your controllers and views.

users_url  #=> "http://localhost:3000/users"
users_path #=> "/users"

Tip

*_url or *_path? A decent rule of thumb is that you should use *_url in controllers and *_path in views. This is because href links in an HTML view are implicitly linked to the current URI of the page. In a controller, though, chances are good that you're generating that URI for a redirect of some sort. In that case you need to use *_url because the HTTP specification mandates that an absolute URI is used for the Location header in 3xx redirects.

Of course, there are exceptions to every rule. If you are rendering a link where the entire host, port and domain are important, such as an RSS feed, you'll need to use *_url to get an absolute URI.

Specifying a root Route

The root route tells your application which controller and action to send requests to the root of your application. The root route is defined as follows:

root :to => 'dashboard#index'
$ rake routes

root  / {:controller=>"dashboard", :action=>"index"}

In this example we're routing the root URI of the application to the index action of the DashboardController. A named route is also defined for the root route, which provides you with root_url and root_path

Scopes

Basically every time you see a block in the routing DSL, there's a scope behind the scenes working its magic. Scopes wrap multiple routes in a block, letting you do a few very useful things to all the enclosed routes at once:

  • confer default options

  • give them a common path prefix

  • give them a common name prefix

A resources block makes use of all of these capabilities in a rather complicated way to make everything work. A constraints block, on the other hand, simply sets a default value for the :constraints option on the enclosed routes.

You can craft your own scopes with the scope method. Dealing with default route options is pretty straightforward:

scope :controller => "forest" do
  get "trees"
  get "bears"
end

To use a common prefix for your routes' path patterns, use the :path option or just provide a string as the first argument like you'd do with a single route definition. The prefix can use dynamic and optional segments just like the routes themselves do. Here's a routes file for an application where any path can be prefixed with an optional segment to select the locale code:

Localized::Application.routes.draw do
  constraints :locale => /[a-z]{2}(-[A-Z]{2})?/ do
    scope "(:locale)" do
      root :to => "welcome#index"

      resources "users", "locales", "articles"
    end
  end
end

Note that without the constraints on :locale, the path /users would be routed to "welcome#index" because "users" would match as the locale. Be careful when you have a dynamic segment at the beginning of a path; it's more likely to conflict with other routes.

To prefix the names of the enclosed routes, use the :as option. Combined with a custom path, this can be useful for managing different ways of accessing the same resource:

resources "bears"
scope ":emotion", :as => "emotional" do
  resources "bears"
end

This would give you a second set of named resource routes like emotional_bears and new_emotional_bear. For example you could link to /angry/bears with emotional_bears_path(:emotion => "angry").

Scopes are a powerful tool to help you create the routes needed to fit your application's unique requirements. Let's say we have a files resource that we really want to reference by path instead of by numeric id. File paths of course can contain both forward slashes and periods, and they usually end in their own extension. This means that you might want to step away from the Rails conventions a little bit by forgoing the usual trailing (.:format) segment. Here's one way we could do that:

scope "(:format)", :except => [:edit], :constraints => {:id => /.*/} do
  resources "files"
end

The :id constraint overrides the normal delimiters and gives that segment the whole rest of the path for the resource member routes that it's defined in. The :except option gets rid of the edit action on any enclosed resources, because that trailing /edit on the member paths would now just be assumed to be part of the :id segment.

Namespaces

A namespace block is a scope that basically does three things to the routes inside: it prefixes paths, it prefixes names, and it specifies a namespace module where the controllers can be found. For example, let's define the singleton account resource underneath the admin namespace:

namespace :admin do
  resource :account
end
$ rake routes

                   POST   /admin/account(.:format)      {:controller=>"admin/accounts", :action=>"create"}
 new_admin_account GET    /admin/account/new(.:format)  {:controller=>"admin/accounts", :action=>"new"}
                   GET    /admin/account(.:format)      {:controller=>"admin/accounts", :action=>"show"}
                   PUT    /admin/account(.:format)      {:controller=>"admin/accounts", :action=>"update"}
     admin_account DELETE /admin/account(.:format)      {:controller=>"admin/accounts", :action=>"destroy"}
edit_admin_account GET    /admin/account/edit(.:format) {:controller=>"admin/accounts", :action=>"edit"}

As you can see from the rake routes output, the route names, path patterns, and controllers are all namespaced in their own way.

Redirects

Instead of routing to a controller and action, you can define a route that will issue a permanent redirect to some other location. Rails provides you with a redirect helper method to do this. Here's the simplest case:

match "foo" => redirect("/bar")

Any request for /foo will then get a 301 response redirecting them to /bar instead. If you want to use params from the requested path to construct the new path, you can use the following syntax:

get "old/:tricks" => redirect("/new/%{tricks}")

Passing an options hash instead of a string will only change the specified elements of the URI:

match "/admin(/*whatever)", :constraints => {:protocol => "http://"},
  :to => redirect(:protocol => "https")

You can also pass a block, which takes the params and request object as arguments, and then you can use whatever logic you want:

match "quiet/:something" => redirect {|params, request|
  "/LOUD/#{params[:something].upcase}"
}

Generating URIs From Routes

Generating URIs with code, instead of placing hard coded strings into your application, provides a layer of abstraction that makes your application more robust, maintainable, testable, and adaptable to change. Not only that, but generating URIs with the Rails helpers is easier, faster, and produces more aesthetically pleasing view templates than writing the URIs by hand.

The preferred way to generate URIs is with the methods provided by named routes. Consider the following route:

get "faq" => "pages#faq"

Rails sensibly gives this route the name of faq, which means we get two methods available in our controllers and views to generate URIs for it: faq_path and faq_url.

faq_path # => "/faq"
faq_url  # => "http://localhost:3000/faq"

If you don't have a named route handy, you can also use url_for and specify the controller and action with a hash:

url_for :controller => "pages", :action => "faq", :only_path => true
# => "/faq"

Many other methods take a hash of this sort which ends up getting passed to url_for. With either named route methods or url_for, extra parameters which aren't reserved option names will get slotted in as either path params or query params. For example, here's a route we've seen before, but now it has a name:

get "color/:hexcode" => "colors#show", :as => "color"

Now we can use color_path with some options and see what it gives us:

color_path(:hexcode => "3a2b4c", :layout => "condensed")
# => "/color/3a2b4c?layout=condensed"
Site last updated on: December 11, 2012 at 10:21:28 PM PST
Cover for Rails 3 in a Nutshell

View 1 comment

  1. ProfessorPixel – Posted March 3, 2011

    It should read "config/routes.rb"

Add a comment

View 1 comment

  1. Shripad – Posted Aug. 26, 2010

    No mention of the route's syntax with the :to option.

Add a comment

View 1 comment

  1. mikel2000 – Posted April 22, 2011

    Wouldn't the above be orders_url: http://localhost/orders orders_path: /orders

Add a comment

View 2 comments

  1. winterheat – Posted March 10, 2011

    I think over here, it'd be good to add explanation about /orders/new is a plain form, and so are /orders/edit and /orders (same as /orders/index) The 4 actions of CRUD are the rest 4: POST to /orders for create, GET /orders/id for retrieve, POST to /orders/id with _method=put for update, POST to /orders/id with _method=delete for destroy. References always use PUT and DELETE but in reality Rails use POST with the _method variable to do the update and destroy instead.

  2. winterheat – Posted March 10, 2011

    that's _method=put and _method=delete for the update and destroy.

Add a comment