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
endThis 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#contactNote
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_noteroute will match URIs like"/notes/5/edit"and"/notes/6/edit.html", but not"/notes/edit"(because the:idsegment 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
faqroute above,"site_info#faq"means that it's using thefaqmethod ofSiteInfoControllerto 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 Verb | Request URI | Controller Action | Helpers |
|---|---|---|---|
The :id in URI
represents the ID of the resource in a
request. | |||
GET | /orders | index | orders_path, orders_url |
POST | /orders | create | orders_path, orders_url |
GET | /orders/new | new | new_order_path, new_order_url |
GET | /orders/:id/edit | edit | edit_order_path, edit_order_url |
GET | /orders/:id | show | order_path, order_url |
PUT | /orders/:id | update | order_path, order_url |
DELETE | /orders/:id | destroy | order_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
indexscreen).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
showview includes a way of editing the resource.update(PUT/orders/:id)This action uses the params provided in a
PUTrequest 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
indexaction shouldn't include it in future results, theshowaction 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
endThe 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
endThis 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:
:controllerand:actionsegments 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/bartoFooController#bar:formatspecifies 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"
endThe 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
endNote 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"
endThe :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"





Add a comment



Add a comment