GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS

Written by thoughtbot

You've Got Mail

There are just some days when you want to interact with real data from a Rails application that you wrote. Doing a database dump, saving the output locally, and loading that data in is the ideal method, but once you do that, you’ve got live data locally. This can be a Bad Thing™ if you’re sending email.

Why?

Mailers can be triggered in any number of places. A callback, a controller, or an observer could be sitting there, waiting to send off an email to an unsuspecting victim without you being aware. This is one reason why playing with live data is a bit dangerous. There’s luckily an easy workaround.

You've Got Mail

ActionMailer::Base has an instance method, recipients, that accepts a string of email addresses to whom you’ll be sending your message. If we override that method in the mailer classes we don’t want sending email, we can intercept any calls and replace recipients if we want.

class ProductMailer < ActionMailer::Base
  # mailer methods

  def recipients(*emails)
    if emails.size == 1
      super(MAILER_RECIPIENT_OVERRIDE || emails.first)
    else
      super(*emails)
    end
  end
end

Using Ruby, we overload the recipients method and call super, passing our MAILER_RECIPIENT_OVERRIDE constant or the email addresses that were passed. Because our mailer inherits from ActionMailer::Base, super will relay the constant or whatever was passed; if the constant is nil, the recipients will work normally. However, in our development environment (for example), if that constant is assigned a string, the email will be delivered to the email instead.

Where does this leave us?

We’re going to want to assign values to this constant for each of our environments. In config/environments/development.rb (or whichever environment you dumped the live data into), we’ll want to assign this to a string. I like sending everything to “test@example.com”. Within our other environments, we’ll want this set to nil so it doesn’t affect our tests or production site emails.

# config/environments/development.rb
MAILER_RECIPIENT_OVERRIDE = "test@example.com"

# config/environments/staging.rb
MAILER_RECIPIENT_OVERRIDE = "test@example.com"

# config/environments/test.rb
MAILER_RECIPIENT_OVERRIDE = nil

# config/environments/production.rb
MAILER_RECIPIENT_OVERRIDE = nil

This solution provides a quick, simple way to override recipients without cluttering up the code and allows a flexible solution when dealing with live data.

There is a caveat, however. You have to do this for every mailer. There are ways around this (like creating an ApplicationMailer base class that your mailers inherit from), but unless you’re dealing with a bunch of mailers, defining recipients in each is a perfectly acceptable solution.