初心者じゃなくても役に立つかもしれないRailsのroutingの記述方をまとめてみた
まとめようと思った経緯
Railsのネストしたrouting書く時に毎回調べてる気がするから自分のブログにまとめておきたい
— shiro16 (@shiro166) January 5, 2015
まとめてあるブログもいくつかあるけど結局自分で書いた文章が一番分かりやすいっていうあたり前のはなし
— shiro16 (@shiro166) January 5, 2015
と思って書き始めたのが1月の中頃
何を言っているのかわからねーと思うが
気づいたら違う記事をいくつか書いて公開していた。
(いつのまにかRuby2.2.1もリリースされていた・・・)
urlを直接指定する
get '/games', to: 'games#index' get '/games/:id', to: 'games#show' get '/games/test', to: 'games#test', as: :test
下記のようにも書ける
get '/games' => 'games#index' get '/games/:id' => 'games#show' get '/games/test' => 'games#test', as: :test
resource
scaffoldを使用した際にroutes.rbに追記される
複数のroutingを作成してくれやつ
resource :users
これだけで下記のようなroutingが生成される(簡単!)
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
users | GET | /users(.:format) | users#index |
POST | /users(.:format) | users#create | |
new_user | GET | /users/new(.:format) | users#new |
edit_user | GET | /users/:id/edit(.:format) | users#edit |
user | GET | /users/:id(.:format) | users#show |
PATCH | /users/:id(.:format) | users#update | |
PUT | /users/:id(.:format) | users#update | |
DELETE | /users/:id(.:format) | users#destroy |
一部のroutingがいらない場合
resourceで作成されるroutingでindexとshowは必要だけどcreateいらないとかのパターンは
下記の用にonly,exceptで絞ることが可能
resource :sports, only: [:index] # indexのみ有効 resource :posts, except: [:index, :show] # index,show以外有効
向き先のcontrollerを変更
下記のようにcontrollerを指定することも可能
この場合「GET /hoge」へのリクエストが「users#index」に
resources :hoge, controller: :users
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
hoge | POST | /hoge(.:format) | users#create |
new_hoge | GET | /hoge/new(.:format) | users#new |
edit_hoge | GET | /hoge/edit(.:format) | users#edit |
GET | /hoge(.:format) | users#show | |
PATCH | /hoge(.:format) | users#update | |
PUT | /hoge(.:format) | users#update | |
DELETE | /hoge(.:format) | users#destroy |
ネストしてみると・・・
resourceをネストしてみると下記のようなroutingが生成される
※結果が長くなってしまうのでonlyで絞っています。
resource :authors, only: [:index, :show, :create] do resource :books, only: [:index, :show, :create] end
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
authors_books | POST | /authors/books(.:format) | books#create |
GET | /authors/books(.:format) | books#show | |
authors | POST | /authors(.:format) | authors#create |
GET | /authors(.:format) | authors#show |
collection
resources :dogs, only: [:index, :show, :create] do get 'test1', on: :collection get 'test2', on: :collection end
下記のようにも書ける
resources :dogs, only: [:index, :show, :create] do collection do get 'test1' get 'test2' end end
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
test1_dogs | GET | /dogs/test1(.:format) | dogs#test1 |
test2_dogs | GET | /dogs/test2(.:format) | dogs#test2 |
dogs | GET | /dogs(.:format) | dogs#index |
POST | /dogs(.:format) | dogs#create | |
dog | GET | /dogs/:id(.:format) | dogs#show |
member
resources :cats do get 'test1', on: :member get 'test2', on: :member end
下記のようにも書ける
resources :cats do member do get 'test1' get 'test2' end end
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
test1_cat | GET | /cats/:id/test1(.:format) | cats#test1 |
test2_cat | GET | /cats/:id/test2(.:format) | cats#test2 |
cats | GET | /cats(.:format) | cats#index |
POST | /cats(.:format) | cats#create | |
new_cat | GET | /cats/new(.:format) | cats#new |
edit_cat | GET | /cats/:id/edit(.:format) | cats#edit |
cat | GET | /cats/:id(.:format) | cats#show |
PATCH | /cats/:id(.:format) | cats#update | |
PUT | /cats/:id(.:format) | cats#update | |
DELETE | /cats/:id(.:format) | cats#destroy |
new
上記のcollection, memberの他にnewも指定可能
resources :companies, only: [:index, :show, :create] do get 'test1', on: :new get 'test2', on: :new end
下記のようにも書ける
resources :companies, only: [:index, :show, :create] do new do get 'test1' get 'test2' end end
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
test1_new_company | GET | /companies/new/test1(.:format) | companies#test1 |
test2_new_company | GET | /companies/new/test2(.:format) | companies#test2 |
companies | GET | /companies(.:format) | companies#index |
POST | /companies(.:format) | companies#create | |
company | GET | /companies/:id(.:format) | companies#show |
namespace
URI Patternとcontrollerにnamespace Admin::を付ける
namespace :admin do resources :users, only: [:index, :show] end
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
admin_users | GET | /admin/users(.:format) | admin/users#index |
admin_user | GET | /admin/users/:id(.:format) | admin/users#show |
scope
controllerにのみnamespace Adminを付ける
scope module: 'admin' do resources :authors end
下記のようにも書ける
resources :authors, module: 'admin'
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
authors | GET | /authors(.:format) | admin/authors#index |
author | GET | /authors/:id(.:format) | admin/authors#show |
concern
同じような記述をgroup化しておけるイメージ(?)
DRYに書けてすっきりする気がする
concern :categorize do resources :large_categories, only: [:index, :show] do get 'test' end end resources :dogs, only: [:index], concerns: :categorize resources :dogs, only: [:index], concerns: :categorize
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
dog_large_category_test | GET | /dogs/:dog_id/large_categories/:large_category_id/test(.:format) | large_categories#test |
dog_large_categories | GET | /dogs/:dog_id/large_categories(.:format) | large_categories#index |
dog_large_category | GET | /dogs/:dog_id/large_categories/:id(.:format) | large_categories#show |
dogs | GET | /dogs(.:format) | dogs#index |
cat_large_category_test | GET | /cats/:cat_id/large_categories/:large_category_id/test(.:format) | large_categories#test |
cat_large_categories | GET | /cats/:cat_id/large_categories(.:format) | large_categories#index |
cat_large_category | GET | /cats/:cat_id/large_categories/:id(.:format) | large_categories#show |
cats | GET | /cats(.:format) | cats#index |
constraints
routingに色々な条件を付与できる
デフォルトでは{id: /[^\/]+/}このような指定になっているようです。
:idを正規表現でチェック
下記のように記述すると:idが数字の場合のみ対応するactionが呼ばれる
resources :small_categories, constraints: {id: /\d+/}
下記のような記述方法もある
constraints(id: /\d+/) do resources :small_categories end
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
small_categories | GET | /small_categories(.:format) | small_categories#index |
POST | /small_categories(.:format) | small_categories#create | |
new_small_category | GET | /small_categories/new(.:format) | small_categories#new |
edit_small_category | GET | /small_categories/:id/edit(.:format) | small_categories#edit {:id=>/\d+/} |
small_category | GET | /small_categories/:id(.:format) | small_categories#show {:id=>/\d+/} |
PATCH | /small_categories/:id(.:format) | small_categories#update {:id=>/\d+/} | |
PUT | /small_categories/:id(.:format) | small_categories#update {:id=>/\d+/} | |
DELETE | /small_categories/:id(.:format) | small_categories#destroy {:id=>/\d+/} |
下記のような場合に便利
resources :small_categories, constraints: {id: /\d+/}, only:[:show] get '/small_categories/test', to: "small_categories#test"
もしこのようにroutingを書いていた場合に「/small_categories/test」にアクセスすれば「small_categories#test」が呼ばれるが、
constraintsがないと「params[:id]に"test"」が格納されている状態で「small_categories#show」が呼ばれる。
記述する順番を逆にすれば回避出来るが毎回順番を気にしなければいけないのでつらめだと個人的には思う。
また、controller側で:idの値を使用する際もどのようなデータが渡ってくるのかある程度予想出来るので
考えることが減る気がする。
subdomainでチェック
下記のように記述するとアクセスしたsubdomainがadminの場合のみ
該当のmethodが呼ばれるようです。(未確認)
resources :books, only: [:index, :show], constraints: {subdomain: 'admin'}
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
books | GET | /books(.:format) | books#index {:subdomain=>"admin"} |
book | GET | /books/:id(.:format) | books#show {:subdomain=>"admin"} |
matches?を使用してチェック
constraintsに渡したclassのmatches? methodがtrueを返す場合に
該当のactionが呼ばれる。
例)下記のような処理ではuaがpcの場合に該当のactionが呼ばれる。
class PcConstraint def matches?(request) # ua check処理 # uaがpcの場合にtrueを返す end end Rails.application.routes.draw do get '/', to: 'pc#index', constraints: PcConstraint.new end
defaults
get '/users/:id/default' => 'users#default', defaults: { format: :json} get '/posts/:id/default' => 'posts#default', defaults: { default_value: :hoge }
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
GET | /users/:id/default(.:format) | users#default {:format=>:json} | |
GET | /posts/:id/default(.:format) | posts#default {:default_value=>:hoge} |
redirect
get "user" => redirect("/users") get "user/:id" => redirect("/users/%{id}") get 'user/*other' => redirect { |params| "/users/#{params[:other]}"}
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
users | GET | /users(.:format) | users#index |
user | GET | /users/:id(.:format) | users#show |
GET | /user(.:format) | redirect(301, /users) | |
GET | /user/:id(.:format) | redirect(301, /users/%{id}) | |
GET | /user/*other(.:format) | redirect(301) |
まとめ
今回使用したサンプルは下記で公開していますので
routing周りをいじって色々試してみてください。
shiro16/blog-samples · GitHub
この記事書いてる間にRailsGuidesのRouting周りの話の日本語版が公開されているので
特に初心者はそっちを見た方がいいとかいう話があるかもしれない・・・