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.
精彩评论