【Rails】 ルーティングのnamespace, module, scopeの違い
この話題は結論から書いてもわかりにくくなると思ったので、順を追って整理する。
たとえば、こんなルーティングがあったとする。
resources :books, only: :show do
resources :reviews, only: :index
end
これは、以下のURLとコントローラー#アクションになる。(only: :indexの書き方の流派などはプロジェクトごとに変わると思うので気にせず)
- URL:
/books/:book_id/reviews - Controller#Action:
reviews#index
だが、これでは「すべてのreviewsを表示する」という仕様があったとき、reviews#indexが使われてるので被ってしまいそうだ。
ここでnamespaceを使うとどうなるか?
resources :books, only: :show do
namespace :books do
resources :reviews, only: :index
end
end
コントローラーとアクションはbooks/reviews#indexとなり、ファイル構成もbooks/reviews_controller.rbと名前空間を分けられる。
しかし、肝心のURLが/books/:book_id/books/reviewsとおかしくなってしまう。
namespaceを使った場合:
- URL:
/books/:book_id/books/reviews(※意味不明なURL) - Controller#Action:
books/reviews#index
このように、namespaceはURLでも、Controller#Action(つまりファイル構成)でもスコープを分ける。
「URLは変えずに、Controller#Actionは分けたい(ファイル構成だけでスコープを分けたい)」というときに使えるのがmoduleオプションだ。
resources :books, only: :show do
- resources :reviews, only: :index
+ resources :reviews, only: :index, module: :books
end
こうすれば、
- URL:
/books/:book_id/reviews - Controller#Action:
books/reviews#index
となり、よい感じになる。
ちなみに、moduleオプションはscope moduleブロックを使っても書ける。
resources :books, only: :show do
scope module: 'books' do
resources :reviews, only: :index
#他のリソースもDRYに書いていける
end
end
こうすることで、reviews以外のリソースがあったときもいちいちmodule: :booksを書かなくてよくなる。
Rubyでmoduleを使って名前空間を作るように、routes.rb内でmoduleオプションを使って名前空間を作れるというイメージで覚えるのが個人的にはしっくりくる。
ただ、scopeとscope module:は似たような記述だが違う挙動になるので注意が必要だ。
resources :books, only: :show do
scope 'books' do # scope moduleではなくscopeだけにしたら...
resources :reviews, only: :index
end
end
- URL:
/books/books/:book_id/reviews(URLとしておかしい) - Controller#Action:
reviews#index(他のと被っちゃいそう)
scopeはmoduleオプションの反対で、Controller#Actionに影響はないが、URLが変わる。
namespce, module, scopeの使い方まとめ
それぞれの用途をまとめると、
-
namespace: URLとファイル構成でスコープを分けたい -
module(scope module:):ファイル構成だけでスコープを分けたい -
scope: URLだけでスコープを分けたい
となる。
Rails のルーティング - Railsガイドにはもっとわかりやすい説明があるので、全体に目を通しておくことをおすすめします。
Discussion