-
We can define customed url helper by
:as
get "/photos/:id", to: "photos#show", as: :"show_photo" <%= link_to 'Photo Record', show_photo_path(15) %> show_photo_path(15) # /photos/15
-
you can supply a series of symbols that Rails maps to parts of an incoming HTTP request.
#####Two of these symbols are special:
# If we have URL: /photos/show/1 # then it can bound to: get ":controller/(:action/:id)" parmas # controller: "photos" # action: "show" # id: "1" # dynamic segments :user_id get ":controller/(:action/:id)/:user_id" # /photos/show/1/50 params # user_id: 50
-
We don't need to explicitly use symbols(
:controller
,:action
...).get 'photos/:id', to: 'photos#show' # /photos/5 => photos#show get 'photos/:id', to: 'photos#show', defaults: { format: 'jpg' } # set extra params key-value pairs # /photos/5 params # format: 'jpg'
-
define named route by
:as
:get ':username', to: 'users#show', as: :user # user_path
-
You can constraint HTTP verbs(GET, POST, PUT, PATCH, and DELETE) with
match
.match "photos", to: "photos#show", via: [:get, :post] # use :all to allowed all HTTP Verbs match "photos", to: "photos#all", via: :all
-
You can use the
:constraints
option to enforce a format for a dynamic segment:get 'photos/:id', to: 'photos#show', constraints: { id: /[A-Z]\d{5}/ } # Terse way to constraint :id get 'photos/:id', to: 'photos#show', id: /[A-Z]\d{5}/ # This match paths such as /photos/A12345, but not /photos/893.
-
You can also constrain a route based on any method on the
Request object
that returns aString
.get 'photos', to: 'photos#index', constraints: { subdomain: 'admin' } # Or using block namespace :admin do constraints subdomain: 'admin' do resources :photos end end
-
If you have a more advanced constraint, you can provide an object that responds to
matches?
that Rails should use.class BlacklistConstraint def initialize @ips = Blacklist.retrieve_ips end def matches?(request) @ips.include?(request.remote_ip) end end Rails.application.routes.draw do get '*path', to: 'blacklist#index', constraints: BlacklistConstraint.new end
Or specify by lambda:
Rails.application.routes.draw do get '*path', to: 'blacklist#index', constraints: lambda { |request| Blacklist.retrieve_ips.include?(request.remote_ip) } end
-
Use
*
to match wildcard segments:get 'books/*section/:title', to: 'books#show' # /some/section/last-words-a-memoir # params[:section] == 'some/section' # params[:title] == 'last-words-a-memoir' get '*a/foo/*b', to: 'test#index' # zoo/woo/foo/bar/baz # params[:a] == 'zoo/woo' # params[:b] == 'bar/baz'.
-
By requesting
'/foo/bar.json'
, yourparams[:pages]
will be equal to'foo/bar'
with the request format of JSON. If you want the old 3.0.x behavior back, you could supply format: false like this:get '*pages', to: 'pages#show', format: false # or true to make the format segment mandatory.
-
You can reuse dynamic segments from the match in the path to redirect to:
get '/stories/:name', to: redirect('/articles/%{name}') # or block to redirect get '/stories/:name', to: redirect do |path_params, req| "/articles/#{path_params[:name].pluralize}" end get '/stories', to: redirect { |path_params, req| "/articles/#{req.subdomain}" }
-
Please note that this redirection is a 301 "Moved Permanently" redirect. Keep in mind that some web browsers or proxy servers will cache this type of redirect, making the old page inaccessible.
-
Instead of a String like
'articles#index'
, which corresponds to the index action in theArticlesController
, you can specify any Rack application as the endpoint for a matcher:match '/application.js', to: Sprockets, via: :all
-
As long as
Sprockets
responds to#call
and returns a[status, headers, body]
, the router won't know the difference between the Rack application and an action. -
'articles#index'
actually expands out toArticlesController.action(:index)
, which returns a valid Rack application. -
matcher or mount:
http://inductor.induktiv.at/blog/2010/05/23/mount-rack-apps-in-rails-3/
# your rack application should expect # the route to be '/admin': match '/admin', to: AdminApp, via: :all # If you would prefer to have your rack application # receive requests at the root path instead use mount: mount AdminApp, at: '/admin' class AdminApp get '/ack' do # route /admin/ack end end
-
You can specify what Rails should route
'/'
to with the root method:root to: 'pages#main' root 'pages#main' # shortcut for the above
-
The root route only routes GET requests to the action. You can also use root inside namespaces and scopes as well.
namespace :admin do root to: "admin#index" end root to: "home#index"
-
You can specify unicode char routes:
get 'こんにちは', to: 'welcome#index'
-
resources :photos
:HTTP Verb Path Controller#Action Named Routes GET /photos photos#index photos_path GET /photos/new photos#new new_photo_path POST /photos photos#create photos_path GET /photos/:id photos#show photo_path(:id) GET /photos/:id/edit photos#edit edit_photo_path(:id) PATCH /PUT /photos/:id photos#update photo_path(:id) DELETE /photos/:id photos#destroy photo_path(:id)
-
resource :geocoder
:HTTP Verb Path Controller#Action Named Routes GET /geocoder/new geocoders#new new_geocoder_path POST /geocoder geocoders#create geocoder_path GET /geocoder geocoders#show geocoder_path GET /geocoder/edit geocoders#edit edit_geocoder_path PATCH/PUT /geocoder geocoders#update geocoder_path DELETE /geocoder geocoders#destroy geocoder_path
-
:namespace
prefix the namespace in both url and controller file path:namespace :admin do resources :articles, :comments end
HTTP Verb Path Controller#Action Named Routes GET /admin/articles admin/articles#index admin_articles_path GET /admin/articles/new admin/articles#new new_admin_article_path POST /admin/articles admin/articles#create admin_articles_path GET /admin/articles/:id admin/articles#show admin_article_path(:id) GET /admin/articles/:id/edit admin/articles#edit edit_admin_article_path PATCH/PUT /admin/articles/:id admin/articles#update admin_article_path(:id) DELETE /admin/articles/:id admin/articles#destroy admin_article_path(:id) -
:scope
to decide either prefix url path, or controller, the named routes remain the same as if you did not use:scope
:# /articles, articles_path, Admin::ArticlesController scope module: 'mod' do resources :articles, :ments end # Controller file can locate to /app/controllers/Mod/articles_controller.rb class ArticlesController < ApplicationController #... end # Or /app/controllers/articles_controller.rb then create class as below: module Mod class ArticlesController < ApplicationController #... end end # call it in rails console Mod::ArticlesController # or single resources resources :articles, module: 'admin' # /admin/comments, comments_path, CommentsController scope '/admin' do resources :articles, :comments end # or single resources resources :comments, path: '/admin/comments'
will be:
| HTTP VERB | PATH | ACTION | NAMED ROUTES | |-----------|------|--------|--------------| | GET | /admin/comments | comments#index | comments_path | POST | /admin/comments |comments#create | | |GET | /admin/comments/new | comments#new | new_comment_path | | GET | /admin/comments/:id/edit |comments#edit | edit_comment_path(:id) | | GET | /admin/comments/:id | comments#show | comment_path(:id) | | GET |/articles | mod/articles#index | articles_path | | GET | /articles/new | mod/articles#new | new_article_path | | GET |/articles/:id/edit | mod/articles#edit |edit_article_path(:id) | | GET |/articles/:id | mod/articles#show |article_path(:id) |
-
Caveat: Resources should never be nested more than 1 level deep.
resources :users do resources :member end
will be:
| HTTP VERB | PATH | ACTION | NAMED ROUTES | |-----------|------|--------|--------------| | GET | /users | users#index | users_path | | POST | /users | users#create | | | GET | /users/new | users#new | new_user_path | | GET | /users/:id/edit | users#edit | edit_user_path(:id) | | GET | /users/:id | users#show | user_path(:id) | | PUT/UPDATE | /users/:id | users#update | | | DELETE | /users/:id | users#destroy | | | GET | /users/:user_id/members | members#index | user_member_index_path | | POST | /users/:user_id/members | members#create | | | GET | /users/:user_id/members/new | members#new | new_user_member_path | | GET | /users/:user_id/members/:id/edit | memberss#edit | edit_user_member_path(:id) | | GET | /users/:user_id/members/:id | users#show | user_member_path(:id) | | PUT/UPDATE | /users/5 | users#update | | | DELETE | /users/5 | users#destroy | |
-
One way to avoid deep nesting (as recommended above) is to generate the collection actions scoped under the parent, so as to get a sense of the hierarchy, but to not nest the member actions.
# collection actions resources :articles do resources :comments, only: [:index, :new, :create] end # member actions resources :comments, only: [:show, :destroy, :update, :edit]
Above code is equialent to:
resources :articles do resources :comments, shallow: true end
-
You can also specify the
:shallow
option in the parent resource, in which case all of the nested resources will be shallow:resources :articles, shallow: true do resources :comments, :quotes, :drafts end
or use DSL:
shallow do resources :articles do resources :comments resources :quotes resources :drafts end end
-
There has two options to change the named routes or URL paths in
:scope
:####Add "sekret" to shallow URL path
scope shallow_path: "sekret" do resources :articles do resources :comments, shallow: true end end
will be:
|HTTP Verb | Path | Controller#Action | Named Routes | |----------|-------|-------------------|--------------| |GET | /articles/:article_id/comments | comments#index | article_comments_path| |POST | /articles/:article_id/comments | comments#create | article_comments_path| |GET |/articles/:article_id/comments/new | comments#new | new_article_comment_path | |GET |/sekret/comments/:id/edit | comments#edit | edit_comment_path | |GET |/sekret/comments/:id | comments#show| comment_path | |PATCH/PUT |/sekret/comments/:id | comments#update | comment_path | |DELETE |/sekret/comments/:id | comments#destroy | comment_path |
####Add "skeret" to shallow named routes
scope shallow_prefix: "sekret" do resources :articles do resources :comments, shallow: true end end
will be:
HTTP Verb Path Controller#Action Named Routes
|GET | /articles/:article_id/comments | comments#index | article_comments_path | |POST | /articles/:article_id/comments | comments#create | article_comments_path | |GET |/articles/:article_id/comments/new | comments#new | new_article_comment_path | |GET | /comments/:id/edit | comments#edit | edit_sekret_comment_path(:id) | |GET | /comments/:id | comments#show | sekret_comment_path(:id) | |PATCH/PUT | /comments/:id | comments#update | sekret_comment_path(:id) | |DELETE | /comments/:id | comments#destroy | sekret_comment_path(:id) |
-
We can group resources and reused inside other resources by
:concerns
:concern :commentable do resources :comments end concern :image_attachable do resources :images, only: :index end # then call it from other resources resources :messages, concerns: :commentable resources :articles, concerns: [:commentable, :image_attachable] # It is equivalent to: resources :messages do resources :comments end resources :articles do resources :comments resources :images, only: :index end
-
You can also put
concerns
to the:namespace
or:scope
.namespace :articles do concerns :commentable end # articles_comments_path => index # new_articles_comment_path => new # edit_articles_comment_path => edit # articles_comment_path => show
-
In addition to using the routing helpers, Rails can also create paths and URLs from an array of parameters.
-
For instance, when using
magazine_ad_path
, you can pass in instances ofMagazine
andAd
instead of the numeric IDs:<%= link_to 'Ad details', magazine_ad_path(@magazine, @ad) %> # or use url_for with array of parameters, Rails will find path for you <%= link_to 'Ad details', url_for([@magazine, @ad]) %> # In helpers like link_to, # you can specify just the object in place of the full url_for call: <%= link_to 'Ad details', [@magazine, @ad] %> # For other actions, you just need to insert the action name as # the first element of the array: <%= link_to 'Edit Ad', [:edit, @magazine, @ad] %>
-
You can also add routes to memeber routes(
:show
,:edit
,:update
,:destroy
) or collection routes(:index
,:new
,:create
).####Add route/aciton to member routes:
resources :photos do member do get 'preview' end end # Or 1 liner resource :photos do get "preview", on: :member end # This result: # preview_photo_path # GET /photos/:id/preview(.:format) # photos#preview
####Add route/action to collection routes:
resource :photos do collection do get 'search' end get 'search', on: :collection end # This result: # search_photos_path # GET # /photos/search(.:format) # photos#search
####Add route/aciotn to new action
resource :photos do get 'sear', on: :new end # This result: # sear_new_photos_path # GET # /photos/new/sear(.:format) # photos#sear
-
The
:controller
option lets you explicitly specify a controller to use for the resource.resources :photos, controller: 'images'
will be:
|HTTP Verb | Path | Controller#Action | Named Helper | |----------|-------|-------------------|--------------| |GET | /photos | images#index | photos_path | |GET | /photos/new | images#new | new_photo_path | |POST | /photos | images#create | photos_path | |GET | /photos/:id | images#show | photo_path(:id) | |GET | /photos/:id/edit | images#edit | edit_photo_path(:id) | |PATCH/PUT | /photos/:id | images#update | photo_path(:id) | |DELETE | /photos/:id | images#destroy | photo_path(:id) |
-
For namespaced controllers you can use the directory notation.
resources :user_permissions, controller: 'admin/user_permissions' # This will route to the Admin::UserPermissions controller.
-
You can use the
:constraints
option to specify a required format on the implicitid
. The router would no longer match/photos/1
to this route. Instead,/photos/RR27
would match.resources :photos, constraints: { id: /[A-Z][A-Z][0-9]+/ }
-
You can specify a single constraint to apply to a number of routes by using the block form:
constraints(id: /[A-Z][A-Z][0-9]+/) do resources :photos resources :accounts end
-
The
:as
option lets you override the normal naming for the named route helpers. For example:resources :photos, as: 'images'
will be:
| HTTP Verb | Path | Controller#Action | Named Helper | |-----------|------|-------------------|--------------| | GET | /photos | photos#index | images_path | | GET | /photos/new | photos#new | new_image_path | | POST | /photos | photos#create | images_path | | GET | /photos/:id | photos#show | image_path(:id) | | GET | /photos/:id/edit | photos#edit | edit_image_path(:id) | | PATCH/PUT | /photos/:id | photos#update | image_path(:id) | | DELETE | /photos/:id | photos#destroy | image_path(:id) |
-
The
:path_names
option lets you override the automatically-generatednew
andedit
segments in paths:resources :photos, path_names: { new: 'make', edit: 'change' } # routing path for new and edit: /photos/make /photos/1/change
-
If you find yourself wanting to change this option uniformly for all of your routes, you can use a scope.
scope path_names: { new: 'make' } do # rest of your routes end
-
You can use the :as option to prefix the named route helpers that Rails generates for a route. Use this option to prevent name collisions between routes using a path scope.
scope 'admin' do resources :photos, as: 'admin_photos' end resources :photos # admin_photos_path, new_admin_photo_path
-
To prefix a group of route helpers, use
:as
with scope:scope 'admin', as: 'admin' do resources :photos, :accounts end resources :photos, :accounts # generate routes such as admin_photos_path and admin_accounts_path # which map to /admin/photos and /admin/accounts respectively.
-
The namespace scope will automatically add
:as
as well as:module
and:path
prefixes. -
You can prefix routes with a named parameter also:
scope ':username' do resources :articles end # /bob/articles/1 # will allow you to reference the username part of # the path as params[:username] in controllers, helpers and views. params[:username] == "bob"
-
Use
only
orexcept
:resources :photos, only: [:index, :show] resources :photos, except: :destroy
-
Using
scope
, we can alter path names generated by resources:scope(path_names: { new: 'neu', edit: 'bearbeiten' }) do # instead /categories, paths now: /kategorien resources :categories, path: 'kategorien' end
will be:
|HTTP Verb | Path | Controller#Action | Named Helper | |----------|------|-------------------|--------------| |GET | /kategorien | categories#index | categories_path | |GET | /kategorien/neu | categories#new | new_category_path | |POST | /kategorien | categories#create | categories_path | |GET | /kategorien/:id | categories#show | category_path(:id) | |GET | /kategorien/:id/bearbeiten | categories#edit | edit_category_path(:id) | |PATCH/PUT | /kategorien/:id | categories#update | category_path(:id)| |DELETE | /kategorien/:id | categories#destroy | category_path(:id) |
-
If you want to define the singular form of a resource, you should add additional rules to the
Inflector
:ActiveSupport::Inflector.inflections do |inflect| inflect.irregular 'tooth', 'teeth' end
-
The
:as
option overrides the automatically-generated name for the resource in nested route helpers. For example:resources :magazines do resources :ads, as: 'periodical_ads' end # named route change to: # magazine_periodical_ads_url # edit_magazine_periodical_ad_path
-
The
:param
option overrides the default resource identifier:id
(name of the dynamic segment used to generate the routes). You can access that segment from your controller usingparams[<:param>]
.resources :videos, param: :identifier # Video model using identifier attribute as their id Video.find_by(identifier: params[:identifier])
will be:
|videos | GET |/videos(.:format) |videos#index| |---------|------|-----------------------------------|------------| | |POST |/videos(.:format) |videos#create| |new_videos |GET |/videos/new(.:format) |videos#new| |edit_videos |GET |/videos/:identifier/edit(.:format) |videos#edit|
-
List routes in
http://localhost:3000/rails/info/routes
under development mode. -
You may restrict the listing to the routes that map to a particular controller setting the
CONTROLLER
environment variable:
```sh
$ CONTROLLER=welcome bin/rake routes
Prefix Verb URI Pattern Controller#Action
root GET / welcome#index
POST / welcome#create
clients POST /clients(.:format) welcome#client_create GET /index/:status(.:format) welcome#get_index {:foo=>"bar"} show_photo GET /photos/:id(.:format) welcome#index ```
> [Setting an environment variable before a command in bash](http://stackoverflow.com/questions/10856129/setting-an-environment-variable-before-a-command-in-bash-not-working-for-second#)
-
Routes should be included in your testing strategy (just like the rest of your application). Rails offers three built-in assertions designed to make testing routes simpler.
assert_generates assert_recognizes assert_routing assert_generates '/photos/1', { controller: 'photos', action: 'show', id: '1' } assert_generates '/about', controller: 'pages', action: 'about'
-
assert_generates
asserts that a particular set of options generate a particular path and can be used with default routes or custom routes. -
assert_recognizes
is theinverse of assert_generates
. It asserts that a given path is recognized and routes it to a particular spot in your application.assert_recognizes({ controller: 'photos', action: 'create' }, { path: 'photos', method: :post })
-
The
assert_routing assertion
checks the route both ways: it tests that the path generates the options, and that the options generate the path. Thus, it combines the functions ofassert_generates
andassert_recognizes
.assert_routing({ path: 'photos', method: :post }, { controller: 'photos', action: 'create' })