Deviseを使ってユーザー認証機能を作ったときに、もうちょっとよくするための方法メモ。Deviseは普通の実装するとログイン後は/にリダイレクトされる。それを別のURLにする方法はここに書いた。
では、ログインに必要なページのURLに直接アクセスがあったときはどうするか。流れとしてはこうなるのが望ましい。
- ログインが必要なページにアクセスがある
- ユーザーにログインフォームを見せる
- ログインが完了したら最初にアクセスしたURLへリダイレクト
ログインが必要かどうかはコントローラレベルで制御する。これはまあよくやるやつ。
1# application_controller.rb
2class ApplicationController < ActionController::Base
3private
4 def sign_in_required
5 redirect_to new_user_session_url unless user_signed_in?
6 end
7end
8
9# hoge_controller.rb
10class HogeController < ApplicationController
11 before_action :sign_in_required
12 def index
13 # ...
14 end
15
16 def ...
17end
全てのコントローラで共有するために、ApplicationControllerにsign_in_requiredというメソッドを定義する。これにはログインしていなければログインページヘリダイレクトする、と書かれている。このsign_in_requiredは各コントローラのbefore_actionで使い、指定したアクションを要ログインにできる。
ここまでの実装だと1と2までは実現できているが、最初に開こうとしたURLへのリダイレクトは実現されておらず、ユーザーとしては非常に面倒くさい。リダイレクト部分を実現するためには最初のURLを覚えておいて、ログイン後のURLを/ではなく別のURLに差し替えてあげる必要がある。
そこでApplicationControllerをさらに拡張する。
1class ApplicationController < ActionController::Base
2 before_action :store_location
3
4 private
5
6 def sign_in_required
7 redirect_to new_user_session_url unless user_signed_in?
8 end
9
10 def store_location
11 return unless request.get?
12 if (request.path != "/users/sign_in" &&
13 request.path != "/users/sign_up" &&
14 request.path != "/users/password/new" &&
15 request.path != "/users/sign_out" &&
16 !request.xhr?)
17 session[:previous_url] = request.fullpath
18 end
19 end
20
21 def after_sign_in_path_for(resource)
22 session[:previous_url] || root_path
23 end
24
25end
store_locationでURLを覚えておき、after_sign_in_path_forで覚えておいたURLを返すということをしている。そしてstore_locationを全てのアクションの【前】に呼び出している。
詳しいことは↓に書かれているが、ここの例ではstore_locationをafter_filterに指定、つまり各アクションの後に呼んでいる。今回の例ではアクション中にログインページにリダイレクトされてしまうため、覚えておくURLが適切なものではなくなってしまう。そこでbefore_actionを指定することでなんとか実現している。