提交 8efbfba2 编写于 作者: A Andrew White 提交者: GitHub

Merge pull request #28425 from rails/remove-duration-deprecation

Remove implicit coercion deprecation of durations
......@@ -76,8 +76,8 @@ def test_should_enqueue_and_run_correctly_in_activejob
test "should enqueue a delivery with a delay" do
travel_to Time.new(2004, 11, 24, 01, 04, 44) do
assert_performed_with(job: ActionMailer::DeliveryJob, at: Time.current + 600.seconds, args: ["DelayedMailer", "test_message", "deliver_now", 1, 2, 3]) do
@mail.deliver_later wait: 600.seconds
assert_performed_with(job: ActionMailer::DeliveryJob, at: Time.current + 10.minutes, args: ["DelayedMailer", "test_message", "deliver_now", 1, 2, 3]) do
@mail.deliver_later wait: 10.minutes
end
end
end
......
* 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.
*Andrew White*
* Update `titleize` regex to allow apostrophes
In 4b685aa the regex in `titleize` was updated to not match apostrophes to
......
......@@ -156,7 +156,7 @@ def write_entry(key, entry, options)
expires_in = options[:expires_in].to_i
if expires_in > 0 && !options[:raw]
# Set the memcache expire a few minutes in the future to support race condition ttls on read
expires_in += 300
expires_in += 5.minutes
end
rescue_error_with false do
@data.send(method, key, value, expires_in, options)
......
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 @@ def initialize(value, parts) #:nodoc:
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 @@ def -(other)
# 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 @@ def sum(sign, time = ::Time.current)
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
......@@ -96,24 +96,20 @@ def test_minus
assert_instance_of ActiveSupport::Duration, 2.seconds - 1.second
assert_equal 1.second, 2.seconds - 1
assert_instance_of ActiveSupport::Duration, 2.seconds - 1
assert_equal 1.second, 2 - 1.second
assert_instance_of ActiveSupport::Duration, 2.seconds - 1
end
def test_multiply
assert_equal 7.days, 1.day * 7
assert_instance_of ActiveSupport::Duration, 1.day * 7
assert_deprecated do
assert_equal 86400, 1.day * 1.second
end
assert_equal 86400, 1.day * 1.second
end
def test_divide
assert_equal 1.day, 7.days / 7
assert_instance_of ActiveSupport::Duration, 7.days / 7
assert_deprecated do
assert_equal 1, 1.day / 1.day
end
assert_equal 1, 1.day / 1.day
end
def test_date_added_with_multiplied_duration
......@@ -121,9 +117,7 @@ def test_date_added_with_multiplied_duration
end
def test_plus_with_time
assert_deprecated do
assert_equal 1 + 1.second, 1.second + 1, "Duration + Numeric should == Numeric + Duration"
end
assert_equal 1 + 1.second, 1.second + 1, "Duration + Numeric should == Numeric + Duration"
end
def test_time_plus_duration_returns_same_time_datatype
......@@ -142,13 +136,6 @@ def test_argument_error
assert_equal 'expected a time or date, got ""', e.message, "ensure ArgumentError is not being raised by dependencies.rb"
end
def test_implicit_coercion_is_deprecated
assert_deprecated { 1 + 1.second }
assert_deprecated { 1 - 1.second }
assert_deprecated { 1 * 1.second }
assert_deprecated { 1 / 1.second }
end
def test_fractional_weeks
assert_equal((86400 * 7) * 1.5, 1.5.weeks)
assert_equal((86400 * 7) * 1.7, 1.7.weeks)
......@@ -286,20 +273,125 @@ def test_hash
def test_comparable
assert_equal(-1, (0.seconds <=> 1.second))
assert_equal(-1, (1.second <=> 1.minute))
assert_deprecated do
assert_equal(-1, (1 <=> 1.minute))
end
assert_equal(-1, (1 <=> 1.minute))
assert_equal(0, (0.seconds <=> 0.seconds))
assert_equal(0, (0.seconds <=> 0.minutes))
assert_equal(0, (1.second <=> 1.second))
assert_equal(1, (1.second <=> 0.second))
assert_equal(1, (1.minute <=> 1.second))
assert_equal(1, (61 <=> 1.minute))
end
assert_deprecated do
assert_equal(1, (61 <=> 1.minute))
def test_implicit_coercion
assert_equal 2.days, 2 * 1.day
assert_instance_of ActiveSupport::Duration, 2 * 1.day
assert_equal Time.utc(2017, 1, 3), Time.utc(2017, 1, 1) + 2 * 1.day
assert_equal Date.civil(2017, 1, 3), Date.civil(2017, 1, 1) + 2 * 1.day
end
def test_scalar_coerce
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_instance_of ActiveSupport::Duration::Scalar, 10 + scalar
assert_instance_of ActiveSupport::Duration, 10.seconds + scalar
end
def test_scalar_delegations
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_kind_of Float, scalar.to_f
assert_kind_of Integer, scalar.to_i
assert_kind_of String, scalar.to_s
end
def test_scalar_unary_minus
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_equal -10, -scalar
assert_instance_of ActiveSupport::Duration::Scalar, -scalar
end
def test_scalar_compare
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_equal 1, scalar <=> 5
assert_equal 0, scalar <=> 10
assert_equal -1, scalar <=> 15
assert_equal nil, scalar <=> "foo"
end
def test_scalar_plus
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_equal 20, 10 + scalar
assert_instance_of ActiveSupport::Duration::Scalar, 10 + scalar
assert_equal 20, scalar + 10
assert_instance_of ActiveSupport::Duration::Scalar, scalar + 10
assert_equal 20, 10.seconds + scalar
assert_instance_of ActiveSupport::Duration, 10.seconds + scalar
assert_equal 20, scalar + 10.seconds
assert_instance_of ActiveSupport::Duration, scalar + 10.seconds
exception = assert_raises(TypeError) do
scalar + "foo"
end
assert_equal "no implicit conversion of String into ActiveSupport::Duration::Scalar", exception.message
end
def test_scalar_minus
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_equal 10, 20 - scalar
assert_instance_of ActiveSupport::Duration::Scalar, 20 - scalar
assert_equal 5, scalar - 5
assert_instance_of ActiveSupport::Duration::Scalar, scalar - 5
assert_equal 10, 20.seconds - scalar
assert_instance_of ActiveSupport::Duration, 20.seconds - scalar
assert_equal 5, scalar - 5.seconds
assert_instance_of ActiveSupport::Duration, scalar - 5.seconds
exception = assert_raises(TypeError) do
scalar - "foo"
end
assert_equal "no implicit conversion of String into ActiveSupport::Duration::Scalar", exception.message
end
def test_scalar_multiply
scalar = ActiveSupport::Duration::Scalar.new(5)
assert_equal 10, 2 * scalar
assert_instance_of ActiveSupport::Duration::Scalar, 2 * scalar
assert_equal 10, scalar * 2
assert_instance_of ActiveSupport::Duration::Scalar, scalar * 2
assert_equal 10, 2.seconds * scalar
assert_instance_of ActiveSupport::Duration, 2.seconds * scalar
assert_equal 10, scalar * 2.seconds
assert_instance_of ActiveSupport::Duration, scalar * 2.seconds
exception = assert_raises(TypeError) do
scalar * "foo"
end
assert_equal "no implicit conversion of String into ActiveSupport::Duration::Scalar", exception.message
end
def test_scalar_divide
scalar = ActiveSupport::Duration::Scalar.new(10)
assert_equal 10, 100 / scalar
assert_instance_of ActiveSupport::Duration::Scalar, 100 / scalar
assert_equal 5, scalar / 2
assert_instance_of ActiveSupport::Duration::Scalar, scalar / 2
assert_equal 10, 100.seconds / scalar
assert_instance_of ActiveSupport::Duration, 2.seconds * scalar
assert_equal 5, scalar / 2.seconds
assert_instance_of ActiveSupport::Duration, scalar / 2.seconds
exception = assert_raises(TypeError) do
scalar / "foo"
end
assert_equal "no implicit conversion of String into ActiveSupport::Duration::Scalar", exception.message
end
def test_twelve_months_equals_one_year
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册