---
title: "Rails 7.1 adds *_deliver callbacks to Action Mailer"
description: "Rails 7.1 adds *_deliver callbacks to Action Mailer"
canonical_url: "https://www.bigbinary.com/blog/rails-7-1-adds-_-deliver-callbacks-to-action-mailer"
markdown_url: "https://www.bigbinary.com/blog/rails-7-1-adds-_-deliver-callbacks-to-action-mailer.md"
---

# Rails 7.1 adds \*\_deliver callbacks to Action Mailer

Rails 7.1 adds \*\_deliver callbacks to Action Mailer

- Author: Calvin Chiramal
- Published: September 26, 2023
- Categories: Rails, Rails 7

Rails 7.1 has added \*\_deliver callbacks to Action Mailer. Let's understand the
use of these callbacks with an example. Consider a case where you want to send
an email after a user has signed up.

```ruby
class UserMailer < ApplicationMailer
  default from: 'notifications@neeto.com'

  def welcome_email
    @user = params[:user]
    mail(to: @user.email, subject: 'Welcome to neeto')
  end
end
```

When the `mail` method is called in the above code, it just renders the mail
template from a view. The mail is actually not sent. The actual delivery may
happen synchronously or asynchronously. To send the mail, we need to call one of
many deliver methods.

```ruby
UserMailer.with(user: @user).welcome_email.deliver_later
```

Before Rails 7.1, we did not have any callbacks around the deliver methods to
execute code around the delivery lifecycle. You would need to use
[interceptors and observers](https://guides.rubyonrails.org/action_mailer_basics.html#intercepting-and-observing-emails)
to hook into the mail delivery lifecycle. Say, you need to send emails to only a
few allowed email addresses in the staging environment. You would need to use an
interceptor and register it to Action Mailer.

```ruby
# interceptors/staging_email_interceptor.rb
module Interceptors
  class StagingEmailInterceptor
    def self.delivering_email(mail)
      if rails.env.staging? && !allowed_emails.include?(mail.to)
        mail.perform_deliveries = false
      end
    end
  end
end
```

```ruby
# config/initializers/action_mailer.rb
ActionMailer::Base.register_interceptor(Interceptors::StagingEmailInterceptor)
```

The new `before_deliver` callback allows you to handle this situation without
using the Interceptors or Observers. You would just need to include the
following in `UserMailer`.

```ruby {3, 10-16}
class UserMailer < ApplicationMailer
  default from: 'notifications@neeto.com'
  before_deliver :filter_allowed_emails

  def welcome_email
    @user = params[:user]
    mail(to: @user.email, subject: 'Welcome to neeto')
  end

  private

    def filter_allowed_emails
      if rails.env.staging? && !allowed_emails.include?(mail.to)
        mail.perform_deliveries = false
      end
    end
end
```

Similarly, suppose you want to update the `mail_delivered_at` attribute of the
`user` instance, you would have to use an observer and register it like so:

```ruby
# observers/staging_email_interceptor.rb
module Observers
  class SetDeliveredAtObserver
    def self.delivered_email(mail)
      user = User.find_by(email: mail.to)
      user.update(mail_delivered_at: mail.date)
    end
  end
end
```

```ruby {3}
# config/initializers/action_mailer.rb
ActionMailer::Base.register_interceptor(Interceptors::StagingEmailInterceptor)
ActionMailer::Base.register_observer(Observers::SetDeliveredAtObserver)
```

With the new `after_deliver` callback this becomes as simple as defining a
method in `UserMailer`.

```ruby {4, 19-21}
class UserMailer < ApplicationMailer
  default from: 'notifications@neeto.com'
  before_deliver :filter_allowed_emails
  after_deliver :set_delivered_at

  def welcome_email
    @user = params[:user]
    mail(to: @user.email, subject: 'Welcome to neeto')
  end

  private

    def filter_allowed_emails
      if rails.env.staging? && !allowed_emails.include?(mail.to)
        mail.perform_deliveries = false
      end
    end

    def set_delivered_at
      @user.update(mail_delivered_at: mail.date)
    end
end
```

An important thing to keep in mind is the order of execution of the callbacks.

- before_action
- after_action
- before_deliver
- after_deliver

This makes sense as the deliver callbacks only wrap around the deliver methods
while the action callbacks wrap around the mail render method.

Please check out this [pull request](https://github.com/rails/rails/pull/47630)
for more details.

## Links

- [Human page](https://www.bigbinary.com/blog/rails-7-1-adds-_-deliver-callbacks-to-action-mailer)
