delayed_job is a nice gem for executing work in the background in Ruby On Rails.
But what happend to me is that mails did not get send correctly. I was expecting to get them send in german and not english (default locale).
The controller did set it correctly depending on the user.
A new record was created that triggered an email to be send. Since delayed_job sends them asynchronously (exactly what i wanted) a job was created in the database and executed in the background.
But the backgroundjob defaulted to the default locale (english) and so the email had the wrong locale that was used for determing translations (I18n.t is used widely in my applications).
So now i had to come up with a solution.
- Remember the locale when the job is created
- set it while executing the job
delayed_job provides a nice API for integrating around job execution (lifecycle-hooks).
So what i did in my task-infrastructure:
1. Add a new Migration for having a attribute locale in the database to store the locale for the job
# Adds a locale attribute to delayed_jobs Table class AddLocaleToDelayedJobs < ActiveRecord::Migration def change change_table :delayed_jobs do |t| t.string :locale # will hold the locale end end end
2. Add an initializer that stores the locale when a job is created
Delayed::Worker.lifecycle.before(:enqueue) do |job| # If Locale is not set if(job.locale.nil? || job.locale.empty? && I18n.locale.to_s != I18n.default_locale.to_s) job.locale = I18n.locale end end
3. Add an initializer that sets the locale around job execution
Delayed::Worker.lifecycle.around(:invoke_job) do |job, &block| # Store locale of worker savedLocale = I18n.locale begin # Set locale from job or if not set use the default if(job.locale.nil?) I18n.locale = I18n.default_locale else I18n.locale = job.locale end # now really perform the job block.call(job) ensure # Clean state from before setting locale I18n.locale = savedLocale end end
4. Work normally and be happy that localization now also works in the background!