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
`ActiveJob::Base` as `provider_job_id`.

View file

@ -5,6 +5,7 @@ require 'active_job/enqueuing'
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 @@ module ActiveJob #:nodoc:
include Execution
include Callbacks
include Logging
include Translation
ActiveSupport.run_load_hooks(:active_job, self)
end

View file

@ -20,6 +20,9 @@ module ActiveJob
# 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 @@ module ActiveJob
'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 @@ module ActiveJob
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

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

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?,
'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

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

View file

@ -45,4 +45,8 @@ module TestCaseHelpers
def job_executed
Dummy::Application.root.join("tmp/#{@id}").exist?
end
def job_output
File.read Dummy::Application.root.join("tmp/#{@id}")
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
--------