2008-03-31 20:05:48 -04:00
module ActiveModel
module Validations
2009-12-22 18:36:51 -05:00
class NumericalityValidator < EachValidator
2009-12-22 19:08:27 -05:00
CHECKS = { :greater_than = > :> , :greater_than_or_equal_to = > :>= ,
:equal_to = > :== , :less_than = > :< , :less_than_or_equal_to = > :< = ,
:odd = > :odd? , :even = > :even? } . freeze
2009-12-23 06:14:00 -05:00
def initialize ( options )
super ( options . reverse_merge ( :only_integer = > false , :allow_nil = > false ) )
end
2009-12-22 19:08:27 -05:00
def check_validity!
2010-01-11 17:37:48 -05:00
keys = CHECKS . keys - [ :odd , :even ]
options . slice ( * keys ) . each do | option , value |
2010-01-08 02:37:58 -05:00
next if value . is_a? ( Numeric ) || value . is_a? ( Proc ) || value . is_a? ( Symbol )
raise ArgumentError , " : #{ option } must be a number, a symbol or a proc "
2009-12-22 19:08:27 -05:00
end
end
2009-12-22 18:36:51 -05:00
def validate_each ( record , attr_name , value )
before_type_cast = " #{ attr_name } _before_type_cast "
2009-12-22 19:08:27 -05:00
raw_value = record . send ( " #{ attr_name } _before_type_cast " ) if record . respond_to? ( before_type_cast . to_sym )
raw_value || = value
2009-12-22 18:36:51 -05:00
2009-12-22 19:08:27 -05:00
return if options [ :allow_nil ] && raw_value . nil?
2009-12-22 18:36:51 -05:00
2010-04-24 13:17:14 -04:00
unless value = parse_raw_value_as_a_number ( raw_value )
2010-05-15 15:55:16 -04:00
record . errors . add ( attr_name , :not_a_number , :value = > raw_value , :default = > options [ :message ] )
2009-12-22 19:08:27 -05:00
return
2009-12-22 18:36:51 -05:00
end
2010-04-24 13:17:14 -04:00
if options [ :only_integer ]
unless value = parse_raw_value_as_an_integer ( raw_value )
2010-05-15 15:55:16 -04:00
record . errors . add ( attr_name , :not_an_integer , :value = > raw_value , :default = > options [ :message ] )
2010-04-24 13:17:14 -04:00
return
end
end
2009-12-22 18:36:51 -05:00
options . slice ( * CHECKS . keys ) . each do | option , option_value |
case option
when :odd , :even
2009-12-22 19:08:27 -05:00
unless value . to_i . send ( CHECKS [ option ] )
2010-05-15 15:55:16 -04:00
record . errors . add ( attr_name , option , :value = > value , :default = > options [ :message ] )
2009-12-22 18:36:51 -05:00
end
else
2009-12-22 19:08:27 -05:00
option_value = option_value . call ( record ) if option_value . is_a? ( Proc )
option_value = record . send ( option_value ) if option_value . is_a? ( Symbol )
unless value . send ( CHECKS [ option ] , option_value )
2010-05-15 15:55:16 -04:00
record . errors . add ( attr_name , option , :default = > options [ :message ] , :value = > value , :count = > option_value )
2009-12-22 18:36:51 -05:00
end
end
2010-04-24 13:17:14 -04:00
end
2009-12-22 18:36:51 -05:00
end
2009-12-22 19:08:27 -05:00
protected
2008-03-31 20:05:48 -04:00
2010-04-24 13:17:14 -04:00
def parse_raw_value_as_a_number ( raw_value )
2010-05-17 10:58:26 -04:00
case raw_value
when / \ A0x /
2010-04-24 13:17:14 -04:00
nil
2010-05-17 10:58:26 -04:00
else
begin
Kernel . Float ( raw_value )
rescue ArgumentError , TypeError
nil
end
2009-12-22 19:08:27 -05:00
end
end
2010-04-24 13:17:14 -04:00
def parse_raw_value_as_an_integer ( raw_value )
raw_value . to_i if raw_value . to_s =~ / \ A[+-]? \ d+ \ Z /
end
2009-12-22 19:08:27 -05:00
end
2010-05-11 06:28:42 -04:00
module HelperMethods
2008-03-31 20:05:48 -04:00
# Validates whether the value of the specified attribute is numeric by trying to convert it to
2009-03-19 19:28:59 -04:00
# a float with Kernel.Float (if <tt>only_integer</tt> is false) or applying it to the regular expression
# <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>only_integer</tt> is set to true).
2008-03-31 20:05:48 -04:00
#
# class Person < ActiveRecord::Base
# validates_numericality_of :value, :on => :create
# end
#
# Configuration options:
2008-05-09 05:38:02 -04:00
# * <tt>:message</tt> - A custom error message (default is: "is not a number").
# * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
# * <tt>:only_integer</tt> - Specifies whether the value has to be an integer, e.g. an integral value (default is +false+).
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is +false+). Notice that for fixnum and float columns empty strings are converted to +nil+.
# * <tt>:greater_than</tt> - Specifies the value must be greater than the supplied value.
# * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be greater than or equal the supplied value.
# * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied value.
# * <tt>:less_than</tt> - Specifies the value must be less than the supplied value.
# * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less than or equal the supplied value.
# * <tt>:odd</tt> - Specifies the value must be an odd number.
# * <tt>:even</tt> - Specifies the value must be an even number.
2008-05-02 09:45:23 -04:00
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
2008-03-31 20:05:48 -04:00
# method, proc or string should return or evaluate to a true or false value.
2008-05-02 09:45:23 -04:00
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
# not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
2008-03-31 20:05:48 -04:00
# method, proc or string should return or evaluate to a true or false value.
2009-08-31 14:34:46 -04:00
#
# The following checks can also be supplied with a proc or a symbol which corresponds to a method:
# * <tt>:greater_than</tt>
# * <tt>:greater_than_or_equal_to</tt>
# * <tt>:equal_to</tt>
# * <tt>:less_than</tt>
# * <tt>:less_than_or_equal_to</tt>
#
# class Person < ActiveRecord::Base
# validates_numericality_of :width, :less_than => Proc.new { |person| person.height }
# validates_numericality_of :width, :greater_than => :minimum_weight
# end
#
2008-03-31 20:05:48 -04:00
def validates_numericality_of ( * attr_names )
2010-01-07 12:44:35 -05:00
validates_with NumericalityValidator , _merge_attributes ( attr_names )
2008-03-31 20:05:48 -04:00
end
end
end
2009-06-08 21:32:08 -04:00
end