8.6 练习
最后更新于:2022-04-01 22:29:44
# 8.6 练习
电子书中有练习的答案,如果想阅读参考答案,请[购买电子书](http://railstutorial-china.org/#purchase)。
避免练习和正文冲突的方法参见[3.6 节](chapter3.html#mostly-static-pages-exercises)中的说明。
1. 在[代码清单 8.32](#listing-user-model-remember) 中,我们定义了生成令牌和摘要的类方法,前面都加上了 `User`。这么定义没问题,而且因为我们会使用 `User.new_token` 和 `User.digest` 调用,或许这样定义意思更明确。不过,定义类方法有两种更常用的方式,一种有点让人困惑,一种极其让人困惑。运行测试组件,确认[代码清单 8.59](#listing-token-digest-self)(有点让人困惑)和[代码清单 8.60](#listing-token-digest-class-self)(极其让人困惑)中的实现方式是正确的。(注意,在[代码清单 8.59](#listing-token-digest-self) 和[代码清单 8.60](#listing-token-digest-class-self) 中,`self` 是 `User` 类,而用户模型中的其他 `self` 都是用户对象实例。这就是让人困惑的根源所在。)
2. [8.4.5 节](#remember-me-checkbox)说过,由于应用现在的设计方式,在[代码清单 8.51](#listing-remember-me-test) 的集成测试中无法获取 `remember_token` 虚拟属性。不过,在测试中使用一个特殊的方法可以获取,这个方法是 `assigns`。在测试中,可以访问控制器中定义的实例变量,方法是把实例变量的符号形式传给 `assigns` 方法。例如,如果 `create` 动作中定义了 `@user` 变量,在测试中可以使用 `assigns(:user)` 获取这个变量。现在,会话控制器中的 `create` 动作定义了一个普通的变量(不是实例变量),名为 `user`,如果我们把它改成实例变量,就可以测试 `cookies` 中是否包含用户的记忆令牌。填写[代码清单 8.61](#listing-login-create-user-instance) 和[代码清单 8.62](#listing-improved-remember-me-test) 中缺少的内容(`?` 和 `FILL_IN`),完成改进后的“记住我”复选框测试。
##### 代码清单 8.59:使用 `self` 定义生成令牌和摘要的方法 GREEN
app/models/user.rb
```
class User < ActiveRecord::Base
.
.
.
# 返回指定字符串的哈希摘要
def self.digest(string) cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# 返回一个随机令牌
def self.new_token SecureRandom.urlsafe_base64
end
.
.
.
end
```
##### 代码清单 8.60:使用 `class << self` 定义生成令牌和摘要的方法 GREEN
app/models/user.rb
```
class User < ActiveRecord::Base
.
.
.
class << self # 返回指定字符串的哈希摘要
def digest(string) cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# 返回一个随机令牌
def new_token SecureRandom.urlsafe_base64
end
end
.
.
.
```
##### 代码清单 8.61:在 `create` 动作中使用实例变量的模板
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
```
##### 代码清单 8.62:改进后的“记住我”复选框测试模板 GREEN
test/integration/users_login_test.rb
```
require 'test_helper'
class UsersLoginTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
end
.
.
.
test "login with remembering" do
log_in_as(@user, remember_me: '1')
assert_equal assigns(:user).FILL_IN, FILL_IN end
test "login without remembering" do
log_in_as(@user, remember_me: '0')
assert_nil cookies['remember_token']
end
.
.
.
end
```
';