From 1d6a87779c73bc7d9d5f235afadc0df4a36f534f Mon Sep 17 00:00:00 2001 From: joker1007 Date: Tue, 29 Jul 2014 20:16:10 +0900 Subject: [PATCH] Fix type casting to Decimal from Float with ... When I defines large precision column at RDBMS, I assigns float value, raise ArgumentError (precision too large). --- activerecord/CHANGELOG.md | 4 ++++ .../lib/active_record/type/decimal.rb | 21 +++++++++++++++---- activerecord/test/cases/type/decimal_test.rb | 5 +++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index b679d64472..252532c768 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Fix Type casting to Decimal from Float with large precision. + + *Tomohiro Hashidate* + * No verbose backtrace by db:drop when database does not exist. Fixes #16295. diff --git a/activerecord/lib/active_record/type/decimal.rb b/activerecord/lib/active_record/type/decimal.rb index ba5d244729..d10778eeb6 100644 --- a/activerecord/lib/active_record/type/decimal.rb +++ b/activerecord/lib/active_record/type/decimal.rb @@ -14,12 +14,25 @@ module ActiveRecord private def cast_value(value) - if value.is_a?(::Numeric) || value.is_a?(::String) + case value + when ::Float + BigDecimal(value, float_precision) + when ::Numeric, ::String BigDecimal(value, precision.to_i) - elsif value.respond_to?(:to_d) - value.to_d else - cast_value(value.to_s) + if value.respond_to?(:to_d) + value.to_d + else + cast_value(value.to_s) + end + end + end + + def float_precision + if precision.to_i > ::Float::DIG + 1 + ::Float::DIG + 1 + else + precision.to_i end end end diff --git a/activerecord/test/cases/type/decimal_test.rb b/activerecord/test/cases/type/decimal_test.rb index 951cd879dd..da30de373e 100644 --- a/activerecord/test/cases/type/decimal_test.rb +++ b/activerecord/test/cases/type/decimal_test.rb @@ -10,6 +10,11 @@ module ActiveRecord assert_equal BigDecimal.new("1"), type.type_cast_from_user(:"1") 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.type_cast_from_user(123.0) + end + def test_type_cast_decimal_from_rational_with_precision type = Decimal.new(precision: 2) assert_equal BigDecimal("0.33"), type.type_cast_from_user(Rational(1, 3))