From 5ef6aa60e834f2980c9894097254d064d059cb35 Mon Sep 17 00:00:00 2001 From: Randy Schmidt Date: Sat, 28 Aug 2010 12:31:42 -0400 Subject: [PATCH] Update to work with TMail and also the Mail gem. --- .bundle/config | 2 + Gemfile | 18 +++ lib/postmark.rb | 67 ++++++++-- lib/postmark/mail_message_extension.rb | 25 ++++ spec/postmark_spec.rb | 161 +++++++++++++++++++------ spec/spec_helper.rb | 2 + 6 files changed, 229 insertions(+), 46 deletions(-) create mode 100644 .bundle/config create mode 100644 Gemfile create mode 100644 lib/postmark/mail_message_extension.rb diff --git a/.bundle/config b/.bundle/config new file mode 100644 index 0000000..b8d4716 --- /dev/null +++ b/.bundle/config @@ -0,0 +1,2 @@ +--- +BUNDLE_WITHOUT: "" diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..ab06d9c --- /dev/null +++ b/Gemfile @@ -0,0 +1,18 @@ +source :gemcutter + +gem "rake" +gem "jeweler" +gem "tmail" +gem "mail" + +group :test do + gem "rspec" + gem "cucumber" + gem "activesupport" + gem "json" + gem "ruby-debug" + gem "fakeweb" + gem "fakeweb-matcher" + gem "timecop" + gem "yajl-ruby" +end \ No newline at end of file diff --git a/lib/postmark.rb b/lib/postmark.rb index 6b94689..0404c34 100644 --- a/lib/postmark.rb +++ b/lib/postmark.rb @@ -1,11 +1,10 @@ require 'net/http' require 'net/https' -require 'rubygems' -require 'tmail' -require 'postmark/tmail_mail_extension' require 'postmark/bounce' require 'postmark/json' require 'postmark/http_client' +require 'postmark/tmail_mail_extension' +require 'postmark/mail_message_extension' module Postmark @@ -13,6 +12,7 @@ class InvalidApiKeyError < StandardError; end class UnknownError < StandardError; end class InvalidMessageError < StandardError; end class InternalServerError < StandardError; end + class UnknownMessageType < StandardError; end module ResponseParsers autoload :Json, 'postmark/response_parsers/json' @@ -71,11 +71,11 @@ def sleep_between_retries def configure yield self end - + def send_through_postmark(message) #:nodoc: @retries = 0 begin - HttpClient.post("email", Postmark::Json.encode(convert_tmail(message))) + HttpClient.post("email", Postmark::Json.encode(convert(message))) rescue Exception => e if @retries < max_retries @retries += 1 @@ -85,17 +85,68 @@ def send_through_postmark(message) #:nodoc: end end end - + + def convert(message) + if message.is_a?(TMail::Mail) + convert_tmail(message) + else + convert_mail(message) + end + end + def delivery_stats HttpClient.get("deliverystats") end protected + + def convert_mail(message) + options = { "From" => message.from.to_s, "Subject" => message.subject } + + headers = extract_mail_headers(message) + options["Headers"] = headers unless headers.length == 0 + + options["To"] = message.to.join(", ") + + options["Tag"] = message.tag.to_s unless message.tag.nil? + + options["Cc"] = message.cc.join(", ").to_s unless message.cc.nil? + + options["Bcc"] = message.bcc.join(", ").to_s unless message.bcc.nil? + + if reply_to = message['reply-to'] + options["ReplyTo"] = reply_to.to_s + end + + html = message.body_html + text = message.body_text + if message.multipart? + options["HtmlBody"] = html + options["TextBody"] = text + elsif html + options["HtmlBody"] = html + else + options["TextBody"] = text + end + options + end + + def extract_mail_headers(message) + headers = [] + message.header.fields.each do |field| + key = field.name + value = field.value + next if bogus_headers.include? key.dup.downcase + name = key.split(/-/).map {|i| i.capitalize }.join('-') + headers << { "Name" => name, "Value" => value } + end + headers + end def convert_tmail(message) options = { "From" => message['from'].to_s, "To" => message['to'].to_s, "Subject" => message.subject } - headers = extract_headers(message) + headers = extract_tmail_headers(message) options["Headers"] = headers unless headers.length == 0 options["Tag"] = message.tag.to_s unless message.tag.nil? @@ -121,7 +172,7 @@ def convert_tmail(message) options end - def extract_headers(message) + def extract_tmail_headers(message) headers = [] message.each_header do |key, value| next if bogus_headers.include? key.dup.downcase diff --git a/lib/postmark/mail_message_extension.rb b/lib/postmark/mail_message_extension.rb new file mode 100644 index 0000000..b4cca2e --- /dev/null +++ b/lib/postmark/mail_message_extension.rb @@ -0,0 +1,25 @@ +module Mail + class Message + def tag + self["TAG"] + end + + def tag=(value) + self["TAG"] = value + end + + def body_html + unless html_part.nil? + html_part.body.to_s + end + end + + def body_text + if text_part.nil? + body.to_s + else + text_part.body.to_s + end + end + end +end \ No newline at end of file diff --git a/spec/postmark_spec.rb b/spec/postmark_spec.rb index 35a55d2..4375db2 100644 --- a/spec/postmark_spec.rb +++ b/spec/postmark_spec.rb @@ -2,7 +2,7 @@ describe "Postmark" do - let :message do + let :tmail_message do TMail::Mail.new.tap do |mail| mail.from = "sheldon@bigbangtheory.com" mail.to = "lenard@bigbangtheory.com" @@ -11,7 +11,7 @@ end end - let :html_message do + let :tmail_html_message do TMail::Mail.new.tap do |mail| mail.from = "sheldon@bigbangtheory.com" mail.to = "lenard@bigbangtheory.com" @@ -20,6 +20,40 @@ mail.content_type = "text/html" end end + + let :mail_message do + Mail.new do + from "sheldon@bigbangtheory.com" + to "lenard@bigbangtheory.com" + subject "Hello!" + body "Hello Sheldon!" + end + end + + let :mail_html_message do + mail = Mail.new do + from "sheldon@bigbangtheory.com" + to "lenard@bigbangtheory.com" + subject "Hello!" + html_part do + body "Hello Sheldon!" + end + end + end + + let :mail_multipart_message do + mail = Mail.new do + from "sheldon@bigbangtheory.com" + to "lenard@bigbangtheory.com" + subject "Hello!" + text_part do + body "Hello Sheldon!" + end + html_part do + body "Hello Sheldon!" + end + end + end context "service call" do @@ -29,28 +63,28 @@ it "should send email successfully" do FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email", {}) - Postmark.send_through_postmark(message) + Postmark.send_through_postmark(tmail_message) FakeWeb.should have_requested(:post, "http://api.postmarkapp.com/email") end it "should warn when header is invalid" do FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email", {:status => [ "401", "Unauthorized" ], :body => "Missing API token"}) - lambda { Postmark.send_through_postmark(message) }.should raise_error(Postmark::InvalidApiKeyError) + lambda { Postmark.send_through_postmark(tmail_message) }.should raise_error(Postmark::InvalidApiKeyError) end it "should warn when json is not ok" do FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email", {:status => [ "422", "Invalid" ], :body => "Invalid JSON"}) - lambda { Postmark.send_through_postmark(message) }.should raise_error(Postmark::InvalidMessageError) + lambda { Postmark.send_through_postmark(tmail_message) }.should raise_error(Postmark::InvalidMessageError) end it "should warn when server fails" do FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email", {:status => [ "500", "Internal Server Error" ]}) - lambda { Postmark.send_through_postmark(message) }.should raise_error(Postmark::InternalServerError) + lambda { Postmark.send_through_postmark(tmail_message) }.should raise_error(Postmark::InternalServerError) end it "should warn when unknown stuff fails" do FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email", {:status => [ "485", "Custom HTTP response status" ]}) - lambda { Postmark.send_through_postmark(message) }.should raise_error(Postmark::UnknownError) + lambda { Postmark.send_through_postmark(tmail_message) }.should raise_error(Postmark::UnknownError) end it "should retry 3 times" do @@ -59,7 +93,7 @@ { :status => [ 500, "Internal Server Error" ] }, { } ] ) - lambda { Postmark.send_through_postmark(message) }.should_not raise_error + lambda { Postmark.send_through_postmark(tmail_message) }.should_not raise_error end end @@ -77,49 +111,100 @@ end context "tmail parse" do + def be_serialized_to(json) + simple_matcher "be serialized to #{json}" do |message| + Postmark.send(:convert_tmail, tmail_message).should == JSON.parse(json) + end + end + it "should set text body for plain message" do - Postmark.send(:convert_tmail, message)['TextBody'].should_not be_nil + Postmark.send(:convert_tmail, tmail_message)['TextBody'].should_not be_nil end it "should set html body for html message" do - Postmark.send(:convert_tmail, html_message)['HtmlBody'].should_not be_nil + Postmark.send(:convert_tmail, tmail_html_message)['HtmlBody'].should_not be_nil end - end - - def be_serialized_to(json) - simple_matcher "be serialized to #{json}" do |message| - Postmark.send(:convert_tmail, message).should == JSON.parse(json) + + it "should encode custom headers headers properly" do + tmail_message["CUSTOM-HEADER"] = "header" + tmail_message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!", "Headers":[{"Name":"Custom-Header", "Value":"header"}]}] end - end - it "should encode custom headers headers properly" do - message["CUSTOM-HEADER"] = "header" - message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!", "Headers":[{"Name":"Custom-Header", "Value":"header"}]}] - end + it "should encode reply to" do + tmail_message.reply_to = ['a@a.com', 'b@b.com'] + tmail_message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "ReplyTo":"a@a.com, b@b.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}] + end - it "should encode reply to" do - message.reply_to = ['a@a.com', 'b@b.com'] - message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "ReplyTo":"a@a.com, b@b.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}] - end + it "should encode tag" do + tmail_message.tag = "invite" + tmail_message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "Tag":"invite", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}] + end - it "should encode tag" do - message.tag = "invite" - message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "Tag":"invite", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}] - end + it "should encode multiple recepients (TO)" do + tmail_message.to = ['a@a.com', 'b@b.com'] + tmail_message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"a@a.com, b@b.com", "TextBody":"Hello Sheldon!"}] + end - it "should encode multiple recepients (TO)" do - message.to = ['a@a.com', 'b@b.com'] - message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"a@a.com, b@b.com", "TextBody":"Hello Sheldon!"}] - end + it "should encode multiple recepients (CC)" do + tmail_message.cc = ['a@a.com', 'b@b.com'] + tmail_message.should be_serialized_to %q[{"Cc":"a@a.com, b@b.com", "Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}] + end - it "should encode multiple recepients (CC)" do - message.cc = ['a@a.com', 'b@b.com'] - message.should be_serialized_to %q[{"Cc":"a@a.com, b@b.com", "Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}] + it "should encode multiple recepients (BCC)" do + tmail_message.bcc = ['a@a.com', 'b@b.com'] + tmail_message.should be_serialized_to %q[{"Bcc":"a@a.com, b@b.com", "Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}] + end end + + context "mail parse" do + def be_serialized_to(json) + simple_matcher "be serialized to #{json}" do |message| + Postmark.send(:convert_mail, mail_message).should == JSON.parse(json) + end + end + + it "should set text body for plain message" do + Postmark.send(:convert_mail, mail_message)['TextBody'].should_not be_nil + end - it "should encode multiple recepients (BCC)" do - message.bcc = ['a@a.com', 'b@b.com'] - message.should be_serialized_to %q[{"Bcc":"a@a.com, b@b.com", "Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}] + it "should set html body for html message" do + Postmark.send(:convert_mail, mail_html_message)['HtmlBody'].should_not be_nil + end + + it "should set html and text body for multipart message" do + Postmark.send(:convert_mail, mail_multipart_message)['HtmlBody'].should_not be_nil + Postmark.send(:convert_mail, mail_multipart_message)['TextBody'].should_not be_nil + end + + it "should encode custom headers properly" do + mail_message.header["CUSTOM-HEADER"] = "header" + mail_message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!", "Headers":[{"Name":"Custom-Header", "Value":"header"}]}] + end + + it "should encode reply to" do + mail_message.reply_to = ['a@a.com', 'b@b.com'] + mail_message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "ReplyTo":"a@a.com, b@b.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}] + end + + it "should encode tag" do + mail_message.tag = "invite" + mail_message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "Tag":"invite", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}] + end + + it "should encode multiple recepients (TO)" do + mail_message.to = ['a@a.com', 'b@b.com'] + mail_message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"a@a.com, b@b.com", "TextBody":"Hello Sheldon!"}] + end + + it "should encode multiple recepients (CC)" do + mail_message.cc = ['a@a.com', 'b@b.com'] + mail_message.should be_serialized_to %q[{"Cc":"a@a.com, b@b.com", "Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}] + end + + it "should encode multiple recepients (BCC)" do + mail_message.bcc = ['a@a.com', 'b@b.com'] + mail_message.should be_serialized_to %q[{"Bcc":"a@a.com, b@b.com", "Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}] + end end context "JSON library support" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b27d6f5..5531bbd 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,7 @@ $LOAD_PATH.unshift(File.dirname(__FILE__)) $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) +require 'mail' +require 'tmail' require 'postmark' require 'rubygems' require 'active_support'