diff options
author | Andrew White <andrew.white@unboxed.co> | 2017-03-15 14:38:21 +0000 |
---|---|---|
committer | Andrew White <andrew.white@unboxed.co> | 2017-03-15 14:56:27 +0000 |
commit | a91ea1d51048342d13fc73f9b09ce4cfd086bb34 (patch) | |
tree | ff81c34c965633f128f08fede00dd43b038ba0b3 /activesupport/lib | |
parent | e79a991ccfdf3b7b56562be35b996e61d7395861 (diff) | |
download | rails-a91ea1d51048342d13fc73f9b09ce4cfd086bb34.tar.gz rails-a91ea1d51048342d13fc73f9b09ce4cfd086bb34.tar.bz2 rails-a91ea1d51048342d13fc73f9b09ce4cfd086bb34.zip |
Remove implicit coercion deprecation of durations
In #28204 we deprecated implicit conversion of durations to a
numeric which represented the number of seconds in the duration
because of unwanted side effects with calculations on durations
and dates. This unfortunately had the side effect of forcing a
explicit cast when configuring third-party libraries like
expiration in Redis, e.g:
redis.expire("foo", 5.minutes)
To work around this we've removed the deprecation and added a
private class that wraps the numeric and can perform calculation
involving durations and ensure that they remain a duration
irrespective of the order of operations.
Diffstat (limited to 'activesupport/lib')
-rw-r--r-- | activesupport/lib/active_support/duration.rb | 88 |
1 files changed, 78 insertions, 10 deletions
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index d26bbac511..99080e34a1 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -1,4 +1,5 @@ require "active_support/core_ext/array/conversions" +require "active_support/core_ext/module/delegation" require "active_support/core_ext/object/acts_like" require "active_support/core_ext/string/filters" require "active_support/deprecation" @@ -9,6 +10,66 @@ module ActiveSupport # # 1.month.ago # equivalent to Time.now.advance(months: -1) class Duration + class Scalar < Numeric #:nodoc: + attr_reader :value + delegate :to_i, :to_f, :to_s, to: :value + + def initialize(value) + @value = value + end + + def coerce(other) + [Scalar.new(other), self] + end + + def -@ + Scalar.new(-value) + end + + def <=>(other) + if Scalar === other || Duration === other + value <=> other.value + elsif Numeric === other + value <=> other + else + nil + end + end + + def +(other) + calculate(:+, other) + end + + def -(other) + calculate(:-, other) + end + + def *(other) + calculate(:*, other) + end + + def /(other) + calculate(:/, other) + end + + private + def calculate(op, other) + if Scalar === other + Scalar.new(value.public_send(op, other.value)) + elsif Duration === other + Duration.seconds(value).public_send(op, other) + elsif Numeric === other + Scalar.new(value.public_send(op, other)) + else + raise_type_error(other) + end + end + + def raise_type_error(other) + raise TypeError, "no implicit conversion of #{other.class} into #{self.class}" + end + end + SECONDS_PER_MINUTE = 60 SECONDS_PER_HOUR = 3600 SECONDS_PER_DAY = 86400 @@ -91,12 +152,11 @@ module ActiveSupport end def coerce(other) #:nodoc: - ActiveSupport::Deprecation.warn(<<-MSG.squish) - Implicit coercion of ActiveSupport::Duration to a Numeric - is deprecated and will raise a TypeError in Rails 5.2. - MSG - - [other, value] + if Scalar === other + [other, self] + else + [Scalar.new(other), self] + end end # Compares one Duration with another or a Numeric to this Duration. @@ -132,19 +192,23 @@ module ActiveSupport # Multiplies this Duration by a Numeric and returns a new Duration. def *(other) - if Numeric === other + if Scalar === other || Duration === other + Duration.new(value * other.value, parts.map { |type, number| [type, number * other.value] }) + elsif Numeric === other Duration.new(value * other, parts.map { |type, number| [type, number * other] }) else - value * other + raise_type_error(other) end end # Divides this Duration by a Numeric and returns a new Duration. def /(other) - if Numeric === other + if Scalar === other || Duration === other + Duration.new(value / other.value, parts.map { |type, number| [type, number / other.value] }) + elsif Numeric === other Duration.new(value / other, parts.map { |type, number| [type, number / other] }) else - value / other + raise_type_error(other) end end @@ -274,5 +338,9 @@ module ActiveSupport def method_missing(method, *args, &block) value.send(method, *args, &block) end + + def raise_type_error(other) + raise TypeError, "no implicit conversion of #{other.class} into #{self.class}" + end end end |