开发者

attr_accessor causes Rspec tests to fail

I've added a :username_or_email property to my User model as such:

class User < ActiveRecord::Base

  #authlogic
  acts_as_authentic do |c|
    c.login_field = :username_or_email
  end

  #virtual field for allowing a user to login with their username or email
  attr_accessor :username_or_email

  attr_accessible :username, :email, :password, :password_confirmation, :username_or_email

  validates :username, :presence => true, 
                       :length => { :within => 3..20 }, 
                       :uniqueness => true, 
                       :format => { :with => /\A[a-z0-9][a-z0-9\-]+[a-z0-9]\z/ }

  validates :email, :presence => true, 
                    :format => { :with => /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i }, 
                    :uniqueness => { :case_sensative => false }

  validates :password, :presence => true,
                       :confirmation => true,
                       :length => { :within => 6..40 }

  def self.find_by_username_or_email(username_or_email)
    User.find_by_username(username_or_email) || User.find_by_email(username_or_email)
  end

end

The purpose of this is to create a virtual field which allows the user to log in with either their username or email using Authlogic. This works perfectly when I run the server, doing exactly what I want it to. However, it causes all of the following specs to fail:

require 'spec_helper'

describe User do

  before(:each) do
    @attr = { 
      :username => "example", 
      :email开发者_如何学JAVA => "user@example.com",
      :password => "password",
      :password_confirmation => "password"
    }
  end

  describe "username validations" do

    it "should allow usernames with numbers" do
      User.create!(@attr.merge(:username => 'example123'))
    end

    it "should allow hyphens" do
      User.create!(@attr.merge(:username => 'example-two'))
    end

    it "should require a username" do
      no_username_user = User.new (@attr.merge(:username => ""))
      no_username_user.should_not be_valid
    end

    it "should reject usernames that are too long" do
      user = User.new(@attr.merge(:username => ("a" * 21)))
      user.should_not be_valid
    end

    it "should reject username that are too short" do
      user = User.new(@attr.merge(:username => ("a" * 2)))
      user.should_not be_valid
    end

    it "should reject username with uppercase letters" do
      user = User.new(@attr.merge(:username => "Example"))
      user.should_not be_valid
    end

    it "should reject duplicate usernames" do
      User.create!(@attr)
      user = User.new(@attr.merge(:email => "user2@example.com"))
      user.should_not be_valid
    end

    it "should reject usernames with whitespace characters" do
      u1 = User.new(@attr.merge(:username => "exa mple"))
      u2 = User.new(@attr.merge(:username => " example"))
      u3 = User.new(@attr.merge(:username => "example "))
      u4 = User.new(@attr.merge(:username => "exa\tmple"))
      u5 = User.new(@attr.merge(:username => "exa\nmple"))

      u1.should_not be_valid
      u2.should_not be_valid
      u3.should_not be_valid
      u4.should_not be_valid
      u5.should_not be_valid
    end

    it "should reject usernames with special characters" do
      u1 = User.new(@attr.merge(:username => 'ex&mple'))
      u2 = User.new(@attr.merge(:username => 'ex*mple'))
      u3 = User.new(@attr.merge(:username => 'ex"mple'))
      u4 = User.new(@attr.merge(:username => 'ex^mple'))
      u5 = User.new(@attr.merge(:username => 'ex;mple'))

      u1.should_not be_valid
      u2.should_not be_valid
      u3.should_not be_valid
      u4.should_not be_valid
      u5.should_not be_valid
    end

    it "should reject usernames that begin with a hypen" do
      user = User.new(@attr.merge(:username => '-example'))
      user.should_not be_valid
    end

    it "should reject usernames that end with a hypen" do
      user = User.new(@attr.merge(:username => 'example-'))
      user.should_not be_valid
    end

  end



  describe "email validations" do

    it "should require an email" do
      no_email_user = User.new (@attr.merge(:email => ""))
      no_email_user.should_not be_valid
    end

    it "should reject duplicate emails" do
      User.create!(@attr)
      user2 = User.new(@attr.merge(:username => "example2"))
      user2.should_not be_valid
    end

    it "should accept valid email addresses" do
      addresses = %w[user@foo.com THE_USER@foo.bar.org first.last@foo.jp]
      addresses.each do |address|
        valid_email_user = User.new(@attr.merge(:email => address))
        valid_email_user.should be_valid
      end
    end

    it "should reject invalid email addresses" do
      addresses = %w[user@foo,com user_at_foo.org example.user@foo.]
      addresses.each do |address|
        invalid_email_user = User.new(@attr.merge(:email => address))
        invalid_email_user.should_not be_valid
      end
    end

    it "should reject email address idential up to case" do
      User.create!(@attr)
      user = User.new(@attr.merge(:email => @attr[:email].capitalize))
      user.should_not be_valid
    end

  end



  describe "password validations" do

    it "should require a password or a password confirmation" do
      user = User.new(@attr.merge(:password => "", :password_confirmation => ""))
      user.should_not be_valid
    end

    it "should require a password" do
      user = User.new(@attr.merge(:password => ""))
      user.should_not be_valid
    end

    it "should require a password confirmation" do
      user = User.new(@attr.merge(:password_confirmation => ""))
      user.should_not be_valid
    end

    it "should require a matching password confirmation" do
      user = User.new(@attr.merge(:password_confirmation => "invalid"))
      user.should_not be_valid
    end

    it "should reject passwords that are too short" do
      password = "a" * 5
      user = User.new(@attr.merge(:password => password, :password_confirmation => password))
      user.should_not be_valid
    end

    it "should reject passwords that are too long" do
      password = "a" * 41
      user = User.new(@attr.merge(:password => password, :password_confirmation => password))
      user.should_not be_valid
    end

    it "should require case sensative password confirmations" do
      password = "password"
      user = User.new(@attr.merge(:password => password, 
      :password_confirmation => password.capitalize))
      user.should_not be_valid
    end

  end

end

They fails with the errors:

Running: spec/models/user_spec.rb
FFFFFFFFFFFFFFFFFFFFFFF

Failures:

  1) User username validations should allow usernames with numbers
     Failure/Error: User.create!(@attr.merge(:username => 'example123'))
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x000001017c1850>
     # ./spec/models/user_spec.rb:17:in `block (3 levels) in <top (required)>'

  2) User username validations should allow hyphens
     Failure/Error: User.create!(@attr.merge(:username => 'example-two'))
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x00000100d50f10>
     # ./spec/models/user_spec.rb:21:in `block (3 levels) in <top (required)>'

  3) User username validations should require a username
     Failure/Error: no_username_user.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x00000100cc9da8>
     # ./spec/models/user_spec.rb:26:in `block (3 levels) in <top (required)>'

  4) User username validations should reject usernames that are too long
     Failure/Error: user.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x00000100c333f8>
     # ./spec/models/user_spec.rb:31:in `block (3 levels) in <top (required)>'

  5) User username validations should reject username that are too short
     Failure/Error: user.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x00000100aafbd0>
     # ./spec/models/user_spec.rb:36:in `block (3 levels) in <top (required)>'

  6) User username validations should reject username with uppercase letters
     Failure/Error: user.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x000001032e2f58>
     # ./spec/models/user_spec.rb:41:in `block (3 levels) in <top (required)>'

  7) User username validations should reject duplicate usernames
     Failure/Error: User.create!(@attr)
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x00000103167f70>
     # ./spec/models/user_spec.rb:45:in `block (3 levels) in <top (required)>'

  8) User username validations should reject usernames with whitespace characters
     Failure/Error: u1.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x000001032dbb40>
     # ./spec/models/user_spec.rb:57:in `block (3 levels) in <top (required)>'

  9) User username validations should reject usernames with special characters
     Failure/Error: u1.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x0000010328d0d0>
     # ./spec/models/user_spec.rb:71:in `block (3 levels) in <top (required)>'

  10) User username validations should reject usernames that begin with a hypen
     Failure/Error: user.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x0000010323f3d0>
     # ./spec/models/user_spec.rb:80:in `block (3 levels) in <top (required)>'

  11) User username validations should reject usernames that end with a hypen
     Failure/Error: user.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x00000103212a10>
     # ./spec/models/user_spec.rb:85:in `block (3 levels) in <top (required)>'

  12) User email validations should require an email
     Failure/Error: no_email_user.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x000001031ed4e0>
     # ./spec/models/user_spec.rb:96:in `block (3 levels) in <top (required)>'

  13) User email validations should reject duplicate emails
     Failure/Error: User.create!(@attr)
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x000001031bf630>
     # ./spec/models/user_spec.rb:100:in `block (3 levels) in <top (required)>'

  14) User email validations should accept valid email addresses
     Failure/Error: valid_email_user.should be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x000001031991b0>
     # ./spec/models/user_spec.rb:109:in `block (4 levels) in <top (required)>'
     # ./spec/models/user_spec.rb:107:in `each'
     # ./spec/models/user_spec.rb:107:in `block (3 levels) in <top (required)>'

  15) User email validations should reject invalid email addresses
     Failure/Error: invalid_email_user.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x0000010315d1b0>
     # ./spec/models/user_spec.rb:117:in `block (4 levels) in <top (required)>'
     # ./spec/models/user_spec.rb:115:in `each'
     # ./spec/models/user_spec.rb:115:in `block (3 levels) in <top (required)>'

  16) User email validations should reject email address idential up to case
     Failure/Error: User.create!(@attr)
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x0000010310a898>
     # ./spec/models/user_spec.rb:122:in `block (3 levels) in <top (required)>'

  17) User password validations should require a password or a password confirmation
     Failure/Error: user.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x0000010172f2c0>
     # ./spec/models/user_spec.rb:135:in `block (3 levels) in <top (required)>'

  18) User password validations should require a password
     Failure/Error: user.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x000001015f5b98>
     # ./spec/models/user_spec.rb:140:in `block (3 levels) in <top (required)>'

  19) User password validations should require a password confirmation
     Failure/Error: user.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x0000010155f120>
     # ./spec/models/user_spec.rb:145:in `block (3 levels) in <top (required)>'

  20) User password validations should require a matching password confirmation
     Failure/Error: user.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x00000101310298>
     # ./spec/models/user_spec.rb:150:in `block (3 levels) in <top (required)>'

  21) User password validations should reject passwords that are too short
     Failure/Error: user.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x00000100eecbf8>
     # ./spec/models/user_spec.rb:156:in `block (3 levels) in <top (required)>'

  22) User password validations should reject passwords that are too long
     Failure/Error: user.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x00000100d7f400>
     # ./spec/models/user_spec.rb:162:in `block (3 levels) in <top (required)>'

  23) User password validations should require case sensative password confirmations
     Failure/Error: user.should_not be_valid
     NoMethodError:
       undefined method `username_or_email_changed?' for #<User:0x00000100cf0b38>
     # ./spec/models/user_spec.rb:169:in `block (3 levels) in <top (required)>'

Finished in 0.19613 seconds
23 examples, 23 failures

It seems like the problem is occurring with the valid? method, but I don't understand why considering :username_or_email doesn't have any validations in place. Why are these errors occurring and how can I fix them?

Thanks.


I would guess that because username_or_email is not an attribute in the ActiveRecord sense, it should not be mentioned with attr_accessible.


The solution was actually pretty simple. In user.rb:

class User < ActiveRecord::Base

  #authlogic
  acts_as_authentic do |c|
    c.login_field = :username_or_email
    c.validates_login_field = false
  end

  ...

end

The documentation for this method can be found here.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜