I am making a simple social network with Ruby on Rails. I wanted to add a restriction of certain characters for the profile name when signing up. So, in my User.rb file, I have the following:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me,
:first_name, :last_name, :profile_name
# attr_accessible :title, :body
validates :first_name, presence: true
validates :last_name, presence: true
validates :profile_name, presence: true,
uniqueness: true,
format: {
with: /^[a-zA-Z0-9_-]+$/,
message: "must be formatted correctly."
}
has_many :statuses
def full_name
first_name + " " + last_name
end
end
I set up a test to validate that it works, and this is what the test is:
test "user can have a correctly formatted profile name" do
user = User.new(first_name: '******', last_name: '****', email: '********#gmail.com')
user.password = user.password_confirmation = '**********'
user.profile_name = '******'
assert user.valid?
end
When I run the test, I keep getting the error saying that something is wrong with my assert user.valid? line. So I am thinking I messed up some syntax in my with: /^[a-zA-Z0-9_-]+$/.
The error I am getting is 1) Failure:
test_user_can_have_a_correctly_formatted_profile_name(UserTest) [test/unit/user_test.rb:40]:
But on line 40, it has this piece of code assert user.valid?
Any help is appreciated :)
So I am thinking I messed up some syntax in my with regexp.
Your syntax is fine.
However, your error message clearly shows you're using a profile name that doesn't match.
Are you using some other character in a profile name, such as a space? Or period?
Try it like this:
/^[a-zA-Z0-9_-]+$/.match "foobar" #=> #<MatchData "foobar">
If the data fails to match, you'll get nil:
/^[a-zA-Z0-9_-]+$/.match "foo bar" #=> nil
Related
I followed the wiki to add a custom email validator with devise. It works but the errors are printed twice once for each validation like below. How to fix this?
Update:
The answer linked in the comment does not work. It probably works only if all validations are done in one validates call. In my case one validation is done by devise and other is added by me. To be clear, the model looks like below:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
attr_accessible :email, :name
before_save do |user|
user.email = email.downcase
end
validates :name, presence: true, length: { maximum: 50 }
validates :email, email: true, presence: true, reduce: true # This causes 'Email is Invalid' to be printed twice
end
If you just need to change the regular expression that devise is using for email validation, you can do it in config/initializers/devise.rb:
config.email_regexp = /\A[^#]+#[^#]+\z/
Then you won't need to add additional validation to the email field.
You can use email validation with Devise email_regexp, just change in config/initializers/devise.rb
FROM (because it validates some incorrect emails as valid, like emn178#gmail..com):
config.email_regexp = /\A[^#\s]+#[^#\s]+\z/
TO:
config.email_regexp = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
I found this solution here.
First, excuse my poor english, I'm french... it's tricky to explain my problem !
I have a model User model in a Rails application:
class User < ActiveRecord::Base
attr_accessible :email, :gender, :lastname, :firstname
end
And a BackUser model that inherit from User:
class BackUser < User
# Class for Backoffice User
devise :database_authenticatable,
:rememberable,
:trackable,
:lockable,
:invitable,
:confirmable,
:validatable,
:validate_on_invite => true
attr_accessible :password, :password_confirmation, :remember_me, :active, :role
validates :role, presence: true,
inclusion: ["admin", "normal"]
validates :gender, presence: true
validates :firstname, presence: true
validates :lastname, presence: true
def admin?
self.role == 'admin'
end
end
This second class should validate the record before invite!
BUT, when I use the console to do the following:
u = BackUser.new
u.invite!
"u" is saved in database and an invitation is send to a blank email...
Do you know what I have to do?
Thans a lot!
I'm sure you've found a solution or workaround to your problem by now, but for any future SO users who encounter the same problem I found a pretty simple fix.
Devise Invitable's model configuration docs don't fully explain how to implement :validate_on_invite, but you have to set the configuration option to true - :validate_on_invite => true.
Here's what my devise method looks like in my User model for this to work correctly.
models/user.rb
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :invitable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :validate_on_invite => true
Now when I attempt to submit an invitation it is fully validating the record with what the validations I've set up in my User model before allowing the invitation to be sent and the user record to be created. Looking at the docs, I'm guessing you can also enable this setting in the devise initializer, but I haven't tried going that route.
*Second possible option to enable validation if needed
config/initializers/devise.rb
config.validate_on_invite = true
I've never been able to get the validation to work correctly for devise invitable. You can't use RobHeaton's suggestion either because you will receive a validation error on the password. I use this little hack to get validation to work:
def create
#user = User.new(user_params)
#user.valid?
#user.errors.messages.except!(:password) #remove password from errors
if (#user.errors.any?)
render 'new'
else
#user.invite!(current_user)
redirect_to user_path(#user)
end
end
It doesn't solve the mystery of why your behaviour is occurring, but:
if u.save
u.invite!
end
will give the end result you are after.
I'm having trouble with devise and password confirmation.
I've created a model test in rails4 which looks like this:
test "user requires a password_confirmation" do
user = FactoryGirl.build(:user)
assert user.valid?, "FactoryGirl should return a valid user"
user.password_confirmation = nil
# These are just some outputs to verify what's going on.
# This is the condition in devises's password_required? method
puts !user.persisted? || !user.password.nil? || !user.password_confirmation.nil? #=> true
puts user.valid? #=> true
assert user.invalid?, "Password confirmation should be required" # Fails here
assert_equal 1, user.errors.size, "Only one error should have occured. #{user.errors.full_messages}"
end
This test fails at second assert ("Password confirmation should be required").
Here's my full User model :
class User < ActiveRecord::Base
has_one :user_entity
has_one :manager,through: :user_entity, source: :entity, source_type: 'Manager'
has_one :client, through: :user_entity, source: :entity, source_type: 'Client'
# Adds default behavior. See: https://github.com/stanislaw/simple_roles#many-strategy
simple_roles
validates :username,
presence: true,
length: { minimum: 4, allow_blank: true, if: :username_changed? },
uniqueness: { allow_blank: true, if: :username_changed? },
format: { with: /\A[A-z_0-9]+\z/, allow_blank: true, if: :username_changed? }
# Include default devise modules. Others available are:
# :token_authenticatable, :timeoutable, :omniauthable, :registerable
devise :database_authenticatable, :recoverable, :rememberable,
:trackable, :validatable, :confirmable, :lockable
# Virtual attribute for authenticating by either username or email
# This is in addition to a real persisted field like 'username'
attr_accessor :login
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
# User need to be active
conditions[:active] = true
if login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
def entity
self.user_entity.try(:entity)
end
def entity=(newEntity)
self.build_user_entity(entity: newEntity)
end
end
I've tried adding in my user model :
validates :password, confirmation: true
After that the test passes but i get an error on the next one:
1) Failure:
UserTest#test_user_requires_a_password [/my/project/test/models/user_test.rb:52]:
Two errors should have occured. ["Password confirmation doesn't match confirmation", "Password confirmation doesn't match confirmation", "Password can't be blank"].
Expected: 2
Actual: 3
As you can see, it's like the confirmation validation occurs two times and fails both time.
I'm using rails4 (edge) and the rails4 branch for devise.
EDIT:
I tried setting
user.password_confirmation = "#{user.password}_diff"
And the test passes. So why is confirmation ignored when nil is set ? As the object is not yet persisted i would assume a password confirmation has to be provided.
I know this is an old question but I was having the same problem.
First, remove this from your model so you don't get duplicate validation checks:
validates :password, confirmation: true
Then add the following. I changed it to only work on create:
validates :password_confirmation, presence: true, on: :create
See the validates_confirmation_of section on this apidock page:
NOTE: This check is performed only if password_confirmation is not nil. To require confirmation, make sure to add a presence check for the confirmation attribute:
validates_presence_of :password_confirmation, if: :password_changed?
I'm working on a Rails app with two-factor authentication. The User model in this app has an attribute, two_factor_phone_number. I have the model validating that this attribute is present before the model can be saved.
To make sure the phone number is saved in a proper format, I've created a custom attribute assignment method that looks like this:
def two_factor_phone_number=(num)
num.gsub!(/\D/, '') if num.is_a?(String)
self[:two_factor_phone_number] = num.to_i
end
I'm doing some acceptance testing, and I've discovered that if this method is in the model, the ActiveRecord validation is ignored/skipped and a new model can be created without a two_factor_phone_number set.
The model code looks like this:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable,
:lockable
attr_accessible :email, :password, :password_confirmation, :remember_me,
:first_name, :last_name, :two_factor_phone_number
validates :first_name, presence: true
validates :last_name, presence: true
validates :two_factor_phone_number, presence: true
# Removes all non-digit characters from a phone number and saves it
#
# num - the number to be saved
#
# Returns the digit-only phone number
def two_factor_phone_number=(num)
num.gsub!(/\D/, '') if num.is_a?(String)
self[:two_factor_phone_number] = num.to_i
end
end
You could add a format validation:
validates :two_factor_phone_number, :format => { :with => /[0-9]/,
:message => "Only digits allowed" }
and/or create another method to set this attribute and call it before validation
before_validation :update_phone_format
def update_phone_format
...
end
I am running into some slightly tricky issues with a legacy db. Everything seems to work fine if I simply change the "password" column name in the db to "encrypted_password"; however, I need to leave the db in place.
So I decided to use
alias_attribute :encrypted_password, :password
Now I get a "stack level too deep" error in the console.
My user model:
class User < ActiveRecord::Base
require "digest/sha1"
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :encryptable, :encryptor => :old_cakephp_auth
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
has_many :events
before_create :add_default_values
#alias_attribute :created_at, :created
#alias_attribute :updated_at, :updated
alias_attribute :encrypted_password, :password
def add_default_values
self.created = Time.now
self.updated = Time.now
self.image = ""
self.name = self.email.split("#").first
#make normal user
self.role_id = 2
self.username = self.email.split("#").first + rand(100000000000000).to_s
self.website = ""
end
def valid_password?(password)
return false if encrypted_password.blank?
Devise.secure_compare(Digest::SHA1.hexdigest(self.password_salt+password), self.encrypted_password)
end
end
Ideas? Thanks!!! :)
I imagine that this is due to devise reserving the word password for their own use (and it in turn calling encrypted_password. Try renaming it to pword and see if the error still occurs. If it doesn't, you'll have to find another name to call the aliased password.
I should say that this is just an assumption. Let me know if it helps.