Hashrocket.com / blog

Silencing Your Staging Environment

posted on and written by Paul Elliott in

Image 100x100 paul elliott

One of the things we like to do when an application is in production is clone the production databases to the staging environment. This makes for some solid real world testing of the app and any migrations, etc. What's better than testing with real live production data, amiright?

Well testing with real live production data does come with at least one catch. All those real live users in your production environment have real live email addresses that receive real live emails. Your staging environment sends real live emails to real live users when things happen. Those real live emails contain links to, oops, your staging environment instead of production. Now you have real live users in a system that looks exactly like production but isn't.

We can't just completely shut off email delivery in staging, either. We need to receive them when we smoke test to verify system components and that email content is correct. Hooking into our mailers is not a great approach. We could filter the emails when we pass them in, but then we have to touch the code in a bunch of places. I don't want to have to remember to do this everytime I use or make a new mailer, either. We are bound to wind up missing a few.

With a little bit of monkey patching we can ensure that emails are delivered to our employees but not the rest of the world though. Whenever you invoke a mailer anywhere it returns a Mail::Message object. This is what you are actually calling deliver on. We can use ruby's alias_method_chain to put a filter on that method and remove emails that aren't ours.

Without further ado, here are the goods. Enjoy!

class Mail::Message
  def deliver_with_filtering
    unless Rails.env.production?
      self.to = scrub_emails(to)
      self.cc = scrub_emails(cc)
      self.bcc = scrub_emails(bcc)
    end

    deliver_without_filtering if deliverable?
  end

  alias_method_chain :deliver, :filtering

  private

  def deliverable?
    to.present? || cc.present? || bcc.present?
  end

  def scrub_emails(emails)
    Array.wrap(emails).select { |email| email[/hashrocket.com$/] }
  end
end

NOTE: We have to make sure the email is still deliverable because our SMTP relay (SendGrid) blows up if you try to send an email with no recipients. You may or may not care if there are recipients depending on your choice of relay. Development and test don't require any.

Posted in Development and tagged with Ruby