heartcombo / devise

Flexible authentication solution for Rails with Warden.
http://blog.plataformatec.com.br/tag/devise/
MIT License
23.89k stars 5.54k forks source link

offering to document: how to REALLY use ActiveJob with Devise (via custom ActiveJob) - but I need the magic incantation #5385

Open jpwynn opened 3 years ago

jpwynn commented 3 years ago

If someone explains it to me I'm happy to do the writing...

Currently the only docs re using an app's ActiveJob for devise is:

... overriding the send_devise_notification method in your model.

def send_devise_notification(notification, *args)
  devise_mailer.send(notification, self, *args).deliver_later
end

Which of course simply uses the default queue. But I'd guess that many developers give emails their very own queue, and therefore their very own Jobs. And would like to send Devise emails through the same backend.

A more complete doc will show how to setup a devise-specific Job, with devise-specific settings (such as using a delayed_job queue if the default queue is sidekiq).

What I tried was:

# app/jobs/devise_mailer_job.rb
class DeviseMailerJob < ApplicationJob
  self.queue_adapter = :delayed_job
  queue_as :default_dj

  def perform(mailer_obj, notification, *args)
    XXXXXXXXX.send(notification, *args).deliver_now
  end
end

and

# user.rb
  def send_devise_notification(notification, *args)
    #### devise_mailer.send(notification, self, *args).deliver_later  # dont want default queue want same queue as ALL other email
    DeviseMailerJob.perform_later(self, notification, *args)
  end
end

The conceptual problem (for me) is what is that missing "XXXXXXXX" ... how to invoke the devise_mailer outside the model class (for simplicity let's assume 'User")

if I use devise_mailer.send... I get undefined method (because, of course, a user's devise_mail is not defined in the Job class)

If I try User.devise_mail.send... same thing, no such method for User class.

If I try Devise::Mailer.send... (which I didn't expect to work anyway, as it nowhere specifies the User model) it gets closer... but I get some sort of ActiveJob serializer error

[{"_aj_serialized"=>"ActiveJob::Serializers::SymbolSerializer", "value"=>"reset_password_instructions"}, "PEPAwKiyZfAULRuEaB4w", {"_aj_symbol_keys"=>[]}] (id=48) (queue=default_dj) FAILED (0 prior attempts) with NoMethodError: undefined method `PEPAwKiyZfAULRuEaB4w' for Devise::Mailer:Class

I understand we can completely replace the Devise::Mailer specify my own class, etc., here:

  # Configure the class responsible to send e-mails.
  # config.mailer = 'Devise::Mailer'

But that seems overkill if one simply wants to configure Devise to "use a specific ActiveJob class".

I suspect there is some fairly simple incantation that can be used inside an ActiveJob to specify the "devise_mailer.send" method of the User model?

ammarshah commented 10 months ago

Option 1: The deliver_later method accepts a queue option:

# app/models/user.rb
protected

def send_devise_notification(notification, *args)
  devise_mailer.send(notification, self, *args).deliver_later(queue: 'mailers')
end

Option 2: You can set this for all Mailers by adding deliver_later_queue_name in each environment.

For example in a development environment:

# config/environments/development.rb
config.action_mailer.deliver_later_queue_name = 'mailers'

Option 3: You can set this for all Mailers regardless of the environment:

# config/initializers/devise.rb
Devise.setup do |config|
  ...

  # Configure the parent class responsible to send e-mails.
  config.parent_mailer = 'ApplicationMailer'

  ...
end

# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  self.deliver_later_queue_name = 'mailers'
end

I prefer the third option.