10. Write more code!
...so I did! The last year has been really productive. Lots of obnoxious problems were sorted out. I’m
really happy with what we got accomplished, and so finally I can with a straight face...
13. MIME::Lite
Mail::Send
Mail::Sender
Mail::Sendmail
so, here are some of the libraries for sending mail. these all let you specify subject, to/from, and
content -- so you can’t actually make an email just hwo you want and pass it in. worthless for any real
use
14. Mail::Mailer
this lets you send mail through pluggable backends, and you provide the mail message. not bad, but
not great; it has this weird API where it isa IO::Handle and you print to it like a filehandle to send your
message. subclassing is a pain because it’s a blessed globref
15. Email::Send
Email::Send simplifies by expecting a string, being a hashref, and being in theory easier to extend (by
using Module::Pluggable)
unfortunately, this pluggability (and some other oddities) ends up making extending things harder and
can cause it to silently lose mail. for real.
17. system(“sendmail @opts < $tempfile”)
&& die sprintf “sendmail died: %s”, errstr($?);
this is probably about about as good as a number of them.
18. Mail::Transport
So, Mail::Transport isn’t bad. It’s part of the Mail-Box distro, which means it tends to get things right,
but it also tends to be confusing and overwhelming for new users. If you’re already using it happily,
that’s great! We weren’t, though, and so we wrote something new, built using just the ideas we liked
from Email::Send, and
25. Email::Sender::Simple
use Email::Sender::Simple qw(sendmail);
my $email = $customer->build_welcome_email;
sendmail($email);
Yup. That’s about it. That will try to send mail with either sendmail or SMTP, depending on what’s
available. If it can’t send the message, it will throw.
26. Email::Sender::Simple
use Email::Sender::Simple qw(sendmail);
my $email = $customer->build_welcome_email;
sendmail(
$email,
{
to => @rcpts,
from => $sender,
}
);
In other words, you can specify the envelope separately from the header. This is of *vital* importance
to real email work. VERP, mailing lists, especially.
27. Email::Sender::Simple
my $smtp = Email::Sender::Transport::SMTP->new({
host => ‘sasl.pobox.com’,
port => 26,
});
sendmail($email, { transport => $smtp });
If you don’t like the auto-detected transport, you can specify one. Transports are really easy to write,
and a bunch already exist:
29. Transports
Sendmail
SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test
database.
30. Transports
Sendmail
SMTP
SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test
database.
31. Transports
Sendmail
SMTP
Persist. SMTP
SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test
database.
32. Transports
Sendmail
SMTP
Persist. SMTP
Maildir, Mbox
SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test
database.
33. Transports
Sendmail DevNull
SMTP
Persist. SMTP
Maildir, Mbox
SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test
database.
34. Transports
Sendmail DevNull
SMTP Print
Persist. SMTP
Maildir, Mbox
SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test
database.
35. Transports
Sendmail DevNull
SMTP Print
Persist. SMTP Test
Maildir, Mbox
SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test
database.
36. Transports
Sendmail DevNull
SMTP Print
Persist. SMTP Test
Maildir, Mbox SQLite
SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test
database.
37. Email::Sender::Simple
my $smtp = Email::Sender::Transport::SMTP->new({
host => ‘sasl.pobox.com’,
port => 26,
});
sendmail($email, { transport => $smtp });
so, you can do this, that’s fine, and you can use any of those cool transports when you call sendmail...
but what about if you want to change the whole default?
38. Email::Sender::Simple
walrus!rjbs:~$ EMAIL_SENDER_TRANSPORT=Maildir
my-awesome-program --auden
now *every* call to Email::Sender::Simple->send will deliver to ./Maildir so you can see exactly what it
would have sent
39. Programmable Failure
so, for example, we have a system that distributes mail across a cluster of hosts with various
mechanisms to cope with failure
Failable lets me test them by using a mailer that always works wrapped in predictable failure, easily
40. Programmable Failure
::Failable transport
so, for example, we have a system that distributes mail across a cluster of hosts with various
mechanisms to cope with failure
Failable lets me test them by using a mailer that always works wrapped in predictable failure, easily
41. Programmable Failure
::Failable transport
wrap any transport
so, for example, we have a system that distributes mail across a cluster of hosts with various
mechanisms to cope with failure
Failable lets me test them by using a mailer that always works wrapped in predictable failure, easily
42. Programmable Failure
::Failable transport
wrap any transport
make it fail when you want
so, for example, we have a system that distributes mail across a cluster of hosts with various
mechanisms to cope with failure
Failable lets me test them by using a mailer that always works wrapped in predictable failure, easily
76. Assembling the Kit
my $kit = Email::MIME::Kit->new({ source => ‘msg.mkit’ });
my $email = $kit->assemble({ user => $user_object });
sendmail($email);
77. Dear ,
Thank you for being a customer since .
Your account has been due to . Please
contact before at or we will be forced
to your lovely wife Tracy’s head.
Cheers,
Anonymous
Ugh. Our $user_object was undef. Now we get crap that looks like mail but stinks and makes us look
like idiots.
We can plug in a validator pretty easily, though...
87. Some Kits...
so, you love mkits and you’re writing them all the time... now you start having this problem...
88. Some Kits...
./body.html
./body.txt
./logo.jpg
./background.jpg
./manifest.json
so, you love mkits and you’re writing them all the time... now you start having this problem...
89. Some Kits...
./body.html ./body.html
./body.txt ./body.txt
./logo.jpg ./logo.jpg
./background.jpg ./background.jpg
./manifest.json ./manifest.json
so, you love mkits and you’re writing them all the time... now you start having this problem...
90. Some Kits...
./body.html ./body.html
./body.txt ./body.txt
./logo.jpg ./logo.jpg
./background.jpg ./background.jpg
./manifest.json ./manifest.json
./body.html
./body.txt
./logo.jpg
./background.jpg
./manifest.json
so, you love mkits and you’re writing them all the time... now you start having this problem...
91. Some Kits...
./body.html ./body.html
./body.txt ./body.txt
./logo.jpg ./logo.jpg
./background.jpg ./background.jpg
./manifest.json ./manifest.json
./body.html ./body.html
./body.txt ./body.txt
./logo.jpg ./logo.jpg
./background.jpg ./background.jpg
./manifest.json ./manifest.json
so, you love mkits and you’re writing them all the time... now you start having this problem...
95. The Kit Reader
EMKit gets at its contents
with the kit reader
normally, just looks for files
in the kit directory
96. SWAK!
/fs (can be chrooted)
/dist
/kit (default, too)
97. SWAK!
you can write your own kit reader
/fs (can be chrooted)
/dist
/kit (default, too)
98. SWAK!
you can write your own kit reader
SWAK: Path::Resolver
/fs (can be chrooted)
/dist
/kit (default, too)
99. SWAK!
you can write your own kit reader
SWAK: Path::Resolver
/kit/body.html
/fs (can be chrooted)
/dist
/kit (default, too)
100. SWAK!
you can write your own kit reader
SWAK: Path::Resolver
/kit/body.html
body.html
/fs (can be chrooted)
/dist
/kit (default, too)
101. SWAK!
you can write your own kit reader
SWAK: Path::Resolver
/kit/body.html
body.html
/fs/usr/share/app/body.html
/fs (can be chrooted)
/dist
/kit (default, too)
102. SWAK in Use
./body.html
./body.txt
./manifest.json
./body.html
./body.txt
./manifest.json
./body.html
./body.txt
./manifest.json
So, we can take those common files and put them in our dist’s shared resources, and reference them
with the “/dist/” prefix, which finds stuff in share-dirs.
So, awesome! What’s still annoying?
103. SWAK in Use
./body.html
./body.txt
./manifest.json
./body.html /dist/YourApp/logo.jpg
/dist/YourApp/background.jpg
./body.txt
./manifest.json
./body.html
./body.txt
./manifest.json
So, we can take those common files and put them in our dist’s shared resources, and reference them
with the “/dist/” prefix, which finds stuff in share-dirs.
So, awesome! What’s still annoying?
104. SWAK in Use
./body.html
./body.txt
./manifest.json
./body.html /dist/YourApp/logo.jpg
/dist/YourApp/background.jpg
./body.txt
./manifest.json
./body.html
./body.txt
./manifest.json
almost certainly, the html and text parts are (a) really close to each other within one kit (b) contain
really different parts between kits (c) contain common boilerplate between kits
105. EMK::Assembler::Markdown
./body.mkdn
./manifest.json
/dist/YourApp/logo.jpg
/dist/YourApp/background.jpg
./body.mkdn
./manifest.json
./body.mkdn
./manifest.json
So, let’s fix that, too. We replace the text and html parts with a single Markdown document. We’ll use
the Markdown itself for the plaintext part and we’ll turn it into HTML to use in the HTML part.
Of course, this still needs work. We want wrapper stuff and copyright and so on in both parts.