Fix invalid string Decimal casting under ruby 2.4

In Ruby 2.4, BigDecimal(), as used by the Decimal cast, was changed so
that it will raise ArgumentError when passed an invalid string, in order
to be more consistent with Integer(), Float(), etc. The other numeric
types use ex. to_i and to_f.

Unfortunately, we can't simply change BigDecimal() to to_d. String#to_d
raises errors like BigDecimal(), unlike all the other to_* methods (this
should probably be filed as a ruby bug).

Instead, this simulates the existing behaviour and the behaviour of the
other to_* methods by finding a numeric string at the start of the
passed in value, and parsing that using BigDecimal().

See also
https://bugs.ruby-lang.org/issues/10286
3081a627ce
This commit is contained in:
John Hawthorn 2016-12-21 15:33:37 -08:00 committed by Rafael Mendonça França
parent feea081199
commit 68926798a5
No known key found for this signature in database
GPG Key ID: FC23B6D0F1EEE948
4 changed files with 20 additions and 1 deletions

View File

@ -21,8 +21,10 @@ module ActiveModel
case value
when ::Float
convert_float_to_big_decimal(value)
when ::Numeric, ::String
when ::Numeric
BigDecimal(value, precision || BIGDECIMAL_PRECISION)
when ::String
value.to_d rescue BigDecimal(0)
else
if value.respond_to?(:to_d)
value.to_d

View File

@ -11,6 +11,14 @@ module ActiveModel
assert_equal BigDecimal.new("1"), type.cast(:"1")
end
def test_type_cast_decimal_from_invalid_string
type = Decimal.new
assert_nil type.cast("")
assert_equal BigDecimal.new("1"), type.cast("1ignore")
assert_equal BigDecimal.new("0"), type.cast("bad1")
assert_equal BigDecimal.new("0"), type.cast("bad")
end
def test_type_cast_decimal_from_float_with_large_precision
type = Decimal.new(precision: ::Float::DIG + 2)
assert_equal BigDecimal.new("123.0"), type.cast(123.0)

View File

@ -9,6 +9,14 @@ module ActiveModel
assert_equal 1.0, type.cast("1")
end
def test_type_cast_float_from_invalid_string
type = Type::Float.new
assert_nil type.cast("")
assert_equal 1.0, type.cast("1ignore")
assert_equal 0.0, type.cast("bad1")
assert_equal 0.0, type.cast("bad")
end
def test_changing_float
type = Type::Float.new

View File

@ -7,6 +7,7 @@ module ActiveModel
class IntegerTest < ActiveModel::TestCase
test "simple values" do
type = Type::Integer.new
assert_nil type.cast("")
assert_equal 1, type.cast(1)
assert_equal 1, type.cast("1")
assert_equal 1, type.cast("1ignore")