NoMethodError: undefined method `password_reset_expired?' for nil:NilClass

NoMethodError: undefined method `password_reset_expired?' for nil:NilClass

我完成了 Michael Hartl 教程的一部分,我正在尝试将密码重置功能(来自第 10 章)添加到我自己的应用程序中。我不想要激活功能,所以我没有添加它。但是我确保添加了与密码重置邮件程序相关的部分(例如,经过身份验证的方法)。

当我 运行 rake 时,这是我得到的:

ERROR["test_password_resets", PasswordResetsTest, 2015-07-06 17:12:16 -0700]
 test_password_resets#PasswordResetsTest (1436227936.11s)
NoMethodError:         NoMethodError: undefined method `password_reset_expired?' for nil:NilClass
            app/controllers/password_resets_controller.rb:60:in `check_expiration'
            test/integration/password_resets_test.rb:26:in `block in <class:PasswordResetsTest>'
        app/controllers/password_resets_controller.rb:60:in `check_expiration'
        test/integration/password_resets_test.rb:26:in `block in <class:PasswordResetsTest>'

  33/33: [=================================] 100% Time: 00:00:01, Time: 00:00:01

Finished in 1.10830s
33 tests, 83 assertions, 0 failures, 1 errors, 0 skips

这是我的密码重置控制器:

class PasswordResetsController < ApplicationController
  before_action :get_user,   only: [:edit, :update]
  before_action :valid_user, only: [:edit, :update]
  before_action :check_expiration, only: [:edit, :update]

  def new
  end

  def create
    @user = User.find_by(email: params[:password_reset][:email].downcase)
    if @user
        @user.create_reset_digest
        @user.send_password_reset_email
        flash[:info] = "An email was sent to " + @user.email + " with password reset instructions."
        redirect_to login_url
    else
        flash.now[:danger] = "Hmm. We don't recognize that email. Make sure you signed up with this email."
        render 'new'
    end
  end

  def edit
  end

  def update
    if params[:user][:password].empty?
      flash.now[:danger] = "Uh oh. Your password can't be empty."
      render 'edit'
    elsif @user.update_attributes(user_params)
      log_in @user
      flash[:success] = "Success! Your password has been reset!"
      redirect_to @user
    else
      render 'edit'
    end
  end

  private

  def user_params
    params.require(:user).permit(:password, :password_confirmation)
  end

  #BEFORE FILTERS

  def get_user
    @user = User.find_by(email: params[:email])
  end

  # Confirms a valid user.
  def valid_user
    if (@user && @user.authenticated?(:reset, params[:id]))
      redirect_to root_url
      puts("Either I don't exist or I'm not authenticated.")
    end
  end

  # Checks expiration of reset token.
  def check_expiration
    if @user.password_reset_expired?
      flash[:danger] = "Uh oh! Your password reset has expired. Please request a new password reset."
      redirect_to new_password_reset_url
    end
  end

end

这是我的用户模型:

class User < ActiveRecord::Base
    attr_accessor :remember_token, :reset_token
    before_save { email.downcase! }


    validates :name, presence: true, length: { maximum: 50 }

    VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i #Set valid email regex to be used
    validates :email, presence: true, length: { maximum: 255 }, format: {with: VALID_EMAIL_REGEX}, uniqueness: { case_sensitive: false}

    has_secure_password
    validates :password, presence: true, length: { minimum: 6 }, allow_nil: true

    # Returns the hash digest of the given string.
    def User.digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                BCrypt::Engine.cost
    BCrypt::Password.create(string, cost: cost)
    end

    # Returns a random token.
    def User.new_token
        SecureRandom.urlsafe_base64
    end

    # Remembers a user in the database for use in persistent sessions.
    def remember
        self.remember_token = User.new_token
        update_attribute(:remember_digest, User.digest(remember_token))
    end

    # Returns true if the given token matches the digest.
    def authenticated?(attribute, token)
      digest = send("#{attribute}_digest")
      return false if digest.nil?
      BCrypt::Password.new(digest).is_password?(token)
    end

    # Forgets a user.
    def forget
        update_attribute(:remember_digest, nil)
    end

    #PASSWORD RESET
      def create_reset_digest
        self.reset_token = User.new_token
        update_attribute(:reset_digest, User.digest(reset_token))
        update_attribute(:reset_sent_at, Time.zone.now)
      end

      def send_password_reset_email
        UserMailer.password_reset(self).deliver_now
      end

      # Returns true if a password reset has expired.
      def password_reset_expired?
        reset_sent_at < 2.hours.ago
      end

end

我检查了一遍又一遍的拼写,但即使我更改了函数的名称也没有任何区别。发生什么事了?

顺便说一句,当我实际尝试使用 localhost:3000 中的密码重置时,它说已发送一封确认电子邮件(但我没有收到,因为我认为这是本地主机的一部分)。然后,当我访问邮件程序预览并使用重置密码时,一切正常。但是在生产中(在 Heroku 上),如果我尝试输入我的电子邮件以接收确认电子邮件,我只会收到一个错误页面 - 500。为什么会这样?

如果我需要添加更多详细信息,请告诉我。

如果找不到您的用户(例如,没有使用给定电子邮件的用户)...当您尝试检查它的过期时会发生什么?

提示:查看您在 valid_user 中所做的事情,以了解您在 check_expiration

中应该做什么的示例