mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Fix NumericalityValidator on object responding to to_f
:
- If you had a PORO that acted like a Numeric, the validator would work correctly because it was previously using `Kernel.Float` which is implicitely calling `to_f` on the passed argument. Since rails/rails@d126c0d , we are now using `BigDecimal` which does not implicitely call `to_f` on the argument, making the validator fail with an underlying `TypeError` exception. This patch replate the `is_decimal?` check with `Kernel.Float`. Using `Kernel.Float` as argument for the BigDecimal call has two advantages: 1. It calls `to_f` implicetely for us. 2. It's also smart enough to detect that `Kernel.Float("a")` isn't a Numeric and will raise an error. We don't need the `is_decimal?` check thanks to that. Passing `Float::DIG` as second argument to `BigDecimal` is mandatory because the precision can't be omitted when passing a Float. `Float::DIG` is what is used internally by ruby when calling `123.to_d` https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/lib/bigdecimal/util.rb#L47 - Another small issue introduced in https://github.com/rails/rails/pull/34693 would now raise a TypeError because `Regexp#===` will just return false if the passed argument isn't a string or symbol, whereas `Regexp#match?` will.
This commit is contained in:
parent
4f62e757ca
commit
f01e38509c
2 changed files with 16 additions and 8 deletions
|
@ -10,7 +10,6 @@ module ActiveModel
|
|||
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]
|
||||
|
@ -92,8 +91,8 @@ module ActiveModel
|
|||
raw_value
|
||||
elsif is_integer?(raw_value)
|
||||
raw_value.to_i
|
||||
elsif is_decimal?(raw_value) && !is_hexadecimal_literal?(raw_value)
|
||||
BigDecimal(raw_value)
|
||||
elsif !is_hexadecimal_literal?(raw_value)
|
||||
Kernel.Float(raw_value).to_d
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -101,12 +100,8 @@ module ActiveModel
|
|||
INTEGER_REGEX.match?(raw_value.to_s)
|
||||
end
|
||||
|
||||
def is_decimal?(raw_value)
|
||||
DECIMAL_REGEX.match?(raw_value.to_s)
|
||||
end
|
||||
|
||||
def is_hexadecimal_literal?(raw_value)
|
||||
/\A0[xX]/.match?(raw_value)
|
||||
/\A0[xX]/.match?(raw_value.to_s)
|
||||
end
|
||||
|
||||
def filtered_options(value)
|
||||
|
|
|
@ -281,6 +281,19 @@ class NumericalityValidationTest < ActiveModel::TestCase
|
|||
assert_predicate topic, :invalid?
|
||||
end
|
||||
|
||||
def test_validates_numericalty_with_object_acting_as_numeric
|
||||
klass = Class.new do
|
||||
def to_f
|
||||
123.54
|
||||
end
|
||||
end
|
||||
|
||||
Topic.validates_numericality_of :price
|
||||
topic = Topic.new(price: klass.new)
|
||||
|
||||
assert_predicate topic, :valid?
|
||||
end
|
||||
|
||||
def test_validates_numericality_with_invalid_args
|
||||
assert_raise(ArgumentError) { Topic.validates_numericality_of :approved, greater_than_or_equal_to: "foo" }
|
||||
assert_raise(ArgumentError) { Topic.validates_numericality_of :approved, less_than_or_equal_to: "foo" }
|
||||
|
|
Loading…
Reference in a new issue