无法将password_reset_token保存到数据库

时间:2013-12-26 08:54:13

标签: ruby-on-rails ruby

我在测试中收到错误,尽管代码正常运行。 Capybara为实例方法中的属性集抛出未定义的方法错误是否正常?我的代码如下:

应用/模型/ user.rb

class User < ActiveRecord::Base
  attr_accessible :email, :first_name, :last_name, :password, :bio, :resume,
                  :website, :focus, :location, :aspirations, :password_reset_token, :profile_photo, :school,                                 :profile_photo_file_name, :profile_photo_content_type,                          :profile_photo_file_size, :profile_photo_updated_at

  attr_accessor :password_reset_token
  attr_reader :password

  validates :email, :first_name, :last_name, presence: true
  validates :email, uniqueness: true
  validates :password, length: { minimum: 6, allow_nil: true }
  validates :password_digest, presence: { message: "Password can't be blank" }

  after_initialize :ensure_session_token

  default_scope order('created_at ASC')

  has_attached_file :profile_photo, styles: { 
    medium: "250x250>", thumb: "50x50>" 
    }, convert_options: { :thumb => "-quality 75 -strip" 
  }

  has_attached_file :resume

  has_many(
    :roles,
    class_name: 'Role',
    foreign_key: :user_id,
    primary_key: :id,
    inverse_of: :user,
    dependent: :destroy
  )

  has_many(
    :taggings,
    as: :taggable,
    dependent: :destroy
  )

  has_many(
    :posts,
    class_name: 'Post',
    foreign_key: :user_id,
    primary_key: :id,
    inverse_of: :user,
    dependent: :destroy
  )

  has_many(
    :comments,
    class_name: 'Comment',
    foreign_key: :user_id,
    primary_key: :id,
    inverse_of: :user,
    dependent: :destroy
  )

  has_many(
    :honors,
    class_name: 'Honor',
    foreign_key: :user_id,
    primary_key: :id,
    inverse_of: :user,
    dependent: :destroy
  )

  has_many(
    :experiences,
    class_name: 'Experience',
    foreign_key: :user_id,
    primary_key: :id,
    inverse_of: :user,
    dependent: :destroy
  )

  has_many :courses, through: :roles, source: :course
  has_many :tags, through: :taggings, source: :tag

  def self.generate_token
    SecureRandom.urlsafe_base64(16)
  end

  def self.find_by_credentials(email, password)
    user = User.find_by_email(email)

    return nil if user.nil?

    user.is_password?(password) ? user : nil
  end

  def password=(password)
    @password = password
    self.password_digest = BCrypt::Password.create(password)
  end

  def is_password?(password)
    BCrypt::Password.new(self.password_digest).is_password?(password)
  end

  def reset_session_token!
    self.session_token = self.class.generate_token
  end

  def full_name
    "#{self.first_name} #{self.last_name}"
  end

  def set_password_reset_token
    self.password_reset_token = self.class.generate_token
  end

  def send_password_reset
    set_password_reset_token
    self.save!
    UserMailer.password_reset(self).deliver!
  end

  private

  def ensure_session_token
    self.session_token ||= self.class.generate_token
  end

  def find_role(course_id)
    Role.select(:title)
        .where(user_id: self.id, course_id: course_id).first.title.titleize
  end
end

应用/控制器/ password_resets_controller.rb

class PasswordResetsController < ApplicationController
  skip_before_filter :require_current_user!

  def new
  end

  def create
    user = User.find_by_email(params[:email])

    user.send_password_reset if user

    flash[:notice] = "Email sent with password reset instructions."
    redirect_to signin_url
  end

  def edit
    @user = User.find_by_password_reset_token(params[:id])
  end

  def update
    @user = User.find_by_password_reset_token(params[:id])

    if @user.update_attributes(params[:user])
      flash[:notice] = "Password has been reset!"
      redirect_to signin_url
    else
      render :edit
    end
  end
end

分贝/ schema.rb

  create_table "users", :force => true do |t|
    t.string   "email",                      :null => false
    t.string   "password_digest",            :null => false
    t.string   "first_name",                 :null => false
    t.string   "last_name",                  :null => false
    t.string   "session_token",              :null => false
    t.string   "bio"
    t.string   "location"
    t.string   "focus"
    t.string   "aspirations"
    t.datetime "created_at",                 :null => false
    t.datetime "updated_at",                 :null => false
    t.string   "profile_photo_file_name"
    t.string   "profile_photo_content_type"
    t.integer  "profile_photo_file_size"
    t.datetime "profile_photo_updated_at"
    t.string   "resume_file_name"
    t.string   "resume_content_type"
    t.integer  "resume_file_size"
    t.datetime "resume_updated_at"
    t.string   "school"
    t.string   "password_reset_token"
  end

  add_index "users", ["email"], :name => "index_users_on_email", :unique => true
  add_index "users", ["session_token"], :name => "index_users_on_session_token", :unique => true

Capybara错误消息

PasswordResets
  updates the user password when confirmation matches (FAILED - 1)

Failures:

  1) PasswordResets updates the user password when confirmation matches
     Failure/Error: user.set_password_reset_token
     NoMethodError:
       undefined method `password_reset_token=' for #<User:0x007fd237d31e20>
     # ./app/models/user.rb:107:in `set_password_reset_token'
     # ./spec/features/password_resets_spec.rb:25:in `block (2 levels) in <top (required)>'

Finished in 0.19426 seconds
1 example, 1 failure

Failed examples:

rspec ./spec/features/password_resets_spec.rb:23 # PasswordResets updates the user password when confirmation matches

1 个答案:

答案 0 :(得分:0)

如果您有一个不想保存到数据库中的值,则应使用attr_accessor。在这种情况下,您不需要它,因为您想将:password_reset_token保存到数据库中,并且rails已经提供了setter和getter方法。

修改

使用attr_accessor更改getter和setter方法并定义实例变量。它确实更改Active Record使用的属性。这意味着保存模型时,即使相关列存在,使用attr_accessor设置的值也不会保存到数据库中。

您可以在打印属性时看到我的意思:

u = User.new
u.password_reset_token = "abc"
u.attributes #=> { ... "password_reset_token"=>nil ... }

在同一属性上同时使用attr_accessorattr_accessible没有意义。您应该从模型中删除attr_accessor :password_reset_token

这可能无法完全解决问题 - 但它应该可以帮助您解决真正的问题所在。

我建议您为User#set_password_reset_token编写单元测试,以确保在编写验收测试之前它的行为符合您的预期。