Railsのルーティング
このガイドでは、Railsルーティング(経路)のユーザー向けの機能について説明します。 このガイドを読むことで、次の事が学べるはずです。
-
routes.rb
内コードの解釈の仕方について -
resourceful(推奨)な、または
match
メソッドを使用した独自のルーティングの組み立て方について - 何のパラメータをアクションが受け取る事を期待するのか
- routeヘルパーを使用したパスとURLの自動生成の方法について
- 制約やRackエンドポイントのような高度なテクニックについて
- 1. Railsルーターの目的
- 2. Resourceルーティング: Railsデフォルト
- 3. 非Resourcefulルート
- 4. Resourcefulルートのカスタマイズ
- 5. ルートの検証とテスト
1. Railsルーターの目的
RailsルーターはURLを解析し、それぞれをコントローラーのアクションに遷移させます。 また、ビュー内においてハードコーディングを必要とせずに、パスとURLを生成することを可能にしてくれます。
1.1 URLからコードへの橋渡し
Railsアプリケーションが次のリクエストを受け取った場合、
GET /patients/17
ルーターに対し、これにマッチするコントローラーアクションが無いかを訪ねます。 もし最初に下記のルートにマッチした場合、
get '/patients/:id', to: 'patients#show'
リクエストはpatients
コントローラーのshow
アクションに、
params
の値{id: '17'}
付きで遷移させられます。
1.2 コードからパスとURLを生成
パスとURLを生成することも出来ます。 もし、上記の例を次のように修正し、
get '/patients/:id', to: 'patients#show', as: 'patient'
アプリケーションのコントローラー内には下記のコードを、
@patient = Patient.find(17)
そして対応するビューにこれを含めると、
<%= link_to 'Patient Record', patient_path(@patient) %>
ルーターは/patients/17
のパスを生成します。
これはビューが脆くなること防ぎ、コードを分かりやすくしてくれます。
ルートヘルパー内では、idの指定が不要であることに注意してください。
2. Resourceルーティング: Railsデフォルト
ブラウザは、RailsがGET
、POST
、PATCH
、PUT
、DELETE
のような、
特定のHTTPメソッドを使用してURLのためのリクエストを作成することによってページにリクエストします。
各メソッドはリソース上の操作を実行するメソッドです。
リソースのルートは、単一コントローラー内のアクションに関連する複数のリクエストをマッピングします。
Railsアプリケーションが次のようなリクエストを受け取ると、
DELETE /photos/17
ルーターにマッピングするコントローラーアクションを問い合わせます。 もし、次のルートが最初にマッチすると、
resources :photos
Railsはリクエストをphotos
コントローラーのdestroy
メソッドへ
params
の値{ id: '17' }
付きで遷移させます。
2.2 CRUD、動詞、アクション
RailsでのResourcefulなルート(経路)は、HTTPの動詞(verbs)とURLを、コントローラーのアクションへのマッピングを提供します。 慣習に従い、各アクションは特定のデータベースのCRUD操作にもマッピングされます。 下記のような1行をルーティングファイルに書き込みます。
resources :photos
アプリケーション内に7つの異なるルートが作成され、全てPhotos
コントローラーへマッピングされます。
HTTP動詞 | パス | アクション | 用途 |
---|---|---|---|
GET | /photos | index | 全てのphotoを一覧表示します。 |
GET | /photos/new | new | 新しいphotoを作成するためのHTMLフォームを返します。 |
POST | /photos | create | photoを新規作成します。 |
GET | /photos/:id | show | 特定のphotoを表示します。 |
GET | /photos/:id/edit | edit | photoを編集するためのHTMLフォームを返します。 |
PATCH/PUT | /photos/:id | update | 特定のphotoを更新します。 |
DELETE | /photos/:id | destroy | 特定のphotoを削除します。 |
ルーターはHTTP動詞とURLの組み合わせでリクエストのマッチを行うため、 4つのURLが異なるアクションにマッピングされます。
Railsのルートは、指定された順にマッチするため、もしresources :photos
を、
get 'photos/poll'
の上に配置していると、この行にマッチする前に、
showアクションは先にresourcesにマッチします。
これを修正するには、その行をresources
行の上に配置します。
2.3 パスとURLヘルパー
Resourcefulの経路の作成により、コントローラー内で幾つかのヘルパーが使用できるようになります。
resources :photos
の場合、
-
photos_path
は、/photos
を返します。 -
new_photo_path
は、/photos/new
を返します。 -
edit_photo_path(:id)
は、/photos/:id/edit
を返します。 (例えば、edit_photo_path(10)は/photos/10/editを返します。) -
photo_path(:id)
は、/photos/:id
を返します。 (例えば、photo_path(10)は/photos/10を返します。)
これらの各ヘルパーは、対応する_url
ヘルパー(photos_url
のような)を持ち、
現在のホスト名、ポート、パスを補った同じものを返します。
2.4 一度に複数のresourceの定義
もし複数のresourceの経路の作成が必要な場合、1つのresources
の呼び出しだけで、
全て定義することが可能です。
resources :photos, :books, :videos
これは下記と同じように動作します。
resources :photos
resources :books
resources :videos
2.5 単数Resources
IDを参照すること無く、常に顧客情報を参照したいケースがあるかもしれません。
例えば、常にログイン中のユーザーのプロフィールを/profile
で表示したいと考えたとします。
この場合、単数のリソースを使用して/profile
(/profile/:idでは無く)をshowアクションにマッピングすることが出来ます。
get 'profile', to: 'users#show'
String
をmatch
へ渡すことは、controller#action
形式が期待されますが、
Symbol
が渡されると直接アクションにマッピングされます。
get 'profile', to: :show
下記のResourcefulなルートは、
resource :geocoder
Geocoders
コントローラーへマッピングする6つの異なるルートを作成します。
HTTP動詞 | パス | アクション | 用途 |
---|---|---|---|
GET | /geocoder/new | new | 新しいgeocoderを作成するためのHTMLフォームを返します。 |
POST | /geocoder | create | geocoderを新規作成します。 |
GET | /geocoder | show | 1つだけのgeocoderの情報を表示します。 |
GET | /geocoder/edit | edit | geocoderを編集するためのHTMLフォームを返します。 |
PATCH/PUT | /geocoder | update | 1つだけのgeocoderの情報を更新します。 |
DELETE | /geocoder | destroy | geocoderを削除します。 |
あなたは、同じコントローラーに単数ルート(/account
)と複数ルート(/accounts/45
)を使用したいかもしれないため、
単数resourcesを複数コントローラーにマッピングします。
そのため、例えばresource :photo
とresources :photos
は単数と複数のルートの両方を作成し、
同じコントローラー(PhotosController
)にマッピングします。(翻訳に自信なし)
単数resourceのルートは、下記のようなヘルパーを生成します。
-
new_geocoder_path
は/geocoder/new
を返します。 -
edit_geocoder_path
は/geocoder/edit
を返します。 -
geocoder_path
は/geocoder
を返します。
複数resourcesと同様、同じヘルパーの最後に_url
を付けると、
現在のホスト名、ポート、パスを補った同じものを返します。
2.6 コントローラーへの名前空間付けとルーティング
コントローラーを名前空間を使用して整理したいケースがあるかもしれません。
多くの場合、管理者系のコントローラーをAdmin::
名前空間下でグループ化したいと考えると思います。
app/controllers/admin
ディレクトリ下にこれらのコントローラーを配置して、
ルーター内でこれらを一緒のグループにすることが可能です。
namespace :admin do
resources :posts, :comments
end
これは、各posts
とcomments
のコントローラーのルートを生成します。
Admin::PostsController
のために、Railsは下記のものを生成します。
HTTP動詞 | パス | アクション | ヘルパー |
---|---|---|---|
GET | /admin/posts | index | admin_posts_path |
GET | /admin/posts/new | new | new_admin_post_path |
POST | /admin/posts | create | admin_posts_path |
GET | /admin/posts/:id | show | admin_post_path(:id) |
GET | /admin/posts/:id/edit | edit | edit_admin_post_path(:id) |
PATCH/PUT | /admin/posts/:id | update | admin_post_path(:id) |
DELETE | /admin/posts/:id | destroy | admin_post_path(:id) |
もし、/posts
(/admin
を付けずに)をAdmin::PostsController
へのルートにしたければ、
下記のように使用することが出来ます。
scope module: 'admin' do
resources :posts, :comments
end
また、1つであれば次のように書くことが出来ます。
resources :posts, module: 'admin'
もし、/admin/posts
をPostsController
(Admin::
モジュール接頭辞無しで)へのルートにしたいのであれば、
次のように書くことが出来ます。
scope '/admin' do
resources :posts, :comments
end
また、1つであれば次のように書くことが出来ます。
resources :posts, path: '/admin/posts'
いずれの場合も、名前が付けられたルートはscope
を使用しなかった場合のものも残します。(翻訳に自信なし)
最後のケースでは、下記のパスがPostsController
にマッピングされます。
HTTP動詞 | パス | アクション | ヘルパー |
---|---|---|---|
GET | /admin/posts | index | posts_path |
GET | /admin/posts/new | new | new_post_path |
POST | /admin/posts | create | posts_path |
GET | /admin/posts/:id | show | post_path(:id) |
GET | /admin/posts/:id/edit | edit | post_path(:id) |
PATCH/PUT | /admin/posts/:id | update | post_path(:id) |
DELETE | /admin/posts/:id | destroy | post_path(:id) |
2.7 入れ子のResources
resourcesが理論的に他のresourcesの子になるという事は、よくある事です。 例えば、あなたのアプリケーションが次のモデルを含むと仮定すると、
class Magazine < ActiveRecord::Base
has_many :ads
end
class Ad < ActiveRecord::Base
belongs_to :magazine
end
入れ子のルートは、あなたのルーティング内において、この関連性を適用出来るようにしてくれます。 このケースでは、下記のルート宣言を含めることが出来ます。
resources :magazines do
resources :ads
end
magazines(雑誌)へのルートに加えて、この宣言はads(広告)のAdsController
へのルートも宣言します。
adのURLはmagazineを必要とします。
HTTP動詞 | パス | アクション | 用途 |
---|---|---|---|
GET | /magazines/:magazine_id/ads | index | 特定の雑誌(magazine)の全ての広告(ads)を一覧表示します。 |
GET | /magazines/:magazine_id/ads/new | new | 特定の雑誌に付属する新しい広告を作成するためのHTMLフォームを返します。 |
POST | /magazines/:magazine_id/ads | create | 特定の雑誌に付属する広告を新規作成します。 |
GET | /magazines/:magazine_id/ads/:id | show | 特定の雑誌に付属する特定の広告を表示します。 |
GET | /magazines/:magazine_id/ads/:id/edit | edit | 特定の雑誌に付属する特定の広告を編集するためのHTMLフォームを返します。 |
PATCH/PUT | /magazines/:magazine_id/ads/:id | update | 特定の雑誌に付属する特定の広告を更新します。 |
DELETE | /magazines/:magazine_id/ads/:id | destroy | 特定の雑誌に付属する特定の広告を削除します。 |
これは、magazine_ads_url
とmagazine_ads_url(@magazine)
)
2.7.1 入れ子の限界
お望みであれば、他の入れ子のresources内に、更にresourcesを入れ子にすることが出来ます。 下記はその例になります。
resources :publishers do
resources :magazines do
resources :photos
end
end
ただし、深い入れ子のresourcesはすぐに破綻します。 このケースでは、例えば、アプリケーションは次のようなパスを解析することになります。
/publishers/1/magazines/2/photos/3
対応するルートヘルパーはpublisher_magazine_photo_url
になり、
各3つの階層全てでそれぞれのオブジェクトが必要になります。
実際これは、Jamis Buckの良質なRails設計のための指南による、
人気記事で書かれているように、
混乱を引き起こすのに十分な状況であると言えます。(翻訳に自信なし)
Resourcesは、1階層よりも深い入れ子を作るべきではありません。
2.7.2 浅い入れ子
深い入れ子を避ける1つの方法として(上記で推奨しているように)、親のスコープ下にコレクションのアクションを生成し、 階層のようでありながら、各アクションが入れ子にならないようにします。 言い換えると、下記のように一意のidのresourceのために、最小の情報によるルートだけを構築するという事になります。
resources :posts do
resources :comments, only: [:index, :new, :create]
end
resources :comments, only: [:show, :edit, :update, :destroy]
この考えに沿うと、説明的なルートと深い入れ子の間のバランスを上手く取ることが出来ます。
また、:shallow
を使用した、上記の略記文法が存在します。
resources :posts do
resources :comments, shallow: true
end
これは最初の例と全く同じルートを生成します。
親resource内に:shallow
オプションを指定することも可能で、
全ての入れ子のresourcesにshallowが適用されます。
resources :posts, shallow: true do
resources :comments
resources :quotes
resources :drafts
end
DSLのshallow
メソッドは、内部の各入れ子がshallowであるスコープを作成します。
これは前述した例と同じルートを生成します。
shallow do
resources :posts do
resources :comments
resources :quotes
resources :drafts
end
end
shallowルートをカスタマイズするscope
のために、2つのオプションが存在します。
:shallow_path
は、指定したパラメーターを使用して各パスに接頭辞を付けます。
scope shallow_path: "sekret" do
resources :posts do
resources :comments, shallow: true
end
end
このcommentsのresourceは、下記のルートを生成します。
HTTP動詞 | パス | ヘルパー |
---|---|---|
GET | /posts/:post_id/comments(.:format) | post_comments |
GET | /posts/:post_id/comments(.:format) | post_comments |
POST | /posts/:post_id/comments/new(.:format) | new_post_comment |
GET | /sekret/comments/:id/edit(.:format) | edit_comment |
GET | /sekret/comments/:id(.:format) | comment |
PATCH/PUT | /sekret/comments/:id(.:format) | comment |
DELETE | /sekret/comments/:id(.:format) | comment |
:shallow_prefix
オプションは、名前付けされたヘルパーへ特定のパラメーターを追加します。
scope shallow_prefix: "sekret" do
resources :posts do
resources :comments, shallow: true
end
end
このcommentsのresourceは、このために生成される下記のルートを持つことになります。
HTTP動詞 | パス | ヘルパー |
---|---|---|
GET | /posts/:post_id/comments(.:format) | post_comments |
GET | /posts/:post_id/comments(.:format) | post_comments |
POST | /posts/:post_id/comments/new(.:format) | new_post_comment |
GET | /sekret/comments/:id/edit(.:format) | edit_sekret_comment |
GET | /comments/:id(.:format) | sekret_comment |
PATCH/PUT | /comments/:id(.:format) | sekret_comment |
DELETE | /comments/:id(.:format) | sekret_comment |
2.8 関係性のルーティング
関連性のルーティングは、他のresourcesとルート内で再利用可能な共通のルートを宣言できるようにしてくれます。 関連性を定義するにはまず次のようにして、
concern :commentable do
resources :comments
end
concern :image_attachable do
resources :images, only: :index
end
これら上記のconcernsをresources内で次のように使用することで、コードの重複を避け、 ルート全体で挙動の共有を可能にしてくれます。
resources :messages, concerns: :commentable
resources :posts, concerns: [:commentable, :image_attachable]
これは下記のように評価されます。
resources :messages do
resources :comments
end
resources :posts do
resources :comments
resources :images, only: :index
end
また、あなたが望むルート内部、例えばscope、namespace呼び出し内に、にこれらを使用することも可能です。
namespace :posts do
concerns :commentable
end
2.9 オブジェクトからパスとURLを生成
更にルーティングヘルパーを使用する事で、Railsはパラメーターの配列からパスとURLを作成することも可能です。 例えば、あなたが下記のルートを持っていると仮定すると、
resources :magazines do
resources :ads
end
magazine_ad_path
を使用する場合、Magazine
とAd
のインスタンスを、
IDの数値の代わりに渡すことが可能になります。
<%= link_to 'Ad details', magazine_ad_path(@magazine, @ad) %>
また、オブジェクトのセットを使用してurl_for
を使用することも可能で、
Railsは自動的に必要とされているルートを判断してくれます。
<%= link_to 'Ad details', url_for([@magazine, @ad]) %>
このケースでは、Railsは@magazine
はMagazine
、@ad
はAd
であることを確認し、
magazine_ad_path
ヘルパーを選択します。
link_to
のようなヘルパー内では、url_for
呼び出しで指定されるオブジェクトだけを指定することも可能です。
<%= link_to 'Ad details', [@magazine, @ad] %>
もし、magazineだけをリンクしたい場合は、次のようにします。
<%= link_to 'Magazine details', @magazine %>
その他のアクションを指定するには、配列の第一要素にアクション名を指定する必要があります。
<%= link_to 'Edit Ad', [:edit, @magazine, @ad] %>
これは、URLとしてモデルのインスタンスを扱う事を可能にし、 resourcefulなスタイルを使用する上での重要な利点になります。
2.10 更にRESTfulアクションの追加
デフォルトで生成されるRESTfulルーティングの7つのルートは、制限されているわけではありません。 お望みであれば、コレクションまたはコレクションの特定のmemberに適用するルートを追加することも出来ます。
2.10.1 メンバー(member)ルートの追加
1要素のルートの追加をするには、resourceブロックへmember
ブロックを追加するだけです。
resources :photos do
member do
get 'preview'
end
end
これは、GETで/photos/1/preview
を認識し、
PhotosController
のpreview
アクションへ、
params[:id]
内にresourceのid値を渡すルーティングを行います。
また、preview_photo_url
とpreview_photo_path
ヘルパーを生成します。
memberルートのブロック内で、各ルート名にはそれが認識できるHTTP動詞を指定します。
get
、patch
、put
、post
、delete
を使用することが出来ます。
もし複数のmemberルートを持たない場合は、ルートに:on
を使用することでブロックを排除することが出来ます。
resources :photos do
get 'preview', on: :member
end
:on
オプションを取り除くことが可能で、これはresourceのid値を除いて、同じmemberルートを作成し、
params[:id]
の代わりに、params[:photo_id]
を利用可能にします。(翻訳に自信なし)
2.10.2 コレクション(collection)ルートの追加
コレクションのルートを追加するには、次のようにします。
resources :photos do
collection do
get 'search'
end
end
これは/photos/search
のようなGETパスの認識をRailsで有効にし、
PhotosController
のsearch
アクションをルート(経路)に加えます。
また、search_photos_url
とsearch_photos_path
のルート(経路)ヘルパーを作成します。
memberルートと同様に、:on
をルートに渡すことが出来ます。
resources :photos do
get 'search', on: :collection
end
2.10.3 新しい追加アクションのためのルート(経路)の追加
:on
ショートカットを使用して、新しいアクションを追加するには下記のようにします。
resources :comments do
get 'preview', on: :new
end
これは/comments/new/preview
のようなGETパスの認識をRailsで有効にし、
CommentsController
のpreview
アクションのルート(経路)を追加します。
また、preview_new_comment_url
とpreview_new_comment_path
のルート(経路)ヘルパーを追加します。
もし、Resourcefulなルートに対して、多くのアクションを追加していることに気づいたのであれば、 一度立ち止まって、これは本来別のresourceを作るべきだったのではないかと自身に問いかけてみてください。
3. 非Resourcefulルート
resourceルーティングに加え、Railsはアクションへの任意のURLルーティングも強力にサポートしてくれます。 ここでは、resourcefulなルーティングによって、自動的に生成されるルート(経路)のグループを取得するということはしません。 代わりに、アプリケーションを分割することで各ルートを設定します。
普段はresourcefulなルーティングを使用すべきですが、 一方でシンプルなルーティングが適切であるケースも存在します。 resourcefulなフレームワークに上手く適合しないものを、無理に押し込めようとする必要はありません。
特に単純なルーティングであれば、古いURLから新しいRailsアクションへ非常に簡単にマッピングしてくれます。
3.1 パラメータのバインド(紐付け)
通常のルートを設定する場合、やって来るHTTPリクエストのパーツをRailsがマッピングする一連のシンボルを提供します。
これらのシンボルの2つは特別なもので、:controller
はコントローラー名のマッピングを、
:action
はそのコントローラー内のアクション名をマッピングします。
例えば、下記のルートで考えてみた場合、
get ':controller(/:action(/:id))'
/photos/show/1
がこのルートにリクエストされると(その前にどのルートにもマッチしなかったと仮定します)、
PhotosController
のshow
アクションが実行され、
最後のパラメータである"1"はparams[:id]
として利用可能になるという結果を得ることになります。
このルートはまた、/photos
へのリクエストは
:action
と:id
が括弧で指定されていることから任意のパラメータとされ、
PhotosController#index
にマッピングします。
3.2 動的セグメント(区分)
お望みであれば、通常のルート内に多くの動的にセグメントとして設定することが可能です。
:controller
または:action
だけが、
params
の一部として利用可能になるわけではありません。
もし、次のようにルートを設定すると、
get ':controller/:action/:id/:user_id'
/photos/show/1/2
のパスは、PhotosController
のshow
アクションに送られ、
params[:id]
は"1"に、params[:user_id]
は"2"になります。
:controller
パスセグメントと一緒に、:namespace
または:module
を使用することは出来ません。
もし、これを行う必要がある場合は、必要とするnamespaceを:controller
でマッチさせるように制約を設けます。
下記はその例になります。
get ':controller(/:action(/:id))', controller: /admin/[^/]+/
デフォルトで、動的セグメントはフォーマット(format)ルートの判定に使用されることから、ドットを受け付けません。
動的セグメントでドットが必要な場合は、制約を追加することでこれを上書きします。
例えば、id: /[^\/]+/
とすれば、スラッシュを除く全てを許可します。
3.3 静的セグメント
ルートを作成する際にセグメントの一部の先頭にコロンを追加すること無く、 静的セグメントを指定することが出来ます。(下記の例では、with_userがそれに該当)
get ':controller/:action/:id/with_user/:user_id'
このルートは/photos/show/1/with_user/2
のようなパスに応答します。
このケースでは、params
は
3.4 クエリー文字列
params
はクエリー文字列から、何らかのパラメータを含むこともあります。
例えば、このルートは
get ':controller/:action/:id'
/photos/show/1?user_id=2
のパスが来ると、
Photos
コントローラーのshow
アクションに送られます。
params
は{controller:'photos', action:'show', id:'1', user_id:'2'}
になります。
3.5 デフォルトの定義
必ずしも、明示的に:controller
と:action
シンボルをルート内で使用する必要はありません。
これらをデフォルトとして提供することも可能です。
get 'photos/:id', to: 'photos#show'
このルート指定では、Railsは/photos/12
のパスを、
PhotosController
のshow
アクションにマッチさせます。
また、:defaults
オプションにハッシュを提供することで、その他のデフォルト定義を行うことも可能です。
これは動的セグメントとして指定しなくても、パラメータが提供されます。
例えば下記のケースでは、
get 'photos/:id', to: 'photos#show', defaults: { format: 'jpg' }
Railsはphotos/12
をPhotosController
のshow
アクションにマッチさせ、
params[:format]
に"jpg"
を設定します。
3.6 ルートへの名前付け
:as
オプションを使用して、ルートに対して名前を指定することが可能です。
get 'exit', to: 'sessions#destroy', as: :logout
これはlogout_path
とlogout_url
を、アプリケーション内の名前付けされたヘルパーとして生成します。
logout_path
が呼び出されると、/exit
を返します。
これを使用することで、resourcesによって定義されたルーティングのメソッドを、 下記のように上書きすることが出来ます。
get ':username', to: 'users#show', as: :user
これはコントローラー、ヘルパー、ビュー内で使用可能な、
/bob
(bobはユーザー名)のようなルートへ遷移するuser_path
メソッドを定義します。
UsersController
のshow
アクション内では、
params[:username]
はユーザーのためのusernameを含むことになります。
もし、パラメーター名を:username
にしたくないのであれば、
ルートの定義で:username
を変更してください。
3.7 HTTP動詞の制約
通常、get
、post
、put
、delete
メソッドを使用して、
ルートに特定の動詞の制約を設けるべきです。
:via
オプション付きでmatch
メソッドを使用して、一度に複数の動詞にマッチさせることが可能です。
match 'photos', to: 'photos#show', via: [:get, :post]
特定のルートにvia: :all
を使用して、全ての動詞にマッチさせることも可能です。
match 'photos', to: 'photos#show', via: :all
GET
とPOST
リクエストの両方を1つのアクションにルーティングする事は、
セキュリティに対して影響を与えることになります。
特に理由がない限り、動詞へのアクションへ全ての動詞をルーティングしてしまう、この書き方は避けるべきです。
3.8 セグメントの制約
:constraints
オプションを使用して、動的セグメントのフォーマットを強制する事が出来ます。
get 'photos/:id', to: 'photos#show', constraints: { id: /[A-Z]d{5}/ }
このルートは/photos/A12345
のようなパスにマッチし、/photos/893
のようなパスにはマッチしません。
下記のようにより簡潔に、同じルートを表す事が出来ます。
get 'photos/:id', to: 'photos#show', id: /[A-Z]d{5}/
:constraints
は、正規表現アンカー(^
や$
など)が使用出来ないという制限のある正規表現を受け付けます。
例えば、下記のルートは動作しません。
get '/:id', to: 'posts#show', constraints: {id: /^d/}
ただし、全てのルートは先頭にアンカーされているので、正規表現アンカーを使用する必要が無いことに注意してください。
例えば、下記のルートは1-hello-world
のような数値から始まるposts
と、
david
のような数値から始まらないto_param
値のユーザーを、
名前空間のルートで共有出来るようにしています。
get '/:id', to: 'posts#show', constraints: { id: /d.+/ }
get '/:username', to: 'users#show'
3.9 リクエストを基にした制約
String
を返すRequestオブジェクト上のメソッドを基に、ルートに制限を掛ける事も可能です。
セグメント制約と同じ方法で、リクエストを基にして制約を指定します。
get 'photos', constraints: {subdomain: 'admin'}
また、ブロック形式の制約を指定することも可能です。
namespace :admin do
constraints subdomain: 'admin' do
resources :photos
end
end
3.10 高度な制約
もし、より高度な制約を指定する必要があるのであれば、
Railsが使用するべきとするmatches?
に応答するオブジェクトを提供することが出来ます。
全てのユーザーに対し、BlacklistController
へのブロックリストを通したルートにしたいとします。
それは、次のようにすることが出来ます。
class BlacklistConstraint
def initialize
@ips = Blacklist.retrieve_ips
end
def matches?(request)
@ips.include?(request.remote_ip)
end
end
TwitterClone::Application.routes.draw do
get '*path', to: 'blacklist#index',
constraints: BlacklistConstraint.new
end
lambdaとして制約を指定することも可能です。
TwitterClone::Application.routes.draw do
get '*path', to: 'blacklist#index',
constraints: lambda { |request| Blacklist.retrieve_ips.include?(request.remote_ip) }
end
matches?
メソッドとlambdaの両方とも、引数としてrequest
オブジェクトを取得します。
3.11 ルートのglobとワイルドカードのセグメント
ルートのglobは、特定のパラメータが残りのルートの全ての部分としてマッチすべき事を指定する方法です。 例えば、
get 'photos/*other', to: 'photos#unknown'
このルートはphotos/12
または/photos/long/path/to/12
にマッチし、
前者ではparams[:other]
に"12"を、後者では"long/path/to/12"
を設定します。
アスタリスク(*)で始まる部分は、"ワイルドカードセグメント"と呼ばれています。
ワイルドカードセグメントは、ルートのどこにでも設定可能です。 例えば、
get 'books/*section/:title', to: 'books#show'
これはbooks/some/section/last-words-a-memoir
にマッチし、
params[:section]
は'some/section'
に、
params[:title]
は、'last-words-a-memoir'
になります。
技術的には、ルートは1つ以上のワイルドカードセグメントを持つことが可能です。 マッチャーはパラメータへ直感的にセグメントを割り当てます。 例えば、
get '*a/foo/*b', to: 'test#index'
は、zoo/woo/foo/bar/baz
へマッチし、
params[:a]
は'zoo/woo'
に、
params[:b]
は'bar/baz'
になります。
'/foo/bar.json'
へのリクエストでは、JSONフォーマットのリクエストとなり、
params[:pages]
は'foo/bar'
になります。
もし、古い3.0.xの挙動に戻したいのであれば、下記のようにformat: false
を提供します。
get '*pages', to: 'pages#show', format: false
もし、フォーマットセグメントを必須にしたいのであれば、
下記のようにformat: true
を提供します。
get '*pages', to: 'pages#show', format: true
3.12 リダイレクト
ルート内でredirect
ヘルパーを使用して、任意のパスを他のパスへリダイレクトさせる事が可能です。
get '/stories', to: redirect('/posts')
また、リダイレクトするパスにマッチする動的セグメントを再利用する事も可能です。
get '/stories/:name', to: redirect('/posts/%{name}')
また、paramsとリクエストオブジェクトを受け取るブロックでリダイレクトを指定することも可能です。
get '/stories/:name', to: redirect {|params, req| "/posts/#{params[:name].pluralize}" }
get '/stories', to: redirect {|p, req| "/posts/#{req.subdomain}" }
このリダイレクトは、301 "Moved Permanently"リダイレクトであることに注意してください。 ブラウザまたはプロキシサーバはこの種のリダイレクトをキャッシュし、古いページにはアクセスしないようにする事を覚えておいてください。
これらのケースの全てで、もし主要ホスト(例:http://www.example.com
)を提供しない場合、
Railsは現在のリクエストから詳細情報を取得します。
3.13 Rackアプリケーションへのルーティング
PostsController
内のindex
アクションに該当する'posts#index'
のような文字列の代わりに、
マッチャーのエンドポイントとしてのRackアプリケーションを指定することが可能です。
match '/application.js', to: Sprockets, via: :all
Sprockets
が、呼び出しと[status, headers, body]
を返すのに応答をする限り、
ルーターはRackアプリケーションとアクションの違いを認識しまんせん。
これは、あなたがRackアプリケーションで全ての動詞を適切に処理したいと考えている場合に、
via: :all
を使用するのに適しています。
'posts#index'
は実際には、有効なRackアプリケーションを返す、
PostsController.action(:index)
を拡張しています。(翻訳に自信なし)
3.14 rootの使用
root
メソッドを使用して、Railsが'/'
ルートをどうするかを指定することが出来ます。
root to: 'pages#main'
root 'pages#main' # 上記の省略文法
一番多く訪問されるルートであり、また最初にマッチされるべきである事から、
ファイルの先頭にroot
ルートを配置すべきです。
root
ルートは、アクションへのGETリクエストへのルートのみになります。
rootは同様に名前空間(namespace)とスコープを使用することが可能です。 例えば、
namespace :admin do
root to: "admin#index"
end
root to: "home#index"
3.15 ユニコード文字のルート
ユニコード文字のルートを直接指定することが可能です。 例えば、
get 'こんにちは', to: 'welcome#index'
4. Resourcefulルートのカスタマイズ
resources :posts
によって生成されるデフォルトのルートとヘルパーは、
何らかの方法でカスタマイズする事が可能です。
Railsは、resourcefulなヘルパーの一般的な部分を実質カスタマイズ出来るようにしてくれます。
4.1 コントローラー使用の指定
:controller
オプションは、resourceに使用するコントローラーを明示的に指定することを可能にしてくれます。
例えば、
resources :photos, controller: 'images'
上記は/photos
で始まるパスを認識しますが、Images
コントローラーへルートを通します。
HTTP動詞 | パス | アクション | ヘルパー |
---|---|---|---|
GET | /photos | index | photos_path |
GET | /photos/new | new | new_photo_path |
POST | /photos | create | photos_path |
GET | /photos/:id | show | photo_path(:id) |
GET | /photos/:id/edit | edit | edit_photo_path(:id) |
PATCH/PUT | /photos/:id | update | photo_path(:id) |
DELETE | /photos/:id | destroy | photo_path(:id) |
photos_path
、new_photo_path
等を使用して、このリソースのパスが生成されます。
名前空間のコントローラーの場合は、ディレクトリの表記を使用することが出来ます。 例えば、
resources :user_permissions, controller: 'admin/user_permissions'
これは、Admin::UserPermissions
コントローラーへのルートになります。
ディレクトリ表記だけがサポートされます。
ruby定数表記でのコントローラーの指定(例: :controller => 'Admin::UserPermissions'
)は、
ルーティングの問題を引き起こし、警告される結果となります。
4.2 制約の指定
:constraints
オプションを使用して、明示的にidで必須とするフォーマットを指定することが可能です。
例えば、
resources :photos, constraints: {id: /[A-Z][A-Z][0-9]+/}
この宣言は、:id
パラメーターが提供された正規表現にマッチする制約を設けます。
そのため、このケースではルーターは/photos/1
をこのルートにマッチさせません。
/photos/RR27
であればマッチします。
ブロック形式を使用することで、複数のルートに同一の制約を指定することが可能です。
constraints(id: /[A-Z][A-Z][0-9]+/) do
resources :photos
resources :accounts
end
もちろん、このコンテキスト内でresourceでは無いもので、より進んだ制約を指定することが可能です。
デフォルトでは、:id
パラメーターはドットを受け付けません。
これは、ドットがフォーマットのルートの区切りとして使用されているためです。
もし、:id
内にドットを使用する必要があれば、制約を追加することでこれを上書きします。
例えば、id: /[^\/]+/
とすれば、スラッシュ以外の全てを受け入れます。
4.3 名前付けされたヘルパーの上書き
:as
オプションは、ルートの名前付けがされたヘルパーの標準の名前の上書きを可能にしてくれます。
例えば、
resources :photos, as: 'images'
上記は、/photos
から始まるパスを認識し、PhotosController
へのリクエストとしてルートを通しますが、
:as
オプションの値が使用された名前がヘルパーに付けられます。
HTTP動詞 | パス | アクション | ヘルパー |
---|---|---|---|
GET | /photos | index | images_path |
GET | /photos/new | new | new_image_path |
POST | /photos | create | images_path |
GET | /photos/:id | show | image_path(:id) |
GET | /photos/:id/edit | edit | edit_image_path(:id) |
PATCH/PUT | /photos/:id | update | image_path(:id) |
DELETE | /photos/:id | destroy | image_path(:id) |
4.4 newとeditの上書き
:path_names
オプションは、パス内に自動的に生成される"new"と"edit"セグメントの上書きを可能にしてくれます。
resources :photos, path_names: { new: 'make', edit: 'change' }
これは次のようなパスを認識するようになります。
/photos/make
/photos/1/change
実際のアクション名は、このオプションによって変更される事はありません。
2つのパスは、new
とedit
アクションへルートしたままになります。
もし、このオプションを全てのルートに対して変更したいと自身で気がついた場合、 スコープを使用してそれを行うことが可能です。
scope path_names: { new: 'make' } do
# ルートを指定
end
4.5 名付けルート(経路)ヘルパーの接頭辞
:as
オプションを使用することで、Railsがルート(経路)のために生成する名付けルートヘルパーに接頭辞を付ける事が可能です。
このオプションを使用することで、パススコープ(scope)を使用したルート名の衝突を避ける事が出来ます。
例えば、
scope 'admin' do
resources :photos, as: 'admin_photos'
end
resources :photos
これはadmin_photos_path
やnew_admin_photo_path
のような、ルートヘルパーを提供します。
ルートヘルパーのグループに接頭辞を付けるには、スコープ(scope)に:as
を使用します。
scope 'admin', as: 'admin' do
resources :photos, :accounts
end
resources :photos, :accounts
これは、admin_photos_path
とadmin_accounts_path
のようなルートを生成し、
それぞれ/admin/photos
と/admin/accounts
にマッピングされます。
namespace
スコープは、:module
と:path
の接頭辞付けと同様に、
自動的に:as
を追加します。
また、名付けパラメーターを使用してルートに接頭辞を付けることも可能です。
scope ':username' do
resources :posts
end
これは、/bob/posts/1
のようなURLを提供し、
コントローラーとヘルパーとビューでparams[:username]
として、パスのusername
の部分を参照できるようにしてくれます。
4.6 生成されたルートの制約
デフォルトでは、Railsは7つのRESTfulなルート(index、show、new、create、edit、update、destroy)を生成します。
:only
と:except
オプションを使用して、この振る舞いを調整することが可能です。
:only
オプションは、Railsに指定したルートだけを作成することを伝えます。
resources :photos, only: [:index, :show]
これにより、/photos
へのGET
リクエストは成功しますが、
/photos
へのPOST
リクエスト(通常はcreateアクションへのルート)は失敗するようになります。
:except
オプションには、Railsが生成すべきでは無いルートまたはルートのリストを指定します。
resources :photos, except: :destroy
このケースでは、Railsはdestroy
(/photos/:id
へのDELETE
リクエスト)を除く全ての通常のルートを生成します。
もし、あなたのアプリケーションが多くのRESTfulなルートを持っているのであれば、
:only
と:except
を使用して、実際に必要とされているるルートのみを生成することで、
ルーティングプロセスのメモリー使用量を抑え、スピードアップさせることが出来るかもしれません。
4.7 翻訳パス
scope
を使用して、resourcesによって生成されたパス名を変更することが可能です。
scope(path_names: { new: 'neu', edit: 'bearbeiten' }) do
resources :categories, path: 'kategorien'
end
こうすることで、Railsは次のようなCategoriesController
へのルートを生成します。
HTTP動詞 | パス | アクション | ヘルパー |
---|---|---|---|
GET | /kategorien | index | categories_path |
GET | /kategorien/neu | new | new_category_path |
POST | /kategorien | create | categories_path |
GET | /kategorien/:id | show | category_path(:id) |
GET | /kategorien/:id/bearbeiten | edit | edit_category_path(:id) |
PATCH/PUT | /kategorien/:id | update | category_path(:id) |
DELETE | /kategorien/:id | destroy | category_path(:id) |
4.8 単数フォームの上書き
もし、resourceの単数フォームを定義したいのであれば、
Inflector
へルールを追加する必要があります。
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'tooth', 'teeth'
end
4.9 入れ子Resources内での:as
の使用
:as
オプションは、入れ子のルートヘルパー内でResourceのために自動生成された名前を上書きします。
例えば、
resources :magazines do
resources :ads, as: 'periodical_ads'
end
これは、magazine_periodical_ads_url
とedit_magazine_periodical_ad_path
のようなルーティングヘルパーを生成します。
5. ルートの検証とテスト
Railsは、ルートを検証・テストするための便利な機能を提供します。
5.1 既存ルートのリスト化
アプリケーション内で利用可能な全ルートを取得するには、
development環境の実行下でhttp://localhost:3000/rails/info/routes
へアクセスします。
また、rake routes
コマンドをターミナル上で実行することで、同じ出力結果を得ることが可能です。
どちらも同じ方法で、routes.rb
内の全てのルートの内容をリスト化します。
各ルートで次の事が確認できるはずです。
- ルート名(指定されていれば)
- 使用されるHTTP動詞(全ての動詞に応答しない経路であれば)
- マッチするURLパターン
- ルートのためのルーティングパラメーター
下記はRESTfulルートに対してrake routes
で出力した一例になります。
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
CONTROLLER
環境変数に特定のコントローラーをマッピングすることで、
ルートのリスト一覧表示の制限をすることが可能です。
$ CONTROLLER=users rake routes
rake routes
による出力が見にくいと感じたなら、
行が折り返さない所までターミナルウインドウの幅を広げてみてください。
5.2 ルート(経路)のテスト
ルートはあなたのテスト計画に含まれているべきです。(アプリケーションの他の部分と同じように) Railsはシンプルにルート(経路)のテストが出来るように設計された3つの組み込みアサーションを提供します。
assert_generates
assert_recognizes
assert_routing
5.2.1 assert_generatesアサーション
assert_generates
は、特定のオプションのセットが特定のパスを生成し、
デフォルトルートまたはカスタムルートで使用可能なのかをアサーションします。
例えば、
assert_generates '/photos/1', { controller: 'photos', action: 'show', id: '1' }
assert_generates '/about', controller: 'pages', action: 'about'
5.2.2 assert_recognizesアサーション
assert_recognizes
はassert_generates
の正反対のものです。
与えられたパスを認識し、アプリケーション内の特定の場所にルートを通しているかをアサーションします。
例えば、
assert_recognizes({ controller: 'photos', action: 'show', id: '1' }, '/photos/1')
:method
引数を提供して、HTTP動詞を指定することが可能です。
assert_recognizes({ controller: 'photos', action: 'create' }, { path: 'photos', method: :post })
5.2.3 assert_routingアサーション
assert_routing
アサーションは、パスによるオプションの生成、オプションによるパスの生成の両方からルートの検証を行います。
そのため、これはassert_generates
とassert_recognizes:
の機能を結合したものになります。
assert_routing({ path: 'photos', method: :post }, { controller: 'photos', action: 'create' })
© 2010 - 2017 STUDIO KINGDOM