Add support for timezones to Active Job

Record what was the current timezone in effect when the job was
enqueued and then restore when the job is executed in same way
that the current locale is recorded and restored.
This commit is contained in:
Andrew White 2018-02-22 14:14:42 +00:00
parent 9c0c90979a
commit a9d1167b1f
10 changed files with 103 additions and 1 deletions

View File

@ -1,5 +1,13 @@
## Rails 6.0.0.alpha (Unreleased) ##
* Add support for timezones to Active Job
Record what was the current timezone in effect when the job was
enqueued and then restore when the job is executed in same way
that the current locale is recorded and restored.
*Andrew White*
* Rails 6 requires Ruby 2.4.1 or newer.
*Jeremy Daer*

View File

@ -9,6 +9,7 @@ require "active_job/execution"
require "active_job/callbacks"
require "active_job/exceptions"
require "active_job/logging"
require "active_job/timezones"
require "active_job/translation"
module ActiveJob #:nodoc:
@ -68,6 +69,7 @@ module ActiveJob #:nodoc:
include Callbacks
include Exceptions
include Logging
include Timezones
include Translation
ActiveSupport.run_load_hooks(:active_job, self)

View File

@ -31,6 +31,9 @@ module ActiveJob
# I18n.locale to be used during the job.
attr_accessor :locale
# Timezone to be used during the job.
attr_accessor :timezone
end
# These methods will be included into any Active Job object, adding
@ -87,7 +90,8 @@ module ActiveJob
"priority" => priority,
"arguments" => serialize_arguments(arguments),
"executions" => executions,
"locale" => I18n.locale.to_s
"locale" => I18n.locale.to_s,
"timezone" => Time.zone.try(:name)
}
end
@ -125,6 +129,7 @@ module ActiveJob
self.serialized_arguments = job_data["arguments"]
self.executions = job_data["executions"]
self.locale = job_data["locale"] || I18n.locale.to_s
self.timezone = job_data["timezone"] || Time.zone.try(:name)
end
private

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
module ActiveJob
module Timezones #:nodoc:
extend ActiveSupport::Concern
included do
around_perform do |job, block, _|
Time.use_zone(job.timezone, &block)
end
end
end
end

View File

@ -54,4 +54,11 @@ class JobSerializationTest < ActiveSupport::TestCase
job.provider_job_id = "some value set by adapter"
assert_equal job.provider_job_id, job.serialize["provider_job_id"]
end
test "serialize stores the current timezone" do
Time.use_zone "Hawaii" do
job = HelloJob.new
assert_equal "Hawaii", job.serialize["timezone"]
end
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require "helper"
require "jobs/timezone_dependent_job"
class TimezonesTest < ActiveSupport::TestCase
setup do
JobBuffer.clear
end
test "it performs the job in the given timezone" do
job = TimezoneDependentJob.new("2018-01-01T00:00:00Z")
job.timezone = "London"
job.perform_now
assert_equal "Happy New Year!", JobBuffer.last_value
job = TimezoneDependentJob.new("2018-01-01T00:00:00Z")
job.timezone = "Eastern Time (US & Canada)"
job.perform_now
assert_equal "Just 5 hours to go", JobBuffer.last_value
end
end

View File

@ -110,6 +110,22 @@ class QueuingTest < ActiveSupport::TestCase
end
end
test "current timezone is kept while running perform_later" do
skip if adapter_is?(:inline)
begin
current_zone = Time.zone
Time.zone = "Hawaii"
TestJob.perform_later @id
wait_for_jobs_to_finish_for(5.seconds)
assert job_executed
assert_equal "Hawaii", job_executed_in_timezone
ensure
Time.zone = current_zone
end
end
test "should run job with higher priority first" do
skip unless adapter_is?(:delayed_job, :que)

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
require_relative "../support/job_buffer"
class TimezoneDependentJob < ActiveJob::Base
def perform(now)
now = now.in_time_zone
new_year = localtime(2018, 1, 1)
if now >= new_year
JobBuffer.add("Happy New Year!")
else
JobBuffer.add("Just #{(new_year - now).div(3600)} hours to go")
end
end
private
def localtime(*args)
Time.zone ? Time.zone.local(*args) : Time.utc(*args)
end
end

View File

@ -21,6 +21,7 @@ class TestJob < ActiveJob::Base
File.open(Rails.root.join("tmp/\#{x}.new"), "wb+") do |f|
f.write Marshal.dump({
"locale" => I18n.locale.to_s || "en",
"timezone" => Time.zone.try(:name) || "UTC",
"executed_at" => Time.now.to_r
})
end

View File

@ -62,4 +62,8 @@ module TestCaseHelpers
def job_executed_in_locale(id = @id)
job_data(id)["locale"]
end
def job_executed_in_timezone(id = @id)
job_data(id)["timezone"]
end
end