Is it a good idea to having validates like this:
validates :serialnumber, presence: true, length: {7..20}, format: {with: /\d{7,20/}
As you see it generates three errors if I don't type serialnumber.
I would like to see only one error.
If I type nothing, I would like to see 'serial number is required' only.
If I type 123ABC I would like to see 'wrong length' only
And if I type 123-ABC-123 I would like to see 'wrong format' only
How to do it?
Regards
You could split it into 2 validators, check if this would work
validates :serialnumber, presence: true
validates :serialnumber, length: {7..20}, format: { with: /\d{7,20}/ }, allow_blank: true
As I understand then you want to see only one error message at a time. If that's the case then custom validation method might help. For ex.
validate :serial_number_validation_one_by_one
private
def serial_number_validation_one_by_one
if !self.serial_number.present?
errors.add(:serial_number, "must be present!")
elsif self.serial_number.length < 7 || self.serial_number.length > 20
errors.add(:serial_number, "must have length between 7 and 20!")
elsif self.serial_number.match(<your regex pattern here>)
errors.add(:serial_number, "must be properly formatted!")
end
end
Keep in mind that custom validation methods are called by validate not by validates.
Related
I have a Rails ActiveModel with two fields date_from and date_to and I want the model to be valid when (and only when)
either of these fields or both are blank
date_from < date_to
In other words, the model should be invalid only when both fields are set but they're in the wrong order. In that case I also want both fields to be marked as invalid.
I tried with
validates :date_from, comparison: { less_than_or_equal_to: :date_to }, allow_blank: true
validates :date_to, comparison: { greater_than_or_equal_to: :date_from }, allow_blank: true
But that fails when exactly one of the fields is set with
#<ActiveModel::Error attribute=date_to, type=comparison of Date with nil failed, options={}>
How can I make the comparison validation pass when the referenced field is blank?
It can be done with two separate validates calls by adding a conditional check with if option
validates :date_from,
comparison: { less_than_or_equal_to: :date_to },
allow_blank: true,
if: :date_to # make sure `date_to` is not `nil`
validates :date_to,
comparison: { greater_than_or_equal_to: :date_from },
allow_blank: true,
if: :date_from
This will skip these validations if one of the dates is nil. When both dates are present it runs both validations and adds two separate errors, which may be not quite right, since it is essentially one error.
To make the intent of this validation more obvious, a validate method is a better fit
validate :date_from_is_less_than_date_to
def date_from_is_less_than_date_to
return unless date_from && date_to # valid if date(s) are missing
unless date_from < date_to
errors.add(:base, 'Date range is invalid')
# NOTE: to add errors to show on form fields
# errors.add(:date_from, 'must come before Date to')
# errors.add(:date_to, 'must come after Date from')
# NOTE: to add errors only for date that changed
# requires ActiveModel::Dirty
# errors.add(:date_from, 'must come before Date to') if date_from_changed?
# errors.add(:date_to, 'must come after Date from') if date_to_changed?
end
end
I want to validate an input box value in two cases:
If nil? then save successfully, no errors
If not nil? then validate its format
I have a simple line here:
validates :ip_addr, format: { with: Regexp.union(Resolv::IPv4::Regex)}
This will work in all cases but won't allow a nil/empty value as it throws an exception. But:
validates :ip_addr, format: { with: Regexp.union(Resolv::IPv4::Regex)}, allow_nil: true
and
validates :ip_addr, format: { with: Regexp.union(Resolv::IPv4::Regex)}, allow_blank: true
Will allow nil/empty values but If the input is invalid e.g. "33####$" then it returns "true".
How could I include both cases ? Is that possible ?
EDIT: It seems that Regexp.union(Resolv::IPv4::Regex).match("something") returns nil so If the validation works the same way, it will return nil in wrong values and allow_nil: true will allow them to be persisted like this.
Try this
validates :ip_addr, format: { with: Regexp.union(Resolv::IPv4::Regex)}, if: :ip_addr
I am not sure whether the above one will work. If it doesn't, do this
validate :ip_addr_format_check, if: :ip_addr
def ip_addr_format_check
unless ip_addr =~ Regexp.union(Resolv::IPv4::Regex)
errors.add(:base, "Error you want to show")
end
end
Typecasting seems to be the problem.
The solution below works fine:
validates :ip_addr, format: { with: Resolv::IPv4::Regex },
presence: false, unless: Proc.new { |ifc| ifc.ip_addr_before_type_cast.blank? }
Most probably we need to check If the variable's value is blank before casting it to inet (postgreSQL).
I have an array of errors as below:
n.errors.full_messages
# => ["Password can't be blank", "Password can't be blank", "Password is too short (minimum is 3 characters)"]
I would like to iterate over the unique elements of this array and display them on the screen.
Validation model
class Member < ActiveRecord::Base
has_one :profile
before_save { self.username = username.downcase }
has_secure_password
validates :password, presence: true, length: { minimum: 3 }
end
n.errors.full_messages.uniq do |a|
puts a
end
uniq is not working, and "Password can't be blank" appears twice in my array. Any ideas?
Password can't be blank
Password can't be blank
Password is too short (minimum is 3 characters)
# => ["Password can't be blank"]
Edit: This doesn't actually answer the question, use the answer from #BroiSatse if google brought you here.
The reason you are getting that message twice is because of has_secure_password:
http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password
The first bullet point appears to add validation that makes the password required.
In your model you also add
validates :password, presence: true, length: { minimum: 3 }
The presence: true part of this adds the same validation. Remove this so its:
validates :password, length: { minimum: 3 }
This should fix the problem.
uniqe with a block uses value of the block to compare the elements. What you want is:
n.errors.full_messages.uniq.each do |m|
Anyhow, I think you're approaching the problem from the wrong angle - your problem is not how to avoid displaying duplicate messages, but how this duplication happened.
You could just do a string comparison.
Have the array that is problematic to you. Go over it and save the elements one by one to a new array.
Now when the first one is added to the new array, check that the elements allready in the array don't match the new element to be added. E.g: "My string".eql? "My sting"
There might be of course a more clever way to do this in Ruby. But that would be the idea when doing it in other languages manually.
I'm currently facing 2 problems using custom validation on Rails 4. First problem, how can I make the following code more generic and efficient (if it's possible) ?
validates :p1, presence: true, numericality: { only_integer: false }
validate :p1_is_greater_than_p2_and_p3
validate :p2_between_p1_and_p3
validate :p3_is_less_than_p2_and_p1
def p1_is_greater_than_p2_and_p3
if self.p1.present?
errors.add(:p1, 'p1 must be greater than everything') unless
(self.p1 > self.p2) && (self.p1 > self.p3)
end
true
end
def p2_between_p1_and_p3
if self.p3.present?
errors.add(:p2, 'p2 bewteen p1 and p3') unless
self.p2.between?(self.p1, self.p3)
end
true
end
def p3_is_less_than_p2_and_p1
if self.p2.present? and self.p3.present?
errors.add(:p3, 'p3 must be inferior to eveything') unless
(self.p2 > self.p3) && (self.p1 > self.p3)
end
true
end
It's really bloated and dirty, isn't it?
Second issue, on errors.add, I can pass a symbol and an error message. However, if I don't pass any message, how can I define a custom yml key for my locales ? such as :
en:
activerecord:
errors:
models:
prices:
attributes:
custom_key_message_here: 'p1 must be greater than everything'
I want to keep this seperation of concern between locales and model. However, if I don't pass any message, it's show me is invalid. I would like something more explixit.
Thanks for your help.
From a quick look at the numericality validator, could you not just use:
validates :p1, presence: true, numericality: { greater_than: :p2 }
validates :p2, presence: true, numericality: { greater_than: :p3 }
validates :p3, presence: true
As long as p1 > p2 and p2 > p3, you shouldn't need to compare p1 and p3 directly. This is assuming all three values must be present, but you could probably adjust things to work if they're optional.
Making a simple Ruby on Rails app as practise that requires a user to sign up.
Everything works well until I implement regex validation on a 'profile_name' field
Here's my 'user' model:
validates :profile_name, presence: true,
uniqueness: true,
format: {
with: /^a-zA-Z0-9_-$/,
message: 'Must be formatted correctly.'
}
And yet the profile name 'jon' simply refuses to pass. Where could this error be coming from, other than my 'user' model?
You need to add brackets around the ranges so that the regex matches "any of the range" rather than "all of the range in order". Put a + on the end to allow it to match anything in the range more than once.
You also need to change your beginning and ending of lines to beginning and ending of strings!
validates :profile_name, presence: true,
uniqueness: true,
format: {
with: /\A[a-zA-Z0-9_-]+\z/,
message: 'Must be formatted correctly.'
}
Details:
\A # Beginning of a string (not a line!)
\z # End of a string
[...] # match anything within the brackets
+ # match the preceding element one or more times
Really useful resource for generating and checking regex: http://www.myezapp.com/apps/dev/regexp/show.ws
Try like this , It is working fine
validates :name, presence: true,
uniqueness: true,
format: {
with: /\A[a-zA-Z0-9_-$]+\z/,
message: 'Must be formatted correctly.'
}
I just tested your regular expression in Rubular with the 'jon'. There is no matching.
I am not optimized regular expression coder. But still the below regular expression works.
/^[a-zA-Z0-9_-]+$/
So try
validates :name, presence: true,
uniqueness: true,
format: {
with: /^[a-zA-Z0-9_-]+$/,
message: 'Must be formatted correctly.'
}