New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prevent ActiveSupport::Duration.build(string)
comparison bug
#37013
Conversation
ActiveSupport::Duration.build(string)
comparison bug
2f1b410
to
1a41988
Compare
@eileencodes @tenderlove @pixeltrix any |
@alexeiemam my preference would be certainly for In fact had I realised that |
@pixeltrix: Would it be appropriate, then, to raise a type error on initialize if the passed value is not Numeric? |
It's arguable that we should deprecate it first but given the fact that it's broken anyway and either gives a result based on comparing two strings using
|
@pixeltrix have modified the code, now raises error on |
ActiveSupport::Duration.build(string)
comparison bugActiveSupport::Duration.build(string)
comparison bug
end | ||
|
||
assert_kind_of TypeError, string_build_response | ||
assert_equal string_build_response.message, "can't build an `ActiveSupport::Duration` from a `String`" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests could be clearer and shorter with assert_raises
:
error = assert_raises(TypeError) do
ActiveSupport::Duration.build("9")
end
assert_equal "can't build an `ActiveSupport::Duration` from a `String`", error.message
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eugeneius: Updated in f8dbaf0
Can you also add an entry to the Active Support CHANGELOG.md and squash it down to a single commit so that we don't have the redundant changes in the Rails commit history, thanks.
@@ -181,6 +181,10 @@ def years(value) #:nodoc: | |||
# 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}`" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you remove the backticks from the message please - the other message doesn't have them and core Ruby exception messages don't so I'd rather stick to that pattern (though I accept there are places elsewhere in Rails they are added).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pixeltrix: Changed in 94fa3e8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pixeltrix: squashed into b8b7e85
ActiveSupport::Duration.build("9") | ||
end | ||
|
||
assert_equal error.message, "can't build an ActiveSupport::Duration from a String" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These assertions are backwards; the expected value should be passed first. See a46b2f8.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eugeneius: changed in 94fa3e8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eugeneius: Squashed into b8b7e85
94fa3e8
to
b8b7e85
Compare
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 rails#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`)
@pixeltrix @eugeneius |
@alexeiemam thanks for your contribution! |
Summary
This change prevents
ActiveSupport::Duration
instances being created byActiveSupport::Duration.build(value)
unlessvalue
is of type::Numeric
(raisesTypeError
)This 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 strings.e.g.
Other Information