challengepost-postmark 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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