Custom email validator with devise - ruby-on-rails

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.

Related

Rails: Simple-Form & Devise: mark email field of user model as required in forms automatically?

I wonder why the field email is not marked as a required field in Simple-Form, as when submitting it empty there is a validation error "can't be blank".
It seems that the validation rules for the email field come from Devise, so they are available to the validation mechanism, but not to Simple-Form. Why's that?
I could simply add another validates :email, presence: true to my User model, but this seems overkill. Or I could add a required: true to the f.input :email method of Simple-Form, but this seems like overkill, too.
Here's the relevant part of my User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :lockable, authentication_keys: [:login]
validates :name, presence: true
Do I have something configured incorrect/incomplete?
From Simple Form's README:
For performance reasons, this detection is skipped on validations that
make use of conditional options, such as :if and :unless.
And you can see that Devise will add validation with an :if in https://github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb
base.class_eval do
validates_presence_of :email, if: :email_required?
validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
validates_format_of :email, with: email_regexp, allow_blank: true, if: :email_changed?
validates_presence_of :password, if: :password_required?
validates_confirmation_of :password, if: :password_required?
validates_length_of :password, within: password_length, allow_blank: true
end
So you have to mark the field as required in your views.
If u submit it blank it will show ask u to fill in the field,
if you submit it a wrong email is will ask for a valid email.
So it check for blank first, if not blank then it checks if valid.
Your form is doing just fine.

devise invitable do not validate model

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.

How To Recover a Password with Devise ( Ruby on Rails)

I'm trying to recover a user's password with devise, but it generates the following error
undefined method `reset_password_sent_at=' for #<User:0x007fb78cfafb68>
Can anyone help me with this, since I'm new to Ruby on Rails?
What is the best way to recover a password and email the user using Devise? Thank you very much...
I'm use devise (2.2.3)
User.rb
require 'digest/md5'
class User < ActiveRecord::Base
# Setup accessible (or protected) attributes for your model
belongs_to :shop
before_create :compute_email_md5
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable,
:recoverable,
:rememberable,
:trackable,
:validatable,
:token_authenticatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email,
:email_md5,
:password,
:password_confirmation,
:shop_id,
:role,
:terms,
:name,
:notify_on_order_received
validates :terms, :acceptance => true, :on => :create
end
THE SOLUTION IS
add reset_password_sent_at column to user table
As you've discovered, passord recovery requires that the model have a reset_password_sent_at column. Adding it via migration should solve this problem.
As for the reason this is happening, I'm guessing you added password recovery (the :recoverable module) after initially generating your Devise-enabled model (User). That's why Devise's generator didn't create that column for you.

Don't receive callback with devise_invitable

I'm using devise_invitable to allow users to invite each other. I want to set values for the user when the invite is created or when it's accepted. One approach is here
Using devise_invitable for adding Users to a Group in Ruby on Rails?
but this seems overkill. The callbacks look like a perfect solution, but they don't seem to fire during my Rspec tests.
Here is the user model:
class User < ActiveRecord::Base
belongs_to :company
rolify
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :invitable, :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :role_ids, :as => :admin
attr_accessible :name, :email, :password, :password_confirmation, :remember_me, :company
validates :company, :presence => true
after_invitation_accepted :email_invited_by
before_invitation_accepted :email_invited_by
private
def email_invited_by
# This is NEVER executed during tests, even when an invite is successfully accepted
puts "Callback worked"
end
end
Any clues about where to look would be appreciated. I find the devise_invitable documentation a bit opaque.
Thanks!
For those still looking for an answer here is what worked for me.
The issue is not with validations since in devise_invitable there is a config to validate on invite and it defaults to false:
# Flag that force a record to be valid before being actually invited
# Default: false
# config.validate_on_invite = true
So my solution is to use the callback provided by devise_invitable:
after_invitation_accepted :create_profile
Do note that this needs to be below devise :invitable
devise_invitable callbacks will be fired if the user has been really invited (i.e. the object is persisted and there's an invitation token set) and the object (user's object) has no validation errors. That said, I see that you're validating presence of company without any conditions, which, I think, could be causing validation errors.
If that's the case, you can add a condition like this
validates :company, :presence => true, :if => Proc.new { self.invitation_token.nil? }
so it won't cause validation errors and fire the callbacks.

Ruby on rails tests keep giving me errors

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

Resources