1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
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:
Johannes Opper 2015-07-07 21:52:28 +02:00
parent e598967548
commit 3860e6b2bf
11 changed files with 111 additions and 2 deletions

View file

@ -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`.

View file

@ -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

View file

@ -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

View 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

View file

@ -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

View 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

View file

@ -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

View 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

View file

@ -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

View file

@ -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

View file

@ -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
-------- --------