Currently I have a function to check if the birthyear is correct:
validates :birth_year, presence: true,
format: {with: /(19|20)\d{2}/i }
I also have a function that checks if the date is correct:
validate :birth_year_format
private
def birth_year_format
errors.add(:birth_year, "should be a four-digit year") unless (1900..Date.today.year).include?(birth_year.to_i)
end
Is it possible to combine the bottom method into the validates at the top instead of the two validates I have now?
You should be able to do something like this:
validates :birth_year,
presence: true,
inclusion: { in: 1900..Date.today.year },
format: {
with: /(19|20)\d{2}/i,
message: "should be a four-digit year"
}
Take a look at: http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validates
:birth_year, presence: true,
format: {
with: /(19|20)\d{2}/i
}
numericality: {
only_integer: true,
greater_than_or_equal_to: 1900,
less_than_or_equal_to: Date.today.year
}
regex
/\A(19|20)\d{2}\z/
will only only allow numbers between 1900 e 2099
\A - Start of string
\z - End of string
Related
This is my code
test 'phonenumber should be 11 digits' do
#user.phonenumber = 11
assert_not #user.valid?
end
this is my model validations, i am using the gem phonelibs
class User < ApplicationRecord
before_save { email.downcase! }
validates :name, presence: true, length: { maximum: 50 }
validates :phonenumber, phone: { possible: true, allow_blank: true }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 256 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
end
this already added the validations because it uses the Google libphonenumber and for Nigeria, it is 11
Phonelib.default_country = 'NG'
but it isnt testing for the exact digits of phone number
You've used a couple of length validations elsewhere - I'd suggest you need the same for your phonenumber field before it will fail validations for being too short.
I.E.
validates_length_of :phonenumber, is: 11
or
validates :phonenumber, length: { is: 11 }
or using a regex:
validates_format_of :phonenumber, with: /[0-9]{11}/
Docs can be found here - hope that helps!
PhoneLib has a lot of extra functionality based on countries and their phone number formats - the best approach here would be to dig properly into that and use their validations based on either a fixed or user-based location.
However, the above should work for ensuring the length of the phonenumber is 11.
Update:
The gem has a number of methods for checking validity of phone numbers, such as the following:
Phonelib.valid? '123456789'
Phonelib.valid_for_country? '123456789', 'XX'
You also need to configure the gem in config/initializers/phonelib.rb - if you could add that code to your question that would be useful.
However, to make use of this, your test could look something like:
test 'phonenumber should be 11 digits' do
#user.phonenumber = 11
assert_not Phonelib.valid?(#user.phonenumber)
end
I am trying to make sure that users have a valid graduation date within my application. Not sure if my variable for the current_year will be updated every time I want to validate a user's graduation date. Here is what I have (I am making this field optional for users):
current_year = Time.now.year
if !(:grad_year.nil?)
validates :grad_year, numericality: { greater_than_or_equal_to: current_year, less_than_or_equal_to: current_year + 5 }
end
validates :grad_year, numericality: { greater_than_or_equal_to: Date.today.year, less_than_or_equal_to: Date.today.year + 5 }, allow_nil: true
Actually, it is not correct to use Date.today.year for validation because the current year will be hardcoded on Rails start and won't update next year (until you restart Rails).
Use Lambda instead:
validates :grad_year, numericality: { greater_than_or_equal_to: -> { Date.today.year }, less_than_or_equal_to: -> { Date.today.year + 5 } }, allow_nil: true
In one of my rails models I have this :only_integer validation:
validates :number, presence: true, numericality: { only_integer: true }
This validation also allows inputs like +82938434 with +-signs.
Which validation should I use to only allow inputs without + - only numbers?
The documentation for only_integer mentions this regex :
/\A[+-]?\d+\z/
It means you could just use:
validates :number, format: { with: /\A\d+\z/, message: "Integer only. No sign allowed." }
Rails 7 added :only_numeric option to numericality validator
validates :age, numericality: { only_numeric: true }
User.create(age: "30") # failure
User.create(age: 30) # success
I have a model and adding some validations
This is what I originally had:
validates :speed,
allow_blank: true,
numericality: { only_integer: true, greater_than: 0 }
But I keep getting errors when importing items from my CSV file stating that
Speed must be an integer
I then changed it to:
validates :speed,
numericality: { only_integer: true, greater_than: 0 }, unless: "speed.nil?"
But I get the same errors here too.
Basically I want it to validate that speed is numeric and greater than 1 unless no speed is passed in and to allow that blank value.
Any ideas?
CSV Importer:
def self.import_from_csv(file)
Coaster.destroy_all
csv_file = CSV.parse(
File.read(
file.tempfile,
{encoding: 'UTF-8'}
),
headers: true,
header_converters: :symbol
)
csv_file.each do |row|
coaster_name = row[:name]
# No need to keep track of coasters already in the database as the CSV only lists each coaster once unlike parks
# create the new coaster
park = Park.find_by_name_and_location_1(row[:park], row[:location_1])
manufacturer = Manufacturer.find_by_name(row[:manufacturer])
coaster = Coaster.create!({
name: row[:name],
height: row[:height],
speed: row[:speed],
length: row[:length],
inversions: row[:inversions] == nil ? 0 : row[:inversions],
material: (row[:material].downcase if row[:material]),
lat: row[:coaster_lat],
lng: row[:coaster_lng],
park_id: park.id,
notes: row[:notes],
powered: row[:powered],
manufacturer_id: (manufacturer.id if manufacturer),
covering: row[:covering],
ride_style: row[:ride_style],
model: row[:model],
layout: row[:layout],
dates_ridden: row[:dates_ridden],
times_ridden: row[:times_ridden],
order: row[:order],
on_ride_photo: row[:on_ride_photo] == 1 ? true : false,
powered: row[:powered] == 1 ? true : false
})
ap "Created #{row[:name]} at #{row[:park]}"
end
end
I think the value for speed from csv is interpreted as string. You may use .to_i with that specific value that you are using for speed. Change your code like this:
park = Park.find_by_name_and_location_1(row[:park], row[:location_1])
manufacturer = Manufacturer.find_by_name(row[:manufacturer])
row_speed = row[:speed].blank? ? nil : row[:speed].to_i
coaster = Coaster.create!({
.....
speed: row_speed,
.....
})
And then in validation:
validates :speed, numericality: { only_integer: true, greater_than: 0 }, allow_nil: true
Validations accept an :allow_nil argument, as noted here in the Rails guides: http://edgeguides.rubyonrails.org/active_record_validations.html#allow-nil
If the attribute is nil when :allow_nil is true, that particular validation will only run if the attribute in question is present.
I think, validates numericality accepts allow_nil attribute. Try this:
validates :speed, numericality: { only_integer: true, greater_than: 0 }, allow_nil: true
Short of extracting shipping and billing addresses into an Address model, how can I remove this validation duplication?
I only want to validate the billing address if it's not the same as the shipping address. How would I go about extracting it into a module? An example would be really helpful as I never know what to include in modules, or self refers to.
validates :shipping_name, :shipping_address1, :shipping_street_number, :shipping_city, presence: true
validates :shipping_state, inclusion: { in: Address.states.values }
validates :shipping_post_code, length: { is: 5 }, numericality: { only_integer: true }
validates :billing_name, :billing_address1, :billing_street_number, :billing_city, presence: true, unless: -> { self.bill_to_shipping_address? }
validates :billing_state, inclusion: { in: Address.states.values }, unless: -> { self.bill_to_shipping_address? }
validates :billing_post_code, length: { is: 5 }, numericality: { only_integer: true }, unless: -> { self.bill_to_shipping_address? }
You can make a method and then pass in the bits that are different between the two types of addresses. In this case, the difference is the prefix word for the fields and the ability to pass in extra options.
module AddressValidator
def validates_address(type, options = {})
validates :"#{type}_name", :"#{type}_address1", :"#{type}_street_number", :"#{type}_city", {presence: true}.merge(options)
validates :"#{type}_state", {inclusion: { in: Address.states.values }}.merge(options)
validates :"#{type}_post_code", {length: { is: 5 }, numericality: { only_integer: true }}.merge(options)
end
end
class MyModel < ActiveRecord::Base
extend AddressValidator
validates_address(:shipping)
validates_address(:billing, unless: -> { self.bill_to_shipping_address? })
end