challengepost-postmark 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/.rake_tasks ADDED
@@ -0,0 +1,22 @@
1
+ build
2
+ check_dependencies
3
+ check_dependencies:development
4
+ check_dependencies:runtime
5
+ clobber_rcov
6
+ clobber_rdoc
7
+ features
8
+ gemcutter:release
9
+ gemspec
10
+ gemspec:generate
11
+ gemspec:validate
12
+ install
13
+ rcov
14
+ rdoc
15
+ release
16
+ rerdoc
17
+ spec
18
+ version
19
+ version:bump:major
20
+ version:bump:minor
21
+ version:bump:patch
22
+ version:write
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Petyo Ivanov
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,83 @@
1
+ = Postmark gem
2
+
3
+ Ruby gem for sending emails through http://postmarkapp.com HTTP API
4
+
5
+ == Install
6
+
7
+ The gem is hosted at gemcutter. If you don't have it, install it first:
8
+
9
+ sudo gem install gemcutter
10
+ Then
11
+ sudo gem install postmark
12
+
13
+ == Example
14
+
15
+ #!/usr/bin/env ruby
16
+
17
+ require 'rubygems'
18
+ require 'postmark'
19
+ require 'tmail'
20
+
21
+ Postmark.api_key = "your-api-key"
22
+
23
+ message = TMail::Mail.new
24
+ # make sure you have a sender signature with that email
25
+ # from and to also accept arrays of emails.
26
+ message.from = "leonard@bigbangtheory.com"
27
+ message.to = "Sheldon Cooper <sheldon@bigbangtheory.com>"
28
+ message.subject = "Hi Sheldon!"
29
+ message.content_type = "text/html"
30
+ message.body = "Hello my friend!"
31
+ # set custom headers at will.
32
+ message["CUSTOM-HEADER"] = "my custom header value"
33
+ # tag message
34
+ message.tag = "my-tracking-tag"
35
+ # set reply to if you need; also, you can pass array of emails.
36
+ message.reply_to = "penny@bigbangtheory.com"
37
+
38
+ p Postmark.send_through_postmark(message).body
39
+
40
+ You can retrieve various information about your server state using the Public bounces API - http://developer.postmarkapp.com/bounces
41
+
42
+ # get delivery stats
43
+ Postmark.delivery_stats # json
44
+ Postmark::Bounce.all # [ bounce, bounce ... ]
45
+ bounce = Postmark::Bounce.find(bounce_id) # Bounce
46
+ bounce.dump # string, containing raw SMTP data
47
+ bounce.reactivate # reactivate hard bounce
48
+
49
+ == Encryption
50
+
51
+ To use SSL encryption when sending email configure the library as follows:
52
+
53
+ Postmark.secure = true
54
+
55
+
56
+ == Requirements
57
+
58
+ The gem relies on TMail for building the message. You will also need postmark account, server and sender signature set up to use it.
59
+ If you plan using it in a rails project, check out the postmark-rails gem, which is meant to integrate with ActionMailer.
60
+
61
+ The plugin will try to use ActiveSupport Json if it is already included. If not, it will attempt using the built-in ruby Json library.
62
+ You can also explicitly specify which one to be used, using
63
+
64
+ Postmark.response_parser_class = :Json # :ActiveSupport or :Yajl is also supported.
65
+
66
+ == Limitations
67
+
68
+ Currently postmark API does not support attachments. For more information, check the docs at:
69
+
70
+ http://developer.postmarkapp.com
71
+
72
+ == Note on Patches/Pull Requests
73
+
74
+ * Fork the project.
75
+ * Make your feature addition or bug fix.
76
+ * Add tests for it. This is important so I don't break it in a
77
+ future version unintentionally.
78
+ * Commit, do not mess with rakefile, version, or history.
79
+ * Send me a pull request. Bonus points for topic branches.
80
+
81
+ == Copyright
82
+
83
+ Copyright (c) 2009 Wildbit LLC. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,69 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "challengepost-postmark"
8
+ gem.summary = %Q{Ruby gem for sending emails through http://postmarkapp.com HTTP API}
9
+ gem.description = %Q{Ruby gem for sending emails through http://postmarkapp.com HTTP API. It relieas on the mail gem for message construction.}
10
+ gem.email = "underlog@gmail.com"
11
+ gem.homepage = "http://postmarkapp.com"
12
+ gem.authors = ["Petyo Ivanov"]
13
+ gem.add_development_dependency "rspec"
14
+ gem.add_development_dependency "cucumber"
15
+ gem.add_dependency "mail"
16
+ gem.files << "lib/postmark/mail_message_extension.rb"
17
+ gem.post_install_message = %q[
18
+ ==================
19
+ Thanks for installing the postmark gem. If you don't have an account, please sign up at http://postmarkapp.com/.
20
+ Review the README.rdoc for implementation details and examples.
21
+ ==================
22
+ ]
23
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
24
+ end
25
+ Jeweler::GemcutterTasks.new
26
+ rescue LoadError
27
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
28
+ end
29
+
30
+ require 'spec/rake/spectask'
31
+ Spec::Rake::SpecTask.new(:spec) do |spec|
32
+ spec.libs << 'lib' << 'spec'
33
+ spec.spec_files = FileList['spec/**/*_spec.rb']
34
+ end
35
+
36
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
37
+ spec.libs << 'lib' << 'spec'
38
+ spec.pattern = 'spec/**/*_spec.rb'
39
+ spec.rcov = true
40
+ end
41
+
42
+ task :spec => :check_dependencies
43
+
44
+ begin
45
+ require 'cucumber/rake/task'
46
+ Cucumber::Rake::Task.new(:features)
47
+
48
+ task :features => :check_dependencies
49
+ rescue LoadError
50
+ task :features do
51
+ abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
52
+ end
53
+ end
54
+
55
+ task :default => :spec
56
+
57
+ require 'rake/rdoctask'
58
+ Rake::RDocTask.new do |rdoc|
59
+ if File.exist?('VERSION')
60
+ version = File.read('VERSION')
61
+ else
62
+ version = ""
63
+ end
64
+
65
+ rdoc.rdoc_dir = 'rdoc'
66
+ rdoc.title = "postmark #{version}"
67
+ rdoc.rdoc_files.include('README*')
68
+ rdoc.rdoc_files.include('lib/**/*.rb')
69
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.7.1
@@ -0,0 +1,15 @@
1
+ Feature: sending emails through Postmark API
2
+ In order to communicate effectively with the world
3
+ My application should be able to deliver emails through the postmark API
4
+
5
+ Background:
6
+ Given the service listens to "http://postmarkapp.local"
7
+ And I have an account with api key "mykey"
8
+
9
+ Scenario: Sending simple email
10
+ Given I send the following email:
11
+ | From | leonard@bigbangtheory.com |
12
+ | To | sheldon@bigbangtheory.com |
13
+ | Subject | About that last night |
14
+ | Body | Sorry for not coming |
15
+ Then the service should receive an email on behalf of "mykey" for "sheldon@bigbangtheory.com"
File without changes
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
2
+ require 'postmark'
3
+
4
+ require 'spec/expectations'
@@ -0,0 +1,56 @@
1
+ module Postmark
2
+ class Bounce
3
+
4
+ attr_reader :email, :bounced_at, :type, :details, :name, :id, :server_id, :tag
5
+
6
+ def initialize(values = {})
7
+ @id = values["ID"]
8
+ @email = values["Email"]
9
+ @bounced_at = Time.parse(values["BouncedAt"])
10
+ @type = values["Type"]
11
+ @name = values["Name"]
12
+ @details = values["Details"]
13
+ @tag = values["Tag"]
14
+ @dump_available = values["DumpAvailable"]
15
+ @inactive = values["Inactive"]
16
+ @can_activate = values["CanActivate"]
17
+ end
18
+
19
+ def inactive?
20
+ !!@inactive
21
+ end
22
+
23
+ def can_activate?
24
+ !!@can_activate
25
+ end
26
+
27
+ def dump
28
+ Postmark::HttpClient.get("bounces/#{id}/dump")["Body"]
29
+ end
30
+
31
+ def activate
32
+ Bounce.new(Postmark::HttpClient.put("bounces/#{id}/activate")["Bounce"])
33
+ end
34
+
35
+ def dump_available?
36
+ !!@dump_available
37
+ end
38
+
39
+ class << self
40
+ def find(id)
41
+ Bounce.new(Postmark::HttpClient.get("bounces/#{id}"))
42
+ end
43
+
44
+ def all(options = {})
45
+ options[:count] ||= 30
46
+ options[:offset] ||= 0
47
+ Postmark::HttpClient.get("bounces", options).map { |bounce_json| Bounce.new(bounce_json) }
48
+ end
49
+
50
+ def tags
51
+ Postmark::HttpClient.get("bounces/tags")
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,77 @@
1
+ require 'cgi'
2
+
3
+ module Postmark
4
+ module HttpClient
5
+ class << self
6
+ def post(path, data = '')
7
+ handle_response(http.post(url_path(path), data, headers))
8
+ end
9
+
10
+ def put(path, data = '')
11
+ handle_response(http.put(url_path(path), data, headers))
12
+ end
13
+
14
+ def get(path, query = {})
15
+ handle_response(http.get(url_path(path + to_query_string(query)), headers))
16
+ end
17
+
18
+ protected
19
+
20
+ def to_query_string(hash)
21
+ return "" if hash.empty?
22
+ "?" + hash.map { |key, value| "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}" }.join("&")
23
+ end
24
+
25
+ def protocol
26
+ Postmark.secure ? "https" : "http"
27
+ end
28
+
29
+ def url
30
+ URI.parse("#{protocol}://#{Postmark.host}:#{Postmark.port}/")
31
+ end
32
+
33
+ def handle_response(response)
34
+ case response.code.to_i
35
+ when 200
36
+ return Postmark::Json.decode(response.body)
37
+ when 401
38
+ raise InvalidApiKeyError, error_message(response.body)
39
+ when 422
40
+ raise InvalidMessageError, error_message(response.body)
41
+ when 500
42
+ raise InternalServerError, response.body
43
+ else
44
+ raise UnknownError, response
45
+ end
46
+ end
47
+
48
+ def headers
49
+ @headers ||= HEADERS.merge({ "X-Postmark-Server-Token" => Postmark.api_key.to_s })
50
+ end
51
+
52
+ def url_path(path)
53
+ Postmark.path_prefix + path
54
+ end
55
+
56
+ def http
57
+ @http ||= build_http
58
+ end
59
+
60
+ def build_http
61
+ http = Net::HTTP::Proxy(Postmark.proxy_host,
62
+ Postmark.proxy_port,
63
+ Postmark.proxy_user,
64
+ Postmark.proxy_pass).new(url.host, url.port)
65
+
66
+ http.read_timeout = Postmark.http_read_timeout
67
+ http.open_timeout = Postmark.http_open_timeout
68
+ http.use_ssl = !!Postmark.secure
69
+ http
70
+ end
71
+
72
+ def error_message(response_body)
73
+ Postmark::Json.decode(response_body)["Message"]
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,22 @@
1
+ module Postmark
2
+ module Json
3
+
4
+ class << self
5
+ def encode(data)
6
+ json_parser
7
+ data.to_json
8
+ end
9
+
10
+ def decode(data)
11
+ json_parser.decode(data)
12
+ end
13
+
14
+ private
15
+
16
+ def json_parser
17
+ ResponseParsers.const_get(Postmark.response_parser_class)
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ module Mail
2
+ class Message
3
+ def tag
4
+ self["TAG"]
5
+ end
6
+
7
+ def tag=(value)
8
+ self["TAG"] = value
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ # assume activesupport is already loaded
2
+ module Postmark
3
+ module ResponseParsers
4
+ module ActiveSupport
5
+ def self.decode(data)
6
+ ::ActiveSupport::JSON.decode(data)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'json'
2
+ module Postmark
3
+ module ResponseParsers
4
+ module Json
5
+ def self.decode(data)
6
+ JSON.parse(data)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ require 'yajl'
2
+ Yajl::Encoder.enable_json_gem_compatability
3
+ module Postmark
4
+ module ResponseParsers
5
+ module Yajl
6
+ def self.decode(data)
7
+ ::Yajl::Parser.parse(data)
8
+ end
9
+ end
10
+ end
11
+ end
data/lib/postmark.rb ADDED
@@ -0,0 +1,155 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'rubygems'
4
+ require 'yaml'
5
+ require 'mail'
6
+ require 'postmark/bounce'
7
+ require 'postmark/json'
8
+ require 'postmark/http_client'
9
+ require 'postmark/mail_message_extension'
10
+
11
+ module Postmark
12
+
13
+ class InvalidApiKeyError < StandardError; end
14
+ class UnknownError < StandardError; end
15
+ class InvalidMessageError < StandardError; end
16
+ class InternalServerError < StandardError; end
17
+
18
+ module ResponseParsers
19
+ autoload :Json, 'postmark/response_parsers/json'
20
+ autoload :ActiveSupport, 'postmark/response_parsers/active_support'
21
+ autoload :Yajl, 'postmark/response_parsers/yajl'
22
+ end
23
+
24
+ HEADERS = {
25
+ 'Content-type' => 'application/json',
26
+ 'Accept' => 'application/json'
27
+ }
28
+
29
+ MAX_RETRIES = 2
30
+
31
+ class << self
32
+ attr_accessor :host, :path_prefix, :port, :secure, :api_key, :http_open_timeout, :http_read_timeout,
33
+ :proxy_host, :proxy_port, :proxy_user, :proxy_pass, :max_retries, :sleep_between_retries
34
+
35
+ attr_writer :response_parser_class
36
+
37
+ def response_parser_class
38
+ @response_parser_class ||= Object.const_defined?(:ActiveSupport) ? :ActiveSupport : :Json
39
+ end
40
+
41
+ # The port on which your Postmark server runs.
42
+ def port
43
+ @port || (secure ? 443 : 80)
44
+ end
45
+
46
+ # The host to connect to.
47
+ def host
48
+ @host ||= 'api.postmarkapp.com'
49
+ end
50
+
51
+ # The path of the listener
52
+ def path_prefix
53
+ @path_prefix ||= '/'
54
+ end
55
+
56
+ def http_open_timeout
57
+ @http_open_timeout ||= 5
58
+ end
59
+
60
+ def http_read_timeout
61
+ @http_read_timeout ||= 15
62
+ end
63
+
64
+ def max_retries
65
+ @max_retries ||= 3
66
+ end
67
+
68
+ def sleep_between_retries
69
+ @sleep_between_retries ||= 10
70
+ end
71
+
72
+ def configure
73
+ yield self
74
+ end
75
+
76
+ def send_through_postmark(message) #:nodoc:
77
+ @retries = 0
78
+ begin
79
+ HttpClient.post("email", Postmark::Json.encode(convert_mail(message)))
80
+ rescue Exception => e
81
+ if @retries < max_retries
82
+ @retries += 1
83
+ retry
84
+ else
85
+ raise
86
+ end
87
+ end
88
+ end
89
+
90
+ def delivery_stats
91
+ HttpClient.get("deliverystats")
92
+ end
93
+
94
+ protected
95
+
96
+ def convert_mail(message)
97
+ options = { "From" => message['from'].to_s, "To" => message['to'].to_s, "Subject" => message.subject }
98
+
99
+ headers = extract_headers(message)
100
+ options["Headers"] = headers unless headers.length == 0
101
+
102
+ options["Tag"] = message['tag'].to_s unless message.tag.nil?
103
+
104
+ options["Cc"] = message['cc'].to_s unless message.cc.nil?
105
+
106
+ options["Bcc"] = message['bcc'].to_s unless message.bcc.nil?
107
+
108
+ if reply_to = message['reply-to']
109
+ options["ReplyTo"] = reply_to.to_s
110
+ end
111
+
112
+ if message.multipart?
113
+ options["HtmlBody"] = message.html_part
114
+ options["TextBody"] = message.text_part
115
+ elsif message.content_type == "text/html"
116
+ options["HtmlBody"] = message.body
117
+ else
118
+ options["TextBody"] = message.body
119
+ end
120
+ options
121
+ end
122
+
123
+ def extract_headers(message)
124
+ headers = []
125
+ message.header.fields.each do |field|
126
+ name = field.name.downcase
127
+ next if bogus_headers.include? name
128
+ name = name.split(/-/).map {|i| i.capitalize }.join('-')
129
+ headers << { "Name" => name, "Value" => field.value }
130
+ end
131
+ headers
132
+ end
133
+
134
+ def bogus_headers
135
+ %q[
136
+ return-path
137
+ x-pm-rcpt
138
+ from
139
+ reply-to
140
+ sender
141
+ received
142
+ date
143
+ content-type
144
+ cc
145
+ bcc
146
+ subject
147
+ tag
148
+ ]
149
+ end
150
+
151
+ end
152
+
153
+ self.response_parser_class = nil
154
+
155
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Bounce" do
4
+ let(:bounce_json) { %{{"Type":"HardBounce","TypeCode":1,"Details":"test bounce","Email":"jim@test.com","BouncedAt":"#{Time.now.to_s}","DumpAvailable":true,"Inactive":false,"CanActivate":true,"ID":12}} }
5
+ let(:bounces_json) { "[#{bounce_json},#{bounce_json}]" }
6
+
7
+ context "single bounce" do
8
+ let(:bounce) { Postmark::Bounce.find(12) }
9
+
10
+ before do
11
+ Timecop.freeze
12
+ FakeWeb.register_uri(:get, "http://api.postmarkapp.com/bounces/12", { :body => bounce_json })
13
+ end
14
+
15
+ after do
16
+ Timecop.return
17
+ end
18
+
19
+ it "should retrieve and parce bounce correctly" do
20
+ bounce.type.should == "HardBounce"
21
+ bounce.bounced_at.should == Time.now
22
+ bounce.details.should == "test bounce"
23
+ bounce.email.should == "jim@test.com"
24
+ end
25
+
26
+ it "should retrieve bounce dump" do
27
+ FakeWeb.register_uri(:get, "http://api.postmarkapp.com/bounces/12/dump", { :body => %{{"Body": "Some SMTP gibberish"}} } )
28
+ bounce.dump.should == "Some SMTP gibberish"
29
+ end
30
+
31
+ it "should activate inactive bounce" do
32
+ FakeWeb.register_uri(:put, "http://api.postmarkapp.com/bounces/12/activate", { :body => %{{"Message":"OK","Bounce":#{bounce_json}}} } )
33
+ bounce.activate.should be_a(Postmark::Bounce)
34
+ end
35
+
36
+ end
37
+
38
+ it "should retrieve bounces" do
39
+ FakeWeb.register_uri(:get, "http://api.postmarkapp.com/bounces?count=30&offset=0", { :body => bounces_json })
40
+ bounces = Postmark::Bounce.all
41
+ bounces.should have(2).entries
42
+ bounces[0].should be_a(Postmark::Bounce)
43
+ end
44
+
45
+ it "should retrieve bounce tags" do
46
+ FakeWeb.register_uri(:get, "http://api.postmarkapp.com/bounces/tags", { :body => '["Signup","Commit Notification"]' })
47
+ tags = Postmark::Bounce.tags
48
+ tags.should have(2).entries
49
+ tags.first.should == "Signup"
50
+ end
51
+ end
@@ -0,0 +1,141 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Postmark" do
4
+
5
+ let :message do
6
+ Mail.new.tap do |mail|
7
+ mail.from = "sheldon@bigbangtheory.com"
8
+ mail.to = "lenard@bigbangtheory.com"
9
+ mail.subject = "Hello!"
10
+ mail.body = "Hello Sheldon!"
11
+ end
12
+ end
13
+
14
+ let :html_message do
15
+ Mail.new.tap do |mail|
16
+ mail.from = "sheldon@bigbangtheory.com"
17
+ mail.to = "lenard@bigbangtheory.com"
18
+ mail.subject = "Hello!"
19
+ mail.body = "<b>Hello Sheldon!</b>"
20
+ mail.content_type = "text/html"
21
+ end
22
+ end
23
+
24
+ context "service call" do
25
+
26
+ before(:all) do
27
+ Postmark.sleep_between_retries = 0
28
+ end
29
+
30
+ it "should send email successfully" do
31
+ FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email", {})
32
+ Postmark.send_through_postmark(message)
33
+ FakeWeb.should have_requested(:post, "http://api.postmarkapp.com/email")
34
+ end
35
+
36
+ it "should warn when header is invalid" do
37
+ FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email", {:status => [ "401", "Unauthorized" ], :body => "Missing API token"})
38
+ lambda { Postmark.send_through_postmark(message) }.should raise_error(Postmark::InvalidApiKeyError)
39
+ end
40
+
41
+ it "should warn when json is not ok" do
42
+ FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email", {:status => [ "422", "Invalid" ], :body => "Invalid JSON"})
43
+ lambda { Postmark.send_through_postmark(message) }.should raise_error(Postmark::InvalidMessageError)
44
+ end
45
+
46
+ it "should warn when server fails" do
47
+ FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email", {:status => [ "500", "Internal Server Error" ]})
48
+ lambda { Postmark.send_through_postmark(message) }.should raise_error(Postmark::InternalServerError)
49
+ end
50
+
51
+ it "should warn when unknown stuff fails" do
52
+ FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email", {:status => [ "485", "Custom HTTP response status" ]})
53
+ lambda { Postmark.send_through_postmark(message) }.should raise_error(Postmark::UnknownError)
54
+ end
55
+
56
+ it "should retry 3 times" do
57
+ FakeWeb.register_uri(:post, "http://api.postmarkapp.com/email",
58
+ [ { :status => [ 500, "Internal Server Error" ] },
59
+ { :status => [ 500, "Internal Server Error" ] },
60
+ { } ]
61
+ )
62
+ lambda { Postmark.send_through_postmark(message) }.should_not raise_error
63
+ end
64
+ end
65
+
66
+ context "delivery stats" do
67
+ let(:response_body) { %{{"InactiveMails":1,"Bounces":[{"TypeCode":0,"Name":"All","Count":2},{"Type":"HardBounce","TypeCode":1,"Name":"Hard bounce","Count":1},{"Type":"SoftBounce","TypeCode":4096,"Name":"Soft bounce","Count":1}]}} }
68
+
69
+ it "should query the service for delivery stats" do
70
+ FakeWeb.register_uri(:get, "http://api.postmarkapp.com/deliverystats", { :body => response_body })
71
+ results = Postmark.delivery_stats
72
+ results["InactiveMails"].should == 1
73
+ results["Bounces"].should be_an(Array)
74
+ results["Bounces"].should have(3).entries
75
+ FakeWeb.should have_requested(:get, "http://api.postmarkapp.com/deliverystats")
76
+ end
77
+ end
78
+
79
+ context "mail parse" do
80
+ it "should set text body for plain message" do
81
+ Postmark.send(:convert_mail, message)['TextBody'].should_not be_nil
82
+ end
83
+
84
+ it "should set html body for html message" do
85
+ Postmark.send(:convert_mail, html_message)['HtmlBody'].should_not be_nil
86
+ end
87
+ end
88
+
89
+ def be_serialized_to(json)
90
+ simple_matcher "be serialized to #{json}" do |message|
91
+ Postmark.send(:convert_mail, message).should == JSON.parse(json)
92
+ end
93
+ end
94
+
95
+ it "should encode custom headers headers properly" do
96
+ message["CUSTOM-HEADER"] = "header"
97
+ 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"}]}]
98
+ end
99
+
100
+ it "should encode reply to" do
101
+ message.reply_to = ['a@a.com', 'b@b.com']
102
+ 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!"}]
103
+ end
104
+
105
+ it "should encode tag" do
106
+ message.tag = "invite"
107
+ message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "Tag":"invite", "To":"lenard@bigbangtheory.com", "TextBody":"Hello Sheldon!"}]
108
+ end
109
+
110
+ it "should encode multiple recepients (TO)" do
111
+ message.to = ['a@a.com', 'b@b.com']
112
+ message.should be_serialized_to %q[{"Subject":"Hello!", "From":"sheldon@bigbangtheory.com", "To":"a@a.com, b@b.com", "TextBody":"Hello Sheldon!"}]
113
+ end
114
+
115
+ it "should encode multiple recepients (CC)" do
116
+ message.cc = ['a@a.com', 'b@b.com']
117
+ 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!"}]
118
+ end
119
+
120
+ it "should encode multiple recepients (BCC)" do
121
+ message.bcc = ['a@a.com', 'b@b.com']
122
+ 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!"}]
123
+ end
124
+
125
+ context "JSON library support" do
126
+ [:Json, :ActiveSupport, :Yajl].each do |lib|
127
+ begin
128
+ original_parser_class = Postmark.response_parser_class
129
+
130
+ it "decodes json with #{lib}" do
131
+ Postmark.response_parser_class = lib
132
+ Postmark::Json.decode(%({"Message":"OK"})).should == { "Message" => "OK" }
133
+ end
134
+
135
+ Postmark.response_parser_class = original_parser_class
136
+ rescue LoadError # No ActiveSupport or Yajl :(
137
+ end
138
+ end
139
+ end
140
+
141
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,3 @@
1
+ --colour
2
+ --format specdoc
3
+ --loadby mtime
@@ -0,0 +1,23 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'postmark'
4
+ require 'rubygems'
5
+ require 'active_support'
6
+ require 'json'
7
+ require 'ruby-debug'
8
+ require 'fakeweb'
9
+ require 'fakeweb_matcher'
10
+ require 'timecop'
11
+ require 'spec'
12
+ require 'spec/autorun'
13
+ require 'yaml'
14
+
15
+ if ENV['JSONGEM']
16
+ # `JSONGEM=Yajl rake spec`
17
+ Postmark.response_parser_class = ENV['JSONGEM'].to_sym
18
+ puts "Setting ResponseParser class to #{Postmark::ResponseParsers.const_get Postmark.response_parser_class}"
19
+ end
20
+
21
+ Spec::Runner.configure do |config|
22
+
23
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: challengepost-postmark
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 7
8
+ - 1
9
+ version: 0.7.1
10
+ platform: ruby
11
+ authors:
12
+ - Petyo Ivanov
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-06-07 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :development
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: cucumber
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 0
41
+ version: "0"
42
+ type: :development
43
+ version_requirements: *id002
44
+ - !ruby/object:Gem::Dependency
45
+ name: mail
46
+ prerelease: false
47
+ requirement: &id003 !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ type: :runtime
55
+ version_requirements: *id003
56
+ description: Ruby gem for sending emails through http://postmarkapp.com HTTP API. It relieas on the mail gem for message construction.
57
+ email: underlog@gmail.com
58
+ executables: []
59
+
60
+ extensions: []
61
+
62
+ extra_rdoc_files:
63
+ - LICENSE
64
+ - README.rdoc
65
+ files:
66
+ - .document
67
+ - .gitignore
68
+ - .rake_tasks
69
+ - LICENSE
70
+ - README.rdoc
71
+ - Rakefile
72
+ - VERSION
73
+ - features/postmark.feature
74
+ - features/step_definitions/postmark_steps.rb
75
+ - features/support/env.rb
76
+ - lib/postmark.rb
77
+ - lib/postmark/bounce.rb
78
+ - lib/postmark/http_client.rb
79
+ - lib/postmark/json.rb
80
+ - lib/postmark/mail_message_extension.rb
81
+ - lib/postmark/response_parsers/active_support.rb
82
+ - lib/postmark/response_parsers/json.rb
83
+ - lib/postmark/response_parsers/yajl.rb
84
+ - spec/bounce_spec.rb
85
+ - spec/postmark_spec.rb
86
+ - spec/spec.opts
87
+ - spec/spec_helper.rb
88
+ has_rdoc: true
89
+ homepage: http://postmarkapp.com
90
+ licenses: []
91
+
92
+ post_install_message: "\n ==================\n Thanks for installing the postmark gem. If you don't have an account, please sign up at http://postmarkapp.com/.\n Review the README.rdoc for implementation details and examples.\n ==================\n "
93
+ rdoc_options:
94
+ - --charset=UTF-8
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ segments:
109
+ - 0
110
+ version: "0"
111
+ requirements: []
112
+
113
+ rubyforge_project:
114
+ rubygems_version: 1.3.6
115
+ signing_key:
116
+ specification_version: 3
117
+ summary: Ruby gem for sending emails through http://postmarkapp.com HTTP API
118
+ test_files:
119
+ - spec/bounce_spec.rb
120
+ - spec/postmark_spec.rb
121
+ - spec/spec_helper.rb