Railsチュートリアル 第9章
9.1 Remember me 機能
9.1.1
演習1
問題なく動作しました.
演習2
もともとリスト9.4の書き方をしていました. リスト9.5の書き方は省略します.
9.1.2
疑問1 current_user
メソッドの定義
第8章では一時セッションにユーザーIDを保存していましたが, 本章ではユーザー情報を永続的に保存するために, クッキーを用いています. そのため, 第8章で定義したcurrent_user
メソッドを修正しています. それがこちらです.
def current_user if (user_id = session[:user_id]) @current_user ||= User.find_by(id: user_id) elsif (user_id = cookies.signed[:user_id]) user = User.find_by(id: user_id) if user && user.authenticated?(cookies[:remember_token]) log_in user @current_user = user end end end
はじめはsession
とcookies
の評価がどうしてこの順番なのかがわかりませんでした.
コードを見た通り, session
が空かどうかをチェックし, 空であった場合はcookies
が空かどうかのチェックに移っています. この評価の順番が正当化されるということは, 「session
は空だがcookies
は空ではない」という状況がありえることになります. それはブラウザを閉じたあとのことですね. session
は短命なのでブラウザを閉じれは消えてしまいます. しかしcookies
は残ったままです. これでこの順番に評価をすることが意味を持つわけですね.
また後に実装するように, ユーザーはRemember me
機能を使わないという選択をとれるようになります. したがってユーザーによってはcookies
には値がない場合もあるわけです. それならば必ず利用されるsession
をはじめに評価するほうが合理的ですね.
疑問2 どうしてログアウトできないのか
演習の直前に, テストスイートが失敗することが述べられています. その理由として, ユーザーがログアウトできないからだと書いてあります. 今までに定義したヘルパーメソッド等を思い出しながら, これについて詳しく見ていきましょう.
実際に統合テストを実行すると失敗します. 失敗した箇所を以下に示します.
# test/integration/users_login_test.rb # login_with_valid_information_followed_by_logout # ログアウトの処理以降のみを抜粋 delete logout_path assert_not is_logged_in? assert_redirected_to root_url follow_redirect! assert_select "a[href=?]", login_path #ここで失敗 assert_select "a[href=?]", logout_path, count: 0 assert_select "a[href=?]", user_path(@user), count: 0
以下テストで実行される流れを書いていきます.
logout_path
にdelete
リクエストが飛びます. これによりSessionsController
のdestroy
アクションが実行されます.
# SessionsController def destroy log_out redirect_to root_url end
ログアウト処理がなされ, root_url
にリダイレクトしていますね. このログアウト処理はなにをしているかというと, session
からユーザーIDを消去し, インスタンス変数の@current_user
を初期化しています.
# app/helpers/sessions_helper.rb def log_out session.delete(:user_id) @current_user = nil end
テストの続きへ進みます.
テストヘルパーメソッドのis_logged_in?
によって, ログインした状態かどうかをチェックしています.
def is_logged_in? !session[:user_id].nil? end
log_out
メソッドによりsession
の内容は消去しているので, 当然このアサーションはクリアします. 次です.
destroy
アクションの定義内に, リダイレクトするように書かれていますから, redirected_to
アサーションもクリアします. 問題は次です.
さて問題はselect
アサーションです. これが失敗する理由は本文にもあるようにログアウトできていないからです. つまりログインしてしまっているからです. ちゃんとsession
を消去したのに, 復活してしまっているんですね. その答えがヘッダー内の次の一行にあります.
# app/views/layouts/_header.html.erb <% if logged_in? %>
ログインしているかどうかによって, ヘッダーの内容を変化させるためのif
文です. ではヘルパーメソッドのlogged_in?
の定義を見てみましょう.
# app/helpers/sessions_helper.rb def logged_in? !current_user.nil? end
current_user
がnil
かどうかで, ログインしているかどうかを判断しています. さてcurrent_user
メソッドはどんな定義でしたっけ...? 疑問1でも取り上げたように, 以下のような定義でしたね.
def current_user if (user_id = session[:user_id]) @current_user ||= User.find_by(id: user_id) elsif (user_id = cookies.signed[:user_id]) user = User.find_by(id: user_id) if user && user.authenticated?(cookies[:remember_token]) log_in user @current_user = user end end end
そろそろ核心に近づいてきました. current_user
メソッドでは, session
が空であった場合はcookies
の中身を見ます. ログアウトしてsession
情報を消去しtも, cookies
の中身は残ったままですよね. ということはそのデータをもとにしてユーザーの検索が行われます. そしてauthenticated?
が通った場合, log_in
メソッドが実行されてしまいます. ここが, session
情報の復活点でした. cookie
情報が残った状態でヘッダーのレンダリングを行えば, 自然とログインしてしまうようになっていたのです.
演習1, 2
省略.
9.1.3
これでログアウトができますね.
演習1
省略.
9.1.4
細かいnil
の扱いは難しいですね...
演習1, 2, 3
省略.
9.2 [Remenber me] チェックボックス
省略.
9.3 [Remember me] のテスト
9.3.1
演習1
sessions_controller.rb
のcreate
内のuser
をインスタンス変数@user
に変えます. するとassigns
メソッドでインスタンス変数を参照できるようになるので, より詳細にテストを書くことができますね.
# test/integration/users_login_test.rb test "login with remembering" do log_in_as(@user, remember_me: '1') assert_equal cookies['remember_token'] , assigns(:user).remember_token end
9.3.2
省略.