From 0d01a737a2110d4dbcdac678a8f74c9b77010502 Mon Sep 17 00:00:00 2001 From: Prem Sichanugrist Date: Mon, 1 Feb 2010 11:50:44 +0700 Subject: Modify the documentation for routing to add the new DSL format of Rails 3 --- railties/guides/source/routing.textile | 206 +++++++++++++++++---------------- 1 file changed, 105 insertions(+), 101 deletions(-) diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index 24f0578545..988c210074 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -45,7 +45,7 @@ There are two components to routing in Rails: the routing engine itself, which i h4. Processing the File -In format, +routes.rb+ is nothing more than one big block sent to +ActionController::Routing::Routes.draw+. Within this block, you can have comments, but it's likely that most of your content will be individual lines of code - each line being a route in your application. You'll find five main types of content in this file: +In format, +routes.rb+ is nothing more than one big block sent to +ApplicationName::Application.routes.draw+. Within this block, you can have comments, but it's likely that most of your content will be individual lines of code - each line being a route in your application. You'll find five main types of content in this file: * RESTful Routes * Named Routes @@ -62,24 +62,32 @@ h4. RESTful Routes RESTful routes take advantage of the built-in REST orientation of Rails to wrap up a lot of routing information in a single declaration. A RESTful route looks like this: -map.resources :books +resources :books h4. Named Routes Named routes give you very readable links in your code, as well as handling incoming requests. Here's a typical named route: + +match 'login' => 'sessions#new', :as => 'login' + + +If you're coming from Rails 2, this route will be equivalent to: + map.login '/login', :controller => 'sessions', :action => 'new' +You will also notice that +sessions#new+ is a shorthand for +:controller => 'sessions', :action => 'new'+ + h4. Nested Routes Nested routes let you declare that one resource is contained within another resource. You'll see later on how this translates to URLs and paths in your code. For example, if your application includes parts, each of which belongs to an assembly, you might have this nested route declaration: -map.resources :assemblies do |assemblies| - assemblies.resources :parts +resources :assemblies do + resources :parts end @@ -88,7 +96,7 @@ h4. Regular Routes In many applications, you'll also see non-RESTful routing, which explicitly connects the parts of a URL to a particular action. For example, -map.connect 'parts/:number', :controller => 'inventory', :action => 'show' +match 'parts/:number' => 'inventory#show' h4. Default Routes @@ -96,8 +104,7 @@ h4. Default Routes The default routes are a safety net that catch otherwise-unrouted requests. Many Rails applications will contain this pair of default routes: -map.connect ':controller/:action/:id' -map.connect ':controller/:action/:id.:format' +match ':controller(/:action(/:id(.:format)))' These default routes are automatically generated when you create a new Rails application. If you're using RESTful routing for everything in your application, you will probably want to remove them. But be sure you're not using the default routes before you remove them! @@ -126,7 +133,7 @@ h4. CRUD, Verbs, and Actions In Rails, a RESTful route provides a mapping between HTTP verbs, controller actions, and (implicitly) CRUD operations in a database. A single entry in the routing file, such as -map.resources :photos +resources :photos creates seven different routes in your application: @@ -164,26 +171,26 @@ photos_path # => "/photos" h4. Defining Multiple Resources at the Same Time -If you need to create routes for more than one RESTful resource, you can save a bit of typing by defining them all with a single call to +map.resources+: +If you need to create routes for more than one RESTful resource, you can save a bit of typing by defining them all with a single call to +resources+: -map.resources :photos, :books, :videos +resources :photos, :books, :videos This has exactly the same effect as -map.resources :photos -map.resources :books -map.resources :videos +resources :photos +resources :books +resources :videos h4. Singular Resources -You can also apply RESTful routing to singleton resources within your application. In this case, you use +map.resource+ instead of +map.resources+ and the route generation is slightly different. For example, a routing entry of +You can also apply RESTful routing to singleton resources within your application. In this case, you use +resource+ instead of +resources+ and the route generation is slightly different. For example, a routing entry of -map.resource :geocoder +resource :geocoder creates six different routes in your application: @@ -226,7 +233,7 @@ h5. Using +:controller+ The +:controller+ option lets you use a controller name that is different from the public-facing resource name. For example, this routing entry: -map.resources :photos, :controller => "images" +resources :photos, :controller => "images" will recognize incoming URLs containing +photo+ but route the requests to the Images controller: @@ -247,7 +254,7 @@ h4. Controller Namespaces and Routing Rails allows you to group your controllers into namespaces by saving them in folders underneath +app/controllers+. The +:controller+ option provides a convenient way to use these routes. For example, you might have a resource whose controller is purely for admin users in the +admin+ folder: -map.resources :adminphotos, :controller => "admin/photos" +resources :adminphotos, :controller => "admin/photos" If you use controller namespaces, you need to be aware of a subtlety in the Rails routing code: it always tries to preserve as much of the namespace from the previous request as possible. For example, if you are on a view generated from the +adminphoto_path+ helper, and you follow a link generated with +<%= link_to "show", adminphoto(1) %>+ you will end up on the view generated by +admin/photos/show+, but you will also end up in the same place if you have +<%= link_to "show", {:controller => "photos", :action => "show"} %>+ because Rails will generate the show URL relative to the current URL. @@ -257,13 +264,13 @@ TIP: If you want to guarantee that a link goes to a top-level controller, use a You can also specify a controller namespace with the +:namespace+ option instead of a path: -map.resources :adminphotos, :namespace => "admin", :controller => "photos" +resources :adminphotos, :namespace => "admin", :controller => "photos" -This can be especially useful when combined with +with_options+ to map multiple namespaced routes together: +This can be especially useful when map multiple namespaced routes together using +namespace+ block by: -map.with_options(:namespace => "admin") do |admin| +namespace :admin do admin.resources :photos, :videos end @@ -275,7 +282,7 @@ h5. Using +:singular+ If for some reason Rails isn't doing what you want in converting the plural resource name to a singular name in member routes, you can override its judgment with the +:singular+ option: -map.resources :teeth, :singular => "tooth" +resources :teeth, :singular => "tooth" TIP: Depending on the other code in your application, you may prefer to add additional rules to the +Inflector+ class instead. @@ -285,7 +292,7 @@ h5. Using +:requirements+ You can use the +:requirements+ option in a RESTful route to impose a format on the implied +:id+ parameter in the singular routes. For example: -map.resources :photos, :requirements => {:id => /[A-Z][A-Z][0-9]+/} +resources :photos, :requirements => {:id => /[A-Z][A-Z][0-9]+/} This declaration constrains the +:id+ parameter to match the supplied regular expression. So, in this case, +/photos/1+ would no longer be recognized by this route, but +/photos/RR27+ would. @@ -299,7 +306,7 @@ h5. Using +:as+ The +:as+ option lets you override the normal naming for the actual generated paths. For example: -map.resources :photos, :as => "images" +resources :photos, :as => "images" will recognize incoming URLs containing +image+ but route the requests to the Photos controller: @@ -320,7 +327,7 @@ h5. Using +:path_names+ The +:path_names+ option lets you override the automatically-generated "new" and "edit" segments in URLs: -map.resources :photos, :path_names => { :new => 'make', :edit => 'change' } +resources :photos, :path_names => { :new => 'make', :edit => 'change' } This would cause the routing to recognize URLs such as @@ -343,7 +350,7 @@ h5. Using +:path_prefix+ The +:path_prefix+ option lets you add additional parameters that will be prefixed to the recognized paths. For example, suppose each photo in your application belongs to a particular photographer. In that case, you might declare this route: -map.resources :photos, :path_prefix => '/photographers/:photographer_id' +resources :photos, :path_prefix => '/photographers/:photographer_id' Routes recognized by this entry would include: @@ -362,9 +369,9 @@ h5. Using +:name_prefix+ You can use the :name_prefix option to avoid collisions between routes. This is most useful when you have two resources with the same name that use +:path_prefix+ to map differently. For example: -map.resources :photos, :path_prefix => '/photographers/:photographer_id', +resources :photos, :path_prefix => '/photographers/:photographer_id', :name_prefix => 'photographer_' -map.resources :photos, :path_prefix => '/agencies/:agency_id', +resources :photos, :path_prefix => '/agencies/:agency_id', :name_prefix => 'agency_' @@ -377,7 +384,7 @@ h5. Using +:only+ and +:except+ By default, Rails creates routes for all seven of the default actions (index, show, new, create, edit, update, and destroy) for every RESTful route in your application. You can use the +:only+ and +:except+ options to fine-tune this behavior. The +:only+ option specifies that only certain routes should be generated: -map.resources :photos, :only => [:index, :show] +resources :photos, :only => [:index, :show] With this declaration, a +GET+ request to +/photos+ would succeed, but a +POST+ request to +/photos+ (which would ordinarily be routed to the create action) will fail. @@ -385,7 +392,7 @@ With this declaration, a +GET+ request to +/photos+ would succeed, but a +POST+ The +:except+ option specifies a route or list of routes that should _not_ be generated: -map.resources :photos, :except => :destroy +resources :photos, :except => :destroy In this case, all of the normal routes except the route for +destroy+ (a +DELETE+ request to +/photos/id+) will be generated. @@ -411,12 +418,12 @@ end Each ad is logically subservient to one magazine. Nested routes allow you to capture this relationship in your routing. In this case, you might include this route declaration: -map.resources :magazines do |magazine| - magazine.resources :ads +resources :magazines do + resources :ads end -TIP: Further below you'll learn about a convenient shortcut for this construct:
+map.resources :magazines, :has_many => :ads+ +TIP: Further below you'll learn about a convenient shortcut for this construct:
+resources :magazines, :has_many => :ads+ In addition to the routes for magazines, this declaration will also create routes for ads, each of which requires the specification of a magazine in the URL: @@ -437,16 +444,16 @@ h5. Using +:name_prefix+ The +:name_prefix+ option overrides the automatically-generated prefix in nested route helpers. For example, -map.resources :magazines do |magazine| - magazine.resources :ads, :name_prefix => 'periodical' +resources :magazines do + resources :ads, :name_prefix => 'periodical' end This will create routing helpers such as +periodical_ads_url+ and +periodical_edit_ad_path+. You can even use +:name_prefix+ to suppress the prefix entirely: -map.resources :magazines do |magazine| - magazine.resources :ads, :name_prefix => nil +resources :magazines do + resources :ads, :name_prefix => nil end @@ -462,16 +469,16 @@ h5. Using +:has_one+ and +:has_many+ The +:has_one+ and +:has_many+ options provide a succinct notation for simple nested routes. Use +:has_one+ to nest a singleton resource, or +:has_many+ to nest a plural resource: -map.resources :photos, :has_one => :photographer, :has_many => [:publications, :versions] +resources :photos, :has_one => :photographer, :has_many => [:publications, :versions] This has the same effect as this set of declarations: -map.resources :photos do |photo| - photo.resource :photographer - photo.resources :publications - photo.resources :versions +resources :photos do + resource :photographer + resources :publications + resources :versions end @@ -480,9 +487,9 @@ h5. Limits to Nesting You can nest resources within other nested resources if you like. For example: -map.resources :publishers do |publisher| - publisher.resources :magazines do |magazine| - magazine.resources :photos +resources :publishers do + resources :magazines do + resources :photos end end @@ -502,9 +509,9 @@ h5. Shallow Nesting The +:shallow+ option provides an elegant solution to the difficulties of deeply-nested routes. If you specify this option at any level of routing, then paths for nested resources which reference a specific member (that is, those with an +:id+ parameter) will not use the parent path prefix or name prefix. To see what this means, consider this set of routes: -map.resources :publishers, :shallow => true do |publisher| - publisher.resources :magazines do |magazine| - magazine.resources :photos +resources :publishers, :shallow => true do + resources :magazines do + resources :photos end end @@ -522,7 +529,7 @@ This will enable recognition of (among others) these routes: With shallow nesting, you need only supply enough information to uniquely identify the resource that you want to work with. If you like, you can combine shallow nesting with the +:has_one+ and +:has_many+ options: -map.resources :publishers, :has_many => { :magazines => :photos }, :shallow => true +resources :publishers, :has_many => { :magazines => :photos }, :shallow => true h4. Route Generation from Arrays @@ -530,8 +537,8 @@ h4. Route Generation from Arrays In addition to using the generated routing helpers, Rails can also generate RESTful routes from an array of parameters. For example, suppose you have a set of routes generated with these entries in routes.rb: -map.resources :magazines do |magazine| - magazine.resources :ads +resources :magazines do + resources :ads end @@ -554,17 +561,16 @@ h4. Namespaced Resources It's possible to do some quite complex things by combining +:path_prefix+ and +:name_prefix+. For example, you can use the combination of these two options to move administrative resources to their own folder in your application: -map.resources :photos, :path_prefix => 'admin', :controller => 'admin/photos' -map.resources :tags, :name_prefix => 'admin_photo_', :path_prefix => 'admin/photos/:photo_id', :controller => 'admin/photo_tags' -map.resources :ratings, :name_prefix => 'admin_photo_', :path_prefix => 'admin/photos/:photo_id', :controller => 'admin/photo_ratings' +resources :photos, :path_prefix => 'admin', :controller => 'admin/photos' +resources :tags, :name_prefix => 'admin_photo_', :path_prefix => 'admin/photos/:photo_id', :controller => 'admin/photo_tags' +resources :ratings, :name_prefix => 'admin_photo_', :path_prefix => 'admin/photos/:photo_id', :controller => 'admin/photo_ratings' The good news is that if you find yourself using this level of complexity, you can stop. Rails supports _namespaced resources_ to make placing resources in their own folder a snap. Here's the namespaced version of those same three routes: -map.namespace(:admin) do |admin| - admin.resources :photos, - :has_many => { :tags, :ratings} +namespace :admin do + resources :photos, :has_many => { :tags, :ratings } end @@ -576,18 +582,24 @@ You are not limited to the seven routes that RESTful routing creates by default. h5. Adding Member Routes -To add a member route, use the +:member+ option: +To add a member route, just add +member+ block into resource block: -map.resources :photos, :member => { :preview => :get } +resources :photos do + member do + get :preview + end +end This will enable Rails to recognize URLs such as +/photos/1/preview+ using the GET HTTP verb, and route them to the preview action of the Photos controller. It will also create the +preview_photo_url+ and +preview_photo_path+ route helpers. -Within the hash of member routes, each route name specifies the HTTP verb that it will recognize. You can use +:get+, +:put+, +:post+, +:delete+, or +:any+ here. You can also specify an array of methods, if you need more than one but you don't want to allow just anything: +Within the block of member routes, each route name specifies the HTTP verb that it will recognize. You can use +get+, +put+, +post+, +delete+, or +any+ here. If you don't have multiple +member+ route, you can also passing +:on+ to the routing. -map.resources :photos, :member => { :prepare => [:get, :post] } +resources :photos do + get :preview, :on => :member +end h5. Adding Collection Routes @@ -595,32 +607,35 @@ h5. Adding Collection Routes To add a collection route, use the +:collection+ option: -map.resources :photos, :collection => { :search => :get } +resources :photos do + collection do + get :search + end +end This will enable Rails to recognize URLs such as +/photos/search+ using the GET HTTP verb, and route them to the search action of the Photos controller. It will also create the +search_photos_url+ and +search_photos_path+ route helpers. -Just as with member routes, you can specify an array of methods for a collection route: +Just as with member routes, you can passing +:on+ to the routing. -map.resources :photos, :collection => { :search => [:get, :post] } +resources :photos do + get :search, :on => :collection +end h5. Adding New Routes -To add a new route (one that creates a new resource), use the +:new+ option: +As of writing, Rails 3 has deprecated +:new+ option from routing. You will need to explicit define the route using +match+ method -map.resources :photos, :new => { :upload => :post } +resources :photos +match 'photos/new/upload' => 'photos#upload', :as => 'upload_new_photos' -This will enable Rails to recognize URLs such as +/photos/new/upload+ using the POST HTTP verb, and route them to the upload action of the Photos controller. It will also create the +upload_new_photos_path+ and +upload_new_photos_url+ route helpers. - -TIP: If you want to redefine the verbs accepted by one of the standard actions, you can do so by explicitly mapping that action. For example:
+map.resources :photos, :new => { :new => :any }+
This will allow the new action to be invoked by any request to +photos/new+, no matter what HTTP verb you use. - h5. A Note of Caution -If you find yourself adding many extra actions to a RESTful route, it's time to stop and ask yourself whether you're disguising the presence of another resource that would be better split off on its own. When the +:member+ and +:collection+ hashes become a dumping-ground, RESTful routes lose the advantage of easy readability that is one of their strongest points. +If you find yourself adding many extra actions to a RESTful route, it's time to stop and ask yourself whether you're disguising the presence of another resource that would be better split off on its own. When the +member+ and +collection+ hashes become a dumping-ground, RESTful routes lose the advantage of easy readability that is one of their strongest points. h3. Regular Routes @@ -633,7 +648,7 @@ h4. Bound Parameters When you set up a regular route, you supply a series of symbols that Rails maps to parts of an incoming HTTP request. Two of these symbols are special: +:controller+ maps to the name of a controller in your application, and +:action+ maps to the name of an action within that controller. For example, consider one of the default Rails routes: -map.connect ':controller/:action/:id' +match ':controller(/:action(/:id))' If an incoming request of +/photos/show/1+ is processed by this route (because it hasn't matched any previous route in the file), then the result will be to invoke the +show+ action of the +Photos+ controller, and to make the final parameter (1) available as +params[:id]+. @@ -643,7 +658,7 @@ h4. Wildcard Components You can set up as many wildcard symbols within a regular route as you like. Anything other than +:controller+ or +:action+ will be available to the matching action as part of the params hash. So, if you set up this route: -map.connect ':controller/:action/:id/:user_id' +match ':controller/:action/:id/:user_id' An incoming URL of +/photos/show/1/2+ will be dispatched to the +show+ action of the +Photos+ controller. +params[:id]+ will be set to 1, and +params[:user_id]+ will be set to 2. @@ -653,7 +668,7 @@ h4. Static Text You can specify static text when creating a route. In this case, the static text is used only for matching the incoming requests: -map.connect ':controller/:action/:id/with_user/:user_id' +match ':controller/:action/:id/with_user/:user_id' This route would respond to URLs such as +/photos/show/1/with_user/2+. @@ -663,17 +678,17 @@ h4. Querystring Parameters Rails routing automatically picks up querystring parameters and makes them available in the +params+ hash. For example, with this route: -map.connect ':controller/:action/:id' +match ':controller/:action/:id An incoming URL of +/photos/show/1?user_id=2+ will be dispatched to the +show+ action of the +Photos+ controller. +params[:id]+ will be set to 1, and +params[:user_id]+ will be equal to 2. h4. Defining Defaults -You do not need to explicitly use the +:controller+ and +:action+ symbols within a route. You can supply defaults for these two parameters in a hash: +You do not need to explicitly use the +:controller+ and +:action+ symbols within a route. You can supply defaults for these two parameters by putting it after +=>+: -map.connect 'photos/:id', :controller => 'photos', :action => 'show' +match 'photos/:id' => 'photos#show' With this route, an incoming URL of +/photos/12+ would be dispatched to the +show+ action within the +Photos+ controller. @@ -681,8 +696,7 @@ With this route, an incoming URL of +/photos/12+ would be dispatched to the +sho You can also define other defaults in a route by supplying a hash for the +:defaults+ option. This even applies to parameters that are not explicitly defined elsewhere in the route. For example: -map.connect 'photos/:id', :controller => 'photos', :action => 'show', - :defaults => { :format => 'jpg' } +match 'photos/:id' => 'photos#show', :defaults => { :format => 'jpg' } With this route, an incoming URL of +photos/12+ would be dispatched to the +show+ action within the +Photos+ controller, and +params[:format]+ will be set to +jpg+. @@ -692,7 +706,7 @@ h4. Named Routes Regular routes need not use the +connect+ method. You can use any other name here to create a _named route_. For example, -map.logout '/logout', :controller => 'sessions', :action => 'destroy' +match 'logout' => 'sessions#destroy', :as => :logout This will do two things. First, requests to +/logout+ will be sent to the +destroy+ action of the +Sessions+ controller. Second, Rails will maintain the +logout_path+ and +logout_url+ helpers for use within your code. @@ -702,15 +716,13 @@ h4. Route Requirements You can use the +:requirements+ option to enforce a format for any parameter in a route: -map.connect 'photo/:id', :controller => 'photos', :action => 'show', - :requirements => { :id => /[A-Z]\d{5}/ } +match 'photo/:id' => 'photos#show', :requirements => { :id => /[A-Z]\d{5}/ } This route would respond to URLs such as +/photo/A12345+. You can more succinctly express the same route this way: -map.connect 'photo/:id', :controller => 'photos', :action => 'show', - :id => /[A-Z]\d{5}/ +match 'photo/:id' => 'photos#show', :id => /[A-Z]\d{5}/ h4. Route Conditions @@ -718,8 +730,7 @@ h4. Route Conditions Route conditions (introduced with the +:conditions+ option) are designed to implement restrictions on routes. Currently, the only supported restriction is +:method+: -map.connect 'photo/:id', :controller => 'photos', :action => 'show', - :conditions => { :method => :get } +match 'photo/:id' => 'photos#show', :conditions => { :method => :get } As with conditions in RESTful routes, you can specify +:get+, +:post+, +:put+, +:delete+, or +:any+ for the acceptable method. @@ -729,7 +740,7 @@ h4. Route Globbing Route globbing is a way to specify that a particular parameter should be matched to all the remaining parts of a route. For example -map.connect 'photo/*other', :controller => 'photos', :action => 'unknown', +match 'photo/*other' => 'photos#unknown' This route would match +photo/12+ or +/photo/long/path/to/12+ equally well, creating an array of path segments as the value of +params[:other]+. @@ -755,7 +766,7 @@ There's one more way in which routing can do different things depending on diffe For instance, consider the second of the default routes in the boilerplate +routes.rb+ file: -map.connect ':controller/:action/:id.:format' +match ':controller(/:action(/:id(.:format)))' This route matches requests such as +/photo/edit/1.xml+ or +/photo/show/2.rss+. Within the appropriate action code, you can issue different responses depending on the requested format: @@ -781,11 +792,10 @@ Mime::Type.register "image/jpg", :jpg h3. The Default Routes -When you create a new Rails application, +routes.rb+ is initialized with two default routes: +When you create a new Rails application, +routes.rb+ is initialized with a default route: -map.connect ':controller/:action/:id' -map.connect ':controller/:action/:id.:format' +match ':controller(/:action(/:id(.:format)))' These routes provide reasonable defaults for many URLs, if you're not using RESTful routing. @@ -796,23 +806,16 @@ h3. The Empty Route Don't confuse the default routes with the empty route. The empty route has one specific purpose: to route requests that come in to the root of the web site. For example, if your site is example.com, then requests to +http://example.com+ or +http://example.com/+ will be handled by the empty route. -h4. Using +map.root+ +h4. Using +root+ -The preferred way to set up the empty route is with the +map.root+ command: +The preferred way to set up the empty route is with the +root+ command: -map.root :controller => "pages", :action => "main" +root :to => 'pages#main' The use of the +root+ method tells Rails that this route applies to requests for the root of the site. -For better readability, you can specify an already-created route in your call to +map.root+: - - -map.index 'index', :controller => "pages", :action => "main" -map.root :index - - Because of the top-down processing of the file, the named route must be specified _before_ the call to +map.root+. h4. Connecting the Empty String @@ -820,7 +823,7 @@ h4. Connecting the Empty String You can also specify an empty route by explicitly connecting the empty string: -map.connect '', :controller => "pages", :action => "main" +match '' => 'pages#main' TIP: If the empty route does not seem to be working in your application, make sure that you have deleted the file +public/index.html+ from your Rails tree. @@ -898,6 +901,7 @@ h3. Changelog "Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/3 +* Febuary 1, 2010: Modifies the routing documentation to match new routing DSL in Rails 3, by Prem Sichanugrist * October 4, 2008: Added additional detail on specifying verbs for resource member/collection routes, by "Mike Gunderloy":credits.html#mgunderloy * September 23, 2008: Added section on namespaced controllers and routing, by "Mike Gunderloy":credits.html#mgunderloy * September 10, 2008: initial version by "Mike Gunderloy":credits.html#mgunderloy -- cgit v1.2.3