Merge pull request #32852 from gmcgibbon/fix_numericality_float_equality
Fix numericality equality validation on floats
This commit is contained in:
commit
3a3a3d607e
@ -1,3 +1,8 @@
|
||||
* Fix numericality equality validation of `BigDecimal` and `Float`
|
||||
by casting to `BigDecimal` on both ends of the validation.
|
||||
|
||||
*Gannon McGibbon*
|
||||
|
||||
* Add `#slice!` method to `ActiveModel::Errors`.
|
||||
|
||||
*Daniel López Prat*
|
||||
|
@ -9,6 +9,9 @@ class NumericalityValidator < EachValidator # :nodoc:
|
||||
|
||||
RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
|
||||
|
||||
INTEGER_REGEX = /\A[+-]?\d+\z/
|
||||
DECIMAL_REGEX = /\A[+-]?\d+\.?\d*(e|e[+-])?\d+\z/
|
||||
|
||||
def check_validity!
|
||||
keys = CHECKS.keys - [:odd, :even]
|
||||
options.slice(*keys).each do |option, value|
|
||||
@ -49,11 +52,7 @@ def validate_each(record, attr_name, value)
|
||||
return
|
||||
end
|
||||
|
||||
if raw_value.is_a?(Numeric)
|
||||
value = raw_value
|
||||
else
|
||||
value = parse_raw_value_as_a_number(raw_value)
|
||||
end
|
||||
value = parse_as_number(raw_value)
|
||||
|
||||
options.slice(*CHECKS.keys).each do |option, option_value|
|
||||
case option
|
||||
@ -69,6 +68,8 @@ def validate_each(record, attr_name, value)
|
||||
option_value = record.send(option_value)
|
||||
end
|
||||
|
||||
option_value = parse_as_number(option_value)
|
||||
|
||||
unless value.send(CHECKS[option], option_value)
|
||||
record.errors.add(attr_name, option, filtered_options(value).merge!(count: option_value))
|
||||
end
|
||||
@ -79,18 +80,29 @@ def validate_each(record, attr_name, value)
|
||||
private
|
||||
|
||||
def is_number?(raw_value)
|
||||
!parse_raw_value_as_a_number(raw_value).nil?
|
||||
!parse_as_number(raw_value).nil?
|
||||
rescue ArgumentError, TypeError
|
||||
false
|
||||
end
|
||||
|
||||
def parse_raw_value_as_a_number(raw_value)
|
||||
return raw_value.to_i if is_integer?(raw_value)
|
||||
Kernel.Float(raw_value) unless is_hexadecimal_literal?(raw_value)
|
||||
def parse_as_number(raw_value)
|
||||
if raw_value.is_a?(Float)
|
||||
raw_value.to_d
|
||||
elsif raw_value.is_a?(Numeric)
|
||||
raw_value
|
||||
elsif is_integer?(raw_value)
|
||||
raw_value.to_i
|
||||
elsif is_decimal?(raw_value) && !is_hexadecimal_literal?(raw_value)
|
||||
BigDecimal(raw_value)
|
||||
end
|
||||
end
|
||||
|
||||
def is_integer?(raw_value)
|
||||
/\A[+-]?\d+\z/ === raw_value.to_s
|
||||
INTEGER_REGEX === raw_value.to_s
|
||||
end
|
||||
|
||||
def is_decimal?(raw_value)
|
||||
DECIMAL_REGEX === raw_value.to_s
|
||||
end
|
||||
|
||||
def is_hexadecimal_literal?(raw_value)
|
||||
|
@ -289,6 +289,13 @@ def test_validates_numericality_with_invalid_args
|
||||
assert_raise(ArgumentError) { Topic.validates_numericality_of :approved, equal_to: "foo" }
|
||||
end
|
||||
|
||||
def test_validates_numericality_equality_for_float_and_big_decimal
|
||||
Topic.validates_numericality_of :approved, equal_to: BigDecimal("65.6")
|
||||
|
||||
invalid!([Float("65.5"), BigDecimal("65.7")], "must be equal to 65.6")
|
||||
valid!([Float("65.6"), BigDecimal("65.6")])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def invalid!(values, error = nil)
|
||||
|
Loading…
Reference in New Issue
Block a user