client_side_validations and callbacks - ruby-on-rails

I have a model with fields that are validated frontend with client side validations gem.
My problem is for the below field (European VAT number for France) :
validates :VAT, length: { maximum: 15 }
validates :VAT, format: { with: /\A[a-zA-Z]{2}\d{0,13}\z/ }
Also I have this callback in order to normalize it before validation :
before_validation :normalize_vat
def normalize_vat
if self.VAT.present?
self.VAT[0]=self.VAT[0].capitalize
self.VAT[1]=self.VAT[1].capitalize
self.VAT = self.VAT.delete(' ')
end
end
This callback capitalize letters and strips the spaces that a user could have input such as :
FR 333 3333 333333
Though it seems client_side_validations is not picking the callback frontend...
I could change the regexp to match any spaced input but it wrecks a bit my validations as I can't validate length no more for example ...
Would there be a trick to fix this ?

Related

How to validate with comparison against a blank value?

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

Regex email : number + format of email in ruby

I have a field email in my database also call emails that should have this format : 123456789#pac.gmail.com where 123456789 should be numbers and a length of 9.
In my model, I tried to format this for my user with the following code :
class Email < ApplicationRecord
EMAIL_PATTERN = /^[0-9]+/#pac.gmail.com
LENGTH = 9
validates :email, presence: true, format: EMAIL_PATTERN, length: LENGTH
end
But with that I have an error unexpected tIVAR, expecting end but I don't get why should I have a END in my code as I don't start any loop or something,
Is anyone knows how to format the email in the right way and why I have this kind of error ?
There is no need to separately check the length; you can do that from within your regex. If I'm understanding correctly, you want to make sure there are 9 digits followed by #pac.gmail.com. You can do it like this:
class Email < ApplicationRecord
EMAIL_PATTERN = /\A[0-9]{9}#pac.gmail.com\z/
validates :email, presence: true, format: EMAIL_PATTERN
end

Rails: get values from length validator for a field in template

I havel a model with validators on some fields. Example:
class Notation < ActiveRecord::Base
validates :name, presence: true, uniqueness: true
validates_length_of :name, minimum: 5, maximum: 128
end
is there a way to get those values from minimum and maximum for the :name field in the template (because I want to show there the value of min and max length to the user, and want to do this dynamically, with the template reflecting the values from the model)?
In the rails console, I can do something like
Notation.validators_on(:name)
which outputs
#<ActiveRecord::Validations::PresenceValidator:0x00000005428420 #attributes=[:name], #options={}>, #<ActiveRecord::Validations::UniquenessValidator:0x0000000541b8d8 #attributes=[:name], #options={:case_sensitive=>true}, #klass=Notation (call 'Notation.connection' to establish a connection)>, #<ActiveModel::Validations::LengthValidator:0x0000000540baf0 #attributes=[:name], #options={:minimum=>5, :maximum=>128}>
If I get using the array's index
Notation.validators_on(:name)[2]
I have:
#<ActiveModel::Validations::LengthValidator:0x0000000540baf0 #attributes=[:name], #options={:minimum=>5, :maximum=>128}>
but I was wondering if there is another way, maybe passing the type of validation I'm 'queryng', because I can't rely on the order of the array.
To get the validator, you can filter:
options = Notation.validators_on(:name)
.select { |v| v.is_a? LengthValidator }
.first.options
Then you can get options[:maximum] etc.
In case there is no such validator, you might have to rescue from the code, as otherwise the first will fail.

Validating number field that has capital letters in Ruby

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.

Rails - validate 25 lines in a text box

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.

Resources