# 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
```
- Ruby on Rails 教程
- 致中国读者
- 序
- 致谢
- 作者译者简介
- 版权和代码授权协议
- 第 1 章 从零开始,完成一次部署
- 1.1 简介
- 1.2 搭建环境
- 1.3 第一个应用
- 1.4 使用 Git 做版本控制
- 1.5 部署
- 1.6 小结
- 1.7 练习
- 第 2 章 玩具应用
- 2.1 规划应用
- 2.2 用户资源
- 2.3 微博资源
- 2.4 小结
- 2.5 练习
- 第 3 章 基本静态的页面
- 3.1 创建演示应用
- 3.2 静态页面
- 3.3 开始测试
- 3.4 有点动态内容的页面
- 3.5 小结
- 3.6 练习
- 3.7 高级测试技术
- 第 4 章 Rails 背后的 Ruby
- 4.1 导言
- 4.2 字符串和方法
- 4.3 其他数据类型
- 4.4 Ruby 类
- 4.5 小结
- 4.6 练习
- 第 5 章 完善布局
- 5.1 添加一些结构
- 5.2 Sass 和 Asset Pipeline
- 5.3 布局中的链接
- 5.4 用户注册:第一步
- 5.5 小结
- 5.6 练习
- 第 6 章 用户模型
- 6.1 用户模型
- 6.2 用户数据验证
- 6.3 添加安全密码
- 6.4 小结
- 6.5 练习
- 第 7 章 注册
- 7.1 显示用户的信息
- 7.2 注册表单
- 7.3 注册失败
- 7.4 注册成功
- 7.5 专业部署方案
- 7.6 小结
- 7.7 练习
- 第 8 章 登录和退出
- 8.1 会话
- 8.2 登录
- 8.3 退出
- 8.4 记住我
- 8.5 小结
- 8.6 练习
- 第 9 章 更新,显示和删除用户
- 9.1 更新用户
- 9.2 权限系统
- 9.3 列出所有用户
- 9.4 删除用户
- 9.5 小结
- 9.6 练习
- 第 10 章 账户激活和密码重设
- 10.1 账户激活
- 10.2 密码重设
- 10.3 在生产环境中发送邮件
- 10.4 小结
- 10.5 练习
- 10.6 证明超时失效的比较算式
- 第 11 章 用户的微博
- 11.1 微博模型
- 11.2 显示微博
- 11.3 微博相关的操作
- 11.4 微博中的图片
- 11.5 小结
- 11.6 练习
- 第 12 章 关注用户
- 12.1 “关系”模型
- 12.2 关注用户的网页界面
- 12.3 动态流
- 12.4 小结
- 12.5 练习