How do you change an email you did not create? Rails Action mailer emails have a ready-made solution: ActionMailer emails interceptor.
My problem was that my E-commerce engine, Solidus a Spree fork, sends the customer an email when a customer orders notifying no one else. By intercepting and changing the email addresses’s ‘bcc’ field you can tell the shipping office without affecting the e-commerce engine.
Email interception requires several steps to complete:
- Email Interceptor to change the order email
- Register the interceptor class with Rails
- Supply email addresses to copy into the email’s bcc field
- Testing Interceptor
- Unit test
- Feature test
Email Interceptor to change the order email
Example 1, is the actual code that changes the order mail. The guard clause, line 5, prevents the interceptor from changing any other ActionMailer delivery. Your class should make any needed modifications directly to the passed in Mail::Message instance. With the bcc email addresses being taken from
Rails.application.secrets though I’ve seen programmers use a separate yml file.
Example 1: Changing the order mail -
1 2 3 4 5 6 7 8 9 class BccEmailInterceptor REVISABLE_MAILERS = [Spree::OrderMailer].freeze def self.delivering_email(message) return unless REVISABLE_MAILERS.include?(message.delivery_handler) message.bcc = Rails.application.secrets['order']['notify'] end end
Register the interceptor class with Rails
Rails needs the interceptor class registering before it can work, see example 2 below.
Example 2: Register interceptor class with Rails
1 2 3 require "bcc_email_interceptor" ActionMailer::Base.register_interceptor(BccEmailInterceptor)
It is more common to see code that only registers interceptors on production (
if Rails.env.production?) but I didn’t want to do this for simplicity unless it became a problem - I filter out example.com email addresses with a rule at the email service level and Rails does not send emails in test environment by default.
Supply email addresses to copy into the email’s bcc field
Bcc Email addresses are taken from a configuration file. In example 3, I am only showing the full production structure but test and development are the same format.
Example 3: Email addresses to notify on a new order -
Testing the interceptor requires:
- Unit test examining that interceptor alters an email as expected.
- Feature test examining if the interceptor is hooking into Rails
For more information the Rails guide has a section on testing mailers
1. Unit test examining that interceptor alters an email as expected
A test isolating the interceptor from the rest of the system as shown in example 4 below - showing it alters the bcc.
Example 4: unit test to see the interceptor code worked
2. Feature test examining if the interceptor is hooking into the Rails
Test the interceptor is active within Rails with an integration test as shown in example 5 below. Rails functional mailer tests call the deliver method and then test the queued mail - we can assume that Rails has tested ActiveMailer.
Example 5: feature test to see email interception was working within Rails
Intercepting emails is a feature of Rails’s ActiveMailer that adds flexibility to your system without incurring the cost of maintaining a patch to someone else’s library.
- Email interception idea came from Solidus support channel thread
- Action Mailer Interceptors by Josh
- Mail interceptors for different Rails environments