Rails 7.1 Adds *_deliver Callbacks To ActionMailer

ActionMailer allows us to send emails from our application using mailer classes and views.

Callbacks are methods that get called at specific points in the lifecycle of a mailer action. With the introduction of the *_deliver callbacks, we can now hook into the email delivery process more effectively.

Before

If we want to hook into the lifecycle of email delivery, we have to use interceptors and observers to modify, monitor, or perform actions on emails before or after they are sent.

class UserInvitationMailer < ApplicationMailer
  before_action :set_user

  def send_invitation_email
    mail(to: @user.email, subject: 'Welcome to Saeloun')
  end

  private

  def set_user
    @user = params[:user]
  end
end
UserInvitationMailer.with(user: @user).send_invitation_email.deliver_later

Interceptors are used to modify or prevent the delivery of emails before they are sent out.

class EmailInterceptor
  def self.delivering_email(message)
    # Log or modify the email before it is sent

    Rails.logger.info "About to deliver email to #{message.to}"
  end
end
# config/initializers/action_mailer.rb

ActionMailer::Base.register_interceptor(EmailInterceptor)

Observers are used to monitor the delivery of emails and perform actions when an email is delivered. Unlike interceptors, observers do not modify the email but are notified after an email has been sent.

class EmailObserver
  def self.delivered_email(message)
    # Log or perform actions after the email is sent

    Rails.logger.info "Successfully delivered email to #{message.to}"
  end
end
# config/initializers/action_mailer.rb

ActionMailer::Base.register_observer(EmailObserver)

However, while interceptors and observers offer distinct separation of concerns, they can lead to fragmented logic, limited flexibility, and more complex testing due to the need for separate classes and registrations.

After

Rails 7.1 adds *_deliver callbacks to ActionMailer which allows us to hook into the lifecycle of email delivery.

With the introduction of before_deliver, around_deliver, and after_deliver callbacks there is no need to initialize interceptors and observers.

class UserInvitationMailer < ApplicationMailer
  before_action :set_user
  before_deliver :log_before_delivery
  after_deliver :log_after_delivery

  def send_invitation_email(user)
    mail(to: @user.email, subject: 'Welcome to Saeloun')
  end

  private

  def set_user
    @user = params[:user]
  end

  def log_before_delivery
    Rails.logger.info "About to deliver email to #{@user.email}"
  end

  def log_after_delivery
    Rails.logger.info "Successfully delivered email to #{@user.email}"
  end
end

These *_deliver callbacks helps us to manage email lifecycle events directly within the mailer, providing a more integrated and flexible approach to modifying and tracking emails before, during, and after delivery.

This enhances code maintainability and simplifies logic compared to using separate interceptors and observers.

Need help on your Ruby on Rails or React project?

Join Our Newsletter