documenti

107
the 2009 PEP talk!!!

Upload: ricardo-signes

Post on 18-Jan-2015

1.461 views

Category:

Technology


1 download

DESCRIPTION

An overview of Email::Sender and Email::MIME::Kit, new Perl libraries to help ease the pain of dealing with email.

TRANSCRIPT

Page 1: Documenti

the 2009 PEP talk!!!

Page 2: Documenti

...I work there!

Page 3: Documenti

YAPC::NA 2006

Page 4: Documenti

PEP: Thoughts from PoboxYAPC::NA 2006

Page 5: Documenti

YAPC::NA 2007

Page 6: Documenti

How I Learned To Stop Worrying and Love Email

YAPC::NA 2007

Page 7: Documenti

YAPC::NA 2008

Page 8: Documenti

EmailYAPC::NA 2008

hates the living!

Page 9: Documenti

What’s the best way to deal with horrible code?

Page 10: Documenti

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

Page 11: Documenti

rjbs<3

emailthe 2009 PEP talk!!!

Page 12: Documenti

sending email

Page 13: Documenti

MIME::LiteMail::Send

Mail::SenderMail::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

Page 14: Documenti

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

Page 15: Documenti

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.

Page 16: Documenti

this is probably about about as good as a number of them.

Page 17: Documenti

system(“sendmail @opts < $tempfile”) && die sprintf “sendmail died: %s”, errstr($?);

this is probably about about as good as a number of them.

Page 18: Documenti

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

Page 19: Documenti
Page 20: Documenti

Email::Sender

Page 21: Documenti

Email::Sender

Page 22: Documenti

Email::Sender

Email-Sender uses Møøse

Page 23: Documenti

Email::Sender

Email-Sender uses MøøseEmail::Sender is a role

Page 24: Documenti

Email::Sender

Email-Sender uses MøøseEmail::Sender is a roleyou use Email::Sender::Simple

Page 25: Documenti

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.

Page 26: Documenti

use Email::Sender::Simple qw(sendmail);

my $email = $customer->build_welcome_email;

sendmail( $email, { to => \@rcpts, from => $sender, });

Email::Sender::Simple

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.

Page 27: Documenti

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:

Page 28: Documenti

Transports

SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test database.

Page 29: Documenti

TransportsSendmail

SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test database.

Page 30: Documenti

TransportsSendmail

SMTP

SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test database.

Page 31: Documenti

TransportsSendmail

SMTP

Persist. SMTP

SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test database.

Page 32: Documenti

TransportsSendmail

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.

Page 33: Documenti

TransportsSendmail

SMTP

Persist. SMTP

Maildir, Mbox

DevNull

SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test database.

Page 34: Documenti

TransportsSendmail

SMTP

Persist. SMTP

Maildir, Mbox

DevNull

Print

SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test database.

Page 35: Documenti

TransportsSendmail

SMTP

Persist. SMTP

Maildir, Mbox

DevNull

Print

Test

SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test database.

Page 36: Documenti

TransportsSendmail

SMTP

Persist. SMTP

Maildir, Mbox

DevNull

Print

Test

SQLite

SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test database.

Page 37: Documenti

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?

Page 38: Documenti

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

Page 39: Documenti

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

Page 40: Documenti

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

Page 41: Documenti

Programmable Failure

::Failable transportwrap 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

Page 42: Documenti

Programmable Failure

::Failable transportwrap any transportmake 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

Page 43: Documenti

Other Random Senderisms

Page 44: Documenti

Other Random Senderisms

structured failure

Page 45: Documenti

Other Random Senderisms

structured failurepartial successes

Page 46: Documenti

making email messages

Page 47: Documenti

Email::Simple

Page 48: Documenti

Email::MIME

Page 49: Documenti

these are pretty low level

Page 50: Documenti

you probably want to send only a few messages

(but with differences)

Page 51: Documenti

MIME Crap to Deal With

Page 52: Documenti

MIME Crap to Deal With

header encoding

Page 53: Documenti

MIME Crap to Deal With

header encodingcontent encoding

Page 54: Documenti

MIME Crap to Deal With

header encodingcontent encodingbuilding html & plain parts

Page 55: Documenti

MIME Crap to Deal With

header encodingcontent encodingbuilding html & plain partsattaching files

Page 56: Documenti

Other Crap to Deal With

Page 57: Documenti

Other Crap to Deal With

templated documents

Page 58: Documenti

Other Crap to Deal With

templated documentsvalidate parameters

Page 59: Documenti

Other Crap to Deal With

templated documentsvalidate parametersreusable hunks of content

Page 60: Documenti
Page 61: Documenti

hate!

Page 62: Documenti

Existing Solutions

Page 63: Documenti

Existing Solutionsnone?

Page 64: Documenti

Existing Solutionsnone?horrible hacks

Page 65: Documenti

Existing Solutionsnone?horrible hacks

make a template in TT

Page 66: Documenti

Existing Solutionsnone?horrible hacks

make a template in TTrender to html

Page 67: Documenti

Existing Solutionsnone?horrible hacks

make a template in TTrender to htmlhtml-to-text

Page 68: Documenti

Existing Solutionsnone?horrible hacks

make a template in TTrender to htmlhtml-to-texthope you guess right at headers

Page 69: Documenti
Page 70: Documenti

Email::MIME::Kit

Page 71: Documenti

Email::MIME::Kit

Page 72: Documenti

Email::MIME::Kit

a bunch of files

Page 73: Documenti

Email::MIME::Kit

a bunch of fileswith instructions on how to assemble them

Page 74: Documenti

Email::MIME::Kit

a bunch of fileswith instructions on how to assemble themand some other data

Page 75: Documenti

Kit Manifest{ "renderer" : "TT", "header" : [ { "Subject": "Hello [% user.name %]" }, { "From": "Test Sender <[email protected]>" }, { "To": "[% user.email %]" } ], "alternatives": [ { "type": "text/plain", "path": "body.txt" }, { "type": "text/html", "path": "body.html" } ], "attachments": [ { "path": "demands.rtf" } ]}

Page 76: Documenti

Assembling the Kit

my $kit = Email::MIME::Kit->new({ source => ‘msg.mkit’ });

my $email = $kit->assemble({ user => $user_object });

sendmail($email);

Page 77: Documenti

Dear ,

Thank you for being a customer since .

Your account has been due to . Please contact before at or we will be forcedto 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...

Page 78: Documenti

Kit Manifest{ "renderer" : "TT", “validator”: “Rx”, "header" : [ { "Subject": "Hello [% user.name %]" }, { "From": "Test Sender <[email protected]>" }, { "To": "[% user.email %]" } ], "alternatives": [ { "type": "text/plain", "path": "body.txt" }, { "type": "text/html", "path": "body.html" } ], "attachments": [ { "path": "demands.rtf" } ]}

Page 79: Documenti

rx.json

{ “type”: “//rec”, “required”: { “user”: { “type”: “/perl/obj”, “isa” : “User::Account” } }}

Page 80: Documenti

Assembling the Kit

my $kit = Email::MIME::Kit->new({ source => ‘msg.mkit’ });

my $email = $kit->assemble({ user => undef });

undefined value tag: [ “/err/nil” ], data path: [ “user” ], schema path: [ “user” ]at slide 42, line 3

Page 81: Documenti

Annoying Crap: Dealt With

Page 82: Documenti

Annoying Crap: Dealt With

if user.name is Ævar...

Page 83: Documenti

Annoying Crap: Dealt With

if user.name is Ævar...if attachments are binary...

Page 84: Documenti

Annoying Crap: Dealt With

if user.name is Ævar...if attachments are binary...text-only (singlepart) mail...

Page 85: Documenti

Annoying Crap: Dealt With

if user.name is Ævar...if attachments are binary...text-only (singlepart) mail...HTML with images as attachments...

Page 86: Documenti

...so what’s still annoying?

Page 87: Documenti

Some Kits...

so, you love mkits and you’re writing them all the time... now you start having this problem...

Page 88: Documenti

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

Page 89: Documenti

Some Kits..../body.html./body.txt./logo.jpg./background.jpg./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...

Page 90: Documenti

Some Kits..../body.html./body.txt./logo.jpg./background.jpg./manifest.json

./body.html

./body.txt

./logo.jpg

./background.jpg

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

Page 91: Documenti

Some Kits..../body.html./body.txt./logo.jpg./background.jpg./manifest.json

./body.html

./body.txt

./logo.jpg

./background.jpg

./manifest.json

./body.html

./body.txt

./logo.jpg

./background.jpg

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

Page 92: Documenti

Some Kits..../body.html./body.txt./logo.jpg./background.jpg./manifest.json

./body.html

./body.txt

./logo.jpg

./background.jpg

./manifest.json

./body.html

./body.txt

./logo.jpg

./background.jpg

./manifest.json

./body.html

./body.txt

./logo.jpg

./background.jpg

./manifest.json

those files are identical everywhere. blaugh! duplication baaaaaaaaaad

Page 93: Documenti

The Kit Reader

Page 94: Documenti

The Kit Reader

EMKit gets at its contents with the kit reader

Page 95: Documenti

The Kit Reader

EMKit gets at its contents with the kit readernormally, just looks for files in the kit directory

Page 96: Documenti

SWAK!

/fs (can be chrooted)/dist/kit (default, too)

...and more...

Page 97: Documenti

SWAK!

you can write your own kit reader

/fs (can be chrooted)/dist/kit (default, too)

...and more...

Page 98: Documenti

SWAK!

you can write your own kit readerSWAK: Path::Resolver

/fs (can be chrooted)/dist/kit (default, too)

...and more...

Page 99: Documenti

SWAK!

you can write your own kit readerSWAK: Path::Resolver

/kit/body.html

/fs (can be chrooted)/dist/kit (default, too)

...and more...

Page 100: Documenti

SWAK!

you can write your own kit readerSWAK: Path::Resolver

/kit/body.htmlbody.html

/fs (can be chrooted)/dist/kit (default, too)

...and more...

Page 101: Documenti

SWAK!

you can write your own kit readerSWAK: Path::Resolver

/kit/body.htmlbody.html/fs/usr/share/app/body.html

/fs (can be chrooted)/dist/kit (default, too)

...and more...

Page 102: Documenti

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?

Page 103: Documenti

SWAK in Use

./body.html

./body.txt

./manifest.json

./body.html

./body.txt

./manifest.json

./body.html

./body.txt

./manifest.json

/dist/YourApp/logo.jpg/dist/YourApp/background.jpg

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?

Page 104: Documenti

SWAK in Use

./body.html

./body.txt

./manifest.json

./body.html

./body.txt

./manifest.json

./body.html

./body.txt

./manifest.json

/dist/YourApp/logo.jpg/dist/YourApp/background.jpg

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

Page 105: Documenti

EMK::Assembler::Markdown

./body.mkdn

./manifest.json

./body.mkdn

./manifest.json

./body.mkdn

./manifest.json

/dist/YourApp/logo.jpg/dist/YourApp/background.jpg

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.

Page 106: Documenti

./body.mkdn

./manifest.json

./body.mkdn

./manifest.json

./body.mkdn

./manifest.json

/dist/YourApp/logo.jpg/dist/YourApp/background.jpg/dist/YourApp/wrapper.html/dist/YourApp/wrapper.text

EMK::Assembler::Markdown

Page 107: Documenti

Thank you!