rails 3: how to abort delivery method in actionmailer?

Viewed 6778

In my mailer controller, under certain conditions (missing data) we abort sending the email.

How do I exit the controller method without still rendering a view in that case?

return if @some_email_data.nil?

Doesn't do the trick since the view is still rendered (throwing an error every place I try to use @some_email_data unless I add a lot of nil checks)

And even if I do the nil checks, it complains there's no 'sender' (because I supposed did a 'return' before getting to the line where I set the sender and subject.

Neither does render ... return

Basically, RETURN DOESN'T RETURN inside a mailer method!

6 Answers

I've found this method that seems the least-invasive, as it works across all mailer methods without requiring you to remember to catch an error. In our case, we just want a setting to completely disable mailers for certain environments. Tested in Rails 6, although I'm sure it'll work just fine in Rails 5 as well, maybe lower.

class ApplicationMailer < ActionMailer::Base
  class AbortDeliveryError < StandardError; end

  before_action :ensure_notifications_enabled
  rescue_from AbortDeliveryError, with: -> {}

  def ensure_notifications_enabled
    raise AbortDeliveryError.new unless <your_condition>
  end

  ...

end

The empty lambda causes Rails 6 to just return an ActionMailer::Base::NullMail instance, which doesn't get delivered (same as if your mailer method didn't call mail, or returned prematurely).

Setting self.message.perform_deliveries = false did not work for me.

I used a similar approach as some of the other answers - using error handling to control the flow and prevent the mail from being sent.

The example below is aborting mail from being sent in non-Production ENVs to non-whitelisted emails, but the helper method logic can be whatever you need for your scenario.

class BaseMailer < ActionMailer::Base
  class AbortedMailer < StandardError; end

  def mail(**args)
    whitelist_mail_delivery(args[:to])
    super(args)
  rescue AbortedMailer
    Rails.logger.info "Mail aborted! We do not send emails to external email accounts outside of Production ENV"
  end

  private

  def whitelist_mail_delivery(to_email)
    return if Rails.env.production?

    raise AbortedMailer.new unless internal_email?(to_email)
  end

  def internal_email?(to_email)
    to_email.include?('@widgetbusiness.com')
  end
end
Related