mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
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.
This commit is contained in:
parent
e598967548
commit
3860e6b2bf
11 changed files with 111 additions and 2 deletions
|
@ -1,3 +1,10 @@
|
||||||
|
* 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
|
* Allow `DelayedJob`, `Sidekiq`, `qu`, and `que` to report the job id back to
|
||||||
`ActiveJob::Base` as `provider_job_id`.
|
`ActiveJob::Base` as `provider_job_id`.
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ require 'active_job/enqueuing'
|
||||||
require 'active_job/execution'
|
require 'active_job/execution'
|
||||||
require 'active_job/callbacks'
|
require 'active_job/callbacks'
|
||||||
require 'active_job/logging'
|
require 'active_job/logging'
|
||||||
|
require 'active_job/translation'
|
||||||
|
|
||||||
module ActiveJob #:nodoc:
|
module ActiveJob #:nodoc:
|
||||||
# = Active Job
|
# = Active Job
|
||||||
|
@ -60,6 +61,7 @@ module ActiveJob #:nodoc:
|
||||||
include Execution
|
include Execution
|
||||||
include Callbacks
|
include Callbacks
|
||||||
include Logging
|
include Logging
|
||||||
|
include Translation
|
||||||
|
|
||||||
ActiveSupport.run_load_hooks(:active_job, self)
|
ActiveSupport.run_load_hooks(:active_job, self)
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,6 +20,9 @@ module ActiveJob
|
||||||
|
|
||||||
# ID optionally provided by adapter
|
# ID optionally provided by adapter
|
||||||
attr_accessor :provider_job_id
|
attr_accessor :provider_job_id
|
||||||
|
|
||||||
|
# I18n.locale to be used during the job.
|
||||||
|
attr_accessor :locale
|
||||||
end
|
end
|
||||||
|
|
||||||
# These methods will be included into any Active Job object, adding
|
# These methods will be included into any Active Job object, adding
|
||||||
|
@ -68,7 +71,8 @@ module ActiveJob
|
||||||
'job_class' => self.class.name,
|
'job_class' => self.class.name,
|
||||||
'job_id' => job_id,
|
'job_id' => job_id,
|
||||||
'queue_name' => queue_name,
|
'queue_name' => queue_name,
|
||||||
'arguments' => serialize_arguments(arguments)
|
'arguments' => serialize_arguments(arguments),
|
||||||
|
'locale' => I18n.locale
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -96,6 +100,7 @@ module ActiveJob
|
||||||
self.job_id = job_data['job_id']
|
self.job_id = job_data['job_id']
|
||||||
self.queue_name = job_data['queue_name']
|
self.queue_name = job_data['queue_name']
|
||||||
self.serialized_arguments = job_data['arguments']
|
self.serialized_arguments = job_data['arguments']
|
||||||
|
self.locale = job_data['locale'] || I18n.locale
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
11
activejob/lib/active_job/translation.rb
Normal file
11
activejob/lib/active_job/translation.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
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
|
GidJob.perform_later @person
|
||||||
assert_equal "Person with ID: 5", JobBuffer.last_value
|
assert_equal "Person with ID: 5", JobBuffer.last_value
|
||||||
end
|
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
|
end
|
||||||
|
|
20
activejob/test/cases/translation_test.rb
Normal file
20
activejob/test/cases/translation_test.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
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?,
|
refute delayed_test_job.provider_job_id.nil?,
|
||||||
'Provider job id should by set for delayed jobs by provider'
|
'Provider job id should by set for delayed jobs by provider'
|
||||||
end
|
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
|
end
|
||||||
|
|
10
activejob/test/jobs/translated_hello_job.rb
Normal file
10
activejob/test/jobs/translated_hello_job.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
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 @@ require "#{File.expand_path("../jobs_manager.rb", __FILE__)}"
|
||||||
JobsManager.current_manager.setup
|
JobsManager.current_manager.setup
|
||||||
CODE
|
CODE
|
||||||
|
|
||||||
|
initializer 'i18n.rb', <<-CODE
|
||||||
|
I18n.available_locales = [:en, :de]
|
||||||
|
CODE
|
||||||
|
|
||||||
file 'app/jobs/test_job.rb', <<-CODE
|
file 'app/jobs/test_job.rb', <<-CODE
|
||||||
class TestJob < ActiveJob::Base
|
class TestJob < ActiveJob::Base
|
||||||
queue_as :integration_tests
|
queue_as :integration_tests
|
||||||
|
|
||||||
def perform(x)
|
def perform(x)
|
||||||
File.open(Rails.root.join("tmp/\#{x}"), "w+") do |f|
|
File.open(Rails.root.join("tmp/\#{x}"), "w+") do |f|
|
||||||
f.write x
|
f.write I18n.locale
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -45,4 +45,8 @@ module TestCaseHelpers
|
||||||
def job_executed
|
def job_executed
|
||||||
Dummy::Application.root.join("tmp/#{@id}").exist?
|
Dummy::Application.root.join("tmp/#{@id}").exist?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def job_output
|
||||||
|
File.read Dummy::Application.root.join("tmp/#{@id}")
|
||||||
|
end
|
||||||
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
|
GlobalID
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue