Recover precision when serializing `Time`, `TimeWithZone` and `DateTime`.
This commit is contained in:
parent
55cfdaf1c0
commit
bda10bf3a8
|
@ -1,3 +1,7 @@
|
|||
* Recover nano precision when serializing `Time`, `TimeWithZone` and `DateTime` objects.
|
||||
|
||||
*Alan Tan*
|
||||
|
||||
* Deprecate `config.active_job.return_false_on_aborted_enqueue`.
|
||||
|
||||
*Rafael Mendonça França*
|
||||
|
|
|
@ -9,6 +9,7 @@ module ActiveJob
|
|||
extend ActiveSupport::Autoload
|
||||
|
||||
autoload :ObjectSerializer
|
||||
autoload :TimeObjectSerializer
|
||||
autoload :SymbolSerializer
|
||||
autoload :DurationSerializer
|
||||
autoload :DateTimeSerializer
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
|
||||
module ActiveJob
|
||||
module Serializers
|
||||
class DateTimeSerializer < ObjectSerializer # :nodoc:
|
||||
def serialize(time)
|
||||
super("value" => time.iso8601)
|
||||
end
|
||||
|
||||
class DateTimeSerializer < TimeObjectSerializer # :nodoc:
|
||||
def deserialize(hash)
|
||||
DateTime.iso8601(hash["value"])
|
||||
end
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ActiveJob
|
||||
module Serializers
|
||||
class TimeObjectSerializer < ObjectSerializer # :nodoc:
|
||||
NANO_PRECISION = 9
|
||||
|
||||
def serialize(time)
|
||||
super("value" => time.iso8601(NANO_PRECISION))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,11 +2,7 @@
|
|||
|
||||
module ActiveJob
|
||||
module Serializers
|
||||
class TimeSerializer < ObjectSerializer # :nodoc:
|
||||
def serialize(time)
|
||||
super("value" => time.iso8601)
|
||||
end
|
||||
|
||||
class TimeSerializer < TimeObjectSerializer # :nodoc:
|
||||
def deserialize(hash)
|
||||
Time.iso8601(hash["value"])
|
||||
end
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
|
||||
module ActiveJob
|
||||
module Serializers
|
||||
class TimeWithZoneSerializer < ObjectSerializer # :nodoc:
|
||||
def serialize(time)
|
||||
super("value" => time.iso8601)
|
||||
end
|
||||
|
||||
class TimeWithZoneSerializer < TimeObjectSerializer # :nodoc:
|
||||
def deserialize(hash)
|
||||
Time.iso8601(hash["value"]).in_time_zone
|
||||
end
|
||||
|
|
|
@ -671,20 +671,6 @@ module ActiveJob
|
|||
at_range = arguments[:at] - 1..arguments[:at] + 1
|
||||
arguments[:at] = ->(at) { at_range.cover?(at) }
|
||||
end
|
||||
arguments[:args] = round_time_arguments(arguments[:args]) if arguments[:args]
|
||||
end
|
||||
end
|
||||
|
||||
def round_time_arguments(argument)
|
||||
case argument
|
||||
when Time, ActiveSupport::TimeWithZone, DateTime
|
||||
argument.change(usec: 0)
|
||||
when Hash
|
||||
argument.transform_values { |value| round_time_arguments(value) }
|
||||
when Array
|
||||
argument.map { |element| round_time_arguments(element) }
|
||||
else
|
||||
argument
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -20,16 +20,19 @@ class ArgumentSerializationTest < ActiveSupport::TestCase
|
|||
|
||||
[ nil, 1, 1.0, 1_000_000_000_000_000_000_000,
|
||||
"a", true, false, BigDecimal(5),
|
||||
:a, 1.day, Date.new(2001, 2, 3), Time.new(2002, 10, 31, 2, 2, 2, "+02:00"),
|
||||
DateTime.new(2001, 2, 3, 4, 5, 6, "+03:00"),
|
||||
ActiveSupport::TimeWithZone.new(Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone["UTC"]),
|
||||
:a,
|
||||
1.day,
|
||||
Date.new(2001, 2, 3),
|
||||
Time.new(2002, 10, 31, 2, 2, 2.123456789r, "+02:00"),
|
||||
DateTime.new(2001, 2, 3, 4, 5, 6.123456r, "+03:00"),
|
||||
ActiveSupport::TimeWithZone.new(Time.utc(1999, 12, 31, 23, 59, "59.123456789".to_r), ActiveSupport::TimeZone["UTC"]),
|
||||
[ 1, "a" ],
|
||||
{ "a" => 1 },
|
||||
ModuleArgument,
|
||||
ModuleArgument::ClassArgument,
|
||||
ClassArgument
|
||||
].each do |arg|
|
||||
test "serializes #{arg.class} - #{arg} verbatim" do
|
||||
test "serializes #{arg.class} - #{arg.inspect} verbatim" do
|
||||
assert_arguments_unchanged arg
|
||||
end
|
||||
end
|
||||
|
|
|
@ -608,6 +608,21 @@ class EnqueuedJobsTest < ActiveJob::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_assert_enqueued_with_time_and_time_precision
|
||||
time_with_zone = ActiveSupport::TimeWithZone.new(
|
||||
Time.utc(1999, 12, 31, 23, 59, "59.123456789".to_r),
|
||||
ActiveSupport::TimeZone["Tokyo"]
|
||||
)
|
||||
|
||||
time = Time.at(946702800, 1234567, :nanosecond)
|
||||
date_time = DateTime.new(2001, 2, 3, 4, 5, 6.123456, "+03:00")
|
||||
args = [{ argument1: [time_with_zone, time, date_time] }]
|
||||
|
||||
assert_enqueued_with(job: MultipleKwargsJob, args: args) do
|
||||
MultipleKwargsJob.perform_later(argument1: [time_with_zone, time, date_time])
|
||||
end
|
||||
end
|
||||
|
||||
def test_assert_enqueued_with_with_no_block_args
|
||||
assert_raise ArgumentError do
|
||||
NestedJob.set(wait_until: Date.tomorrow.noon).perform_later
|
||||
|
|
|
@ -1768,25 +1768,6 @@ class ProductTest < ActiveSupport::TestCase
|
|||
end
|
||||
```
|
||||
|
||||
### Asserting Time Arguments in Jobs
|
||||
|
||||
When serializing job arguments, `Time`, `DateTime`, and `ActiveSupport::TimeWithZone` lose microsecond precision. This means comparing deserialized time with actual time doesn't always work. To compensate for the loss of precision, `assert_enqueued_with` and `assert_performed_with` will remove microseconds from time objects in argument assertions.
|
||||
|
||||
```ruby
|
||||
require "test_helper"
|
||||
|
||||
class ProductTest < ActiveSupport::TestCase
|
||||
include ActiveJob::TestHelper
|
||||
|
||||
test "that product is reserved at a given time" do
|
||||
now = Time.now
|
||||
assert_performed_with(job: ReservationJob, args: [product, now]) do
|
||||
product.reserve(now)
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Testing Action Cable
|
||||
--------------------
|
||||
|
||||
|
|
Loading…
Reference in New Issue