このガイドでは、開発者に向けてRailsのルーティング機能を解説します(訳注: routeとrootを区別するため、訳文ではrouteを基本的に「ルーティング」と訳します)。
このガイドの内容:
config/routes.rb
のコードの読み方match
メソッドによるルーティングも可能です)Railsのルーター(router)は、URLパスに基づいて、受信したHTTPリクエストをRailsアプリケーション内の特定のコントローラーアクションに対応付けます(Rackアプリケーションに転送することも可能です)。ルーターは、ルーターで構成されたリソースに基づいて、パスとURLヘルパーも生成します。
RailsアプリケーションがHTTPリクエストを受け取ると、このリクエストをコントローラーのアクション(メソッドとも呼ばれます)に対応付けるようルーターに要求します。たとえば、以下の受信リクエストを考えてみましょう。
GET /users/17
最初にマッチしたのが以下のルーティングだとします。
get "/users/:id", to: "user#show"
このリクエストはUsersController
クラスのshow
アクションに一致し、params
ハッシュには{ id: '17' }
が含まれます。
to:
オプションは、コントローラ名#アクション名
形式の文字列が渡されることを前提としています。
to:
オプションでアクション名を文字列で指定する代わりに、action:
オプションでアクション名のシンボルを指定することも可能です。
さらにcontroller:
オプションも使えば、以下のように#
記号なしの文字列を指定することも可能です。
get "/users/:id", controller: "users", action: :show
Railsではルーティングを指定するときにスネークケースを使います。たとえばUserProfilesController
のような複合語のコントローラを使う場合は、user_profiles#show
のように指定します。
ルーターは、アプリケーションのパスやURLヘルパーメソッドを自動的に生成します。これらのメソッドを使うことで、パスやURL文字列をハードコードすることを回避できます。
たとえば、以下のルーティングを定義することで、user_path
とuser_url
というヘルパーメソッドを利用できます。
get "/users/:id", to: "users#show", as: "user"
as:
オプションは、ルーティングのカスタム名を指定するときに使います。ここで指定した名前は、URLとパスヘルパーを生成するときに使われます。
そして、アプリケーションのコントローラに以下のコードがあるとします。
@user = User.find(params[:id])
上記に対応するビューは以下です。
<%= link_to 'User Record', user_path(@user) %>
すると、ルーターによって/patients/17
というパスが生成されます。これを利用することでビューが改修しやすくなり、コードも読みやすくなります。このidはルーティングヘルパーで指定する必要がない点にご注目ください。
ルーターは、user_path(@user)
から/users/17
というパスを生成します。このuser_path
ヘルパーを使えば、ビューにパスをハードコードする必要がなくなります。こうすることで、最終的にルーティングを別のURLに移動するときに、対応するビューのコードを更新する必要がなくなるので、便利です。
ルーターは、同様の目的を持つuser_url
も生成します。上述のuser_path
が生成するのは/users/17
のような相対URLですが、user_url
は上記の例で言うとhttps://example.com/users/17
のような絶対URLを生成する点が異なります。
アプリケーションやエンジンのルーティングはconfig/routes.rb
ファイルの中に存在します。これは、典型的なRailsアプリケーションでのルーティングの配置場所です。
次のセクションでは、このファイルで使われるさまざまなルーティングヘルパーについて説明します。
Rails.application.routes.draw do resources :brands, only: [:index, :show] do resources :products, only: [:index, :show] end resource :basket, only: [:show, :update, :destroy] resolve("Basket") { route_for(:basket) } end
これは通常のRubyソースファイルなので、ルーティング定義には、条件やループなど、Rubyのあらゆる機能を利用できます。
ルーティング定義をラップするRails.application.routes.draw do ... end
ブロックは、ルーターDSL(Domain Specific Language: ドメイン固有言語)のスコープを確定するのに不可欠なので、削除してはいけません。
routes.rb
ファイルで変数名を使う場合は、ルーターのDSLメソッドと名前が衝突しないように十分ご注意ください。
リソースベースのルーティング (以下リソースルーティング) を使うことで、指定のリソースコントローラでよく使われるすべてのルーティングを手軽に宣言できます。resources
を宣言するだけで、コントローラのindex
、show
、new
、edit
、create
、update
、destroy
アクションを個別に宣言しなくても1行で宣言が完了します。
ブラウザはRailsに対してリクエストを送信する際に、特定のHTTP verb(GET
、POST
、PATCH
、PUT
、DELETE
など)を使って、URLに対するリクエストを作成します。上に述べたHTTP verbは、いずれもリソースに対して特定の操作の実行を指示するリクエストです。リソースルーティングでは、関連するリクエストを1つのコントローラ内のアクションに割り当てます。
Railsアプリケーションが以下のHTTPリクエストを受け取ったとします。
DELETE /photos/17
このリクエストは、特定のコントローラ内アクションにマッピングさせるようルーターに要求しています。最初にマッチしたのが以下のルーティングだとします。
resources :photos
RailsはこのリクエストをPhotosController
内のdestroy
アクションに割り当て、params
ハッシュに{ id: '17' }
を含めます。
Railsのリソースフルルーティングでは、(GET、PUTなどの)各種HTTP verb(動詞、HTTPメソッドとも呼ばれます) と、コントローラ内アクションを指すURLが対応付けられます。1つのアクションは、データベース上での特定のCRUD(Create/Read/Update/Delete)操作に対応付けられるルールになっています。たとえば、以下のようなルーティングが1つあるとします。
resources :photos
上の記述により、アプリケーション内に以下の7つのルーティングが作成され、いずれもPhotosController
に対応付けられます。
HTTP verb | パス | コントローラ#アクション | 目的 |
---|---|---|---|
GET | /photos | photos#index | すべての写真の一覧を表示 |
GET | /photos/new | photos#new | 写真を1つ作成するためのHTMLフォームを返す |
POST | /photos | photos#create | 写真を1つ作成する |
GET | /photos/:id | photos#show | 特定の写真を表示する |
GET | /photos/:id/edit | photos#edit | 写真編集用のHTMLフォームを1つ返す |
PATCH/PUT | /photos/:id | photos#update | 特定の写真を更新する |
DELETE | /photos/:id | photos#destroy | 特定の写真を削除する |
Railsのルーターでは、サーバーへのリクエストをマッチさせる際にHTTP verbとURLを組み合わせる形で使っているため、4種類のURL(/photos
、/photos/new
、/photos/:id
、/photos/:id/edit
)を7種類の異なるアクション(index
、show
、new
、create
、edit
、update
、destroy
)に割り当てています。たとえば、同じphotos/
パスであっても、HTTP verbがGET
のときはphotos#index
にマッチし、HTTP verbがPOST
のときはphotos#create
にマッチします。
Railsのルーティングファイルroutes.rb
では、ルーティングを記載する順序が重要であり、「上からの記載順に」マッチします。たとえば、resources :photos
というルーティングがget 'photos/poll'
よりも上の行にあれば、resources
行のshow
アクションがget
行の記述よりも優先されるので、get 'photos/poll'
行のルーティングは有効になりません。get 'photos/poll'
を最初にマッチさせるには、get 'photos/poll'
行をresources
行 よりも上 に移動する必要があります。
リソースフルなルーティングを作成すると、アプリケーションのコントローラやビューで多くのヘルパーが利用できるようになります。
たとえば、resources :photos
というルーティングをルーティングファイルに追加すると、コントローラやビューで以下の _path
ヘルパーが使えるようになります。
_path ヘルパー |
返すURL |
---|---|
photos_path |
/photos |
new_photo_path |
/photos/new |
edit_photo_path(:id) |
/photos/:id/edit |
photo_path(:id) |
/photos/:id |
上記の:id
などのパスヘルパーのパラメーターは、生成されたURLに渡されます。つまり、edit_photo_path(10)
は/photos/10/edit
を返します。
これらの_path
ヘルパーに対応する_url
ヘルパー(photos_url
など)も生成されます。_url
ヘルパーは、同じパスの前に「現在のホスト名」「ポート番号」「パスのプレフィックス」を追加して返します。
"_path"や"_url"の前に付けられるプレフィックスには、ルーティング名が使われます。これは、rails routes
コマンド出力の"prefix"列を確認することで特定できます。詳しくは、後述の既存のルールを一覧表示するを参照してください。
リソースをいくつも定義しなければならない場合は、以下のような略記法で一度に定義することでタイプ量を節約できます。
resources :photos, :books, :videos
上の記法は、以下の記法のショートカットです。
resources :photos resources :books resources :videos
場合によっては、ユーザーがリソースを1個しか持たないことが前提となることもあります(この場合、そのリソースのすべての値を一覧表示するindex
アクションを用意する意味はありません)。このような場合は、複数形のresources
の代わりに単数形のresource
を指定できます。
以下のリソースフルなルーティングは、アプリケーション内に6つのルーティングを作成して、それらすべてをGeocoders
コントローラーに対応付けます。
resource :geocoder resolve("Geocoder") { [:geocoder] }
上のresolve
呼び出しは、Geocoder
のインスタンスをレコード識別を介して単数形ルーティングに変換するために必要です。
Geocoders
コントローラに割り当てられた以下の6つのルーティングを作成します。
HTTP verb | パス | コントローラ#アクション | 目的 |
---|---|---|---|
GET | /geocoder/new | geocoders#new | geocoder作成用のHTMLフォームを返す |
POST | /geocoder | geocoders#create | geocoderを作成する |
GET | /geocoder | geocoders#show | 1つしかないgeocoderリソースを表示する |
GET | /geocoder/edit | geocoders#edit | geocoder編集用のHTMLフォームを返す |
PATCH/PUT | /geocoder | geocoders#update | 1つしかないgeocoderリソースを更新する |
DELETE | /geocoder | geocoders#destroy | geocoderリソースを削除する |
単数形リソースは、複数形のコントローラに対応付けられます。たとえば、geocoder
という単数形リソースは、GeocodersController
という複数形の名前を持つコントローラに対応付けられます。
単数形のリソースフルなルーティングを使うと、以下のヘルパーメソッドが生成されます。
_path ヘルパー |
返すURL |
---|---|
new_geocoder_path |
/geocoder/new |
edit_geocoder_path |
/geocoder/edit |
geocoder_path) |
/geocoder |
複数形リソースの場合と同様に、末尾が_url
で終わる同じヘルパー名でも「現在のホスト名」「ポート番号」「パスのプレフィックス」が含まれます。
大規模なアプリケーションでは、コントローラーを名前空間でグループ化して整理したい場合があります。たとえば、app/controllers/admin
ディレクトリ内にあるAdmin::
名前空間の下に、複数のコントローラーがあるとします。以下のようにnamespace
ブロックを使うと、このようなグループへルーティングできます。
namespace :admin do resources :articles end
上のルーティングにより、articles
コントローラやcomments
コントローラへのルーティングが多数生成されます。たとえば、Admin::ArticlesController
向けに作成されるルーティングは以下のとおりです。
HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
---|---|---|---|
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(:id) |
PATCH/PUT | /admin/articles/:id | admin/articles#update | admin_article_path(:id) |
DELETE | /admin/articles/:id | admin/articles#destroy | admin_article_path(:id) |
上記の例では、namespace
のデフォルトの規則に沿って、すべてのパスに/admin
プレフィックスが追加されていることにご注目ください。
例外的に、(/admin
が前についていない) /articles
をAdmin::ArticlesController
にルーティングしたい場合は、以下のようにscope
ブロックでモジュールを指定できます。
scope module: "admin" do resources :articles end
上は以下のようにscope
を使わない書き方も可能です。
resources :articles, module: "admin"
逆に、/admin/articles
を (Admin::
モジュールのプレフィックスなしの) ArticlesController
にルーティングしたい場合は、以下のようにscope
ブロックでパスを指定できます。
scope "/admin" do resources :articles end
上は以下のようにscope
を使わない書き方も可能です。
resources :articles, path: "/admin/articles"
いずれの場合も、名前付きルーティング(named route)は、scope
を使わなかった場合と同じになることにご注目ください。最後の例の場合は、以下のパスがArticlesController
に割り当てられます。
上述の2つの書き方(パスに/admin
をプレフィックスしない場合、モジュールにAdmin::
をプレフィックスしない場合)は、どちらの場合も、名前付きルーティングヘルパーはscope
を使わなかった場合と同じになります。
最後のケースでは、ArticlesController
に以下のパスが対応付けられます。
HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
---|---|---|---|
GET | /admin/articles | articles#index | articles_path |
GET | /admin/articles/new | articles#new | new_article_path |
POST | /admin/articles | articles#create | articles_path |
GET | /admin/articles/:id | articles#show | article_path(:id) |
GET | /admin/articles/:id/edit | articles#edit | edit_article_path(:id) |
PATCH/PUT | /admin/articles/:id | articles#update | article_path(:id) |
DELETE | /admin/articles/:id | articles#destroy | article_path(:id) |
namespace
ブロックの内部で異なるコントローラ名前空間を使いたい場合、「get '/foo', to: '/foo#index'
」のような絶対コントローラパスを指定することもできます。
他のリソースの配下に論理的な子リソースを配置することはよくあります。たとえば、Railsアプリケーションに以下のモデルがあるとします。
class Magazine < ApplicationRecord has_many :ads end class Ad < ApplicationRecord belongs_to :magazine end
ルーティングをネストする(入れ子にする)宣言を使うことで、この親子関係をルーティングで表せるようになります。
resources :magazines do resources :ads end
上のルーティングによって、雑誌(magazines)へのルーティングに加えて、広告(ads)をAdsController
にもルーティングできるようになりました。ネストしたads
リソースの全ルーティングは以下のようになります。
HTTP verb | パス | コントローラ#アクション | 目的 |
---|---|---|---|
GET | /magazines/:magazine_id/ads | ads#index | ある雑誌1冊に含まれる広告をすべて表示する |
GET | /magazines/:magazine_id/ads/new | ads#new | ある1冊の雑誌用の広告を1つ作成するHTMLフォームを返す |
POST | /magazines/:magazine_id/ads | ads#create | ある1冊の雑誌用の広告を1つ作成する |
GET | /magazines/:magazine_id/ads/:id | ads#show | ある雑誌1冊に含まれる広告を1つ表示する |
GET | /magazines/:magazine_id/ads/:id/edit | ads#edit | ある雑誌1冊に含まれる広告1つを編集するHTMLフォームを返す |
PATCH/PUT | /magazines/:magazine_id/ads/:id | ads#update | ある雑誌1冊に含まれる広告を1つ更新する |
DELETE | /magazines/:magazine_id/ads/:id | ads#destroy | ある雑誌1冊に含まれる広告を1つ削除する |
これによって、パスとURLについて通常のルーティングヘルパーも作成されます。ヘルパーはmagazine_ads_url
やedit_magazine_ad_path
のような名前になります。ads
リソースはmagazines
の下にネストしているので、adのURLではmagazineを省略できません。これらのヘルパーは、最初のパラメータとしてMagazineモデルのインスタンスを1つ受け取ります(magazine_ads_url(@magazine, @ad)
)。
次のように、ネストしたリソースの中で別のリソースをネストできます。
resources :publishers do resources :magazines do resources :photos end end
たとえば上のルーティング例は、アプリケーションで以下のようなパスとして認識されます。
/publishers/1/magazines/2/photos/3
このURLに対応するルーティングヘルパーはpublisher_magazine_photo_url
となります。このヘルパーを使うには、毎回3つの階層すべてでオブジェクトを指定する必要があります。このように、リソースのネストを深くすると複雑になりすぎてしまい、ルーティングをメンテナンスしにくくなります。
リソースのネスティングの深さは、経験則として1階層にとどめておくべきです。
(上記で推奨したような)深いネストを回避する方法の1つとして、コレクションアクションの生成場所を親のスコープに移動するという方法があります。この方法を使うと、階層化されたという感覚を得ながら、メンバーアクションをネストしないようにできます。言い換えると、最小限の情報でリソースを一意に指定できるルーティングを作成するということです。
"メンバー"アクションとは、個別のリソースに適用され、アクションの対象となる特定のリソースを識別するためにIDを指定する必要のあるアクション(show
やedit
など)を指します。"コレクション"アクションとは、リソースのセット全体を操作するアクション(index
など)を指します。
resources :articles do resources :comments, only: [:index, :new, :create] end resources :comments, only: [:show, :edit, :update, :destroy]
上のルーティングでは、:only
オプションを用いることで、指定したルーティングだけを生成するようRailsに指示しています。
この方法は、ルーティングの記述を複雑にせずに済み、かつ深いネストを作らずに済むという絶妙なバランスを保っています。:shallow
オプションを使うことで、上と同じ内容をさらに簡単に記述できます。
resources :articles do resources :comments, shallow: true end
これによって生成されるルーティングは、最初の例と完全に同じです。親リソースで:shallow
オプションを指定すると、すべてのネストしたリソースが浅くなります。
resources :articles, shallow: true do resources :comments resources :quotes end
このarticles
リソースでは以下のルーティングが生成されます。
HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
---|---|---|---|
GET | /articles/:article_id/comments(.:format) | comments#index | article_comments_path |
POST | /articles/:article_id/comments(.:format) | comments#create | article_comments_path |
GET | /articles/:article_id/comments/new(.:format) | comments#new | new_article_comment_path |
GET | /comments/:id/edit(.:format) | comments#edit | edit_comment_path |
GET | /comments/:id(.:format) | comments#show | comment_path |
PATCH/PUT | /comments/:id(.:format) | comments#update | comment_path |
DELETE | /comments/:id(.:format) | comments#destroy | comment_path |
GET | /articles/:article_id/quotes(.:format) | quotes#index | article_quotes_path |
POST | /articles/:article_id/quotes(.:format) | quotes#create | article_quotes_path |
GET | /articles/:article_id/quotes/new(.:format) | quotes#new | new_article_quote_path |
GET | /quotes/:id/edit(.:format) | quotes#edit | edit_quote_path |
GET | /quotes/:id(.:format) | quotes#show | quote_path |
PATCH/PUT | /quotes/:id(.:format) | quotes#update | quote_path |
DELETE | /quotes/:id(.:format) | quotes#destroy | quote_path |
GET | /articles(.:format) | articles#index | articles_path |
POST | /articles(.:format) | articles#create | articles_path |
GET | /articles/new(.:format) | articles#new | new_article_path |
GET | /articles/:id/edit(.:format) | articles#edit | edit_article_path |
GET | /articles/:id(.:format) | articles#show | article_path |
PATCH/PUT | /articles/:id(.:format) | articles#update | article_path |
DELETE | /articles/:id(.:format) | articles#destroy | article_path |
ブロック内でshallow
メソッドを使うと、すべてのネストが1階層浅くなるように内側にスコープを1つ作成します。これによって生成されるルーティングは、最初の例と完全に同じです。
shallow do resources :articles do resources :comments resources :quotes end end
scope
メソッドには、「浅い」ルーティングをカスタマイズするためのオプションが2つあります。:shallow_path
と:shallow_prefix
です。
:shallow_path
オプションは、指定されたパラメータをメンバーのパスの冒頭に追加します。
scope shallow_path: "sekret" do resources :articles do resources :comments, shallow: true end end
上の場合、comments
リソースのルーティングは以下のようになります。
HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
---|---|---|---|
GET | /articles/:article_id/comments(.:format) | comments#index | article_comments_path |
POST | /articles/:article_id/comments(.:format) | comments#create | article_comments_path |
GET | /articles/:article_id/comments/new(.:format) | comments#new | new_article_comment_path |
GET | /sekret/comments/:id/edit(.:format) | comments#edit | edit_comment_path |
GET | /sekret/comments/:id(.:format) | comments#show | comment_path |
PATCH/PUT | /sekret/comments/:id(.:format) | comments#update | comment_path |
DELETE | /sekret/comments/:id(.:format) | comments#destroy | comment_path |
:shallow_prefix
オプションを使うと、指定されたパラメータを _path
および_url
ルーティングヘルパー名の冒頭に追加します。
scope shallow_prefix: "sekret" do resources :articles do resources :comments, shallow: true end end
上の場合、comments
リソースのルーティングは以下のようになります。
HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
---|---|---|---|
GET | /articles/:article_id/comments(.:format) | comments#index | article_comments_path |
POST | /articles/:article_id/comments(.:format) | comments#create | article_comments_path |
GET | /articles/:article_id/comments/new(.:format) | comments#new | new_article_comment_path |
GET | /comments/:id/edit(.:format) | comments#edit | edit_sekret_comment_path |
GET | /comments/:id(.:format) | comments#show | sekret_comment_path |
PATCH/PUT | /comments/:id(.:format) | comments#update | sekret_comment_path |
DELETE | /comments/:id(.:format) | comments#destroy | sekret_comment_path |
concernを使うことで、他のリソース内で使いまわせる共通のルーティングを宣言できます。concernは以下のようにconcern
ブロックで定義します。
concern :commentable do resources :comments end concern :image_attachable do resources :images, only: :index end
concernを利用すると、同じようなルーティングを繰り返し記述せずに済み、複数のルーティング間で同じ振る舞いを共有できます。
resources :messages, concerns: :commentable resources :articles, concerns: [:commentable, :image_attachable]
上のコードは以下と同等です。
resources :messages do resources :comments end resources :articles do resources :comments resources :images, only: :index end
scope
ブロック内やnamespace
ブロック内では、以下のように複数形のconcerns
を呼び出すことでも上と同じ結果を得られます。
namespace :messages do concerns :commentable end namespace :articles do concerns :commentable concerns :image_attachable end
ルーティングヘルパーを使う方法の他に、パラメータの配列からパスやURLを作成することもできます。例として、以下のようなルーティングがあるとします。
resources :magazines do resources :ads end
magazine_ad_path
を使うと、idを数字で渡す代わりにMagazine
とAd
のインスタンスを引数として渡せます。
<%= link_to 'Ad details', magazine_ad_path(@magazine, @ad) %>
上で生成されるパスは/magazines/5/ads/42
のようになります。
以下のように複数のオブジェクトを持つ配列に対してurl_for
を使うときも、上と同じようにパスを得られます。
<%= 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] %>
1冊の雑誌にだけリンクしたい場合は、以下のように書きます。
<%= link_to 'Magazine details', @magazine %>
それ以外のアクションについては、配列の第1要素にアクション名を挿入する必要があります。たとえば、edit_magazine_ad_path
と書く代わりに以下のように書けます。
<%= link_to 'Edit Ad', [:edit, @magazine, @ad] %>
これにより、モデルのインスタンスをURLとして扱えるようになります。これはリソースフルなスタイルを採用する大きなメリットの1つです。
[@magazine, @ad]
のようなオブジェクトからパスとURLを自動的に取得するために、RailsではActiveModel::Naming
モジュールとActiveModel::Conversion
モジュールのメソッドを利用してします。具体的には、@magazine.model_name.route_key
はmagazines
を返し、@magazine.to_param
はモデルのid
の文字列表現を返します。したがって、[@magazine, @ad]
オブジェクトに対して生成されるパスは、/magazines/1/ads/42
のようになります。
デフォルトで作成されるRESTfulなルーティングは7つありますが、7つと決まっているわけではありません。必要であれば、コレクションやコレクションの各メンバーに対して適用されるルーティングを追加することも可能です。
以下のセクションでは、メンバールーティングとコレクションルーティングの追加について説明します。member
という用語は、単一の要素に作用するルーティング(show
、update
、destroy
など)を指します。collection
という用語は、複数の要素(要素のコレクション)を操作するルーティング(index
ルーティングなど)を指します。
member
ブロックは、以下のようにリソースブロックに追加できます。
resources :photos do member do get "preview" end end
上のルーティングはGETリクエストとそれに伴う/photos/1/preview
を認識し、リクエストをPhotos
コントローラのpreview
アクションにルーティングし、リソースid値をparams[:id]
に渡します。同時に、preview_photo_url
ヘルパーとpreview_photo_path
ヘルパーも作成されます。
/photos/1/preview
への受信GETリクエストは、PhotosController
のpreview
アクションにルーティングされます。リソースID値はparams[:id]
で得られます。また、preview_photo_url
ヘルパーおよびpreview_photo_path
ヘルパーも作成されます。
member
ブロック内では、各ルート定義で HTTP verb(上記の例ではget 'preview'
のget
)が指定されます。get
の他に、patch
、put
、post
、またはdelete
も利用できます。
member
ルーティングが1つしかない場合は、以下のようにルーティングで:on
オプションを指定することでブロックを省略できます。
resources :photos do get "preview", on: :member end
上の:on
オプションは省略することも可能です。この場合、リソースidの値の取得にparams[:id]
ではなくparams[:photo_id]
を使う点を除いて、同じメンバールーティングが生成されます。ルーティングヘルパーも、preview_photo_url
がphoto_preview_url
に、preview_photo_path
がphoto_preview_path
にそれぞれリネームされます。
ルーティングにコレクション(collection)を追加するには以下のようにcollection
ブロックを使います。
resources :photos do collection do get "search" end end
上のルーティングは、GETリクエスト + /photos/search
などの(idを伴わない)パスを認識し、リクエストをPhotos
コントローラのsearch
アクションにルーティングします。このときsearch_photos_url
やsearch_photos_path
ルーティングヘルパーも同時に作成されます。
collectionルーティングでもmemberルーティングのときと同様に:on
オプションを使えます。
resources :photos do get "search", on: :collection end
追加のresourceルーティングをシンボルで第1引数として定義する場合は、文字列で定義した場合と振る舞いが同等ではなくなる点にご注意ください。文字列はパスとして推測されますが、シンボルはコントローラのアクションとして推測されます。
:on
オプションを使って、たとえば以下のように別のnewアクションを追加できます。
resources :comments do get "preview", on: :new end
上のようにすることで、GET + /comments/new/preview
のようなパスが認識され、Comments
コントローラのpreview
アクションにルーティングされます。preview_new_comment_url
やpreview_new_comment_path
ルーティングヘルパーも同時に作成されます。
リソースフルなルーティングにアクションが多数追加されていることに気付いたら、それ以上アクションを追加するのをやめて、そこに別のリソースが隠されているのではないかと疑ってみる方がよいでしょう。
resources
で生成されるデフォルトのルーティングやヘルパーは、カスタマイズ可能です。詳しくは、リソースフルルーティングをカスタマイズするセクションを参照してください。
Railsでは、resources
によるリソースフルなルーティングの他に、任意のURLをアクションにルーティングすることも可能です。この方式を使う場合、リソースフルルーティングのような自動的なルーティンググループの生成は行われません。従って、アプリケーションで必要なルーティングを個別に設定することになります。
基本的にはリソースフルルーティングを使うべきですが、このような単純なルーティングの方が適している場合もあります。リソースフルルーティングが適していない場合に、アプリケーションのあらゆる部分を無理にリソースフルなフレームワークに押し込める必要はありません。
リソースフルでないルーティングが適しているユースケースの1つに、既存のレガシーURLを新しいRailsアクションに対応付ける場合があります。
通常のルーティングを設定する場合は、RailsがルーティングをブラウザからのHTTPリクエストに割り当てるためのシンボルをいくつか渡します。以下のルーティングを例にとってみましょう。
get "photos(/:id)", to: "photos#display"
/photos/1
に対するブラウザからのGET
リクエストが上のルーティングで処理されると、PhotosController
のdisplay
アクションが呼び出され、URL末尾のパラメータ"1"
へのアクセスはparams[:id]
で行なえます。:id
が必須パラメータではないことが丸かっこ()
で示されているので、このルーティングは、上の例の/photos
をPhotosController#display
にルーティングすることもできます。
通常のルーティングの一部として、文字列を固定しない動的なセグメントを自由に使えます。あらゆるセグメントはparams
の一部に含めてアクションに渡せます。以下のルーティングを設定したとします。
get "photos/:id/:user_id", to: "photos#show"
上のルーティングは、/photos/1/2
のようなパスにマッチします。このときアクションで使えるparams
は{ controller: "photos", action: "show", id: "1", user_id: "2" }
となります。
デフォルトでは動的なセグメント分割にドット.
を渡せません。ドットはフォーマット済みルーティングでは区切り文字として使われるためです。どうしても動的セグメント内でドットを使いたい場合は、デフォルト設定を上書きする制限を与えます。たとえばid: /[^\/]+/
とすると、スラッシュ以外のすべての文字が使えます。
ルーティング作成時にコロンを付けなかった部分は、静的なセグメントとして固定文字列が指定されます。
get "photos/:id/with_user/:user_id", to: "photos#show"
上のルーティングは、/photos/1/with_user/2
のようなパスにマッチします。このときアクションで使えるparams
は { controller: 'photos', action: 'show', id: '1', user_id: '2' }
となります。
クエリ文字列(訳注: URLの末尾に?パラメータ名=値
の形式で追加されるパラメータ)で指定されているパラメータも、すべてparams
に含まれます。以下のルーティングを例にとってみましょう。
get "photos/:id", to: "photos#show"
ブラウザからのGET
リクエストで/photos/1?user_id=2
というパスが渡されると、通常通りPhotosController
クラスのshow
アクションに割り当てられます。このときのparams
は{ controller: 'photos', action: 'show', id: '1', user_id: '2' }
となります。
:defaults
オプションにハッシュを1つ渡すことで、ルーティング内にデフォルト値を定義できます。このとき、動的なセグメントとして指定する必要のないパラメータを次のように適用することも可能です。
get "photos/:id", to: "photos#show", defaults: { format: "jpg" }
上のルーティングはブラウザからの/photos/12
パスにマッチし、Photos
コントローラのshow
アクションに割り当てられます。params[:format]
は"jpg"
に設定されます。
ブロック形式のdefaults
を使うと、複数の項目についてデフォルト値を設定することもできます。
defaults format: :json do resources :photos resources :articles end
セキュリティ上の理由により、クエリパラメータでデフォルト値をオーバーライドすることはできません。オーバーライド可能なデフォルト値は、URLパスの置き換えによる動的なセグメントのみです。
:as
オプションを使うと、任意のルーティングの_path
ヘルパーと_url
ヘルパーで使われる名前を指定できます。
get 'exit', to: 'sessions#destroy', as: :logout
上のルーティングではlogout_path
とlogout_url
がアプリケーションのルーティングヘルパーとして作成されます。logout_path
を呼び出すと/exit
が返されます。
リソースを定義する前に、以下のようにas
でカスタムルーティングを配置すると、リソースで定義されたルーティングヘルパー名をオーバーライドすることも可能です。
get ":username", to: "users#show", as: :user resources :users
上のルーティングでは、/:username
(/jane
など)にマッチするuser_path
ヘルパーが生成されます。UsersController
のshow
アクションの内部でparams[:username]
にアクセスすると、ユーザー名を取り出せます。
あるルーティングを特定のHTTP verbに割り当てるために、通常はget
、patch
、put
、post
、delete
メソッドのいずれかを使う必要があります。match
メソッドと:via
オプションを使うことで、複数のHTTP verbに同時にマッチするルーティングを作成できます。
match "photos", to: "photos#show", via: [:get, :post]
上のルーティングは、PhotosController
のshow
アクションに対してGET
リクエストとPOST
リクエストの両方を受け取ります。
via: :all
を指定すると、すべてのHTTP verbにマッチする特別なルーティングを作成できます。
match "photos", to: "photos#show", via: :all
1つのアクションにGET
リクエストとPOST
リクエストの両方をルーティングすると、セキュリティ上の悪影響が生じます。たとえば、GET
アクションはCSRFトークンをチェックしません(したがって、GET
リクエストでデータベースに書き込むことは推奨されていません。詳しくはセキュリティガイドのCSRF対策を参照してください)。一般に、どうしても必要な理由がない限り、1つのアクションにすべてのHTTP verbをルーティングしてはいけません。
:constraints
オプションを使って、動的セグメントのURLフォーマットを特定の形式に制限できます。
get "photos/:id", to: "photos#show", constraints: { id: /[A-Z]\d{5}/ }
上のルーティング定義では、id
は5文字の英数字でなければなりません。したがって、上のルーティングは/photos/A12345
のようなパスにはマッチしますが、/photos/893
にはマッチしません。
以下のようにもっと簡潔な方法でも記述できます。
get "photos/:id", to: "photos#show", id: /[A-Z]\d{5}/
:constraints
では正規表現を使えますが、ここでは正規表現の「アンカー(^
や$
など)」は使えないという制限があることにご注意ください。たとえば、以下のルーティングは無効です。
get '/:id', to: 'articles#show', constraints: { id: /^\d/ }
対象となるすべてのルーティングは冒頭と末尾が既にアンカーされているので、このようなアンカーを指定する必要はないはずです。 以下の例をご覧ください。
get "/:id", to: "articles#show", constraints: { id: /\d.+/ } get "/:username", to: "users#show"
上のルーティングでは、root名前空間が共有され、さらに以下の振る舞いも共有可能になります。
/1-hello-world
はarticles
にid
値を渡す)/david
はusers
にusername
値を渡す)また、String
を返すRequestオブジェクトの任意のメソッドに基いてルーティングを制限することもできます。
リクエストに応じた制限は、セグメントを制限するときと同様の方法で指定できます。
get "photos", to: "photos#index", constraints: { subdomain: "admin" }
上は、受信リクエストをadmin
サブドメインへのパスと照合します。
constraints
ブロックで制限を指定することも可能です。
namespace :admin do constraints subdomain: "admin" do resources :photos end end
上はhttps://admin.example.com/photos
のようなURLにマッチします。
リクエストベースの制限は、Requestオブジェクトに対してあるメソッドを呼び出し、戻り値をハッシュと比較する形で機能します。たとえば、constraints: { subdomain: 'api' }
という制限はapi
サブドメインに期待どおりマッチしますが、constraints: { subdomain: :api }
のようにシンボルを使った場合はapi
サブドメインに一致しません(request.subdomain
が返す'api'
は文字列型であるため)。
制約の値は、対応するリクエストオブジェクトのメソッドの戻り値型と一致する必要があります。
format
の制限には例外があります。これはRequestオブジェクトのメソッドですが、すべてのパスに含まれる暗黙的なオプションのパラメータでもあります。format
の制限よりセグメント制限が優先され、format
制約はハッシュを通じて強制される場合にのみ適用されます。たとえば、get "foo"、constraints: { format: "json" }
はGET /foo
と一致します。
get "foo", constraints: lambda { |req| req.format == :json }
のように制約でlambdaを指定すると、明示的なJSONリクエストへのルーティングのみを一致させることも可能です。
より高度な制限を使いたい場合、Railsで必要なmatches?
に応答できるオブジェクトを渡す方法があります。例として、制限リストに記載されているすべてのユーザーをRestrictedListController
にルーティングしたいとします。この場合、以下のように設定します。
class RestrictedListConstraint def initialize @ips = RestrictedList.retrieve_ips end def matches?(request) @ips.include?(request.remote_ip) end end Rails.application.routes.draw do get "*path", to: "restricted_list#index", constraints: RestrictedListConstraint.new end
制限をlambdaとして指定することもできます。
Rails.application.routes.draw do get "*path", to: "restricted_list#index", constraints: lambda { |request| RestrictedList.retrieve_ips.include?(request.remote_ip) } end
matches?
メソッドおよびlambdaは、どちらも引数としてrequest
オブジェクトを受け取ります。
制限はブロック形式で指定することも可能です。これは以下のように、同一のルールを複数のルーティングに適用する必要がある場合に便利です。
class RestrictedListConstraint # ...上述の例と同じ end Rails.application.routes.draw do constraints(RestrictedListConstraint.new) do get "*path", to: "restricted_list#index" get "*other-path", to: "other_restricted_list#index" end end
制限をlambda
で指定することもできます。
Rails.application.routes.draw do constraints(lambda { |request| RestrictedList.retrieve_ips.include?(request.remote_ip) }) do get "*path", to: "restricted_list#index" get "*other-path", to: "other_restricted_list#index" end end
ルーティング定義ではワイルドカードセグメント(*
)を利用できます。これは、*other
のように*
をセグメントにプレフィックスしたものです。
get "photos/*other", to: "photos#unknown"
上のルーティングはphotos/12
や/photos/long/path/to/12
(long/path/to
は長いパス)にマッチし、params[:other]
には"12"
や"long/path/to/12"
が設定されます。冒頭にアスタリスク*
が付いているセグメントを「ワイルドカードセグメント」と呼びます。
ワイルドカードセグメントを使うと、特定のパラメーター(上記の*other
)がルーティングの残りの部分に一致する形で指定する、「ルートグロビング(route globbing)」と呼ばれる方法が利用できます。
したがって、上記のルーティングは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つのルーティングに2つ以上のワイルドカードセグメントを含めることは可能です。マッチャによるセグメントのパラメータ割り当ては、出現順に行われます。
get "*a/foo/*b", to: "test#index"
たとえば、上のルーティングはzoo/woo/foo/bar/baz
にマッチし、params[:a]
には'zoo/woo'
が保存され、params[:b]
には'bar/baz'
が保存されます。
以下のルーティング定義があるとします。
get "*pages", to: "pages#show"
このルーティングに対して'/foo/bar.json'
をリクエストしたときのparams[:pages]
は、"foo/bar"
でリクエストフォーマットparams[:format]
にJSONを指定したものと等しくなります。
format
のデフォルトの振る舞いは、URLにフォーマット指定が含まれていれば、それをURLから自動的にキャプチャしてparams[:format]
に含めますが、この場合、URLのformat
パラメータは必須ではありません。
フォーマットが明示的に指定されていない場合のURLにマッチさせ、フォーマット拡張子を含むURLを無視する場合は、以下のようにformat: false
オプションを指定できます。
get "*pages", to: "pages#show", format: false
逆に、フォーマットセグメントを必須にして省略不可にしたい場合は、format: true
を指定します。
get "*pages", to: "pages#show", format: true
ルーティングでredirect
を使うと、任意のパスを他のパスにリダイレクトできます。
get "/stories", to: redirect("/articles")
パスにマッチする動的セグメントを再利用してリダイレクトすることもできます。
get "/stories/:name", to: redirect("/articles/%{name}")
リダイレクトにブロックを渡すこともできます。このリダイレクトは、シンボル化されたパスパラメータとrequestオブジェクトを受け取ります。
get "/stories/:name", to: redirect { |path_params, req| "/articles/#{path_params[:name].pluralize}" } get "/stories", to: redirect { |path_params, req| "/articles/#{req.subdomain}" }
デフォルトのリダイレクトは、HTTPステータス「301 "Moved Permanently"」のリダイレクトになる点にご注意ください。一部のWebブラウザやプロキシサーバーはこの種のリダイレクトをキャッシュすることがあり、その場合リダイレクト前の古いページにはアクセスできなくなります。次のように:status
オプションを使うことでレスポンスのステータスを変更できます。
get "/stories/:name", to: redirect("/articles/%{name}", status: 302)
どの場合であっても、ホスト(http://www.example.com
など)が指定されていない場合は、Railsは(以前のリクエストではなく)現在のリクエストから詳細を取得します。
:to
オプションに'articles#index'
(ArticlesController
クラスのindex
アクションに対応する)のような文字列を指定する代わりに、任意のRackアプリケーションをマッチャーのエンドポイントとして指定できます。
match "/application.js", to: MyRackApp, via: :all
Railsルーターは、MyRackApp
がcall
に応答して[status, headers, body]
を返す限り、ルーティング先がRackアプリケーションであるかコントローラのアクションであるかを区別しません。これによって、適切と考えられるすべてのHTTP verbをRackアプリケーションで扱えるようになるので、これはvia: :all
の適切な利用法です。
参考までに、'articles#index'
は実際にはArticlesController.action(:index)
という形に展開されます。これは有効なRackアプリケーションを返します。
procやlambdaはcall
に応答するオブジェクトなので、たとえばヘルスチェックで用いるルーティングをget '/health', to: ->(env) { [204, {}, ['']] }
のように極めてシンプルにインライン実装できます。
Rackアプリケーションをマッチャーのエンドポイントとして指定すると、それを受け取るRackアプリケーションのルーティングは変更されない点にご留意ください。以下のルーティングでは、Rackアプリケーションは/admin
へのルーティングを期待するべきです。
match "/admin", to: AdminApp, via: :all
Rackアプリケーションがrootパスでリクエストを受け取るようにしたい場合は、mount
を使います。
mount AdminApp, at: "/admin"
root
を使うroot
メソッドを使うことで、'/'
によるルーティング先を指定できます。
root to: "pages#main" root "pages#main" # 上の省略形
root
ルーティングは、ルーティングファイルの冒頭に記述するのが一般的です(最初にマッチする必要があるため)。
root
ルーティングがデフォルトで処理するのはGET
リクエストですが、それ以外のHTTP verbを指定することも一応可能です(例: root "posts#index", via: :post
)。
root
ルーティングは、以下のように名前空間やスコープの内側でも指定できます。
root to: "home#index" namespace :admin do root to: "admin#index" end
上は、/admin
がAdminController
のindex
アクションにマッチし、/
はHomeController
のindex
アクションにマッチします。
Unicode文字列を以下のようにルーティングで直接使うこともできます。
get "こんにちは", to: "welcome#index"
direct
を呼び出すことで、カスタムURLヘルパーを次のように直接作成できます。
direct :homepage do "http://www.rubyonrails.org" end # >> homepage_url # => "http://www.rubyonrails.org"
このブロックの戻り値は、必ずurl_for
メソッドで有効な1個の引数にしなければなりません。これによって、有効な「文字列URL」「ハッシュ」「配列」「Active Modelインスタンス」「Active Modelクラス」のいずれか1つを渡せるようになります。
direct :commentable do |model| [ model, anchor: model.dom_id ] end direct :main do { controller: "pages", action: "index", subdomain: "www" } end # >> main_url # => "http://www.example.com/pages"
resolve
を使うresolve
メソッドを使うと、モデルのポリモーフィックなマッピングを次のようにカスタマイズできます。
resource :basket resolve("Basket") { [:basket] }
<%= form_with model: @basket do |form| %> <!-- basket form --> <% end %>
上のコードは、通常の/baskets/:id
ではなく、単数形の/basket
というURLを生成します。
ほとんどの場合、resources
で生成されるデフォルトのルーティングやヘルパーで用は足りますが、ルーティングを何らかの方法でカスタマイズしたくなることもあります。Railsでは、リソースフルルーティングやヘルパーをカスタマイズするためのさまざまな方法が用意されています。このセクションでは、利用可能なオプションについて詳しく説明します。
:controller
オプションは、リソースで使うコントローラを以下のように明示的に指定します。
resources :photos, controller: "images"
上のルーティングは、/photos
で始まるパスを認識しますが、ルーティング先をImages
コントローラにします。
HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
---|---|---|---|
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) |
名前空間内のコントローラは以下のように直接指定できます。
resources :user_permissions, controller: "admin/user_permissions"
上はAdmin::UserPermissionsController
のインスタンスにルーティングされます。
ここでサポートされている記法は、/
で区切る「ディレクトリ記法」のみです。コントローラをRubyの定数表記法(controller: "Admin::UserPermissions"
など)で指定する記法はサポートされていません。
id
で指定する:constraints
オプションを使うと、暗黙で使われるid
に対して以下のように必須のフォーマットを指定できます。
resources :photos, constraints: { id: /[A-Z][A-Z][0-9]+/ }
上の宣言は:id
パラメータに制限を加え、指定した正規表現にのみマッチするようにします。上のルーティング例では/photos/1
のようなパスにはマッチしなくなり、代わりに/photos/RR27
のようなパスにマッチするようになります。
以下のようにブロック形式を使うことで、1つの制限を多数のルーティングに対してまとめて指定することも可能です。
constraints(id: /[A-Z][A-Z][0-9]+/) do resources :photos resources :accounts end
このコンテキストでは、リソースフルでないルーティングのより高度な制限セクションで説明した方法も利用できます。
デフォルトでは:id
パラメータにドット.
を渡せません。ドットはフォーマット済みルーティングでは区切り文字として使われるためです。どうしても:id
内でドットを使いたい場合は、デフォルト設定を上書きする制限を与えます。たとえばid: /[^\/]+/
とすると、スラッシュ以外のすべての文字が使えます。
:as
オプションを使うと、ルーティングヘルパーのデフォルトの命名方法を以下のように上書きしてルーティングヘルパー名を変えられます。
resources :photos, as: "images"
上のルーティングは/photos
にマッチし、リクエストを通常どおりPhotosController
にルーティングしますが、ヘルパーには:as
オプションで指定した値を用いてimages_path
などの名前を付けます。
HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
---|---|---|---|
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) |
new
やedit
のパス名をリネームする:path_names
オプションを使うと、パスに含まれているデフォルトの"new"セグメントや"edit"セグメントをオーバーライドできます。
resources :photos, path_names: { new: "make", edit: "change" }
これにより、ルーティングで/photos/new
の代わりに/photos/make
、/photos/1/edit
の代わりに/photos/1/change
というパスを認識できるようになります。
このオプションを指定しても、実際のルーティングヘルパーやコントローラアクション名が変更されるわけではありません。表示されるパスには new_photo_path
ヘルパーとedit_photo_path
ヘルパーが引き続き存在し、ルーティング先もnew
アクションとedit
アクションのままです。
この:path_names
オプションをブロック付きscope
で使うと、スコープ内のすべてのルーティングに対してパス名を変更できます。
scope path_names: { new: "make" } do # ブロック内の残りすべてのルーティング end
:as
でプレフィックスを追加する以下のように:as
オプションを使うことで、Railsがルーティングに対して生成する名前付きルーティングヘルパー名の冒頭に文字を追加(プレフィックス)できます。パススコープを使うルーティング同士での名前の衝突を避けたい場合にお使いください。
scope "admin" do resources :photos, as: "admin_photos" end resources :photos
上のようにas:
を使うと、/admin/photos
のルーティングヘルパーが、photos_path
、new_photos_path
などからadmin_photos_path
、new_admin_photo_path
などに変更されます。
as: "admin_photos"
をスコープ付きresources :photos
に追加しない場合は、スコープなしのresources :photos
はルーティングヘルパーを持つことができません。
ルーティングヘルパーのグループにまとめてプレフィックスを追加するには、以下のようにscope
メソッドで:as
オプションを使います。
scope "admin", as: "admin" do resources :photos, :accounts end resources :photos, :accounts
上のルーティングは、先ほどと同様に/admin
のスコープ付きリソースヘルパーをadmin_photos_path
とadmin_accounts_path
に変更し、さらにスコープなしのリソースphotos_path
とaccounts_path
も利用可能になります。
namespace
スコープを使うと、:module
や:path
プレフィックスに加えて:as
も自動的に追加されます。
:as
を使う:as
オプションを使うと、以下のようにネストしたルーティング内のリソースルーティングヘルパー名もオーバーライドできます。
resources :magazines do resources :ads, as: "periodical_ads" end
これにより、デフォルトのmagazine_ads_url
やedit_magazine_ad_path
の代わりに、magazine_periodical_ads_url
やedit_magazine_periodical_ad_path
などのルーティングヘルパーが作成されます。
名前付きパラメータを持つルーティングにプレフィックスを追加できます。
scope ":account_id", as: "account", constraints: { account_id: /\d+/ } do resources :articles end
上のルーティングは/1/articles/9
のようなパスを提供します。パスのaccount_id
部分をparams[:account_id]
の形でコントローラ、ヘルパー、ビューで参照できるようになります。
また、account_
をプレフィックスとするパスヘルパーやURLヘルパーも生成され、これらにオブジェクトを渡すこともできます。
account_article_path(@account, @article) # => /1/article/9 url_for([@account, @article]) # => /1/article/9 form_with(model: [@account, @article]) # => <form action="/1/article/9" ...>
as
オプションは必須ではありませんが、これがないとurl_for([@account, @article])
や、url_for
に依存するヘルパー(form_with
など)の評価時にエラーが発生します。
resources
を使うと、デフォルトで7つのアクション(index
、show
、new
、create
、edit
、update
、destroy
)へのルーティングを作成します。作成されるルーティングは、:only
オプションや:except
オプションで制限をかけられます。
:only
オプションは、指定したルーティングだけを生成するよう指示します。
resources :photos, only: [:index, :show]
これで、/photos
や/photos/:id
へのGET
リクエストは成功し、/photos
へのPOST
リクエストは失敗します。
:except
オプションは逆に、生成しないルーティング(またはルーティングのリスト)を指定します。
resources :photos, except: :destroy
この場合、destroy
(/photos/:id
へのDELETE
リクエスト)を除いた通常のルーティングが生成されます。
アプリケーションでRESTfulルーティングが多数使われている場合は、適宜:only
や:except
を用いて実際に必要なルーティングのみを生成することで、メモリ使用量の節約と未使用のルーティングの削減によるルーティング処理の速度向上が見込めます。
scope
メソッドを使うことで、resources
によって生成されるデフォルトのパス名を変更できます。
scope(path_names: { new: "neu", edit: "bearbeiten" }) do resources :categories, path: "kategorien" end
上のようにすることで、以下のようなCategories
コントローラへのルーティングが作成されます。
HTTP verb | パス | コントローラ#アクション | 名前付きルーティングヘルパー |
---|---|---|---|
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) |
リソースの単数形の名前をオーバーライドしたい場合、以下のようにActiveSupport::Inflector
のinflections
で活用形ルールを追加します。
ActiveSupport::Inflector.inflections do |inflect| inflect.irregular "tooth", "teeth" end
id
をリネームする:param
オプションを指定することで、以下のようにデフォルトのid
パラメータ名を別の名前に変更できます。
resources :videos, param: :identifier
これにより、params[:id]
の代わりにparams[:identifier]
が使われるようになります。
videos GET /videos(.:format) videos#index POST /videos(.:format) videos#create new_video GET /videos/new(.:format) videos#new edit_video GET /videos/:identifier/edit(.:format) videos#edit
Video.find_by(id: params[:identifier]) # ↓変更前 Video.find_by(id: params[:id])
関連するモデルのActiveRecord::Base#to_param
を以下のようにオーバーライドしてURLを作成できます。
class Video < ApplicationRecord def to_param identifier end end
irb> video = Video.find_by(identifier: "Roman-Holiday") irb> edit_video_path(video) => "/videos/Roman-Holiday/edit"
Railsには、ルーティングを調べるさまざまな機能(inspection)とテスト機能が備わっています。
現在のアプリケーションで利用可能なルーティングをすべて表示するには、サーバーがdevelopment環境で動作している状態でhttp://localhost:3000/rails/info/routes
をブラウザで開きます。ターミナルでbin/rails routes
コマンドを実行しても同じ結果を得られます。
どちらの方法を使った場合でも、config/routes.rb
ファイルに記載された順にルーティングが表示されます。1つのルーティングについて以下の情報が表示されます。
以下は、あるRESTfulルーティングに対してbin/rails 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
一番左のルーティング名(上のnew_user
など)は、生成されるルーティングヘルパー名のベースとみなせます。
ルーティングヘルパー名を取得するには、このルーティング名にサフィックス(_path
や_url
)を追加します(例: new_user_path
)。
--expanded
オプションを指定することで、ルーティングテーブルのフォーマットを以下のような詳細モードに切り替えることも可能です。
$ bin/rails routes --expanded --[ Route 1 ]---------------------------------------------------- Prefix | users Verb | GET URI | /users(.:format) Controller#Action | users#index --[ Route 2 ]---------------------------------------------------- Prefix | Verb | POST URI | /users(.:format) Controller#Action | users#create --[ Route 3 ]---------------------------------------------------- Prefix | new_user Verb | GET URI | /users/new(.:format) Controller#Action | users#new --[ Route 4 ]---------------------------------------------------- Prefix | edit_user Verb | GET URI | /users/:id/edit(.:format) Controller#Action | users#edit
-g
(grepオプション)でルーティングを絞り込めます。URLヘルパー名、HTTP verb、URLパスのいずれかに部分マッチするルーティングが出力されます。
$ bin/rails routes -g new_comment $ bin/rails routes -g POST $ bin/rails routes -g admin
特定のコントローラに対応するルーティングだけを表示したい場合は、-c
オプションでコントローラ名を指定します。
$ bin/rails routes -c users $ bin/rails routes -c admin/users $ bin/rails routes -c Comments $ bin/rails routes -c Articles::CommentsController
bin/rails routes
コマンド出力を読みやすく表示するには、出力が折り返されなくなるまでターミナルウィンドウを拡大するか、--expanded
オプションを指定します。
--unused
オプションを指定することで、アプリケーションで使われていないルーティングをスキャンできます。Railsにおける「未使用」ルーティングとは、config/routes.rb
ファイルには定義されているが、アプリケーション内のどのコントローラーアクションやビューからも参照されていないルーティングのことです。
$ bin/rails routes --unused Found 8 unused routes: Prefix Verb URI Pattern Controller#Action people GET /people(.:format) people#index POST /people(.:format) people#create new_person GET /people/new(.:format) people#new edit_person GET /people/:id/edit(.:format) people#edit person GET /people/:id(.:format) people#show PATCH /people/:id(.:format) people#update PUT /people/:id(.:format) people#update DELETE /people/:id(.:format) people#destroy
Railsコンソール内では、Rails.application.routes.url_helpers
でルーティングヘルパーにアクセスできます。ルーティングヘルパーは、appオブジェクト経由でもアクセスできます。
irb> Rails.application.routes.url_helpers.users_path => "/users" irb> user = User.first => #<User:0x00007fc1eab81628 irb> app.edit_user_path(user) => "/users/1/edit"
Railsでは、テストをシンプルに書くための3つの組み込みアサーションが用意されています。
assert_generates
アサーションassert_generates
は、特定のオプションの組み合わせを使った場合に特定のパスが生成されること、そしてそれらがデフォルトのルーティングでもカスタムルーティングでも使えることをテストするアサーション(assertion: 主張)です。
assert_generates "/photos/1", { controller: "photos", action: "show", id: "1" } assert_generates "/about", controller: "pages", action: "about"
assert_recognizes
アサーションassert_recognizes
はassert_generates
と逆方向のテスティングを行います。与えられたパスが認識可能であること、アプリケーションの特定の場所にルーティングされることをテストするアサーションです。
assert_recognizes({ controller: "photos", action: "show", id: "1" }, "/photos/1")
:method
引数でHTTP verbを指定することもできます。
assert_recognizes({ controller: "photos", action: "create" }, { path: "photos", method: :post })
assert_routing
アサーションassert_routing
アサーションは、assert_generates
とassert_recognizes
の機能を組み合わせたもので、ルーティングを2つの観点(与えられたパスによってオプションが生成されること、そのオプションによって元のパスが生成されること)でまとめてチェックします。
assert_routing({ path: "photos", method: :post }, { controller: "photos", action: "create" })
ルーティングが数千にもおよぶ大規模アプリケーションでは、複雑なconfig/routes.rb
ファイル1個だけでは読みづらくなります。Railsでは、このような巨大routes.rb
ファイルをdraw
マクロで小さなルーティングファイルに分割する方法が提供されています。
たとえばadmin.rb
にはadmin関連のルーティングをすべて含め、API関連リソースのルーティングはapi.rb
ファイルで記述するといったことが可能です。
# config/routes.rb Rails.application.routes.draw do get "foo", to: "foo#bar" draw(:admin) # `config/routes/admin.rb`にある別のルーティングファイルを読み込む end
# config/routes/admin.rb namespace :admin do resources :comments end
Rails.application.routes.draw
自身の中でdraw(:admin)
を呼び出すと、指定の引数と同じ名前のルーティングファイル(この例ではadmin.rb
)の読み込みを試行します。
このファイルは、config/routes
ディレクトリの下か、任意のサブディレクトリ(config/routes/admin.rb
やconfig/routes/external/admin.rb
など)に存在する必要があります。
admin.rb
ルーティングファイル内でも通常のルーティングDSLを利用できますが、Rails.application.routes.draw
ブロックで囲んではいけません。Rails.application.routes.draw
ブロックは、メインのconfig/routes.rb
ファイルでのみ使われるべきです。
ルーティングファイルの分割機能は、本当に必要になるまでは使ってはいけません。ルーティングファイルが複数になると、1つのファイルでルーティングを探すときよりも手間がかかります。ほとんどのアプリケーションでは(ルーティングが数百件にのぼる場合であっても)、ルーティングファイルを1つにまとめておく方が開発者にとって扱いやすくなります。RailsのルーティングDSLでは、namespace
やscope
でルーティングを体系的に分割する方法がすでに提供されています。
Railsガイドは GitHub の yasslab/railsguides.jp で管理・公開されております。本ガイドを読んで気になる文章や間違ったコードを見かけたら、気軽に Pull Request を出して頂けると嬉しいです。Pull Request の送り方については GitHub の README をご参照ください。
原著における間違いを見つけたら『Rails のドキュメントに貢献する』を参考にしながらぜひ Rails コミュニティに貢献してみてください 🛠💨✨
本ガイドの品質向上に向けて、皆さまのご協力が得られれば嬉しいです。
Railsガイド運営チーム (@RailsGuidesJP)
Railsガイドは下記の協賛企業から継続的な支援を受けています。支援・協賛にご興味あれば協賛プランからお問い合わせいただけると嬉しいです。