Ruby on Railsチュートリアルの演習問題と解答をまとめる。
アウトプットすることで、より自分の理解を深めることを目的としています。 自分なりに調べて考えた回答のため、記載内容に誤りがある場合はコメントいただけると幸いです。
- 演習9.1.1 記憶トークンと暗号化
- 演習9.1.2 ログイン状態の保持
- 演習9.1.3 ユーザーを忘れる
- 演習9.1.4 2つ目の目立たないバグ
- 演習9.2 [Remember me] チェックボックス
- 演習9.3.1 [Remember me] ボックスをテストする
- 演習9.3.2 [Remember me] をテストする
演習9.1.1 記憶トークンと暗号化
問題1
コンソールを開き、データベースにある最初のユーザーを変数userに代入してください。その後、そのuserオブジェクトからrememberメソッドがうまく動くかどうか確認してみましょう。また、remember_tokenとremember_digestの違いも確認してみてください。
解答
>> user = User.first User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.org", created_at: "2019-10-22 11:28:15", updated_at: "2019-10-22 11:28:15", password_digest:"$2a$10$qJNgcZhLTc4MMK1hAcmuk.QnAXOdwbkcu65k9FCE4Zh...", remember_digest: nil> >> user.remember (0.1ms) begin transaction SQL (1.4ms) UPDATE "users" SET "updated_at" = ?, "remember_digest" = ? WHERE "users"."id" = ? [["updated_at", "2019-10-24 06:17:58.570963"], ["remember_digest", "$2a$10$Jn19ceDBERSUki1AU/wHle0ZS0yPw5di83QHuTlyIWOjEe9V6BKRm"], ["id", 1]] (2.2ms) commit transaction => true >> user.remember_token => "WB3ckrAFSrNFVM8VHJCTjQ" >> user.remember_digest => "$2a$10$Jn19ceDBERSUki1AU/wHle0ZS0yPw5di83QHuTlyIWOjEe9V6BKRm"
remember_tokenは、base64によって長さ22の文字列
remember_digestは、BCryptによって長さ60の文字列
問題2
リスト 9.3では、明示的にUserクラスを呼び出すことで、新しいトークンやダイジェスト用のクラスメソッドを定義しました。実際、User.new_tokenやUser.digestを使って呼び出せるようになったので、おそらく最も明確なクラスメソッドの定義方法であると言えるでしょう。しかし実は、より「Ruby的に正しい」クラスメソッドの定義方法が2通りあります。1つはややわかりにくく、もう1つは非常に混乱するでしょう。テストスイートを実行して、ややわかりにくいリスト 9.4の実装でも、非常に混乱しやすいリスト 9.5の実装でも、いずれも正しく動くことを確認してみてください。ヒント: selfは、通常の文脈ではUser「モデル」、つまりユーザーオブジェクトのインスタンスを指しますが、リスト 9.4やリスト 9.5の文脈では、selfはUser「クラス」を指すことにご注意ください。わかりにくさの原因の一部はこの点にあります。
解答
リスト9.4 もリスト9.5 もテストはGREEN
演習9.1.2 ログイン状態の保持
問題1
ブラウザのcookieを調べ、ログイン後のブラウザではremember_tokenと暗号化されたuser_idがあることを確認してみましょう。
解答
remember_tokenとuser_idが追加されている
問題2
コンソールを開き、リスト 9.6のauthenticated?メソッドがうまく動くかどうか確かめてみましょう。
解答
>> user = User.first User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]] => #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.org", created_at: "2019-10-22 11:28:15", updated_at: "2019-10-24 07:06:00", password_digest:"$2a$10$qJNgcZhLTc4MMK1hAcmuk.QnAXOdwbkcu65k9FCE4Zh...", remember_digest: "$2a$10$8VDBZindjJR3ThlPJtVRoOcaLzdDQbnP9z0TAe4atiE..."> >> user.remember (0.1ms) begin transaction SQL (0.8ms) UPDATE "users" SET "updated_at" = ?, "remember_digest" = ? WHERE "users"."id" = ? [["updated_at", "2019-10-24 07:13:08.437799"], ["remember_digest","$2a$10$TFQh12eQXaaED.QNr6E0UOcTqA/dp/XYtBxOokoddKeXzxMhNCJ1y"], ["id", 1]] (1.4ms) commit transaction => true >> user.authenticated?(user.remember_token) => true
authenticated?メソッドの引数である(remember_token)を、rememberメソッドで作成する。
self.remember_tokenが作成されるので、引数には(user.remember_token)と入れる
演習9.1.3 ユーザーを忘れる
問題1
ログアウトした後に、ブラウザの対応するcookiesが削除されていることを確認してみましょう。
解答
デベロッパーツールで確認するとcookiesが削除されていることが確認できます。
演習9.1.4 2つ目の目立たないバグ
問題1
リスト 9.16で修正した行をコメントアウトし、2つのログイン済みのタブによるバグを実際に確かめてみましょう。まず片方のタブでログアウトし、その後、もう1つのタブで再度ログアウトを試してみてください。
解答
動作確認のみのため省略
問題2
リスト 9.19で修正した行をコメントアウトし、2つのログイン済みのブラウザによるバグを実際に確かめてみましょう。まず片方のブラウザでログアウトし、もう一方のブラウザを再起動してサンプルアプリケーションにアクセスしてみてください。
解答
動作確認のみなので省略
問題3
上のコードでコメントアウトした部分を元に戻し、テストスイートが red から greenになることを確認しましょう。
解答
コメントアウトを戻しましょーう
演習9.2 [Remember me] チェックボックス
問題1
ブラウザでcookies情報を調べ、[remember me] をチェックしたときに意図した結果になっているかどうかを確認してみましょう。
解答
Remember meにチェックを付けてログインする。
Cookiesを調べる。
一度ブラウザを閉じて、Homeページ(localhost:3000/)を開く。
ログイン状態のヘッダーが表示されているのを確認
Cookiesを調べると
_sample_app_sessionは変わっているが、remember_tokenとuser_idは変わっていないので[Remember me]がちゃんと機能している。
問題2
コンソールを開き、三項演算子を使った実例を考えてみてください (コラム 9.2)。
解答
>> name = "kuro" => "kuro" >> puts name == "kuro" ? "こんにちわ" : "だれ?" こんにちわ => nil >> name = "tama" => "tama" >> puts name == "kuro" ? "こんにちわ" : "だれ?" だれ? => nil
使い方はあっているはず!!!!
演習9.3.1 [Remember me] ボックスをテストする
問題1
リスト 9.25の統合テストでは、仮想のremember_token属性にアクセスできないと説明しましたが、実は、assignsという特殊なテストメソッドを使うとアクセスできるようになります。コントローラで定義したインスタンス変数にテストの内部からアクセスするには、テスト内部でassignsメソッドを使います。このメソッドにはインスタンス変数に対応するシンボルを渡します。例えばcreateアクションで@userというインスタンス変数が定義されていれば、テスト内部ではassigns(:user)と書くことでインスタンス変数にアクセスできます。本チュートリアルのアプリケーションの場合、Sessionsコントローラのcreateアクションでは、userを (インスタンス変数ではない) 通常のローカル変数として定義しましたが、これをインスタンス変数に変えてしまえば、cookiesにユーザーの記憶トークンが正しく含まれているかどうかをテストできるようになります。このアイデアに従ってリスト 9.27とリスト 9.28の不足分を埋め (ヒントとして?やFILL_INを目印に置いてあります)、[remember me] チェックボックスのテストを改良してみてください。
解答
userを@userに変更。(インスタンス変数にするため)
[app/controllers/sessions_controller.rb] class SessionsController < ApplicationController def new end def create @user = User.find_by(email: params[:session][:email].downcase) if @user && @user.authenticate(params[:session][:password]) log_in @user params[:session][:remember_me] == '1' ? remember(@user) : forget(@user) redirect_to @user else flash.now[:danger] = 'Invalid email/password combination' render 'new' end end def destroy log_out if logged_in? redirect_to root_url end end
[test/integration/users_login_test.rb] require 'test_helper' class UsersLoginTest < ActionDispatch::IntegrationTest (中略) test "login with remembering" do log_in_as(@user, remember_me: '1') assert_equal cookies['remember_token'], assigns(:user).remember_token end test "login without remembering" do # クッキーを保存してログイン log_in_as(@user, remember_me: '1') delete logout_path # クッキーを削除してログイン log_in_as(@user, remember_me: '0') assert_empty cookies['remember_token'] end end
assert_equal(期待される値, 実際の値) assigns(:user).remember_tokenの値は、cookies['remember_token']と同じのはず!!というテスト
演習9.3.2 [Remember me] をテストする
問題1
リスト 9.33にあるauthenticated?の式を削除すると、リスト 9.31の2つ目のテストで失敗することを確かめてみましょう (このテストが正しい対象をテストしていることを確認してみましょう)。
解答
動作確認のみです
完