I am using PhoneLib (https://github.com/daddyz/phonelib) to validate my phone numbers.
I have a model Subscription with a column phone_number.
I'm testing this phone number: 3052612151.
I can see that Phonelib considers it to be a valid number:
> Phonelib.valid_for_country? "3052612151", :us
#=> true
However, when I use Phonelib's validator, my record is not considered to be valid.
class Subscription < ApplicationRecord
validates :email, presence: true
validates :phone_number, phone: { countries: :us }
> s = Subscription.create(phone_number: '3052612151', email: 'example#example.com')
> s.valid?
#=> false
> s.errors.full_messages
#=> ["Phone number is invalid"]
I've been scratching my head for over an hour. Can anyone see what I've done wrong? Why does Phonelib consider the number to be valid when calling #valid_for_country? but not when used as a validator?
The only thing I can think of is maybe you should set the default country in an initializer
# config/initializers/phonelib.rb
Phonelib.default_country = "US"
Then just use validates :phone_number, phone: true in your model.
Try using countries: [:us], the validator actually does a & operation and checks if the size is greater than 0, maybe it's expecting an array. https://github.com/daddyz/phonelib/blob/master/lib/validators/phone_validator.rb#L85
The validator actually checks 4 validations, it's a really bad design that all validations return the same error message, weird :S. https://github.com/daddyz/phonelib/blob/master/lib/validators/phone_validator.rb#L63
Related
I am very curious about what this expected behavior is for Rails 4.2 and I have not been able to find an answer to my question.
I'm adding validation to a model on all the time attributes. I want them to ONLY accept integers, not numerical strings. The data type in my schema for this attribute is an integer. I have my validation like so:
RANGE = 0..59
validates :start_minute, inclusion: { in: RANGE }, numericality: true
I've tried these other validations as well. I get the same result.
validates_numericality_of :start_minute, inclusion: { 0..59, only_integer: true }
validates :start_minute, inclusion: { in: 0..59 }, numericality: { only_integer: true }
When I pass my params to my controller from the request spec, start_minute is "12". BUT when I look at the created object, the start_minute is 12.
According to this article by ThoughtBot:
"This is because Active Record automatically type casts all input so that it matches the database schema. Depending on the type, this may be incredibly simple, or extremely complex."
Shouldn't the object not be able to be created? Is the typecasting taking precedence of my validation? Or is there something wrong with my validation? I appreciate any insight to this as I haven't been able to determine what is happening here. I've also created a model spec for this and I'm still able to create a new object with numerical strings.
Thank you for any insight you can give on this. I am still learning the magic of Rails under the hood.
From the rails docs it says,
If you set :only_integer to true, then it will use the
/\A[+-]?\d+\z/
What it(only_integer validator) does is that it validates that the format of value matches the regex above and a string value that contains only numbers like '12' is a match(returns a truthy value which is 0 and passes the validation).
2.3.1 :001 > '12' =~ /\A[+-]?\d+\z/
=> 0
I am trying to validate the entry a user makes in an amount field.
The field is amount_money
This field is a string which is validated on form submission
monetize :amount, :as => :amount_money
validates :amount, numericality: {only_integer: true}
validates :amount_money, numericality: {greater_than_or_equal_to: 0}
validate :amount_money_within_limit
validate :is_a_valid_number
I want to ensure there are no letters or symbols and that the amount is in an acceptable range.
the code to do this is
def amount_money_within_limit
if amount_money && amount_money.cents > 10_000_00
errors.add(:amount_money, 'cannot exceed $10,000.')
end
if amount_money && amount_money.cents < 1_00
errors.add(:amount_money, 'Problem with Amount')
end
end
this works great for input numbers, of numbers and letters, of letters, of special characters but
If I try Bob - the validation kicks in
but if I try BBob - the validation is bypassed.
If the input contains 2 Capital letters next to each other - it fails.
I've tried a downcase - but that doesn't suit as the field is monetized (money gem) - and the downcase screws up if there is valid input.
If the input to the field contains two uppercase letters - all the validations are bypassed So something like AA is not caught by any on the above validations
Why don't you use regular expressions? Something like this:
def is_a_valid_number? amount_money
amount_money =~ /\d+/
end
It seems you have put 1 validation on the wrong field, you should've put validations only on amount field (your real db field), and not on the amount_money which is automagical field from rails-money gem. I'll apply their documentation on numerical validations to your case:
monetize :amount,
:numericality => {
:only_integer => true,
:greater_than_or_equal_to => 1_00,
:less_than_or_equal_to => 10_000_00
}
You won't need any other custom validations with this setup.
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.
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.
I want to use the validates method on one of my text fields in a model so that the end user must enter at least 25 lines (separated with an enter key). So for now i validate the presence of the input so it won't be blank:
validates :lines, :presence => true
This must use some regex to check for maybe the presence of at least 24 '\n' ?
How can i accomplish that in my model?
EDIT: values need to be utf8
Maybe you have to do it with validate method:
validate do
errors.add(:lines, "must be at least 25 lines") if lines.lines.to_a.size < 25
end
If you really need to do it with regexp, try something like
/([^\n]*\n[^\n]*){24,}/
but it counts empty lines as well.