Rails: 5.2.4.4
概要
例えば以下のようなルーティングがあります。
resources :top
resources :users
namespace :admin do
resources :companies
end
以下はrails routes
の実行結果(から抜粋)
top_index GET /top(.:format) top#index
users GET /users(.:format) users#index
admin_companies GET /admin/companies(.:format) admin/companies#index
各コントローラーはApplicationControllerを継承している
class ApplicationController < ActionController::Base
before_action :login_required
def login_required
redirect_to controller: :top, action: :index unless current_user
end
end
ログインしていない時は top#indexにリダイレクトする、という実装ですね。
さて、ログインしていない状態で/users
にアクセスを試みると/top
にリダイレクトします。
同様に/admin/companies
にアクセスを試みると/top
に0ダイレクトする・・・かと思いきや、500エラーが起きます。なぜ?
原因
発生したエラーを見ると以下のようになっていました。
No route matches {:action=>"index", :controller=>"admin/top"}
つまり、adminのnamespace内でtop#indexにリダイレクトしようとしており、そんなルーティングは存在しないのでエラーになっていた、と。。
対策
名前付きルーティングヘルパーを使うと良さそうです。
def login_required
- redirect_to controller: :top, action: :index unless current_user
+ redirect_to top_index_url unless current_user
end
まとめ
RSpecを書いていた時に今回のエラーに遭遇し、3時間ほどハマりました...
共通処理かつ他のコントローラーテストでは通っていた(⇔ namespaceのあるコントローラーでリダイレクトするケースがなかった)ので、ここの箇所ではないはず、と先入観を持ってしまったんですよね。。
わかってしまえば単純なことなので、記憶の片隅にとどめておきましょう。
リダイレクトを実装する時は、名前付きルーティングヘルパーを使っておくのが無難そうです。