deviseとdevise_invitable
deviseとはRailsで作成したアプリケーションへ簡単に認証機能を実装することができるgem(ライブラリ)の一つで、devise_invitableはdeviseに招待機能を追加するgemになります。
やりたいこと
devise_invitableで招待メールを送信
↓
パスワード設定用の招待メールが送られてくる
↓
パスワード設定
↓
希望の画面にリダイレクトする (今回はこれをやりたい
問題点
パスワードを設定すると自動でログインしてくれるのですが、
設定後にroot_pathに遷移してしてしまう。
ハマったところ
deviseには元来after_sign_up_path_for
(登録後のリダイレクト先を変更できるメソッド)や
after_sign_in_path_for
(ログイン後のリダイレクト先を変更できるメソッド)が存在しており、
招待メールからパスワードを設定したのちに自動でログイン状態になっているので同様のやり方でいけると思っていました。
-
after_sign_up_path_for
やafter_sign_in_path_for
については
こちらの記事で分かりやすくまとめられていました。
解決策
ログを読んでみたところ、招待メールからパスワードを登録した際にはライブラリのinvitations_controller
のupdate
アクションが呼ばれていることがわかりましたのでGitHubに公開されているコードを確認しました。
# PUT /resource/invitation
def update
raw_invitation_token = update_resource_params[:invitation_token]
self.resource = accept_resource
invitation_accepted = resource.errors.empty?
yield resource if block_given?
if invitation_accepted
if resource.class.allow_insecure_sign_in_after_accept
flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
set_flash_message :notice, flash_message if is_flashing_format?
resource.after_database_authentication
sign_in(resource_name, resource)
respond_with resource, location: after_accept_path_for(resource) // ←ここ
else
set_flash_message :notice, :updated_not_active if is_flashing_format?
respond_with resource, location: new_session_path(resource_name)
end
else
resource.invitation_token = raw_invitation_token
respond_with_navigational(resource) { render :edit, status: :unprocessable_entity }
end
end
招待を受けた場合の処理として、ログイン(sign_in)を行なったのちafter_accept_path_for(resource)
でroot_pathに遷移させていることが分かりました。
実際の作業
本来、外部ライブラリであるdevise又はdevise_invitableのコントローラーはGem内に記述されているため編集することはできません。
そこで、このcontrollerをオーバーライドしてカスタマイズをしていきます。
まずはオーバーライドするためのファイルを作成します。
$ rails g controller users/invitations
ファイルを作成したらGitHubのUpdateアクションの部分をコピペして先ほどのafter_accept_path_for(resource)
の部分を遷移させたいパスに編集します。
module Users
class InvitationsController < Devise::InvitationsController
def update
raw_invitation_token = update_resource_params[:invitation_token]
self.resource = accept_resource
invitation_accepted = resource.errors.empty?
yield resource if block_given?
if invitation_accepted
if resource.class.allow_insecure_sign_in_after_accept
flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
set_flash_message :notice, flash_message if is_flashing_format?
resource.after_database_authentication
sign_in(resource_name, resource)
respond_with resource, location: users_path(resource)
else
set_flash_message :notice, :updated_not_active if is_flashing_format?
respond_with resource, location: new_session_path(resource_name)
end
else
resource.invitation_token = raw_invitation_token
respond_with_navigational(resource) { render :edit, status: :unprocessable_entity }
end
end
end
end
このとき、routes.rb
でルーティングの設定を忘れないようにします。
devise_for :users, controllers: {
sessions: 'users/sessions',
passwords: 'users/passwords',
registrations: 'users/registrations',
invitations: 'users/invitations'
}
これでいけました。