Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validating a Model Property is Greater than Another

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
like image 315
Michael Wales Avatar asked Dec 22 '09 13:12

Michael Wales


3 Answers

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. :)

like image 188
Simone Carletti Avatar answered Oct 23 '22 22:10

Simone Carletti


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

like image 34
Blair Anderson Avatar answered Oct 23 '22 23:10

Blair Anderson


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 }
like image 9
dignoe Avatar answered Oct 23 '22 22:10

dignoe