knirirr
(knirirr)
May 12, 2025, 2:26pm
1
I’m wondering if there’s any way to append text to the subject of every email sent by an application.
The use case is that I have two copies of an application (production and staging) and would like both to send email, and for it to be clear where that email came from. So subjects could be:
"[Production] Please confirm your email address"
"[Staging] Please confirm your email address"
…and so on.
I can do this by overriding the subject of each email individually, but it would be nice if it could be done in one place.
You can do this using a mail interceptor on your staging environment. Start here: ActionMailer::Base and see where it leads you. We do this at work, and it is pretty drop-in-and-forget.
Here’s some more about this solution:
Relevant RailsGuide: Action Mailer Basics — Ruby on Rails Guides
Sample interceptor:
# frozen_string_literal: true
require 'nokogiri'
class CustomMailInterceptor
def self.delivering_email(message)
prepend_subject(message)
append_intended_addresses_to_text_part(message)
append_intended_addresses_to_html_part(message)
override_recipients(message)
end
def self.append_intended_addresses_to_html_part(message)
html_part = message.html_part || message
return unless html_part.content_type.include?('text/html')
doc = Nokogiri::HTML5.fragment html_part.body.to_s
doc.add_child '<hr>'
doc.add_child build_table(intended_addresses(message)).to_s
html_part.body = doc.to_html
end
def self.append_intended_addresses_to_text_part(message)
text_part = message.text_part || message
return unless text_part.content_type.include?('text/plain')
text_part.body = text_part.body.to_s + intended_addresses(message) if text_part.present?
end
def self.intended_addresses(message)
# rubocop:disable Naming/HeredocDelimiterNaming
<<~EOF
----------------
Intended sender: #{message.from}
Intended recipient(s): #{message.to}
Intended cc(s): #{message.cc}
Intended bcc(s): #{message.bcc}
EOF
# rubocop:enable Naming/HeredocDelimiterNaming
end
def self.build_table(text)
text.lines.compact.map do |line|
parts = line.strip.split(': ')
next if parts.length < 2
build_row(parts)
end.join('').prepend('<table style="border-collapse: collapse">').concat('</table>')
end
def self.build_row(parts)
<<~HTML
<tr>
<td style="font-weight: bold; text-align: right; padding-right: 1em">
#{parts.first}
</td>
<td>
#{parts.second}
</td>
</tr>
HTML
end
def self.override_recipients(message)
message.from = "your-server@your.domain"
message.to = "you@your.domain"
message.cc = nil
message.bcc = nil
end
def self.prepend_subject(message)
message.subject = "[TESTING IN #{Rails.env.upcase}] " + message.subject
end
end
knirirr
(knirirr)
May 12, 2025, 3:50pm
4
Thanks, that looks useful - I’ll give it a try.
I found this guide as well: Mail Interceptors and Observers for Logging and Development | Leigh Halliday