Nowadays it is fairly easy to
intercept and modify mails
Show archive.org snapshot
globally before they are sent. All you have to do is register an interceptor class which responds to .delivering_email(message)
. This card will show you two common use cases.
Subject prefix:
Usually you want to prefix the subject line of emails with the current environment (except production) so you can differentiate between production mails and mails from other environments. Of course a prefix can be set directly in the mailer, however, it is safer and cleaner to do this with an interceptor as you don't have to modify the subject line for each new mailer method.
class SubjectPrefixInterceptor
def self.delivering_email(message)
message.subject = "[#{Rails.env}] #{message.subject}"
end
end
unless Rails.env.production?
ActionMailer::Base.register_interceptor(SubjectPrefixInterceptor)
end
Define it in an initializer like config/initializers/mail_subject_prefixing.rb
.
Allowlisted email addresses:
Sometimes you might want to try sending mails from staging but just to selected recipients. This example will show you how to define a email allowlist for staging using a mail interceptor.
class Allowlist
def initialize(config)
if config.empty?
@active = false
else
@active = true
@config = config
end
end
def active?
@active
end
def fallback_email
@fallback_email ||= @config.fetch('fallback_email')
end
def allowed
@allowed ||= @config.fetch('allowlist')
end
end
class AllowlistConfig
module ClassMethods
def for(environment)
@allowlists ||= {}
@allowlists[environment] ||= Allowlist.new(config.fetch(environment, {}))
end
private
def config
@config ||= YAML.load_file(Rails.root.join('config/mail_allowlist.yml')).freeze
end
end
extend ClassMethods
end
class AllowlistInterceptor
def self.delivering_email(message)
allowlist = AllowlistConfig.for(Rails.env)
if allowlist.active?
unless (message.to - allowlist.allowed).empty?
message.subject = "#{message.subject} [#{message.to}]"
message.to = allowlist.fallback_email
end
end
end
end
ActionMailer::Base.register_interceptor(AllowlistInterceptor)
Your allowlist config should be defined in config/mail_allowlist/staging.yml
like this:
staging:
fallback_email: fallback@example.com
allowlist:
- personal1@example.com
- personal2@example.com
A spec could look something like this:
describe AllowlistInterceptor do
describe '.delivering_email' do
def send_email(**options)
ActionMailer::Base.mail(from: 'app@example.com', subject: 'My Subject', body: 'My Message', **options).deliver_now
end
context 'on staging' do
before { allow(Rails).to receive(:env).and_return('staging'.inquiry) }
it 'redirects e-mails that would be sent to public recipients' do
email = send_email(to: 'not-allowed@example.com')
expect(email.to).to contain_exactly('fallback@example.com')
expect(email.subject).to eq('My Subject [not-allowed@example.com]')
end
it 'allows sending e-mails to allowlisted recipients' do
email = send_email(to: 'personal1@example.com')
expect(email.to).to contain_exactly('personal1@example.com')
expect(email.subject).to eq('My Subject')
end
end
context 'on production' do
before { allow(Rails).to receive(:env).and_return('production'.inquiry) }
it 'does not redirect e-mails' do
email = send_email(to: 'not-allowed@example.com')
expect(email.to).to contain_exactly('not-allowed@example.com')
expect(email.subject).to eq('My Subject')
end
end
end
end
Related:
You might also be interested in gems for mail interception: