Canonical

Canonical is an ActionMailer extension that acts just like the Postfix utility of the same name. Essentially, Canonical lets you substitute the To, CC, and/or BCC email destinations with one of many replacements via a rule.

You may want this behavior for many reasons; especially if you’re actually using Postfix’s canonical functionality. Let’s say you don’t want to setup your integration or staging environment to send email in test mode, but you also do not want to accidentally send email to real users. You may add a canonical rule like the following to send any email from the system to any address to the QA email distribution list:

TMail::Mail.add_canonical_override(/.*/, '[email protected]')

Assuming you are working at Foo Bar, that rule will match any destination address that has any character in it - meaning, it matches everything - and will subsitute the matched email with [email protected]. This is the same as saying the following in Postfix’s canonical file:

/.*/, [email protected]

What you should notice is that canonical expects to see a regular expression as the first argument. Passing in anything that does not respond to =~ will result in an error.

First matched, first replaced (FMFR)

Now, let’s say you didn’t want to just replace everything. Let’s you wanted to send everything for your boss to your team, everything to marketing to another email address, and catch everything else. The rules would then be defined as:

TMail::Mail.add_canonical_override(/ceo@foo\.bar/, '[email protected]')
TMail::Mail.add_canonical_override(/marketing@/, '[email protected]')
TMail::Mail.add_canonical_override(/.*/, '[email protected]')

What you should notice here is the ordering. Canonical works with multiple rules, but will process a substition according to the rule that matches first. Thus, whichever rule you define first will be checked first. If the rule matches the email address being checked, the substitution is made and matching stops. Hence the FMFR; first matched, first replaced.

The best way to take advantage of rules is to put the most specific rule at the top of the list and the most generic rule at the bottom. If we had reversed the order of the above rules, everything would go to [email protected].

Passing Through

Perhaps you have a catch-all rule like the ones used above (e.g. /.*/), but you want certain email addresses to pass-through. Sort of like an opt-in approach, which might be considered safer in a closed environment. You could match on the specific address and then substitute it with the same thing, like so:

TMail::Mail.add_canonical_override(/ceo@foo\.bar/, '[email protected]')

But that’s kind of silly; and what if you wanted to match on everything for your company? This is where Canonical deviates slightly from Postfix’s version. This Canonical will allow you to provide a passthrough for a pattern. For instance:

TMail::Mail.add_canonical_passthrough(/@foo\.bar/)

Will explicitly allow any email address with “@foo.bar” in it. Even if there is a catch-all rule; so long as this rule comes before any other rules that would match.

Handy? Yes!

How does it work

Essentially, Canonical works by hijacking the TMail::Mail#destinations which is used at the moment ActionMailer is about to write the RCPT TOs to the SMTP server chosen. Canonical only modifies destinations, it does not modify anything about the body of the email. Thus, the To, Cc, and Bcc will all look the same as an unmodified email would to the recipient.

Canonical also disables the :sendmail option since it only tries to work with SMTP. At some point in the future, this may change. If you need sendmail support then fix the code and send a patch :)

How should I use it?

The most obvious way to use canonical is to put the rules in your Rails environment files. We suggest an opt-in approach to rule setting such that you catch-all in config/environment.rb and specifically allow emails in config/environments/production.rb.

For example:

# config/environment.rb
...
TMail::Mail.add_canonical_override(/.*/, '[email protected]')

# config/environments/production.rb
...
TMail::Mail.add_canonical_passthrough(/.*/)

That’s the most basic way to localize email in every environment but production.

License

Copyright © 2008 Centro, released under the MIT license.

Authored by:

{Eric Schwartz}[[email protected]]
{Brett Neumeier}[[email protected]]
{Justin Knowlden}[[email protected]]