This seems like a simple question but I can't seem to find an answer short of writing custom validators. I have this validator
validates :password, :presence => true, :confirmation => true, :length => { :minimum => 5}
there are more rules applied such as some regex for complexity, but this gives the gist.
The issue is that I only want presence applied on create, everything else needs to be on create and update. Because the user may not be changing a password when updating their information.
I tried splitting the rules
validates :password, :presence => true, :on => :create
validates :password, # The rest of the rules
This resulted in all rules being ignored for update. Is there a simple way to apply only one rule to create and the rest to everything?
You can try keeping it in one line, but applying :on => :create to just the :presence check:
validates :password, :presence => {:on => :create}, :confirmation => true, :length => { :minimum => 5}
However, I'm not sure it makes sense to always require a minimum length, but not always require presence -- if you update an existing record with a blank password, it's going to fail validations anyway since the length is 0.
My hunch is that the problem is that the validate :password call is not additive. Can you switch the presence check to:
validates_presence_of :password, :on=>:create
And then keep your other validations using the validate. Does that work?
Related
I am trying to implement a user authentication system inside rails, this is my model:
class User < ActiveRecord::Base
attr_accessible :id, :email, :name, :password, :created_at, :updated_at
has_secure_password
before_save { email.downcase! }
validates :email, presence: true, :uniqueness => { :case_senstive => false }
validates :name, presence: true
validates :password, presence: true, length: { minimum: 6 }
end
Running in the console i can read the User table successfully, then when i try to create a record:
User.new(:name => "A", :email => "a#a.a", :password => "password")
running valid on it retrurns true, but when saving the record, i get error:
users.password may not be NULL
Extracting the password out of the hash works fine.
What is the problem?
Thanks
Where are you setting :password_confirmation? The example in the docs suggests you need it.
http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html
Main problem is in has_secure_password. If you are using that you should have string field :password_digest (it's where your encrypted password will be saved). And you need delete field :password and your validator of presence for that too. After add gem 'bcrypt-ruby', '~> 3.0.0' .
And now that should work
user = User.new(:name => "A", :email => "a#a.a",
:password => "password", :password_confirmation => "password")
But better use great gem https://github.com/plataformatec/devise . It has everything you need.
NEW ADDED
you should have attr_accessible for :password_confirmation too and that field should be used in your form.
more info about has_secure_password
I have a model that takes an email address, but I do not want to require it for instance creation. I tried the following:
validates :email, :presence => false, :format => { :with => email_regex }
But this fails my test suite, the format regex apparently makes the presence of the email attribute required.
I thought it might be possible with a before_save method: is it possible to reject saving the object in a before_save method? Is there a better, "Rails way" of doing this?
You can use :allow_blank (or :allow_nil):
validates :email, :allow_blank => true, :format => { :with => email_regex }
I have an invoice model with approver_note, po_number and state_id.
I need validations to check:
validates :approver_note, :presence => true, {:scope => state_id == 3}
validates :po_number, :presence => true, {:scope => state_id ==2}
So, if the user selects state_id = 3, he must enter a note.
If he selects state_id = 2, he must enter a po_number.
Any assistance would be great... thanks!
You're looking for the :if option instead of :scope.
validates :approver_note, :presence => true,
:if => lambda { |invoice| invoice.state_id == 3 }
But since a lambda is a little ugly, I'd probably add a method to encapsulate what you're doing a bit better:
validates :approver_note, :presence => true, :if => :requires_note?
validates :po_number, :presence => true, :if => requires_po_number?
def requires_note?
state_id == 3
end
def requires_po_number?
state_id == 2
end
If you actually have a bunch of different attributes that are required when state_id is 3, not just a note, then you may want something like this:
validates :approver_note, :presence => true, :if => :green_state?
validates :po_number, :presence => true, :if => orange_state?
def green_state?
state_id == 3
end
def orange_state?
state_id == 2
end
(Replace "green" with -- I dunno -- "high_documentation" or whatever makes sense in your world.)
Or maybe you want to let the state decide what it is:
def green_state?
state.green?
end
It really does help to make the terminology in your code adhere more closely to your real-world language, as opposed to "3" and "2".
In attempting to seed my database I ran into a validation error on my User model's email attribute. The error:
Validation failed: Email is too short (minimum is 5 characters), Email is invalid
The thing is, my email is xxxxxxxx#gmail.com. I have five characters. Sorry for the beginner question but I don't know what is going on. I recently followed Railscasts to reset a User's password, and enable CanCan. I'm not sure if CanCan would affect anything, but prior to exploring that new functionality I've been able to fully seed my database without problems. I've pasted in some of my code below. I'm running Rails 3.0.5 and Ruby 1.9.2.
An example of how I create a User in my seed file:
me = User.create(:email => 'me#gmail.com', :password => 'test', :profile => my_profile)
User.rb model:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :password, :password_confirmation
before_save :encrypt_new_password
before_create { generate_token(:auth_token) }
before_validation :downcase_email
has_one :profile, :dependent => :destroy
accepts_nested_attributes_for :profile
validates :email, :uniqueness => true,
:length => { :within => 5..50 },
:format => { :with => /^[^#][\w.-]+#[\w.-]+[.][a-z]{2,4}$/i }
validates :password, :confirmation => true,
:length => { :within => 4..20 },
:presence => true,
:if => :password_required?
Add :email to attr_accessible to allow mass assignment on it. Without that the email field will not even be set so validation will fail.
What is the difference between validates :presence and validates_presence_of? Looking through ActiveModel it looks like they setup the validation the same way. However, given the following model definition:
class Account < ActiveRecord::Base
has_one :owner_permission, :class_name => 'AccountPermission', :conditions => { :owner => true, :admin => true }
has_one :owner, :class_name => 'User', :through => :owner_permission, :source => :user
validate :owner, :presence => true
validates_associated :owner
end
Calling save on an instance of Account does not validate the presence of owner. Though, if I use validates_presence_of it will.
All those validates_whatever_of :attr macros do is call validates :attr, :whatever => true.
The problem is you are using validate and not validates.
In Rails 3.x and 4.x - it is now encouraged to use the following syntax:
validates :email, presence: true
validates :password, presence: true
Instead of the 2.x way:
validates_presence_of :email
validates_presence_of :password
In fact validates and validates_presence_of is not entirely equal !
validates_presence_of is allowing you to also lazily check by example of the value in the field is included in another table.
Like that:
validates_presence_of :pay_type, :inclusion => PaymentType.names
Which is something you can't do as easily with something like that
validates :pay_type, presence, :inclusion => PaymentType.names
Cause the inclusion is only evaluated the first time (not in a lazy way)
I would have thought that it is appropriate to use validates :foo presence: true when you want to include other validations of :foo such as length or uniqueness. But if you know the only validation you'll need for an attribute is presence, then validates_presence_of appears to be more efficient.
So:
validates :foo, length: {maximum: 50}, uniqueness: true,
format: {with: /bar/},
presence: true # lots of validations needed
But:
validates_presence_of :foo # only presence validation needed