diff options
Diffstat (limited to 'activesupport/lib/active_support/duration.rb')
-rw-r--r-- | activesupport/lib/active_support/duration.rb | 111 |
1 files changed, 109 insertions, 2 deletions
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index 003f6203ef..99080e34a1 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -1,5 +1,8 @@ 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" module ActiveSupport # Provides accurate date and time measurements using Date#advance and @@ -7,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 @@ -88,6 +151,24 @@ module ActiveSupport @parts.default = 0 end + def coerce(other) #:nodoc: + if Scalar === other + [other, self] + else + [Scalar.new(other), self] + end + end + + # Compares one Duration with another or a Numeric to this Duration. + # Numeric values are treated as seconds. + def <=>(other) + if Duration === other + value <=> other.value + elsif Numeric === other + value <=> other + end + end + # Adds another Duration or a Numeric to this Duration. Numeric values # are treated as seconds. def +(other) @@ -109,6 +190,28 @@ module ActiveSupport self + (-other) end + # Multiplies this Duration by a Numeric and returns a new Duration. + def *(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 + raise_type_error(other) + end + end + + # Divides this Duration by a Numeric and returns a new Duration. + def /(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 + raise_type_error(other) + end + end + def -@ #:nodoc: Duration.new(-value, parts.map { |type, number| [type, -number] }) end @@ -180,6 +283,7 @@ module ActiveSupport sum(1, time) end alias :from_now :since + alias :after :since # Calculates a new Time or Date that is as far in the past # as this Duration represents. @@ -187,6 +291,7 @@ module ActiveSupport sum(-1, time) end alias :until :ago + alias :before :ago def inspect #:nodoc: parts. @@ -210,8 +315,6 @@ module ActiveSupport ISO8601Serializer.new(self, precision: precision).serialize end - delegate :<=>, to: :value - private def sum(sign, time = ::Time.current) @@ -235,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 |