Rails 5.2 allows mailers to use custom Active Job class

Prathamesh Sonpatki

Prathamesh Sonpatki

January 15, 2018

This blog is part of our  Rails 5.2 series.

Rails allows sending emails asynchronously via Active Job.

1Notifier.welcome(User.first).deliver_later

It uses ActionMailer::DeliveryJob as the default job class to send emails. This class is defined internally by Rails.

The DeliveryJob defines handle_exception_with_mailer_class method to handle exception and to do some housekeeping work.

1def handle_exception_with_mailer_class(exception)
2  if klass = mailer_class
3    klass.handle_exception exception
4  else
5    raise exception
6  end
7end

One might need more control on the job class to retry the job under certain conditions or add more logging around exceptions.

Before Rails 5.2, it was not possible to use a custom job class for this purpose.

Rails 5.2 has added a feature to configure the job class per mailer.

1class CustomNotifier < ApplicationMailer
2  self.delivery_job = CustomNotifierDeliveryJob
3end

By default, Rails will use the internal DeliveryJob class if the delivery_job configuration is not present in the mailer class.

Now, Rails will use CustomNotifierDeliveryJob for sending emails for CustomNotifier mailer.

1CustomNotifier.welcome(User.first).deliver_later

As mentioned above CustomNotifierDeliveryJob can be further configured for logging, exception handling and reporting.

By default, deliver_later will pass following arguments to the perform method of the CustomNotifierDeliveryJob.

  • mailer class name
  • mailer method name
  • mail delivery method
  • original arguments with which the mail is to be sent
1class CustomNotifierDeliveryJob < ApplicationJob
2
3  rescue_from StandardError, with: :handle_exception_with_mailer_class
4
5  retry_on CustomNotifierException
6
7  discard_on ActiveJob::DeserializationError
8
9  def perform(mailer, mail_method, delivery_method, *args)
10    logger.log "Mail delivery started"
11    klass = mailer.constantize
12    klass.public_send(mail_method, *args).send(delivery_method)
13    logger.log "Mail delivery completed"
14  end
15
16  def handle_exception_with_mailer_class(exception)
17    if klass = mailer_class
18      klass.handle_exception exception
19    else
20      raise exception
21    end
22  end
23end

We can also simply inherit from the ActionMailer::DeliveryJob and override the retry logic.

1class CustomNotifierDeliveryJob < ActionMailer::DeliveryJob
2  retry_on CustomNotifierException
3end

If this blog was helpful, check out our full blog archive.

Stay up to date with our blogs.

Subscribe to receive email notifications for new blog posts.