おもしろwebサービス開発日記

Ruby や Rails を中心に、web技術について書いています

devise でログインログアウトのパスを変更したいときの注意点

例えば User モデルで devise を設定したとき、ログイン用のパスは /users/sign_in、ログアウトは /users/sign_out になります。個人的には /login /logout の方が好みなのでroutes.rb を下記のようにしました。

  devise_for :users, skip: [:sessions]

  devise_scope :user do
    get 'login' => 'devise/sessions#new'
    post 'login' => 'devise/sessions#create'
    get 'logout' => 'devise/sessions#destroy'
  end

1行目の devise_for :users, skip: [:sessions] でデフォルトのログイン関連のパスを削除して、その後で /login /logout のパスの設定を入れています。これでばっちりですね!

…と思いきや、思わぬ落とし穴がありました。パスワードリセットをした後に遷移するパスを生成するところでエラーが発生するのです。該当部分のソースを見てみると、下記のようになっています。

class Devise::PasswordsController < DeviseController
  # ...

  # The path used after sending reset password instructions
  def after_sending_reset_password_instructions_path_for(resource_name)
    new_session_path(resource_name) if is_navigational_format?
  end

  # ...
end

new_session_path(resource_name) は User モデルで devise を使っているときは new_user_session_path を返します。しかし routes.rb で devise_for :users, skip: [:sessions] としてデフォルトのログイン関連のパスを削除しているためにエラーになってしまいます。

devise は、ログイン後に遷移するパスは ApplicationController#after_sign_in_path_for を定義すれば設定を変更できるようになっています。ただ、「パスワードリセットをした後に遷移するパス」については対応していないようです。

対処法

結局

  • config/initializers/ 配下にファイルを作り、その中でDevise::PasswordsController#after_sending_reset_password_instructions_path_for をオーバライドする
  • config/routes.rbdevise_for :users, skip: [:sessions] の skip オプションを削除する
    • ただ、そうすると/users/sign_in/login が混在してしまう

のどちらかしかなさそうです。どっちも微妙ですね…。deviseさん…

追記

エントリ公開後にコメントいただきました!@deeeki さんありがとうございます!

ということで調べてみました。

devise_for :users, :path => '', :path_names => {:sign_in => 'login', :sign_out => 'logout'}

のようにすると、確かに /login/logout ができます。ただ

  • named routes が new_user_session_path になってしまう (login_path が理想)
  • :path => '' としているため、他の/users 配下のパスも影響を受ける
    • 例:/users/confirmation/confirmation

なので、これでバッチリ!とはいきませんでした。ざんねん><

named routes を妥協できるなら下記のように書くのが一番よさそうです。

devise_for :users, skip: [:sessions]

devise_scope :user do
  get 'login' => 'devise/sessions#new', as: :new_user_session
  post 'login' => 'devise/sessions#create', as: :user_session
  get 'logout' => 'devise/sessions#destroy', as: :destroy_user_session
end