1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Disallow all non-numerics as Duration.build input, Fixes #37012

Prevent `ActiveSupport::Duration.build(value)` from creating instances of
    `ActiveSupport::Duration` unless `value` is of type `Numeric`.

    Addresses the errant set of behaviours described in #37012 where
    `ActiveSupport::Duration` comparisons would fail confusingly
    or return unexpected results when comparing durations built from instances of `String`.

    Before:

        small_duration_from_string = ActiveSupport::Duration.build('9')
        large_duration_from_string = ActiveSupport::Duration.build('100000000000000')
        small_duration_from_int = ActiveSupport::Duration.build(9)

        large_duration_from_string > small_duration_from_string
            => false

        small_duration_from_string == small_duration_from_int
            => false

        small_duration_from_int < large_duration_from_string
            => ArgumentError (comparison of ActiveSupport::Duration::Scalar
                    with ActiveSupport::Duration failed)

        large_duration_from_string > small_duration_from_int
            => ArgumentError (comparison of String with ActiveSupport::Duration failed)

    After:

        small_duration_from_string = ActiveSupport::Duration.build('9')
            => TypeError (can't build an `ActiveSupport::Duration` from a `String`)
This commit is contained in:
Alexei Emam 2019-08-22 16:11:42 +01:00
parent 1f325fab01
commit 1a48d50363
3 changed files with 53 additions and 0 deletions

View file

@ -1,3 +1,36 @@
* Prevent `ActiveSupport::Duration.build(value)` from creating instances of
`ActiveSupport::Duration` unless `value` is of type `Numeric`.
Addresses the errant set of behaviours described in #37012 where
`ActiveSupport::Duration` comparisons would fail confusingly
or return unexpected results when comparing durations built from instances of `String`.
Before:
small_duration_from_string = ActiveSupport::Duration.build('9')
large_duration_from_string = ActiveSupport::Duration.build('100000000000000')
small_duration_from_int = ActiveSupport::Duration.build(9)
large_duration_from_string > small_duration_from_string
=> false
small_duration_from_string == small_duration_from_int
=> false
small_duration_from_int < large_duration_from_string
=> ArgumentError (comparison of ActiveSupport::Duration::Scalar
with ActiveSupport::Duration failed)
large_duration_from_string > small_duration_from_int
=> ArgumentError (comparison of String with ActiveSupport::Duration failed)
After:
small_duration_from_string = ActiveSupport::Duration.build('9')
=> TypeError (can't build an ActiveSupport::Duration from a String)
*Alexei Emam*
* Add `ActiveSupport::Cache::Store#delete_multi` method to delete multiple keys from the cache store.
*Peter Zhu*

View file

@ -181,6 +181,10 @@ module ActiveSupport
# ActiveSupport::Duration.build(2716146).parts # => {:months=>1, :days=>1}
#
def build(value)
unless value.is_a?(::Numeric)
raise TypeError, "can't build an #{self.name} from a #{value.class.name}"
end
parts = {}
remainder = value.to_f

View file

@ -661,6 +661,22 @@ class DurationTest < ActiveSupport::TestCase
assert_equal 660, (d1 + 60).to_i
end
def test_string_build_raises_error
error = assert_raises(TypeError) do
ActiveSupport::Duration.build("9")
end
assert_equal "can't build an ActiveSupport::Duration from a String", error.message
end
def test_non_numeric_build_raises_error
error = assert_raises(TypeError) do
ActiveSupport::Duration.build(nil)
end
assert_equal "can't build an ActiveSupport::Duration from a NilClass", error.message
end
private
def eastern_time_zone
if Gem.win_platform?