8.6 练习

电子书中有练习的答案,如果想阅读参考答案,请购买电子书

避免练习和正文冲突的方法参见3.6 节中的说明。

  1. 代码清单 8.32 中,我们定义了生成令牌和摘要的类方法,前面都加上了 User。这么定义没问题,而且因为我们会使用 User.new_tokenUser.digest 调用,或许这样定义意思更明确。不过,定义类方法有两种更常用的方式,一种有点让人困惑,一种极其让人困惑。运行测试组件,确认代码清单 8.59(有点让人困惑)和代码清单 8.60(极其让人困惑)中的实现方式是正确的。(注意,在代码清单 8.59代码清单 8.60 中,selfUser 类,而用户模型中的其他 self 都是用户对象实例。这就是让人困惑的根源所在。)

  2. 8.4.5 节说过,由于应用现在的设计方式,在代码清单 8.51 的集成测试中无法获取 remember_token 虚拟属性。不过,在测试中使用一个特殊的方法可以获取,这个方法是 assigns。在测试中,可以访问控制器中定义的实例变量,方法是把实例变量的符号形式传给 assigns 方法。例如,如果 create 动作中定义了 @user 变量,在测试中可以使用 assigns(:user) 获取这个变量。现在,会话控制器中的 create 动作定义了一个普通的变量(不是实例变量),名为 user,如果我们把它改成实例变量,就可以测试 cookies 中是否包含用户的记忆令牌。填写代码清单 8.61代码清单 8.62 中缺少的内容(?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 &lt;&lt; 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