演習
コンソールを開き、データベースにある最初のユーザーを変数userに代入してください。その後、そのuserオブジェクトからrememberメソッドがうまく動くかどうか確認してみましょう。また、remember_tokenとremember_digestの違いも確認してみてください。
リスト 9.3では、明示的にUserクラスを呼び出すことで、新しいトークンやダイジェスト用のクラスメソッドを定義しました。実際、User.new_tokenやUser.digestを使って呼び出せるようになったので、おそらく最も明確なクラスメソッドの定義方法であると言えるでしょう。しかし実は、より「Ruby的に正しい」クラスメソッドの定義方法が2通りあります。1つはややわかりにくく、もう1つは非常に混乱するでしょう。テストスイートを実行して、リスト 9.4 (ややわかりにくい) や、リスト 9.5 (非常に混乱する) の実装でも、正しく動くことを確認してみてください。ヒント: selfは、通常の文脈ではUser「モデル」、つまりユーザーオブジェクトのインスタンスを指しますが、リスト 9.4やリスト 9.5の文脈では、selfはUser「クラス」を指すことにご注意ください。わかりにくさの原因の一部はこの点にあります。
>> user.remember
(0.1ms) begin transaction
SQL (2.4ms) UPDATE "users" SET "updated_at" = ?, "remember_digest" = ? WHERE "users"."id" = ? [["updated_at", "2019-04-16 02:18:14.763221"], ["remember_digest", "$2a$10$bV9LuZZGAGhLIE26.MfRp.Ms7IdZgeHWm2kMSO61gX5lUvktdpaXC"], ["id", 1]]
(9.1ms) commit transaction
=> true
>> user.remember_token
=> "Ow4MHVqTFKXp1P73yWSO3g"
>> user.remember_digest
=> "$2a$10$bV9LuZZGAGhLIE26.MfRp.Ms7IdZgeHWm2kMSO61gX5lUvktdpaXC"
>>
9.4
テストGREEN
9.5
GREEN
Class << selfとは特異クラスいう。クラスメソッドを定義するイディオムの一つ
https://magazine.rubyist.net/articles/0046/0046-SingletonClassForBeginners.html
class << self の特異クラス形式
特異クラス方式では、class << self と書いた行から end までの間に def class_method のようにクラス名を書かずにインスタンスメソッドと同じようなメソッド定義を書いていきます。この間に書いたものはクラスメソッドとして定義されます。
どちらも正しいクラスメソッドの定義の仕方ですが、特異メソッド方式では複数のクラスメソッドをまとめて定義したい場合に都度の self. を書くのが面倒なため、そのようなときは特異クラス方式がとられることが多いようです。まずはこれだけわかれば Ruby を使う上で困ることは少ないでしょう
だそうです。
毎回selfを書selfくよりselfを書くメソッドをまとめて囲った方が楽で短いコードになるからと推測される。
9.1.2
演習
ブラウザのcookieを調べ、ログイン後のブラウザではremember_tokenと暗号化されたuser_idがあることを確認してみましょう。
コンソールを開き、リスト 9.6のauthenticated?メソッドがうまく動くかどうか確かめてみましょう。
>> user = User.first
User Load (0.2ms) 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-04-13 05:30:47", updated_at: "2019-04-16 06:33:23", password_digest: "$2a$10$gr18EYrgzmjFoagV/HyQXuXMbr7DZgH5Y.kPFwBibpn...", remember_digest: "$2a$10$mq34Xt7u.4wcU0.CFu00MOUDUZz0APlPfzq8tUPSJhF...">
>> user.remember
(0.1ms) begin transaction
SQL (3.7ms) UPDATE "users" SET "updated_at" = ?, "remember_digest" = ? WHERE "users"."id" = ? [["updated_at", "2019-04-16 06:39:42.666490"], ["remember_digest", "$2a$10$8P8KYxNqJv6Z1kwbI0BPIeUv0pDB/A98bLdePHNhRXOADYk7TUb.u"], ["id", 1]]
(8.4ms) commit transaction
=> true
>> user.authenticated?(user.remember_token)
=> true
9.1.3
演習
ログアウトした後に、ブラウザの対応するcookiesが削除されていることを確認してみましょう。
確認
9.1.3
演習
リスト 9.16で修正した行をコメントアウトし、2つのログイン済みのタブによるバグを実際に確かめてみましょう。まず片方のタブでログアウトし、その後、もう1つのタブで再度ログアウトを試してみてください。
リスト 9.19で修正した行をコメントアウトし、2つのログイン済みのブラウザによるバグを実際に確かめてみましょう。まず片方のブラウザでログアウトし、もう一方のブラウザを再起動してサンプルアプリケーションにアクセスしてみてください。
上のコードでコメントアウトした部分を元に戻し、テストスイートが red から greenになることを確認しましょう。
1
ログアウトできなくなった
3
確認
9.2.4
演習
ブラウザでcookies情報を調べ、[remember me] をチェックしたときに意図した結果になっているかどうかを確認してみましょう。
コンソールを開き、三項演算子を使った実例を考えてみてください (コラム 9.2)。
チェックなし
>> 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-04-13 05:30:47", updated_at: "2019-04-16 12:37:31", password_digest: "$2a$10$gr18EYrgzmjFoagV/HyQXuXMbr7DZgH5Y.kPFwBibpn...", remember_digest: "$2a$10$wi5.c.RDMBDaG5ZhM0LQIOPov6z2BsTIcpwBww.//bX...">
>> name = user.name
=> "Rails Tutorial"
>> name.empty? ? "empty" : "not empty"
=> "not empty"
9.3
演習
リスト 9.25の統合テストでは、仮想のremember_token属性にアクセスできないと説明しましたが、実は、assignsという特殊なテストメソッドを使うとアクセスできるようになります。コントローラで定義したインスタンス変数にテストの内部からアクセスするには、テスト内部でassignsメソッドを使います。このメソッドにはインスタンス変数に対応するシンボルを渡します。例えばcreateアクションで@userというインスタンス変数が定義されていれば、テスト内部ではassigns(:user)と書くことでインスタンス変数にアクセスできます。本チュートリアルのアプリケーションの場合、Sessionsコントローラのcreateアクションでは、userを (インスタンス変数ではない) 通常のローカル変数として定義しましたが、これをインスタンス変数に変えてしまえば、cookiesにユーザーの記憶トークンが正しく含まれているかどうかをテストできるようになります。このアイデアに従ってリスト 9.27とリスト 9.28の不足分を埋め (ヒントとして?やFILL_INを目印に置いてあります)、[remember me] チェックボックスのテストを改良してみてください。
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
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
演習
リスト 9.33にあるauthenticated?の式を削除すると、リスト 9.31の2つ目のテストで失敗することを確かめてみましょう (このテストが正しい対象をテストしていることを確認してみましょう)。
確認