Rails greater_than model validation against model attribute - ruby-on-rails

I've got a Trip model, which among other attributes has a start_odometer and end_odometer value.
In my model, i'd like to validate that the end odometer is larger than the starting odometer.
The end odometer can also be blank because the trip may not have finished yet.
However, I can't figure out how to compare one attribute to another.
In trip.rb:
comparing against the symbol:
validates_numericality_of :end_odometer, :greater_than => :start_odometer, :allow_blank => true
gives me the error:
ArgumentError in TripsController#index
:greater_than must be a number
comparing against the variable:
validates_numericality_of :end_odometer, :greater_than => start_odometer, :allow_blank => true
NameError in TripsController#index
undefined local variable or method `start_odometer' for #

You don't necessarily need a custom validation method for this case. In fact, it's a bit overkill when you can do it with one line. jn80842's suggestion is close, but you must wrap it in a Proc or Lambda for it to work.
validates_numericality_of :end_odometer, :greater_than => Proc.new { |r| r.start_odometer }, :allow_blank => true

You'll probably need to write a custom validation method in your model for this...
validate :odometer_value_order
def odometer_value_order
if self.end_odometer && (self.start_odometer > self.end_odometer)
self.errors.add_to_base("End odometer value must be greater than start odometer value.")
end
end

You can validate against a model attribute if you use a Proc.
In rails4 you would do something like this:
class Trip < ActiveRecord::Base
validates_numericality_of :start_odometer, less_than: ->(trip) { trip.end_odometer }
validates_numericality_of :end_odometer, greater_than: ->(trip) { trip.start_odometer }
end

It's a bit kludgy, but this seems to work:
validates_numericality_of :end_odometer, :greater_than => :start_odometer.to_i, :allow_blank => true

Related

update_attributes validation fails though values are correct

I have in my model
class Account < ActiveRecord::Base
validates_length_of :amount, :in 1..255, :on => update, :if => Proc.new { |a| false if a.id == nil;a.amount.blank? }
validates_length_of :name, :in 1..255, :on => update, :if => Proc.new { |a| false if a.id == nil;a.name.blank? }, :unless => user_has_amount?
end
when I comment out the if condition, it works fine but with them validation fails which is confusing. I know that the validation should only run if the proc returns true or unless returns false
in my controller I have
#account.update_attributes({:name => "king", :amount => "1223"}
the save fails and when I check errors I get
#account.errors.details
{:name =>[{:error=>:too_short, :count=>1}], :amount =>[{:error=>:too_short, :count=>1}]}
Strong Params are not an issue because I have
def self.params
params.require(:account).permit!
end
I have no idea why it fails though the value are present and correct.
Try this the following:
class Account < ActiveRecord::Base
validates :amount, length: { in: 1..255 }, on: :update
validates :name, length: { in: 1..255 }, on: :update
end
Check your strong parameters. Your error tells you that something is wrong before you get to validation: :name =>[{:error=>:too_short, :count=>1}] This is saying that the minimum string count is 1 but that your string is not even that long. Here is the docs on that.
You can try: Account.find(1).update_attributes({:name => "king", :amount => "1223"} to see if #account is not being set properly.
You can also customize the language on your errors to help further:
validates_length_of : name, within: 1..255, too_long: 'pick a shorter name', too_short: 'pick a longer name'
The issue that was happening was for one reason. The code was inheriting from a class that had:
slef.reload
in one of the function that were being called during validations.
so every time we try to update the update failed because once that validation was hit it reloaded the original values from database and ignored new values which if old values are empty is just a head scratcher.
Thanks everyone for the help

Rails 4 - Validate Unique with Condition [duplicate]

I'm trying to create a condition in which the attribute 'one' is zero and the attribute 'two' is one, then a model is not valid. But when I make:
Model.create(:one => 1, :two => 0).valid?
The unit test returns true! Why?
validates :one, :two, :presence => true, :if => :if condition_testing?
def condition_testing?
!(one == 0 && two == 1)
end
I think you have an error in your syntax:
validates :one, :two, :presence => true, :if => :condition_testing?
def condition_testing?
!(one == 0 && two == 1)
end
There was one :if too many in there...
And if I understand correctly you want to have it only validate in case one == 0 && two == 1?
Then you condition_testing? is inverted (leave out the !())
If unsure you could try to use pry and insert a breakpoint into your condition_testing? method to see what's going on.
(Please note added ":" before condition testing)
You can validate it in one line:
validates :one, :two, :presence => true, :if => Proc.new { |a| !(a.one == 0 && a.two == 1) }
You are better off using numericality and equal to.
validates :one, :numericality => { :equal_to => 0 }
validates :two, :numericality => { :equal_to => 1 }
http://guides.rubyonrails.org/active_record_validations_callbacks.html#numericality
The problem is that you're using a presence validator with a condition that checks the values of the attributes. This is incorrect. A presence validator checks to make sure those attributes are set. What's worse, you're passing the if option (#Tigraine was correct about your syntax being wrong, by the way), which means whenever that method returns true, the presence won't be checked at all. The way you have this set up, the validator will run only when one is equal to 1 and two is equal to 0. Otherwise, no validations are run at all! I think the best option here is to write a custom validation:
validates :one_and_two
def one_and_two
errors.add(:base, "one must be 1 and two must be 0") if !(one == 0 && two == 1)
end
This will add an error to the model with the specified message if the condition returns true. (Note: I'm still not clear on what condition is valid and which is invalid, so feel free to change that last part to suit your needs.)

In ActiveRecord, validate that an attribute is present but may be empty

I'm trying to set up my model in Rails 3.2.8 such that particular values must be present, but are allowed to be the empty string. Does anyone know how to do this?
The behavior I'm looking for looks like:
#foo.value = "this is a real value"
#foo.valid? # => true
#foo.value = nil
#foo.valid? # => false
#foo.value = ""
#foo.valid? # => true
If I use
validates :foo, :presence => true
then I get what I want for 1 and 2, but not 3. And, helpfully, :allow_blank => true is ignored when validating presence.
I also tried
validates :foo, :length => { :minimum => 0 }, :allow_nil => false
Same behavior: get what I want for 1 and 2, but not 3.
I suppose I could just mark the column as NOT NULL in the database, but then I have to deal with catching the exception; I'd really rather catch this at the validation stage.
Any ideas?
I would try something like this:
validates :foo, presence: true, unless: lambda { |f| f.foo === "" }

Rails: Multiple if conditions in validation

Using: Rails 3.0.3
I have validations such as this one:
validates_numericality_of :person_weight_kg, :greater_than => 0, :message => "value_must_be_number_over_zero", :if => :bmi_calculation?, :if => :is_metric?
That I would like to validate for multiple if-conditions (such as in the example). But it seems, however, that Rails treats these statements as OR. One returns false and one returns true which makes the validation go through.
So, how do I check that this validation fulfills BOTH if-statements (bmi_calculation AND is_metric)?
Use a lambda as the if condition:
validates_numericality_of :person_weight_kg,
if: -> record { record.bmi_calculation? && record.is_metric? }

Rails: How to set different ":message" for each one of the possible errors?

I use this validation:
validates_numericality_of :price, :greater_than_or_equal_to => 0, :less_than => 1000000
How could I set a different :message for each one of the following cases ?
price < 0
price >= 1000000
Assuming you're using Rails 3, another option you have is to create a custom validator:
# You can put this in lib/better_numericality_validator.rb
class BetterNumericalityValidator < ActiveModel::EachValidator
def validate_each(record,attribute,value)
if value < 0
record.errors[attribute] << "must be greater than or equal to 0"
elsif value >= 1000000
record.errors[attribute] << "must be less than 1000000")
end
end
end
Then you can use your custom validator in your model:
# In your model.rb
validates :price, :better_numericality => true
This method is very similar to Anubhaw's answer. But pulling the logic out into the a custom validator makes it so that you can reuse the validation elsewhere easily, you can easily unit test the validator in isolation, and I personally think that validates :price, :better_numericality => true leaves your model looking cleaner than the alternative.
You can use following in model.rb:-
def validate
if self.price < 0
errors.add(:price, "custom message")
elsif self.price > 1000000
errors.add(:price, "custom message")
end
end
Thanks....
How about:
validates_numericality_of :price, :greater_than_or_equal_to => 0, :message => "Foo"
validates_numericality_of :price, :less_than => 1000000, :message => "Bar"
I've not tested it, but it should work?
Alternatively, Anubhaw's question is a good fallback.
At some point, you should probably ask yourself whether it isn't time to apply some convention over configuration.
In my opinion, an error message such as "Please enter a valid price greater than 0 and less than 1 million" (or similar) is a perfectly valid solution to the problem. It prevents you from adding unnecessary complexity to your application and allows you to move on to other (presumably more important) features.

Resources