Activerecord field validation one after the other - ruby-on-rails

I would like to know how do you validate a field after passing other validations, for example, I have:
validates_numericality_of :field
validates_inclusion_of :field (after validating field's numericality)
Thanks in advance.

You have to write a custom validation method for this.
This is how I would do it:
validate :custom_inclusion
private
def custom_inclusion
range = (1..100)
begin
Kernel.float(field)
rescue ArgumentError
errors.add(:field,"is not a number") and return
end
if !(range.min < field.to_i && range.max > field.to_i)
errors.add(:field,"is not between #{range.min} and #{range.max}")
end
end
where field is a model attribute you want to validate.

Related

Rails validation not failing

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

Rails: how to validate an object field's value before save?

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

Custom form Validation Rails

I am wondering if i have set up my method incorrectly as my validation attempt is being ignored halfway through the method. (i have provided a basic example, i want to get this working before adding more)
I have two variations of a form all belonging to the same object. The forms are differentiated by a column animal_type as you can see in my method
class Animal < ActiveRecord::Base
before_save :animal_form_validation
private
def animal_form_validation
if self.animal_type == 'Dog'
ap('Im validating the dog Form')
if self.name.length <= 0
errors.add(:name, "Name cannot be blank")
end
elsif self.animal_type == 'Cat'
ap('Im validating the cat form')
if self.name.length <= 0
errors.add(:name, "Name cannot be blank")
end
end
end
end
whether i am submitting a cat or dog i get the correct message in the console (using awesome print), so the method is running and knows which form im submitting, but as for the next if statement it is being ignored.
So i have an error with my syntax? or am i calling the wrong validation check on the name field ?
Thanks
Use validation instead of a before_save callback:
validate :animal_form_validation
Also, you can add conditional validation if you're checking the same condition prior to validating. Example: validate :animal_form_validation, if: name.blank?
You are calling those validation in before_save, which is after the record has been validated and is assumed to be valid. You need to run it as a validation:
validate :animal_form_validation

Pass field name as parameter to custom validation method Rails 4

I have a custom validation method:
def my_custom_validation
errors.add(specific_field, "error message") if specific_field.delete_if { |i| i.blank? }.blank?
end
The goal is to disallow parameters which contains [""] pass through validation, but I need to call this method like:
validate :my_custom_validation #and somehow pass here my field names
For example:
validate :my_custom_validation(:industry)
Since you need to validate multiple attributes this way I would recommend a custom validator like so:
class EmptyArrayValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors[attribute] << (options[:message] || "cannot be emtpy") if value.delete_if(&:blank?).empty?
end
end
Then validate as
validates :industry, empty_array: true
validates :your_other_attribute, empty_array: true
Or if you don't want to specifically create a class because it is only needed for 1 model you could include this in the model itself
validates_each :industry, :your_other_attribute, :and_one_more do |record, attr, value|
record.errors.add(attr, "cannot be emtpy") if value.delete_if(&:blank?).empty?
end
If you'd like to keep the method based validation you can use a Ruby lambda like below:
validate -> { my_custom_validation(some_model_field) }
See similar question

validate and update single attribute rails

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"]}

Resources