I have the following validation on a model field:
validates :invoice_date, :presence => true, :unless => Proc.new { |invoice| invoice.invoice_date.future? }
It looks pretty simple, but it doesn't work. There is no error thrown if the date is in the future. And the Proc indeed returns false in that case.
Any idea why isn't there any validation error shown?
The 'unless' condition is for deciding if the validation should run or not, not if it should succeed or fail. So your validation is essentially saying "validate the presence of invoice_date, unless invoice_date is in the future in which case don't validate its presence" (which makes no sense)
It sounds like you want two validations, presence and date fencing.
validate :invoice_date_in_past
def invoice_date_in_past
if invoice_date.future?
errors.add(:invoice_date, 'must be a date in the past')
end
end
validates :invoice_date, :presence => true
validate :is_future_invoice_date?
private
def is_future_invoice_date?
if invoice_date.future?
errors.add(:invoice_date, 'Sorry, your invoice date is in future time.')
end
end
Presence true simply ensures, invoice_date must be present.
for validating whether the date is a future date or not we have specified a custom validation method.(is_future_invoice_date?)
This method, will add error message against our invoice_date attribute if the date is of future date.
More info here: http://guides.rubyonrails.org/active_record_validations.html#custom-methods
Try like that :--
validate check_invoice_date_is_future
def check_invoice_date_is_future
if invoice_date.present?
errors.add(:invoice_date, "Should not be in future.") if invoice_date.future?
else
errors.add(:invoice_date, "can't be blank.")
end
end
Related
I have two validations:
validates :email, format: { with: /\A(.+)#(aol|gmail|office365|outlook|verizon|yahoo)\.com\Z/i }, if: Proc.new { |user| user.imap_server.blank? }
validates :email, presence: true
validates :imap_server, presence: true, if: Proc.new { |user| user.email.present? && user.email_invalid? }
def email_invalid?
self.email =~ /\A(.+)#(aol|gmail|office365|outlook|verizon|yahoo)\.com\Z/i
end
I show a user a form. It displays an email field but not imap_server field. If the value in email field does not match the specific regex, then I want to show them form again with the imap_server field present as well. If they enter a value for the imap_server field, then I no longer want to validate the regex of email field (although it must still be present).
The problem it feels like I am duplicating a validation. Both email_invalid? and the validates :email, format: ... do the same thing. How can I clean this up?
You could replace the validates :email, format: ... with
validate :email_format
def email_format
errors.add(:email, 'format invalid') if imap_server.blank? && email_invalid?
end
which is slightly more lines but lets you define the format validation in one place.
I suspect that the issue is you're trying to check the result of a validation (email_invalid?) while you're * still doing * the validations... you don't know what order the validations are going to be run (the order on the page is not something I'd trust)... so the best way to solve it is to just write all these things into a single validates method eg a quick-and-dirty way:
validates :email_or_imap_server
def email_or_imap_server
email_valid = false # for scoping
if email.present?
# note: email validation via regex is harder than you think...
# google it...
email_valid = email.match(/#{VALID_EMAIL_FORMATS}/)
if email_invalid
errors.add(:email, "email invalid format should be...")
errors.add(:imap_server, "email or imap-server must be present") unless imap_server.present?
end
else
errors.add(:imap_server, "either email or imap-server must be present") unless imap_server.present?
end
end
etc.
Note: the code above is almost certainly full of bugs and typos... don't copy/paste it almost certainly won't work and the logic doesn't exactly match that of your validations... but do something like this.
I'm writing a Redmine plugin that should check if some fields of an Issue are filled depending on values in other fields.
I've written a plugin that implements validate callback, but I don't know how to check field values which are going to be saved.
This is what I have so far:
module IssuePatch
def self.included(receiver)
receiver.class_eval do
unloadable
validate :require_comment_when_risk
protected
def require_comment_when_risk
risk_reduction = self.custom_value_for(3)
if risk_reduction.nil? || risk_reduction.value == 0
return true
end
comment2 = self.custom_value_for(4)
if comment2.nil? || comment2.value.empty?
errors.add(:comment2, "Comment2 is empty")
end
end
end
end
end
The problem here is that self.custom_value_for() returns the value already written to the DB, but not the one that is going to be written, so validation doesn't work. How do I check for the value that was passed from the web-form?
Any help will be greatly appreciated.
The nice thing about rails is that in your controller you don't have to validate anything. You are suppose to do all of this in your model. so in your model you should be doing something like
validates :value_that_you_care_about, :numericality => { :greater_than_or_equal_to => 0 }
or
validates :buyer_name, presence: true, :length => {:minimum => 4}
or
validates :delivery_location, presence: true
If any of these fail this will stop the object from being saved and if you are using rails scaffolding will actually highlight the field that is incorrect and give them and error message explaining what is wrong. You can also write your own validations such as
def enough_red_flowers inventory
if inventory.total_red_flowers-self.red_flower_quantity < 0
self.errors.add(:base, 'There are not enough Red Flowers Currently')
return false
end
inventory.total_red_flowers = inventory.total_red_flowers-self.red_flower_quantity
inventory.save
true
end
To write your own custom message just follow the example of self.errors.add(:base, 'your message')
You can find more validations here
Better way it's create custom validator
class FileValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
# some logic for validation
end
end
then in model:
validates :file, file: true
I have a form where i'm trying to validate that a field called "birthday" is not blank, and that the date is a valid date format that the Chronic gem can parse. I always get the error message "Birthday is invalid". I've been trying a simple format "10/10/2010".
How can i validate that the birthday field is of a format that chronic can parse?
User.rb model
class User < ActiveRecord::Base
validates :birthday, :presence => true
validate :birthday_is_date
validate :position, :presence => true
# validate the birthday format
def birthday_is_date
errors.add(:birthday ,Chronic.parse(birthday)) # testing to see the value of :birthday
errors.add(:birthday, "is invalid test message") if ((Chronic.parse(:birthday) rescue ArgumentError) == ArgumentError)
end
end
contacts_controller.rb
# POST /contacts/1/edit
# actually updates the users data
def update_user
#userProfile = User.find(params[:id])
respond_to do |format|
if #userProfile.update_attributes(params[:user])
format.html {
flash[:success] = "Information updated successfully"
redirect_to(profile_path)
}
else
format.html {
flash[:error] = resource.errors.full_messages
render :edit
}
end
end
end
Your birthday_is_date validation always adds an error on the first line. It should be written as follows:
def birthday_is_date
errors.add(:birthday, "is invalid") unless Chronic.parse(birthday)
end
try this
install validates_date_time gem
you can pass validation for date, for example
class Person < ActiveRecord::Base
validates_date :date_of_birth
validates_time :time_of_birth
validates_date_time :date_and_time_of_birth
end
Use :allow_nil to allow the value to be blank.
class Person < ActiveRecord::Base
validates_date :date_of_birth, :allow_nil => true
end
Source : https://github.com/smtlaissezfaire/validates_date_time
From chronic.rubyforge.org:
Chronic uses Ruby’s built in Time class for all time storage and
computation. Because of this, only times that the Time class can
handle will be properly parsed. Parsing for times outside of this
range will simply return nil. Support for a wider range of times is
planned for a future release.
Time zones other than the local one are not currently supported.
Support for other time zones is planned for a future release.
From Date validation in Ruby using the Date object:
A simple validate function
One way to test for a valid date is to try to create a Date object. If
the object is created, the date is valid, and if not, the date is
invalid. Here is a function that accepts year, month, and day, then
returns true if the date is valid and false if the date is invalid.
def test_date(yyyy, mm, dd)
begin
#mydate = Date.new(yyyy, mm, dd)
return true
rescue ArgumentError
return false
end
end
From the accepted answer of How do I validate a date in rails?:
If you're looking for a plugin solution, I'd checkout the
validates_timeliness plugin. It works like this (from the github
page):
class Person < ActiveRecord::Base
validates_date :date_of_birth, :on_or_before => lambda { Date.current }
# or
validates :date_of_birth, :timeliness => {:on_or_before => lambda { Date.current }, :type => :date}
end
The list of validation methods available are as follows:
validates_date - validate value as date
validates_time - validate value as time only i.e. '12:20pm'
validates_datetime - validate value as a full date and time
validates - use the :timeliness key and set the type in the hash.
In an multilingual application, a user can input their Chinese and English names. The user can input either or both, but must input at least one name.
class Person < ActiveRecord::Base
validates :zh_name, :presence => true
validates :en_name, :presence => true
validates :fr_name, :presence => true
end
Since the built-in :validates_presence_of method can only validate both attributes at once, is there a way to validate the presence of at least one of many attributes in rails?
Like a magical, validates_one_of :zh_name, :en_name, :fr_name
Thank you in advance,
validate :at_least_one_name
def at_least_one_name
if [self.zh_name, self.en_name, self.fr_name].reject(&:blank?).size == 0
errors[:base] << ("Please choose at least one name - any language will do.")
end
end
Taking #micapam's answer a step futher, may I suggest:
validate :has_a_name
def has_a_name
unless [zh_name?, en_name?, fr_name?].include?(true)
errors.add :base, 'You need at least one name in some language!'
end
end
just a quick shot out, you can pass a "if" or "unless" to the validator, maybe you can get it working this way. i have something like this in mind
validates :zh_name, :presence => { :if => (fr_name.blank? && en_name.blank?) }
validate :has_a_name
def has_a_name
unless [zh_name, en_name, fr_name].any?{|val| val.present? }
errors.add :base, 'You need at least one name in some language!'
end
end
Max Williams' answer is fine, but I didn't see the need to count hits when any? returns a boolean.
I have the following in my user model
attr_accessible :avatar, :email
validates_presence_of :email
has_attached_file :avatar # paperclip
validates_attachment_size :avatar,
:less_than => 1.megabyte,
:message => 'Image cannot be larger than 1MB in size',
:if => Proc.new { |imports| !imports.avatar_file_name.blank? }
in one of my controllers, I ONLY want to update and validate the avatar field without updating and validating email.
How can I do this?
for example (this won't work)
if #user.update_attributes(params[:user])
# do something...
end
I also tried with update_attribute('avatar', params[:user][:avatar]), but that would skip the validations for avatar field as well.
You could validate the attribute by hand and use update_attribute, that skips validation. If you add this to your User:
def self.valid_attribute?(attr, value)
mock = self.new(attr => value)
if mock.valid?
true
else
!mock.errors.has_key?(attr)
end
end
And then update the attribute thusly:
if(!User.valid_attribute?('avatar', params[:user][:avatar])
# Complain or whatever.
end
#user.update_attribute('avatar', params[:user][:avatar])
You should get your single attribute updated while only (manually) validating that attribute.
If you look at how Milan Novota's valid_attribute? works, you'll see that it performs the validations and then checks to see if the specific attr had issues; it doesn't matter if any of the other validations failed as valid_attribute? only looks at the validation failures for the attribute that you're interested in.
If you're going to be doing a lot of this stuff then you could add a method to User:
def update_just_this_one(attr, value)
raise "Bad #{attr}" if(!User.valid_attribute?(attr, value))
self.update_attribute(attr, value)
end
and use that to update your single attribute.
A condition?
validates_presence_of :email, :if => :email_changed?
Have you tried putting a condition on the validates_presence_of :email ?
http://ar.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M000083
Configuration options:
if - Specifies a method, proc or string to call to determine if the validation should occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The method, proc or string should return or evaluate to a true or false value.
unless - Specifies a method, proc or string to call to determine if the validation should not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The method, proc or string should return or evaluate to a true or false value.
I am assuming you need this, because you have a multi-step wizard, where you first upload the avatar and the e-mail is filled in later.
To my knowledge, with your validations as they are, I see no good working solution. Either you validate all, or you update the avatar without validations. If it would be a simple attribute, you could check if the new value passes the validation seperately, and then update the model without validations (e.g. using update_attribute).
I can suggest two possible alternative approaches:
either you make sure that the e-mail is always entered first, which I believe is not a bad solution. And then, with each save, the validation is met.
otherwise, change the validation. Why would you declare a validation on a model, if there are records in the database that do not meet the validation? That is very counter-intuitive.
So I would propose something like this:
validate :presence_of_email_after_upload_avatar
def presence_of_email_after_upload_avatar
# write some test, when the email should be present
if avatar.present?
errors.add(:email, "Email is required") unless email.present?
end
end
Hope this helps.
Here is my solution.
It keeps the same behaviour than .valid? method, witch returns true or false, and add errors on the model on witch it was called.
class MyModel < ActiveRecord::Base
def valid_attributes?(attributes)
mock = self.class.new(self.attributes)
mock.valid?
mock.errors.to_hash.select { |attribute| attributes.include? attribute }.each do |error_key, error_messages|
error_messages.each do |error_message|
self.errors.add(error_key, error_message)
end
end
self.errors.to_hash.empty?
end
end
> my_model.valid_attributes? [:first_name, :email] # => returns true if first_name and email is valid, returns false if at least one is not valid
> my_modal.errors.messages # => now contain errors of the previous validation
{'first_name' => ["can't be blank"]}