10.5 练习

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

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

  1. 填写代码清单 10.57 中缺少的代码,为代码清单 10.52 中的密码重设超时失效分支编写集成测试。(代码清单 10.57 用到了 response.body,用来获取返回页面中的 HTML。)检查是否过期有很多方法,代码清单 10.57 使用的方法是,检查响应主体中是否包含单词“expired”(不区分大小写)。

  2. 现在,用户列表页面会显示所有用户,而且各用户还可以通过 /users/:id 查看。不过,更合理的做法是只显示已激活的用户。填写代码清单 10.58 中缺少的代码,实现这一需求。[9](这段代码中使用了 Active Record 提供的 where 方法,11.3.3 节会详细介绍。)附加题:为 /users 和 /users/:id 编写集成测试。

  3. 代码清单 10.42 中,activatecreate_reset_digest 方法中都调用了两次 update_attribute 方法,每一次调用都要单独执行一个数据库事务(transaction)。填写代码清单 10.59 中缺少的代码,把两个 update_attribute 调用换成一个 update_columns 调用,这样修改后每个方法只会和数据库交互一次。然后再运行测试组件,确保仍能通过。

代码清单 10.57:测试密码重设超时失效了 GREEN

test/integration/password_resets_test.rb

require 'test_helper'

class PasswordResetsTest < ActionDispatch::IntegrationTest

  def setup
    ActionMailer::Base.deliveries.clear
    @user = users(:michael)
  end
  .
  .
  .
  test "expired token" do
    get new_password_reset_path
    post password_resets_path, password_reset: { email: @user.email }

    @user = assigns(:user)
    @user.update_attribute(:reset_sent_at, 3.hours.ago)
    patch password_reset_path(@user.reset_token),
          email: @user.email,
          user: { password:              "foobar",
                  password_confirmation: "foobar" }
    assert_response :redirect
    follow_redirect!
 assert_match /FILL_IN/i, response.body  end
end
代码清单 10.58:只显示已激活用户的代码模板

app/controllers/users_controller.rb

class UsersController < ApplicationController
  .
  .
  .
  def index
 @users = User.where(activated: FILL_IN).paginate(page: params[:page])  end

  def show
    @user = User.find(params[:id])
 redirect_to root_url and return unless FILL_IN  end
  .
  .
  .
end
代码清单 10.59:使用 update_columns 的代码模板

app/models/user.rb

class User < ActiveRecord::Base
  attr_accessor :remember_token, :activation_token, :reset_token
  before_save   :downcase_email
  before_create :create_activation_digest
  .
  .
  .
  # 激活账户
  def activate
 update_columns(activated: FILL_IN, activated_at: FILL_IN)  end

  # 发送激活邮件
  def send_activation_email
    UserMailer.account_activation(self).deliver_now
  end

  # 设置密码重设相关的属性
  def create_reset_digest
    self.reset_token = User.new_token
 update_columns(reset_digest:  FILL_IN, reset_sent_at: FILL_IN)  end

  # 发送密码重设邮件
  def send_password_reset_email
    UserMailer.password_reset(self).deliver_now
  end
  .
  .
  .
end