Posted over 2 years ago. Visible to the public. Repeats.

Rails: how to write custom email interceptors

Nowadays it is fairly easy to intercept and modify mails 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.

Copy
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.

Whitelisted 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 whitelist for staging using a mail interceptor.

Copy
class Whitelist 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('whitelist') end end class WhitelistConfig module ClassMethods def for(environment) @whitelists ||= {} @whitelists[environment] ||= Whitelist.new(config.fetch(environment, {})) end private def config @config ||= YAML.load_file(Rails.root.join('config/mail_whitelist.yml')).freeze end end extend ClassMethods end class WhitelistInterceptor def self.delivering_email(message) whitelist = WhitelistConfig.for(Rails.env) if whitelist.active? unless (message.to - whitelist.allowed).empty? message.subject = "#{message.subject} [#{message.to}]" message.to = whitelist.fallback_email end end end end ActionMailer::Base.register_interceptor(WhitelistInterceptor)

Your whitelist config should be defined in config/mail_whitelist/staging.yml like this:

Copy
staging: fallback_email: fallback@example.com whitelist: - personal1@example.com - personal2@example.com

A spec could look something like this:

Copy
describe WhitelistInterceptor do describe '.delivering_email' do context 'for defined stages' do before :each do expect(Rails).to receive(:env).and_return('staging') end it 'changes the recipient for a not whitelisted address' do email = ActionMailer::Base.mail(to: 'not_whitelistet@example.com', body: 'message') WhitelistInterceptor.delivering_email(email) expect(email.to).to eq ['fallback@example.com'] end it 'changes recipient if any address is not whitelisted' do email = ActionMailer::Base.mail(to: ['personal1@example.com', 'not_whitelistet@example.com'], body: 'message') WhitelistInterceptor.delivering_email(email) expect(email.to).to eq ['fallback@example.com'] end it 'does not change the recipient for whitelisted addresses' do email = ActionMailer::Base.mail(to: 'personal1@example.com', body: 'message') WhitelistInterceptor.delivering_email(email) expect(email.to).to eq ['personal1@example.com'] end end context 'for undefined stages' do it 'does not crash and does not alter the recipient' do expect(Rails).to receive(:env).and_return('test') email = ActionMailer::Base.mail(to: 'foo@makandra.de', body: 'message') WhitelistInterceptor.delivering_email(email) expect(email.to).to eq ['foo@makandra.de'] end end end end

Related:

You might also be interested in gems for mail interception:

Once an application no longer requires constant development, it needs periodic maintenance for stable and secure operation. makandra offers monthly maintenance contracts that let you focus on your business while we make sure the lights stay on.

Owner of this card:

Avatar
Daniel Straßner
Last edit:
9 months ago
by Daniel Straßner
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Daniel Straßner to makandra dev
This website uses cookies to improve usability and analyze traffic.
Accept or learn more