Skip to content
bemurphy edited this page Mar 11, 2013 · 14 revisions

If you use the devise gem to manage authentication, you may have noticed that it's not easy to make devise send emails using sidekiq's delay helper. Here are two options on how you can easily reroute devise mail through sidekiq without any monkeypatching.

Use devise-async

Add the gem devise-async to your Gemfile.
Also see the Devise Wiki Page to this topic: [How To: Send devise emails in background (Resque, Sidekiq and Delayed::Job)]( https://github.com/plataformatec/devise/wiki/How-To:-Send-devise-emails-in-background-(Resque,-Sidekiq-and-Delayed::Job\))

# Gemfile
gem "devise-async"

Include Devise::Async::Model to your Devise model when using Devise >= 2.1.1

class User < ActiveRecord::Base
  devise :database_authenticatable, :confirmable, :async # etc ...
end

And finally tell DeviseAsync to use Sidekiq to enqueue the emails.

# config/initializers/devise_async.rb
Devise::Async.backend = :sidekiq

Note: devise-async enqueues to the mailer queue by default, so don't forget to tell your workers to process this queue or configure devise-async to use a different queue.

Do it yourself

First, configure Devise to use the new proxy class we are about to create instead of the real devise mailer.

# config/initializers/devise.rb
Devise.setup do |config|
  # ...stuff here
  config.mailer = "DeviseBackgrounder"
  # ...more stuff here
end

Then, create a new file to hold the simple proxy class that will ensure devise mails get queued for sidekiq to send.

# lib/devise_backgrounder.rb

# Mailer proxy to send devise emails in the background
class DeviseBackgrounder

  def self.confirmation_instructions(record, opts = {})
    new(:confirmation_instructions, record, opts)
  end

  def self.reset_password_instructions(record, opts = {})
    new(:reset_password_instructions, record, opts)
  end

  def self.unlock_instructions(record, opts = {})
    new(:unlock_instructions, record, opts)
  end
  
  def initialize(method, record, opts = {})
    @method, @record, @opts = method, record, opts
  end
  
  def deliver
    # You need to hardcode the class of the Devise mailer that you
    # actually want to use. The default is Devise::Mailer.
    Devise::Mailer.delay.send(@method, @record, @opts)
  end

end

That's it! Boot your app, and devise will send mail in the background. I had some trouble with my tests where the DeviseBackgrounder would choke on mocks, so I also added this line to my spec_helper.rb.

Devise.mailer = Devise::Mailer

That removes the backgrounding proxy during tests, and made everything work again for me.

Clone this wiki locally