the 2009 PEP talk!!!
...I work there!
YAPC::NA 2006
PEP: Thoughts from Pobox
        YAPC::NA 2006
YAPC::NA 2007
How I Learned To
Stop Worrying and Love Email
          YAPC::NA 2007
YAPC::NA 2008
Email
hates the living!
      YAPC::NA 2008
What’s the best way to
deal with horrible code?
Write more code!


...so I did! The last year has been really productive. Lots of obnoxious problems were sorted out. I’m
...
rjbs
               <3
             email
the 2009 PEP talk!!!
sending email
MIME::Lite
                       Mail::Send
                      Mail::Sender
                      Mail::Sendmail

so, ...
Mail::Mailer


this lets you send mail through pluggable backends, and you provide the mail message. not bad, but
not grea...
Email::Send


Email::Send simplifies by expecting a string, being a hashref, and being in theory easier to extend (by
usin...
this is probably about about as good as a number of them.
system(“sendmail @opts < $tempfile”)
         && die sprintf “sendmail died: %s”, errstr($?);




this is probably about a...
Mail::Transport


So, Mail::Transport isn’t bad. It’s part of the Mail-Box distro, which means it tends to get things righ...
Email::Sender
Email::Sender
Email::Sender

Email-Sender uses Møøse
Email::Sender

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

Email-Sender uses Møøse
Email::Sender is a role
you use Email::Sender::Simple
Email::Sender::Simple


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

              my $email = $customer->build_...
Email::Sender::Simple
              use Email::Sender::Simple qw(sendmail);

              my $email = $customer->build_we...
Email::Sender::Simple

         my $smtp = Email::Sender::Transport::SMTP->new({
           host => ‘sasl.pobox.com’,
    ...
Transports




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




SQLite is especially handy because you can dump all mail from a forking program in...
Transports
                Sendmail
                SMTP




SQLite is especially handy because you can dump all mail from...
Transports
                Sendmail
                SMTP
                Persist. SMTP




SQLite is especially handy beca...
Transports
                Sendmail
                SMTP
                Persist. SMTP
                Maildir, Mbox


SQL...
Transports
                Sendmail                                   DevNull
                SMTP
                Persist...
Transports
                Sendmail                                   DevNull
                SMTP                        ...
Transports
                Sendmail                                   DevNull
                SMTP                        ...
Transports
                Sendmail                                   DevNull
                SMTP                        ...
Email::Sender::Simple

         my $smtp = Email::Sender::Transport::SMTP->new({
           host => ‘sasl.pobox.com’,
    ...
Email::Sender::Simple


         walrus!rjbs:~$ EMAIL_SENDER_TRANSPORT=Maildir
           my-awesome-program --auden




n...
Programmable Failure




so, for example, we have a system that distributes mail across a cluster of hosts with various
me...
Programmable Failure

                 ::Failable transport




so, for example, we have a system that distributes mail ac...
Programmable Failure

                 ::Failable transport
                 wrap any transport




so, for example, we ha...
Programmable Failure

                 ::Failable transport
                 wrap any transport
                 make it f...
Other Random Senderisms
Other Random Senderisms


  structured failure
Other Random Senderisms


  structured failure
  partial successes
making email messages
Email::Simple
Email::MIME
these are pretty low level
you probably want to
send only a few messages
  (but with differences)
MIME Crap to Deal With
MIME Crap to Deal With

 header encoding
MIME Crap to Deal With

 header encoding
 content encoding
MIME Crap to Deal With

 header encoding
 content encoding
 building html & plain parts
MIME Crap to Deal With

 header encoding
 content encoding
 building html & plain parts
 attaching files
Other Crap to Deal With
Other Crap to Deal With

 templated documents
Other Crap to Deal With

 templated documents
 validate parameters
Other Crap to Deal With

 templated documents
 validate parameters
 reusable hunks of content
hate!
Existing Solutions
Existing Solutions
none?
Existing Solutions
none?
horrible hacks
Existing Solutions
none?
horrible hacks
 make a template in TT
Existing Solutions
none?
horrible hacks
 make a template in TT
 render to html
Existing Solutions
none?
horrible hacks
 make a template in TT
 render to html
 html-to-text
Existing Solutions
none?
horrible hacks
 make a template in TT
 render to html
 html-to-text
 hope you guess right at head...
Email::MIME::Kit
Email::MIME::Kit
Email::MIME::Kit

a bunch of files
Email::MIME::Kit

a bunch of files
with instructions on how to
assemble them
Email::MIME::Kit

a bunch of files
with instructions on how to
assemble them
and some other data
Kit Manifest
{
    "renderer" : "TT",
    "header"     : [
       { "Subject": "Hello [% user.name %]"        },
       { ...
Assembling the Kit


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

my $email = $kit->assemble({ user => $use...
Dear ,

              Thank you for being a customer since .

              Your account has been due to . Please
        ...
Kit Manifest
{
    "renderer" : "TT",
    “validator”: “Rx”,
    "header"     : [
       { "Subject": "Hello [% user.name ...
rx.json

{
    “type”: “//rec”,
    “required”: {
      “user”: {
        “type”: “/perl/obj”,
        “isa” : “User::Acco...
Assembling the Kit

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

my $email = $kit->assemble({ user => undef...
Annoying Crap: Dealt With
Annoying Crap: Dealt With
  if user.name is Ævar...
Annoying Crap: Dealt With
  if user.name is Ævar...
  if attachments are binary...
Annoying Crap: Dealt With
  if user.name is Ævar...
  if attachments are binary...
  text-only (singlepart) mail...
Annoying Crap: Dealt With
  if user.name is Ævar...
  if attachments are binary...
  text-only (singlepart) mail...
  HTML...
...so what’s still annoying?
Some Kits...




so, you love mkits and you’re writing them all the time... now you start having this problem...
Some Kits...
          ./body.html
          ./body.txt
          ./logo.jpg
          ./background.jpg
          ./manife...
Some Kits...
          ./body.html                                   ./body.html
          ./body.txt                     ...
Some Kits...
          ./body.html                                   ./body.html
          ./body.txt                     ...
Some Kits...
          ./body.html                                   ./body.html
          ./body.txt                     ...
Some Kits...
          ./body.html                                 ./body.html
          ./body.txt                       ...
The Kit Reader
The Kit Reader

EMKit gets at its contents
with the kit reader
The Kit Reader

EMKit gets at its contents
with the kit reader
normally, just looks for files
in the kit directory
SWAK!




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

         you can write your own kit reader




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

         you can write your own kit reader
         SWAK: Path::Resolver




/fs    (can be chrooted)
/dist
/kit (d...
SWAK!

         you can write your own kit reader
         SWAK: Path::Resolver

                           /kit/body.html...
SWAK!

         you can write your own kit reader
         SWAK: Path::Resolver

                           /kit/body.html...
SWAK!

         you can write your own kit reader
         SWAK: Path::Resolver

                           /kit/body.html...
SWAK in Use
       ./body.html
       ./body.txt
       ./manifest.json



       ./body.html
       ./body.txt
       ./m...
SWAK in Use
       ./body.html
       ./body.txt
       ./manifest.json



       ./body.html                             ...
SWAK in Use
       ./body.html
       ./body.txt
       ./manifest.json



       ./body.html                             ...
EMK::Assembler::Markdown
       ./body.mkdn
       ./manifest.json


                                                     ...
EMK::Assembler::Markdown
./body.mkdn
./manifest.json


                  /dist/YourApp/logo.jpg
                  /dist/Yo...
Thank you!
i &lt;3 email
i &lt;3 email
i &lt;3 email
Upcoming SlideShare
Loading in …5
×

i &lt;3 email

1,984 views

Published on

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

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,984
On SlideShare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
8
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

i &lt;3 email

  1. 1. the 2009 PEP talk!!!
  2. 2. ...I work there!
  3. 3. YAPC::NA 2006
  4. 4. PEP: Thoughts from Pobox YAPC::NA 2006
  5. 5. YAPC::NA 2007
  6. 6. How I Learned To Stop Worrying and Love Email YAPC::NA 2007
  7. 7. YAPC::NA 2008
  8. 8. Email hates the living! YAPC::NA 2008
  9. 9. What’s the best way to deal with horrible code?
  10. 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...
  11. 11. rjbs <3 email the 2009 PEP talk!!!
  12. 12. sending email
  13. 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. 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. 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.
  16. 16. this is probably about about as good as a number of them.
  17. 17. system(“sendmail @opts < $tempfile”) && die sprintf “sendmail died: %s”, errstr($?); this is probably about about as good as a number of them.
  18. 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
  19. 19. Email::Sender
  20. 20. Email::Sender
  21. 21. Email::Sender Email-Sender uses Møøse
  22. 22. Email::Sender Email-Sender uses Møøse Email::Sender is a role
  23. 23. Email::Sender Email-Sender uses Møøse Email::Sender is a role you use Email::Sender::Simple
  24. 24. 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.
  25. 25. 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.
  26. 26. 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:
  27. 27. Transports SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test database.
  28. 28. Transports Sendmail SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test database.
  29. 29. Transports Sendmail SMTP SQLite is especially handy because you can dump all mail from a forking program into an easy-to-test database.
  30. 30. 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.
  31. 31. 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.
  32. 32. 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.
  33. 33. 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.
  34. 34. 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.
  35. 35. 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.
  36. 36. 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?
  37. 37. 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
  38. 38. 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
  39. 39. 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
  40. 40. 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
  41. 41. 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
  42. 42. Other Random Senderisms
  43. 43. Other Random Senderisms structured failure
  44. 44. Other Random Senderisms structured failure partial successes
  45. 45. making email messages
  46. 46. Email::Simple
  47. 47. Email::MIME
  48. 48. these are pretty low level
  49. 49. you probably want to send only a few messages (but with differences)
  50. 50. MIME Crap to Deal With
  51. 51. MIME Crap to Deal With header encoding
  52. 52. MIME Crap to Deal With header encoding content encoding
  53. 53. MIME Crap to Deal With header encoding content encoding building html & plain parts
  54. 54. MIME Crap to Deal With header encoding content encoding building html & plain parts attaching files
  55. 55. Other Crap to Deal With
  56. 56. Other Crap to Deal With templated documents
  57. 57. Other Crap to Deal With templated documents validate parameters
  58. 58. Other Crap to Deal With templated documents validate parameters reusable hunks of content
  59. 59. hate!
  60. 60. Existing Solutions
  61. 61. Existing Solutions none?
  62. 62. Existing Solutions none? horrible hacks
  63. 63. Existing Solutions none? horrible hacks make a template in TT
  64. 64. Existing Solutions none? horrible hacks make a template in TT render to html
  65. 65. Existing Solutions none? horrible hacks make a template in TT render to html html-to-text
  66. 66. Existing Solutions none? horrible hacks make a template in TT render to html html-to-text hope you guess right at headers
  67. 67. Email::MIME::Kit
  68. 68. Email::MIME::Kit
  69. 69. Email::MIME::Kit a bunch of files
  70. 70. Email::MIME::Kit a bunch of files with instructions on how to assemble them
  71. 71. Email::MIME::Kit a bunch of files with instructions on how to assemble them and some other data
  72. 72. Kit Manifest { "renderer" : "TT", "header" : [ { "Subject": "Hello [% user.name %]" }, { "From": "Test Sender <test@test.com>" }, { "To": "[% user.email %]" } ], "alternatives": [ { "type": "text/plain", "path": "body.txt" }, { "type": "text/html", "path": "body.html" } ], "attachments": [ { "path": "demands.rtf" } ] }
  73. 73. Assembling the Kit my $kit = Email::MIME::Kit->new({ source => ‘msg.mkit’ }); my $email = $kit->assemble({ user => $user_object }); sendmail($email);
  74. 74. 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...
  75. 75. Kit Manifest { "renderer" : "TT", “validator”: “Rx”, "header" : [ { "Subject": "Hello [% user.name %]" }, { "From": "Test Sender <test@test.com>" }, { "To": "[% user.email %]" } ], "alternatives": [ { "type": "text/plain", "path": "body.txt" }, { "type": "text/html", "path": "body.html" } ], "attachments": [ { "path": "demands.rtf" } ] }
  76. 76. rx.json { “type”: “//rec”, “required”: { “user”: { “type”: “/perl/obj”, “isa” : “User::Account” } } }
  77. 77. 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
  78. 78. Annoying Crap: Dealt With
  79. 79. Annoying Crap: Dealt With if user.name is Ævar...
  80. 80. Annoying Crap: Dealt With if user.name is Ævar... if attachments are binary...
  81. 81. Annoying Crap: Dealt With if user.name is Ævar... if attachments are binary... text-only (singlepart) mail...
  82. 82. Annoying Crap: Dealt With if user.name is Ævar... if attachments are binary... text-only (singlepart) mail... HTML with images as attachments...
  83. 83. ...so what’s still annoying?
  84. 84. Some Kits... so, you love mkits and you’re writing them all the time... now you start having this problem...
  85. 85. 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...
  86. 86. 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...
  87. 87. 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...
  88. 88. 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...
  89. 89. 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 those files are identical everywhere. blaugh! duplication baaaaaaaaaad
  90. 90. The Kit Reader
  91. 91. The Kit Reader EMKit gets at its contents with the kit reader
  92. 92. The Kit Reader EMKit gets at its contents with the kit reader normally, just looks for files in the kit directory
  93. 93. SWAK! /fs (can be chrooted) /dist /kit (default, too)
  94. 94. SWAK! you can write your own kit reader /fs (can be chrooted) /dist /kit (default, too)
  95. 95. SWAK! you can write your own kit reader SWAK: Path::Resolver /fs (can be chrooted) /dist /kit (default, too)
  96. 96. SWAK! you can write your own kit reader SWAK: Path::Resolver /kit/body.html /fs (can be chrooted) /dist /kit (default, too)
  97. 97. SWAK! you can write your own kit reader SWAK: Path::Resolver /kit/body.html body.html /fs (can be chrooted) /dist /kit (default, too)
  98. 98. 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)
  99. 99. 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?
  100. 100. 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?
  101. 101. 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
  102. 102. 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.
  103. 103. EMK::Assembler::Markdown ./body.mkdn ./manifest.json /dist/YourApp/logo.jpg /dist/YourApp/background.jpg ./body.mkdn /dist/YourApp/wrapper.html ./manifest.json /dist/YourApp/wrapper.text ./body.mkdn ./manifest.json
  104. 104. Thank you!

×