This blog has been archived. Our writing has moved to makandra cards.
The blog of , a Ruby on Rails development team

Overriding e-mail recipients in ActionMailer

Our customers like to have a staging environment so that they can always test the latest and greatest bleeding edge features. But what if an application is sending e-mails and you would not want someone@somecorp to be told to visit your awesome staging website?

Setting ActionMailer's delivery method to :test somewhat does the trick but there will be no real e-mail until the application goes live. That's sad. Maybe you would want those mails to get delivered somewhere, like your customer's e-mail address or your own.

We ran into some difficulties using the sanitize_email gem on Rails 2.3.5 and were a bit unhappy with the way the gem was doing its work (configuring it application-wide felt somewhat weird). So we came up with a different solution.

For the magic to happen we put two lines into the designated environments files (like environments/staging.rb). If we want e-mails to be delivered to admin@example.com and foo@bar.baz instead of their actual recipients it looks like this:

require 'override_mail_recipients'
ActionMailer::Base.override_recipients = %w[ admin@example.com foo@bar.baz ]

Of course, there is a bit more to it. Put the following code into a file like lib/override_mail_recipients.rb and ActionMailer will know what to do.

ActionMailer::Base.class_eval do

  @@override_recipients = nil
  cattr_accessor :override_recipients

  def deliver_with_override!(mail = @mail)
    if override_recipients.present?
      mail.override_recipients! override_recipients
    end
    deliver_without_override! mail
  end
  alias_method_chain :deliver!, :override

end

TMail::Mail.class_eval do

  def override_recipients!(recipients)
    recipients = Array(recipients)

    original_addresses = {
      :to => override(:to, recipients),
      :cc => override(:cc, recipients),
      :bcc => override(:bcc, recipients)
    }
    self.body = original_addresses.to_yaml + "\\n" + self.body
  end

  def override(method, recipients)
    original_recipients = send("#{method}")
    self.send "#{method}=", recipients if original_recipients
    original_recipients
  end

end

Obviously we "only" replace any existing e-mail recipients with ours and do not want to do anything fancy like secretly adding ourselves to BCC here. On staging every email is sent to the e-mail address(es) defined in the environment file and the original recipients are quoted at the beginning of each mail.

All other environments will behave like nothing ever happened.

Update: We extracted this trick into its own gem, Mail Magnet.

Growing Rails Applications in Practice
Check out our e-book:
Learn to structure large Ruby on Rails codebases with the tools you already know and love.

Recent posts

Our address:
makandra GmbH
Werner-von-Siemens-Str. 6
86159 Augsburg
Germany
Contact us:
+49 821 58866 180
info@makandra.de
Commercial register court:
Augsburg Municipal Court
Register number:
HRB 24202
Sales tax identification number:
DE243555898
Chief executive officers:
Henning Koch
Thomas Eisenbarth