Email::MIME::Kit is cool!

While looking into better, easier, more maintainable ways of doing email, I came across Email::MIME::Kit. It thought it was so spiffy I had to tell somebody!

I'm used to doing things the old way, with Net::SMTP and MIME::Entity, which is not pretty, but neither is it difficult. Well, Email::Simple and Email::Sender::Simple make it much prettier and quite a bit easier, too. But great as they are, I am most pleased with another product of the Perl Email Project: Email::MIME::Kit.

This module lets you abstract away all of the dirty internals of email composition, templates, and attachments, and put them in a neatly packaged directory of 'kits'. The various components that work on a kit to produce a well-formed Email::MIME message are Moose classes, which you can extend* or replace using roles. RJBS did a presentation on this back in early 2009, but I missed it somehow. In a nutshell:

  • Your "kit" is a bunch of content files (templates, images, the like) and a manifest file that puts it all together.
  • Your manifest can be JSON or YAML out of the box.
  • Your email content can be rendered using a templating engine of your choice, plugins exist for Template::Toolkit, Text::Template, and MicroMason.
  • Your email headers, specified in the manifest, can also contain template directives.
  • You can very easily extend from having a text-only email to having a multipart/alternative format, with both text and HTML and embedded content.
  • You pass a small set of data (called the stash) which your kit then uses to create a complete email message according to your specifications.
  • You can write your own modules, using Moose and roles, to create new renderers, assemblers, manifest readers, whatever.

Finding this post was a tease, for me, because it hinted that you could write a custom assembler which would gather attachments for your kit, based on data in the stash. Like I said, the documentation is a little sparse, but it was actually really easy. To implement the custom assembler I literally only had to write one method**, assemble(), which asks the purchase order for paths to attachments, and returns a list of Email::MIME objects for each one. Easy!

Once I got it working, and created a kit for myself, all I have to do is say something like this:

my $kit = Email::MIME::Kit->new({ 
    source => $path_to_kit, 
    manifest_reader => 'YAML'
});

my $email = $kit->assemble({ order => $purchase_order });

sendmail($email);  # Using Email::Sender::Simple
I only had to specify a manifest_reader because I wanted YAML rather than the default JSON. The kit manifest looks like this:
---
renderer:  TT
header:
- From:    '"AT Billing" <...>'
- To:      '[% order.user.email %]'
- Subject: 'Your Sales Order, Number S[% order.id | format("%05d") %]'
alternatives:
- type:    text/plain
  path:    body.txt
attachments:
- assembler: ATOrder::Attachments

It's magical! My $purchase_order contains customer data, which I use for the subject line and the to: address of the email, as well as some template contents. Additionally, since my purchase order knows about its file attachments, I can extract those, too, using a custom assembler, and they become attachments on the email.

Now, I only just started playing with it, so by no means am I an expert. From the looks of the docs, this module is still in beta, the documentation is sparse, and there are warnings about using it for important things. For example, users are specifically urged not to extend the standard Assembler class. Thankfully, I haven't had to do that to get what I wanted. I've already reduced my email sending code to a subroutine that takes the name of a kit and some arguments, and pushed everything else into these kits themselves.

I'm looking forward to using this on another project where I've got no less than 83 different email templates. I think a nice extension there will be to read the templates out of a database rather than from a file. I'll have to post again if that works out, but I'm feeling confident.

I just hope this module gets some more well-deserved attention! Because it's cool.

** Actually, I did have to also write an empty stub for _set_attachment_info(), for some reason. But I think this is a minor bug, and I reported it.

1 Comment

Very nice. Will definitely look at it for my next project.

Leave a comment

About Adam Bellaire

user-pic I lurk in blogs about Perl.