提交 3860e6b2 编写于 作者: J Johannes Opper

Fixes #20799

When `#perform_later` is called the locale isn't stored on the
queue, which results in a locale reset when the job is performed.

An example of the problem:

    I18n.locale = 'de'
    HelloJob.perform_now # german message, correct

but

    I18n.locale = 'de'
    HelloJob.perform_later # english message, incorrect

This PR attaches the current I18n.locale to every job during the
serialization process. It is then restored during deserialization
and used to perform the job with the correct locale.

It falls back to the default locale if no serialized locale is
found in order to provide backward compatibility with previously
stored jobs. It is not necessary to clear the queue for the update.
上级 e5989675
* Include I18n.locale into job serialization/deserialization and use it around
`perform`.
Fixes #20799.
*Johannes Opper*
* Allow `DelayedJob`, `Sidekiq`, `qu`, and `que` to report the job id back to
`ActiveJob::Base` as `provider_job_id`.
......
......@@ -5,6 +5,7 @@
require 'active_job/execution'
require 'active_job/callbacks'
require 'active_job/logging'
require 'active_job/translation'
module ActiveJob #:nodoc:
# = Active Job
......@@ -60,6 +61,7 @@ class Base
include Execution
include Callbacks
include Logging
include Translation
ActiveSupport.run_load_hooks(:active_job, self)
end
......
......@@ -20,6 +20,9 @@ module Core
# ID optionally provided by adapter
attr_accessor :provider_job_id
# I18n.locale to be used during the job.
attr_accessor :locale
end
# These methods will be included into any Active Job object, adding
......@@ -68,7 +71,8 @@ def serialize
'job_class' => self.class.name,
'job_id' => job_id,
'queue_name' => queue_name,
'arguments' => serialize_arguments(arguments)
'arguments' => serialize_arguments(arguments),
'locale' => I18n.locale
}
end
......@@ -96,6 +100,7 @@ def deserialize(job_data)
self.job_id = job_data['job_id']
self.queue_name = job_data['queue_name']
self.serialized_arguments = job_data['arguments']
self.locale = job_data['locale'] || I18n.locale
end
private
......
module ActiveJob
module Translation #:nodoc:
extend ActiveSupport::Concern
included do
around_perform do |job, block, _|
I18n.with_locale(job.locale, &block)
end
end
end
end
......@@ -12,4 +12,20 @@ class JobSerializationTest < ActiveSupport::TestCase
GidJob.perform_later @person
assert_equal "Person with ID: 5", JobBuffer.last_value
end
test 'serialize includes current locale' do
assert_equal :en, HelloJob.new.serialize['locale']
end
test 'deserialize sets locale' do
job = HelloJob.new
job.deserialize 'locale' => :es
assert_equal :es, job.locale
end
test 'deserialize sets default locale' do
job = HelloJob.new
job.deserialize({})
assert_equal :en, job.locale
end
end
require 'helper'
require 'jobs/translated_hello_job'
class TranslationTest < ActiveSupport::TestCase
setup do
JobBuffer.clear
I18n.available_locales = [:en, :de]
@job = TranslatedHelloJob.new('Johannes')
end
teardown do
I18n.available_locales = [:en]
end
test 'it performs the job in the given locale' do
@job.locale = :de
@job.perform_now
assert_equal "Johannes says Guten Tag", JobBuffer.last_value
end
end
......@@ -68,4 +68,21 @@ class QueuingTest < ActiveSupport::TestCase
refute delayed_test_job.provider_job_id.nil?,
'Provider job id should by set for delayed jobs by provider'
end
test 'current locale is kept while running perform_later' do
skip if adapter_is?(:inline)
begin
I18n.available_locales = [:en, :de]
I18n.locale = :de
TestJob.perform_later @id
wait_for_jobs_to_finish_for(5.seconds)
assert job_executed
assert_equal 'de', job_output
ensure
I18n.available_locales = [:en]
I18n.locale = :en
end
end
end
require_relative '../support/job_buffer'
class TranslatedHelloJob < ActiveJob::Base
def perform(greeter = "David")
translations = { en: 'Hello', de: 'Guten Tag' }
hello = translations[I18n.locale]
JobBuffer.add("#{greeter} says #{hello}")
end
end
......@@ -8,13 +8,17 @@
JobsManager.current_manager.setup
CODE
initializer 'i18n.rb', <<-CODE
I18n.available_locales = [:en, :de]
CODE
file 'app/jobs/test_job.rb', <<-CODE
class TestJob < ActiveJob::Base
queue_as :integration_tests
def perform(x)
File.open(Rails.root.join("tmp/\#{x}"), "w+") do |f|
f.write x
f.write I18n.locale
end
end
end
......
......@@ -45,4 +45,8 @@ def wait_for_jobs_to_finish_for(seconds=60)
def job_executed
Dummy::Application.root.join("tmp/#{@id}").exist?
end
def job_output
File.read Dummy::Application.root.join("tmp/#{@id}")
end
end
......@@ -280,6 +280,19 @@ UserMailer.welcome(@user).deliver_later
```
Internationalization
--------------------
Each job uses the `I18n.locale` set when the job was created. Useful if you send
emails asynchronously:
```ruby
I18n.locale = :eo
UserMailer.welcome(@user).deliver_later # Email will be localized to Esparanto.
```
GlobalID
--------
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册