First, let me say I am extremely new to Rails (toyed with it a time or two but forcing myself to write a complete project with it now, started on it yesterday).
I am now trying to validate that a model property (terminology?) is greater than another. This appeared to be a perfect instance for validates_numericality_of
with the greater_than
option, but alas that throws an error telling me greater_than expects a number, not a symbol
. If I try to typecast that symbol .to_f
I get an undefined method
error.
Here is what I eventually did and I am curious as to whether there is a better way. It's just a simple system to control project releases, we only have major/minor releases (one-dot) so float felt like the right decision here.
class Project < ActiveRecord::Base
validates_numericality_of :current_release
validates_numericality_of :next_release
validate :next_release_is_greater
def next_release_is_greater
errors.add_to_base("Next release must be greater than current release") unless next_release.to_f > current_release.to_f
end
end
This works - it passes the relevant unit test (below for your viewing pleasure), I'm just curious as to if there is an easier way - something I could have tried otherwise.
Relevant unit test:
# Fixture data:
# PALS:
# name: PALS
# description: This is the PALS project
# current_release: 1.0
# next_release: 2.0
# project_category: 1
# user: 1
def test_release_is_future
project = Project.first(:conditions => {:name => 'PALS'})
project.current_release = 10.0
assert !project.save
project.current_release = 1.0
assert project.save
end
As you noticed, the only way is to use a custom validator. The :greater_than option should be an integer. The following code won't work because both current and next release are available only at instance-level.
class Project < ActiveRecord::Base
validates_numericality_of :current_release
validates_numericality_of :next_release, :greater_than => :current_release
end
The purpose of the greater_than
option is to validate the value against a static constant or an other class method.
So, don't mind and go ahead with your custom validator. :)
validates_numericality_of
accepts a large list of options and some of them can be supplied with a proc or a symbol(this means you can basically pass an attribute or an entire method).
to validate the numericality of a property is higher than another value:
class Project < ActiveRecord::Base
validates_numericality_of :current_release, less_than: ->(project) { project.next_release }
validates_numericality_of :next_release,
greater_than: Proc.new { project.current_release }
end
To clarify, any of these options can accept a proc or symbol:
:greater_than
:greater_than_or_equal_to
:equal_to :less_than
:less_than_or_equal_to
validates_numericality docs: http://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html#method-i-validates_numericality_of
using procs with validations: http://guides.rubyonrails.org/active_record_validations.html#using-a-proc-with-if-and-unless
With Rails 3.2, you can validate two fields against each other on the fly by passing in a proc.
validates_numericality_of :next_release, :greater_than => Proc.new {|project| project.current_release }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With