Rails custom validation one after another - ruby-on-rails

I have a password field and that validates presence and length and both are working fine. But when I submit the form with blank password field, it displays error messages for both validations.
What I want is if the password is blank then length validator must not checked and display error message for only presence validator. Length validator will only be checked if password is present.

You can use Object#with_options and ActiveRecord::Base#new_record?:
class User < ActiveRecord::Base
with_options :if => :new_record? do |user|
user.validates :password, presence: true, length: { maximum: 20 }
end
end
Look rails conditional validation.

Along with other validations pass this
:allow_blank => true
For example
validates :password, :presence => true, :length => { :maximum => 20, :allow_blank => true }

Related

Ruby on Rails 4. Model conditional validations

I have model with some validation rules:
class Order < ActiveRecord::Base
validates :zip_code, presence: true, length: {is: 5}, numericality: {only_integer: true, :greater_than => 0}
end
And when zip_code is blank i don't need to perform other zip_code validations (it is redundant and all other validation messages on user page look very strange if zip_code is blank)
How can i implement this logic? i need to validate length, is_integer and greater_than only if zip_code is not blank? and i need to show only zip_code can't be blank message on user page
You can do something like
validates :zip_code, presence: true
validates :zip_code, length: {is: 5}, numericality: {only_integer: true, :greater_than => 0}, :if => :zip_code?
Hope it helps!

How can I update particular fields of model with validation in Ruby on Rails?

There is an AcviteRecord Model named User like this:
class User < ActiveRecord::Base
validates :name, :presence => true
validates :email, :presence => true, :uniqueness => true
validates :plain_password, :presence => true, :confirmation => true
validates :plain_password_confirmation, :presence => true
#...other codes
end
It requires that the update of name and email and the update of password are separated.
When only update name and password, using update or update_attributes will cause password validation which is not needed. But using update_attribute will save name and email without validation.
Are there any ways to update particular fields of model with validation without causing the other fields' validation?
Give it a try, might help
class User < ActiveRecord::Base
validates :name, presence: true
validates :email, presence: true, :uniqueness => true
validates :plain_password, length: { in: 4..255, allow_nil: true }, confirmation: true
validates :plain_password_confirmation, presence: true, if: -> (user){ user.plain_password.present? }
# ......
# ......
end
Apart from this you should reconsider about saving plain_password ;)
You can adjust your validations to only run on create. Requiring confirmation ensures changes on edit are applied.
validates :plain_password,
confirmation: true,
presence: {
on: :create },
length: {
minimum: 8,
allow_blank: true }
validates :plain_password_confirmation,
presence: {
on: :create }
I am assuming you are hashing your passwords, so this would accompany code similar to:
attr_accessor :plain_password
before_save :prepare_password
def encrypted_password( bcrypt_computational_cost = Rails.env.test? ? 1 : 10)
BCrypt::Password.create plain_password, cost: bcrypt_computational_cost
end
private #===========================================================================================================
# Sets this users password hash to the encrypted password, if the password is not blank.
def prepare_password
self.password_hash = encrypted_password if plain_password.present?
end
A better way to handle this is to not include the fields in the rest of the edit form, but rather provide a link to "Change my password". This link would direct to a new form (perhaps in a modal window) which will require the new password, confirmation of the new password, and the old password, to prevent account hijacking.
In your case you can use has_secure_password The password presence is only validated on creation.

How do I get validation error for either invalid email or email field left blank?

I am trying to validate fields in a form where I want two different messages for two different problems with the input.
I have the following code:
validates_format_of :email,
:with => /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i,
:allow_blank => false
If the email field is left blank then the error message "Email is invalid" is shown in the website.
How can I get the validation to return a message saying the field cannot be blank if it is left out by the user, instead of just saying it is too short?
I like to handle this with 2 different validations (and make sure they don't both fire at the same time). So something like this:
validates_format_of :email,
:with => /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i,
:allow_blank => true
validates_presence_of :email
The validates_presence_of handles making sure the email is not blank. And changing validates_format_of to use :allow_blank => true will make sure the formatting validation won't run if the email is blank.
In Rails 3.0+ you can also combine the two validations into a single one using validates:
validates :email,
presence: true,
format: { with: /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i,
allow_blank: true }
You create a separate validation for the blank case:
validates_presence_of :email
validates_format_of :email,
:with => /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i,
:allow_blank => true
You need to add another validation checking for presence separately, like:
validates :email, presence: true, format: { with: YOUR_REGEX }

Validate presence of field only if another field is blank - Rails

I have a form with a mobile/cell number and a home phone number.
I want to have only validate presence of mobile/cell number if the phone number has been left blank or vice versa.
My current validations for these fields are as follows.
validates_presence_of :mobile_number
validates_presence_of :home_phone
validates_length_of :home_phone, :minimum => 12, :maximum => 12
validates_length_of :mobile_number, :minimum => 10, :maximum => 10, :allow_blank => true
validates_format_of :home_phone, :with => /\A[0-9]{2}\s[0-9]{4}\s[0-9]{4}/, :message => "format should be 02 9999 9999"
I thought I could have something like the following but not sure how to do this exactly.
validates_presence_of :mobile_number, :unless => :home_phone.blank?
I'm using Rails 3.
You don't need a lambda. This will do:
validates_presence_of :mobile_number, :unless => :home_phone?
Also, all of the validators take the same if/unless options, so you can make them conditional at will.
Update: Looking back at this answer a few days later, I see that I should explain why it works:
If you set a validator's :unless option to be a symbol, Rails will look for an instance method of that name, invoke that method on the instance that's being validated -- at validation time -- and only perform the validation if the method returns false.
ActiveRecord automatically creates question mark methods for each of your model's attributes, so the existence of a home_phone column in your model's table causes Rails to create a handy #home_phone? method. This method returns true if and only if home_phone is present (i.e. not blank). If the home_phone attribute is nil or an empty string or a bunch of white space, home_phone? will return false.
UPDATE: Confirmed that this old technique continues to work in Rails 5.
You must use a lambda / Proc object:
validates_presence_of :mobile_number, :unless => lambda { self.home_phone.blank? }
Starting in Rails 4, you can pass a block to presence. Concisely:
validates :mobile_number, presence: {unless: :home_phone?}
Also, :home_phone? returns false for nil or blank.
Here is another way that works in rails 4
validates_presence_of :job, if: :executed_at?
validates :code,
presence: true,
length: { minimum: 10, maximum: 50 },
uniqueness: { case_sensitive: false },
numericality: { only_integer: true }
a short solution:
validates_presence_of :mobile_number, unless: -> { home_phone.blank? }
In newer versions of Rails, instead of relying on old validates_presence_of, you should use validates and list validations for each attribute:
validates :mobile_number, presence: { if: -> { home_phone.present? } }
Tested in Rails 7, this works flawlessly:
validates :mobile_number, presence: { unless: :home_phone }

Ruby on Rails Password Validation

So I have interesting password validation requirements:
When a user signs up, I want them to have to type in password and confirm and be between 6..40 (GOT THIS WORKING 100%)
When a user updates their profile, the same validation rules apply (GOT THIS WORKING 100%)
When an admin adds a user, they only have to enter the password once and it should be validated (NOT WORKIG)
When an admin edits a user and the password field is blank, it shouldn't update the password, if they type something, it should be validated. (PARTIAL WORKING)
validates :password, :presence => true,
:confirmation => true,
:length => {:within => 6..40},
:unless => :force_submit
The only cases I can't cover are when an admin adds a user, it is not validated and when an admin edits a user (and types in a password) it is not validated.
the :force_submit is passed in from the admin form, so the password isn't validated. (So the case of an updating empty password works)
Any ideas/magic?
Building slightly on the accepted answer, here's the code that I used in a Rails project at work. (Note: We're using devise to handle user authentication, and devise_invitable to create new users.)
PASSWORD_FORMAT = /\A
(?=.{8,}) # Must contain 8 or more characters
(?=.*\d) # Must contain a digit
(?=.*[a-z]) # Must contain a lower case character
(?=.*[A-Z]) # Must contain an upper case character
(?=.*[[:^alnum:]]) # Must contain a symbol
/x
validates :password,
presence: true,
length: { in: Devise.password_length },
format: { with: PASSWORD_FORMAT },
confirmation: true,
on: :create
validates :password,
allow_nil: true,
length: { in: Devise.password_length },
format: { with: PASSWORD_FORMAT },
confirmation: true,
on: :update
The below seem to meet my requirements...I am actually now requiring a confirmation for all users.. (It makes the view cleaner). But on an update I am allowing blanks.
validates :password, :presence => true,
:confirmation => true,
:length => {:within => 6..40},
:on => :create
validates :password, :confirmation => true,
:length => {:within => 6..40},
:allow_blank => true,
:on => :update
this works for blank password on update action:
validates :password, :presence => true, :on => :update,
:if => lambda{ !password.nil? }
validates :password,
:confirmation => true,
:length => { :minimum => 6},
:if => lambda{ new_record? || !password.nil? }
yet another variant
validates_presence_of :password_digest
validates_length_of :password, minimum: 6, if: Proc.new { |user| user.password.present? }

Resources