DEV Community

Cover image for The two returning values in Action Mailer [bugfix]
Fabio Bazurto Blacio
Fabio Bazurto Blacio

Posted on

The two returning values in Action Mailer [bugfix]

Background

  • Rails 7.1
  • Ruby 3.x

Your production environment is sending emails using Action Mailer and smtp delivery method. You implemented a very common pattern using Action:

class NotificationMailer < ApplicationMailer
  def processed_success(user:)
    if user.pass_custom_condition?
      mail(to: user.email, subject: 'Record processed')
    else
      Rails.logger.info('Record processed but notification is not delivered')
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Inside the Mailer, there is a condition that will return two possible values: a fully configured mail object and automatically is returned a nullable object ActionMailer::Base::MailNull.

The Iteration

An iteration that implements dynamic delivery settings.[1]

class NotificationMailer < ApplicationMailer
  include Customizable # concern to apply changes from db 

  def processed_success(user:)
    if user.pass_custom_condition?
      mail(to: user.email, subject: 'Record processed')
    else
      Rails.logger.info('Record processed but notification is not delivered')
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Concern

module Customizable
  extend ActiveSupport::Concern

  included do # include callback into the mailers
    after_action :overrides_mail_settings, if: has_overrides_enabled?
  end

  def overrides_mail_settings
    mail.delivery_method.settings.merge!(@business.smtp_settings)
  end

  def has_overrides_enabled?
    @business.customize.present?
  end
end
Enter fullscreen mode Exit fullscreen mode

The Bug

Following the suggested implementation in the rails documentation and the backward common pattern, you should receive this error when you try to deliver NotificationMailer#deliver_now

[ArgumentError] SMTP To address may not be blank: []
Enter fullscreen mode Exit fullscreen mode
  • Before the callback change, the mailing process finishes with a return value: Mail::Message or ActionMailer::Base::MailNull.
  • After adding the callback, the mailer lifecycle expands with a message object copying default values into it.

Solution

Our goal is ensuring backward compatibility by returning: Mail::Message or ActionMailer::Base::MailNull.

Concern

module Customizable
  extend ActiveSupport::Concern

  included do # include callback into the mailers
    after_action :overrides_mail_settings, if: overrides_allowed?
  end

  def overrides_mail_settings
    mail.delivery_method.settings.merge!(@business.smtp_settings)
  end

  def overrides_allowed?
    @business.customize.present? && message.to.present?
  end
end
Enter fullscreen mode Exit fullscreen mode

Final Words

Although this fixes our problem and ensures backward compatibility, I would rather move business validation out of the mailer. In my opinion, a mailer class is in the presentation layer responsible for configuring delivery details, customizing layouts and showing content. Any business rule should be in a different component like a service or job.

Bibliography

  1. Action Mailer Basics โ€” Ruby on Rails Guides. (2024). Ruby on Rails Guides. https://guides.rubyonrails.org/action_mailer_basics.html
  2. Rails Api. (2025). rails/actionmailer/lib/action_mailer/base.rb at main ยท rails/rails. GitHub. https://github.com/rails/rails/blob/main/actionmailer/lib/action_mailer/base.rb

โ€Œ

Top comments (0)