postmark 0.9.19 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.travis.yml +8 -0
  2. data/CHANGELOG.rdoc +20 -0
  3. data/Gemfile +6 -0
  4. data/README.md +351 -91
  5. data/VERSION +1 -1
  6. data/lib/postmark.rb +40 -132
  7. data/lib/postmark/api_client.rb +162 -0
  8. data/lib/postmark/bounce.rb +20 -17
  9. data/lib/postmark/handlers/mail.rb +10 -3
  10. data/lib/postmark/helpers/hash_helper.rb +35 -0
  11. data/lib/postmark/helpers/message_helper.rb +62 -0
  12. data/lib/postmark/http_client.rb +44 -28
  13. data/lib/postmark/inbound.rb +21 -0
  14. data/lib/postmark/inflector.rb +28 -0
  15. data/lib/postmark/message_extensions/mail.rb +50 -5
  16. data/lib/postmark/message_extensions/shared.rb +23 -28
  17. data/lib/postmark/version.rb +1 -1
  18. data/postmark.gemspec +4 -7
  19. data/spec/data/empty.gif +0 -0
  20. data/spec/integration/api_client_hashes_spec.rb +101 -0
  21. data/spec/integration/api_client_messages_spec.rb +127 -0
  22. data/spec/integration/mail_delivery_method_spec.rb +80 -0
  23. data/spec/spec_helper.rb +15 -5
  24. data/spec/support/helpers.rb +11 -0
  25. data/spec/{shared_examples.rb → support/shared_examples.rb} +0 -0
  26. data/spec/unit/postmark/api_client_spec.rb +246 -0
  27. data/spec/unit/postmark/bounce_spec.rb +142 -0
  28. data/spec/unit/postmark/handlers/mail_spec.rb +39 -0
  29. data/spec/unit/postmark/helpers/hash_helper_spec.rb +34 -0
  30. data/spec/unit/postmark/helpers/message_helper_spec.rb +115 -0
  31. data/spec/unit/postmark/http_client_spec.rb +204 -0
  32. data/spec/unit/postmark/inbound_spec.rb +88 -0
  33. data/spec/unit/postmark/inflector_spec.rb +35 -0
  34. data/spec/unit/postmark/json_spec.rb +37 -0
  35. data/spec/unit/postmark/message_extensions/mail_spec.rb +205 -0
  36. data/spec/unit/postmark_spec.rb +164 -0
  37. metadata +45 -93
  38. data/lib/postmark/attachments_fix_for_mail.rb +0 -48
  39. data/lib/postmark/message_extensions/tmail.rb +0 -115
  40. data/spec/bounce_spec.rb +0 -53
  41. data/spec/postmark_spec.rb +0 -253
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.8.7"
4
+ - "1.9.2"
5
+ - "1.9.3"
6
+ - "2.0.0"
7
+ - "jruby-19mode"
8
+ script: bundle exec rake spec
@@ -1,5 +1,25 @@
1
1
  = Changelog
2
2
 
3
+ == 1.0.0
4
+
5
+ * Introduced new instance-based architecture (see README for more details).
6
+ * Removed TMail support.
7
+ * Added support for sending emails in batches.
8
+ * Added API to send emails without Mail library.
9
+ * Introduced lock-free approach for Mail::Postmark delivery method.
10
+ * Deprecated the Mail::Message#postmark_attachments method
11
+ * Added Postmark::Inbound module.
12
+ * Added integration tests.
13
+ * Added support for the "server" endpoint of the Postmark API.
14
+ * Improved unit test coverage.
15
+ * Added more examples to the README file.
16
+ * Added official JRuby support.
17
+ * Fixed the inconsistent behaviour of Mail::Message#tag method added by the gem.
18
+ * Added Mail::Message#delivered property and Mail::Message#delivered? predicate.
19
+ * Added Mail::Message#postmark_response method.
20
+ * Removed Postmark::AttachmentsFixForMail class (that hack no longer works).
21
+ * Added Travis-CI for integration tests.
22
+
3
23
  == 0.9.19
4
24
 
5
25
  * Added support for native attachments API provided by Ruby Mail library.
data/Gemfile CHANGED
@@ -2,3 +2,9 @@ source "http://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in postmark.gemspec
4
4
  gemspec
5
+
6
+ group :test do
7
+ gem 'rspec', '~> 2.13.0'
8
+ gem 'fakeweb'
9
+ gem 'fakeweb-matcher'
10
+ end
data/README.md CHANGED
@@ -1,42 +1,315 @@
1
1
  # Postmark Gem
2
+ [![Build Status](https://travis-ci.org/wildbit/postmark-gem.png?branch=master)](https://travis-ci.org/wildbit/postmark-gem) [![Code Climate](https://codeclimate.com/github/wildbit/postmark-gem.png)](https://codeclimate.com/github/wildbit/postmark-gem)
2
3
 
3
- This gem is an official wrapper for [Postmark HTTP API](http://postmarkapp.com). Use it to send emails and retrieve info about bounces.
4
+ This gem is the official wrapper for the [Postmark HTTP API](http://postmarkapp.com). Postmark allows you to send your application's emails with high delivery rates, including bounce/spam processing and detailed statistics. In addition, Postmark can parse incoming emails which are forwarded back to your application.
4
5
 
5
- ## Getting Started
6
+ ## Install the gem
6
7
 
7
- ### Install the gem
8
+ With Bundler:
8
9
 
9
- ``` bash
10
- gem install postmark
10
+ ``` ruby
11
+ gem 'postmark'
11
12
  ```
12
13
 
13
- ### Install [Mail](http://rubygems.org/gems/mail) library
14
-
15
- In addition to the `postmark` gem you also need to install `mail` gem.
14
+ Without Bundler:
16
15
 
17
16
  ``` bash
18
- gem install mail
17
+ gem install postmark
19
18
  ```
20
19
 
21
- You can also use the gem with `tmail` library. This is not recommended for any
22
- new projects, but may be useful for legacy Ruby 1.8.7 projects.
23
-
24
- ### Get Postmark API key
20
+ ## Get a Postmark API key
25
21
 
26
22
  In order to send emails using Postmark ruby gem, you will need a
27
23
  [Postmark](http://postmarkapp.com) account. If you don't have one please
28
24
  register at https://postmarkapp.com/sign_up.
29
25
 
30
- If you didn't create any servers yet, please create one, proceed to
26
+ If you didnt create any servers yet, please create one, proceed to the
31
27
  `Credentials` tab and copy an API key. API keys should be frequently rotated for
32
28
  security reasons.
33
29
 
34
- ## Using with [Mail](http://rubygems.org/gems/mail) library
30
+ ## Communicating with the API
35
31
 
36
32
  Make sure you have a [sender signature](https://postmarkapp.com/signatures) for
37
- every From email you specify. From can also accept array of addresses.
33
+ every From email address you specify.
34
+
35
+ Create an instance of `Postmark::ApiClient` to start sending emails.
36
+
37
+ ``` ruby
38
+ your_api_key = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
39
+ client = Postmark::ApiClient.new(your_api_key)
40
+ ```
41
+
42
+ `Postmark::ApiClient` accepts various options:
43
+
44
+ ``` ruby
45
+ client = Postmark::ApiClient.new(your_api_key, secure: true
46
+ http_open_timeout: 15)
47
+ ```
48
+
49
+ Some useful options are:
50
+
51
+ * `secure` (`true` or `false`): set to true to use SSL connection.
52
+ * `http_read_timeout` (positive number): limit HTTP read time to `n` seconds.
53
+ * `http_open_timeout` (positive number): limit HTTP open time to `n` seconds.
54
+ * `proxy_host` (string): proxy address to use.
55
+ * `proxy_port` (positive number): proxy port to use.
56
+ * `proxy_user` (string): proxy user.
57
+ * `proxy_pass` (string): proxy password.
58
+
59
+ ## Sending a plain text message
60
+
61
+ ``` ruby
62
+ client.deliver(from: 'sheldon@bigbangtheory.com',
63
+ to: 'Leonard Hofstadter <leonard@bigbangtheory.com>',
64
+ subject: 'Re: Come on, Sheldon. It will be fun.',
65
+ text_body: 'That\'s what you said about the Green Lantern ' \
66
+ 'movie. You were 114 minutes of wrong.')
67
+ # => {:to=>"Leonard Hofstadter <leonard@bigbangtheory.com>", :submitted_at=>"2013-05-09T02:45:16.2059023-04:00", :message_id=>"b2b268e3-6a70-xxxx-b897-49c9eb8b1d2e", :error_code=>0, :message=>"OK"}
68
+ ```
69
+
70
+ ## Sending an HTML message
71
+
72
+ ``` ruby
73
+ client.deliver(from: 'sheldon@bigbangtheory.com',
74
+ to: 'Leonard Hofstadter <leonard@bigbangtheory.com>',
75
+ subject: 'Re: What, to you, is a large crowd?',
76
+ html_body: '<p>Any group big enough to trample me to death. ' \
77
+ 'General rule of thumb is 36 adults or 70 ' \
78
+ 'children.</p>')
79
+ # => {:to=>"Leonard Hofstadter <leonard@bigbangtheory.com>", :submitted_at=>"2013-05-09T02:51:08.8789433-04:00", :message_id=>"75c28987-564e-xxxx-b6eb-e8071873ac06", :error_code=>0, :message=>"OK"}
80
+ ```
81
+
82
+ ## Sending a message with attachments
83
+
84
+ You can add
85
+ [attachments](http://developer.postmarkapp.com/developer-build.html#attachments)
86
+ to your messages. Keep in mind message size limit (contents and attachment) is currently 10 MB.
87
+
88
+ ``` ruby
89
+ client.deliver(from: 'leonard@bigbangtheory.com',
90
+ to: 'Dr. Sheldon Cooper <sheldon@bigbangtheory.com>',
91
+ subject: 'Have you seen these pictures of yours?',
92
+ text_body: 'You look like a real geek!',
93
+ attachments: [File.open('1.jpeg'),
94
+ {name: 'sheldon.jpeg',
95
+ content: [File.read('2.jpeg')].pack('m'),
96
+ content_type: 'image/jpeg'}])
97
+ # => {:to=>"Dr. Sheldon Cooper <sheldon@bigbangtheory.com>", :submitted_at=>"2013-05-09T02:56:12.2828813-04:00", :message_id=>"8ec0d283-8b93-xxxx-9d65-241d1777cf0f", :error_code=>0, :message=>"OK"}
98
+ ```
99
+
100
+ ## Sending a multipart message
101
+
102
+ ``` ruby
103
+ client.deliver(from: 'sheldon@bigbangtheory.com',
104
+ to: 'Leonard Hofstadter <leonard@bigbangtheory.com>',
105
+ subject: 'Re: Anything Can Happen Thursday',
106
+ text_body: 'Apparently the news didn\'t reach my digestive ' \
107
+ 'system, which when startled has it\'s own version ' \
108
+ 'of "Anything Can Happen Thursday"',
109
+ html_body: '<p>Apparently the news didn&rsquo;t reach my ' \
110
+ 'digestive system, which when startled has ' \
111
+ 'it&rsquo;s own version of &ldquo;Anything Can '\
112
+ 'Happen Thursday&rdquo;</p>')
113
+ # => {:to=>"Leonard Hofstadter <leonard@bigbangtheory.com>", :submitted_at=>"2013-05-09T02:58:00.089828-04:00", :message_id=>"bc973458-1315-xxxx-b295-6aa0a2b631ac", :error_code=>0, :message=>"OK"}
114
+ ```
115
+
116
+ ## Tagging messages
117
+
118
+ You can categorize outgoing email using the optional `:tag` property. If you use
119
+ different tags for the different types of emails your application generates,
120
+ you will be able to get detailed statistics for them through the Postmark user
121
+ interface.
122
+
123
+ ``` ruby
124
+ client.deliver(from: 'sheldon@bigbangtheory.com',
125
+ to: 'Penny <penny@bigbangtheory.com>',
126
+ subject: 'Re: You cleaned my apartment???',
127
+ text_body: 'I couldn\'t sleep knowing that just outside my ' \
128
+ 'bedroom is our living room and just outside our ' \
129
+ 'living room is that hallway and immediately adjacent ' \
130
+ 'to that hallway is this!',
131
+ tag: 'confidential')
132
+
133
+ # => {:to=>"Penny <penny@bigbangtheory.com>", :submitted_at=>"2013-05-09T03:00:55.4454938-04:00", :message_id=>"34aed4b3-3a95-xxxx-bd1d-88064909cc93", :error_code=>0, :message=>"OK"}
134
+ ```
135
+
136
+ ## Sending to multiple recipients
137
+
138
+ You can pass multiple recipient addresses in the `:to` field and the optional
139
+ `:cc` and `:bcc` fields. Note that Postmark has a limit of twenty recipients
140
+ per message in total. You need to take care not to exceed that limit.
141
+ Otherwise, you will get an error.
142
+
143
+ ``` ruby
144
+ client.deliver(from: 'sheldon@bigbangtheory.com',
145
+ to: ['Leonard Hofstadter <leonard@bigbangtheory.com>',
146
+ 'Penny <penny@bigbangtheory.com>'],
147
+ cc: ['Dr. Koothrappali <raj@bigbangtheory.com>'],
148
+ bcc: 'secretsheldonstorage@bigbangtheory.com',
149
+ subject: 'Re: Come on, Sheldon. It will be fun.',
150
+ text_body: 'That\'s what you said about the Green Lantern ' \
151
+ 'movie. You were 114 minutes of wrong.')
152
+ # => {:to=>"Leonard Hofstadter <leonard@bigbangtheory.com>, Penny <penny@bigbangtheory.com>", :submitted_at=>"2013-05-09T05:04:16.3247488-04:00", :message_id=>"d647c5d6-xxxx-466d-9411-557dcd5c2297", :error_code=>0, :message=>"OK"}
153
+ ```
154
+
155
+ ## Sending in batches
156
+
157
+ While Postmark is focused on transactional email, we understand that developers
158
+ with higher volumes or processing time constraints need to send their messages
159
+ in batches. To facilitate this we provide a batching endpoint that permits you
160
+ to send up to 500 well-formed Postmark messages in a single API call.
161
+
162
+ ``` ruby
163
+ messages = []
164
+
165
+ messages << {from: 'sheldon@bigbangtheory.com',
166
+ to: 'Leonard Hofstadter <leonard@bigbangtheory.com>',
167
+ subject: 'Re: Come on, Sheldon. It will be fun.',
168
+ text_body: 'That\'s what you said about the Green Lantern ' \
169
+ 'movie. You were 114 minutes of wrong.'}
170
+
171
+ messages << {from: 'sheldon@bigbangtheory.com',
172
+ to: 'Penny <penny@bigbangtheory.com>',
173
+ subject: 'Re: You cleaned my apartment???',
174
+ text_body: 'I couldn\'t sleep knowing that just outside my ' \
175
+ 'bedroom is our living room and just outside our ' \
176
+ 'living room is that hallway and immediately ' \
177
+ 'adjacent to that hallway is this!',
178
+ tag: 'confidential'}
179
+
180
+ client.deliver_in_batches(messages)
181
+ # => [{:to=>"Leonard Hofstadter <leonard@bigbangtheory.com>", :submitted_at=>"2013-05-09T05:19:16.3361118-04:00", :message_id=>"247e43a9-6b0d-4914-a87f-7b74bf76b5cb", :error_code=>0, :message=>"OK"}, {:to=>"Penny <penny@bigbangtheory.com>", :submitted_at=>"2013-05-09T05:19:16.3517099-04:00", :message_id=>"26467642-f169-4da8-87a8-b89154067dfb", :error_code=>0, :message=>"OK"}]
182
+ ```
183
+
184
+ ## Parsing inbound
185
+
186
+ Inbound processing allows you (or your users) to send emails to Postmark, which we then
187
+ process and deliver to you via a web hook in a nicely formatted JSON document.
188
+
189
+ Here is a simple Ruby/Sinatra application that does basic inbound processing.
190
+
191
+ ``` ruby
192
+ logger = Logger.new(STDOUT)
193
+
194
+ class Comment
195
+ attr_accessor :attributes
196
+
197
+ def self.create_from_inbound_hook(message)
198
+ self.new(:text => message["TextBody"],
199
+ :user_email => message["From"],
200
+ :discussion_id => message["MailboxHash"])
201
+ end
202
+
203
+ def initialize(attributes={})
204
+ @attributes = attributes
205
+ end
206
+ end
207
+
208
+ post '/inbound' do
209
+ request.body.rewind
210
+ comment = Comment.create_from_inbound_hook(Postmark::Json.decode(request.body.read))
211
+ logger.info comment.inspect
212
+ end
213
+ ```
214
+
215
+ If you don’t like that the fields of the Inbound JSON document are all in CamelCase, you
216
+ can use the `Postmark::Inbound.to_ruby_hash` method to give it some Ruby flavor.
217
+
218
+ ```
219
+ postmark_hash = Postmark::Json.decode(request.body.read)
220
+ ruby_hash = Postmark::Inbound.to_ruby_hash(postmark_hash)
221
+ # => {:from=>"myUser@theirDomain.com", :from_full=>{:email=>"myUser@theirDomain.com", :name=>"John Doe"}, :to=>"451d9b70cf9364d23ff6f9d51d870251569e+ahoy@inbound.postmarkapp.com", :to_full=>[{:email=>"451d9b70cf9364d23ff6f9d51d870251569e+ahoy@inbound.postmarkapp.com", :name=>""}], :cc=>"\"Full name\" <sample.cc@emailDomain.com>, \"Another Cc\" <another.cc@emailDomain.com>", :cc_full=>[{:email=>"sample.cc@emailDomain.com", :name=>"Full name"}, {:email=>"another.cc@emailDomain.com", :name=>"Another Cc"}], :reply_to=>"myUsersReplyAddress@theirDomain.com", :subject=>"This is an inbound message", :message_id=>"22c74902-a0c1-4511-804f2-341342852c90", :date=>"Thu, 5 Apr 2012 16:59:01 +0200", :mailbox_hash=>"ahoy", :text_body=>"[ASCII]", :html_body=>"[HTML(encoded)]", :tag=>"", :headers=>[{:name=>"X-Spam-Checker-Version", :value=>"SpamAssassin 3.3.1 (2010-03-16) onrs-ord-pm-inbound1.wildbit.com"}, {:name=>"X-Spam-Status", :value=>"No"}, {:name=>"X-Spam-Score", :value=>"-0.1"}, {:name=>"X-Spam-Tests", :value=>"DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,SPF_PASS"}, {:name=>"Received-SPF", :value=>"Pass (sender SPF authorized) identity=mailfrom; client-ip=209.85.160.180; helo=mail-gy0-f180.google.com; envelope-from=myUser@theirDomain.com; receiver=451d9b70cf9364d23ff6f9d51d870251569e+ahoy@inbound.postmarkapp.com"}, {:name=>"DKIM-Signature", :value=>"v=1; a=rsa-sha256; c=relaxed/relaxed; d=wildbit.com; s=google; h=mime-version:reply-to:date:message-id:subject:from:to:cc :content-type; bh=cYr/+oQiklaYbBJOQU3CdAnyhCTuvemrU36WT7cPNt0=; b=QsegXXbTbC4CMirl7A3VjDHyXbEsbCUTPL5vEHa7hNkkUTxXOK+dQA0JwgBHq5C+1u iuAJMz+SNBoTqEDqte2ckDvG2SeFR+Edip10p80TFGLp5RucaYvkwJTyuwsA7xd78NKT Q9ou6L1hgy/MbKChnp2kxHOtYNOrrszY3JfQM="}, {:name=>"MIME-Version", :value=>"1.0"}, {:name=>"Message-ID", :value=>"<CAGXpo2WKfxHWZ5UFYCR3H_J9SNMG+5AXUovfEFL6DjWBJSyZaA@mail.gmail.com>"}], :attachments=>[{:name=>"myimage.png", :content=>"[BASE64-ENCODED CONTENT]", :content_type=>"image/png", :content_length=>4096}, {:name=>"mypaper.doc", :content=>"[BASE64-ENCODED CONTENT]", :content_type=>"application/msword", :content_length=>16384}]}
222
+ ```
223
+
224
+ ## Working with bounces
225
+
226
+ Use `#get_bounces` to retrieve a list of bounces (use `:count` and `:offset`
227
+ parameters to control pagination).
228
+
229
+ ``` ruby
230
+ client.get_bounces(count: 1, offset: 0)
231
+ # => [{:id=>654714902, :type=>"Transient", :type_code=>2, :name=>"Message delayed", :message_id=>"1fdf3729-xxxx-4d5c-8a7b-96da7a23268b", :description=>"The server could not temporarily deliver your message (ex: Message is delayed due to network troubles).", :details=>"action: failed\r\n", :email=>"tema@wildbit.org", :bounced_at=>"2013-04-10T01:01:35.0965184-04:00", :dump_available=>true, :inactive=>false, :can_activate=>true, :subject=>"bounce test"}]
232
+ ```
233
+
234
+ Use `#get_bounced_tags` to retrieve a list of tags used for bounced emails.
235
+
236
+ ``` ruby
237
+ client.get_bounced_tags
238
+ # => ["confidential"]
239
+ ```
240
+
241
+ Use `#get_bounce` to get info for a specific bounce using ID:
242
+
243
+ ``` ruby
244
+ client.get_bounce(654714902)
245
+ # => {:id=>654714902, :type=>"Transient", :type_code=>2, :name=>"Message delayed", :message_id=>"1fdf3729-xxxx-xxxx-8a7b-96da7a23268b", :description=>"The server could not temporarily deliver your message (ex: Message is delayed due to network troubles).", :details=>"action: failed\r\n", :email=>"tema@wildbit.com", :bounced_at=>"2013-04-10T01:01:35.0965184-04:00", :dump_available=>true, :inactive=>false, :can_activate=>true, :subject=>"bounce test", :content=>"..."}
246
+ ```
247
+
248
+ Use `#dump_bounce` to get the full bounce body:
249
+
250
+ ``` ruby
251
+ client.dump_bounce(654714902)
252
+ # => {:body=>"Return-Path: <>\r\nReceived: from m1.mtasv.net (74.205.19.136) by sc-ord-mail2.mtasv.net id hcjov61jk5ko for <pm_bounces@pm.mtasv.net>; Wed, 10 Apr 2013 01:00:35 -0400 (envelope-from <>)\r\nDate: Wed, 10 Apr 2013 01:00:48 -0400\r\nFrom: postmaster@m1.mtasv.net\r\n..."}
253
+ ```
38
254
 
39
- ### Plain text message
255
+ You can activate email addresses that were disabled due to a hard bounce by using `#activate_bounce`:
256
+
257
+ ``` ruby
258
+ client.activate_bounce(654714902)
259
+ # => {:id=>654714902, :type=>"Transient", :type_code=>2, :name=>"Message delayed", :message_id=>"1fdf3729-xxxx-xxxx-xxxx-96da7a23268b", :description=>"The server could not temporarily deliver your message (ex: Message is delayed due to network troubles).", :details=>"action: failed\r\n", :email=>"tema@wildbit.com", :bounced_at=>"2013-04-10T01:01:35.0965184-04:00", :dump_available=>true, :inactive=>false, :can_activate=>true, :subject=>"bounce test"}
260
+ ```
261
+
262
+ ## Getting delivery stats
263
+
264
+ Currently delivery stats only include a summary of inactive emails and bounces
265
+ by type.
266
+
267
+ ``` ruby
268
+ stats = client.delivery_stats
269
+ # => {:inactive_mails=>1, :bounces=>[{:name=>"All", :count=>3}, {:type=>"HardBounce", :name=>"Hard bounce", :count=>2}, {:type=>"Transient", :name=>"Message delayed", :count=>1}]}
270
+ ```
271
+
272
+ ## Server Info
273
+
274
+ The gem also allows you to read and update the server info:
275
+
276
+ ``` ruby
277
+ client.server_info
278
+ # => {:name=>"Testing", :color=>"blue", :bounce_hook_url=>"", :inbound_hash=>"c2ffffff74f8643e5f6086c81", :inbound_hook_url=>"", :smtp_api_activated=>true}
279
+ ```
280
+
281
+ For example, you can use `#update_server_info` to set inbound hook URL:
282
+
283
+ ``` ruby
284
+ client.update_server_info inbound_hook_url: 'http://example.org/bounces'
285
+ ```
286
+
287
+ # Using Postmark with the [Mail](http://rubygems.org/gems/mail) library
288
+
289
+ You can use Postmark with the `mail` gem.
290
+
291
+ ``` bash
292
+ gem install mail
293
+ ```
294
+
295
+ Make sure you have a [sender signature](https://postmarkapp.com/signatures) for
296
+ every `From` email address you specify.
297
+
298
+ To send a `Mail::Message` via Postmark you’ll need to specify `Mail::Postmark` as
299
+ a delivery method for the message:
300
+
301
+ ``` ruby
302
+ message = Mail.new do
303
+ # ...
304
+ delivery_method Mail::Postmark, api_key: 'your-postmark-api-key', secure: true
305
+ end
306
+ ```
307
+
308
+ Delivery method accepts all options supported by `Postmark::ApiClient`
309
+ documented above. A new instance of `Postmark::ApiClient` is created every time
310
+ you deliver a message to preserve thread safety.
311
+
312
+ ## Plain text message
40
313
 
41
314
  ``` ruby
42
315
  require 'rubygems'
@@ -58,7 +331,7 @@ message.deliver
58
331
  # => #<Mail::Message:70355890541720, Multipart: false, Headers: <From: sheldon@bigbangtheory.com>, <To: leonard@bigbangtheory.com>, <Message-ID: e439fec0-4c89-475b-b3fc-eb446249a051>, <Subject: Re: Come on, Sheldon. It will be fun.>>
59
332
  ```
60
333
 
61
- ### HTML message
334
+ ## HTML message
62
335
 
63
336
  ``` ruby
64
337
  require 'rubygems'
@@ -82,7 +355,7 @@ message.deliver
82
355
  # => #<Mail::Message:70355902117460, Multipart: false, Headers: <From: sheldon@bigbangtheory.com>, <To: leonard@bigbangtheory.com>, <Message-ID: 3a9370a2-6c24-4304-a03c-320a54cc59f7>, <Subject: Re: What, to you, is a large crowd?>, <Content-Type: text/html; charset=UTF-8>>
83
356
  ```
84
357
 
85
- ### Message with attachments
358
+ ## Message with attachments
86
359
 
87
360
  ``` ruby
88
361
  message = Mail.new do
@@ -98,12 +371,12 @@ end
98
371
  message.attachments['sheldon.jpeg'] = File.read('2.jpeg')
99
372
 
100
373
  message.deliver
101
- # => #<Mail::Message:70185826686240, Multipart: true, Headers: <From: leonard@bigbangtheory.com>, <To: sheldon@bigbangtheory.com>, <Message-ID: ba644cc1-b5b1-4bcb-aaf8-2f290b5aad80>, <Subject: Have you seen these pictures of yours?>, <Content-Type: multipart/mixed; boundary=--==_mimepart_5121f9f1ec653_12c53fd569035ad817726>>
374
+ # => #<Mail::Message:70185826686240, Multipart: true, Headers: <From: leonard@bigbangtheory.com>, <To: sheldon@bigbangtheory.com>, <Message-ID: ba644cc1-b5b1-4bcb-aaf8-2f290b5aad80>, <Subject: Have you seen these pictures of yours?>, <Content-Type: multipart/mixed; boundary=--==_mimepart_5121f9f1ec653_12c53fd569035ad817726>>
102
375
  ```
103
376
 
104
- ### Multipart message
377
+ ## Multipart message
105
378
 
106
- You can send multipart messages containing both text and HTML using Postmark gem.
379
+ You can send multipart messages containing both text and HTML using the Postmark gem.
107
380
 
108
381
  ``` ruby
109
382
  require 'rubygems'
@@ -135,7 +408,7 @@ message.deliver
135
408
  # => #<Mail::Message:70355901588620, Multipart: true, Headers: <From: sheldon@bigbangtheory.com>, <To: leonard@bigbangtheory.com>, <Message-ID: cadba131-f6d6-4cfc-9892-16ee738ba54c>, <Subject: Re: Anything Can Happen Thursday>, <Content-Type: multipart/alternative; boundary=--==_mimepart_50ef7a6234a69_a4c73ffd01035adc207b8>>
136
409
  ```
137
410
 
138
- ### Tagged message
411
+ ## Tagged message
139
412
 
140
413
  Postmark also lets you tag your messages.
141
414
 
@@ -152,85 +425,85 @@ message = Mail.new do
152
425
  body 'I couldn\'t sleep knowing that just outside my bedroom is ' \
153
426
  'our living room and just outside our living room is that ' \
154
427
  'hallway and immediately adjacent to that hallway is this!'
428
+ tag 'confidential'
155
429
 
156
430
  delivery_method Mail::Postmark, :api_key => 'your-postmark-api-key'
157
431
  end
158
432
 
159
- message.tag = 'confidential'
160
-
161
433
  message.deliver
162
434
  # => #<Mail::Message:70168327829580, Multipart: false, Headers: <From: sheldon@bigbangtheory.com>, <To: penny@bigbangtheory.com>, <Message-ID: af2570fd-3481-4b45-8b27-a249806d891a>, <Subject: Re: You cleaned my apartment???>, <TAG: confidential>>
163
435
  ```
164
436
 
165
- ### Accessing Postmark Message-ID
437
+ ## Sending in batches
166
438
 
167
- You might want to save identifiers of messages you send. Postmark provides you
168
- with unique Message-ID, which you can
169
- [use to retrieve bounces](http://blog.postmarkapp.com/post/24970994681/using-messageid-to-retrieve-bounces)
170
- later. This example shows you how to access Message-ID of a sent email message.
439
+ You can also send `Mail::Message` objects in batches. Create an instance of
440
+ `Postmark::ApiClient` as described in "Communicating with the API" section.
171
441
 
172
442
  ``` ruby
173
- message = Mail.new
174
- # ...
175
- message.deliver
443
+ messages = []
176
444
 
177
- message['Message-ID']
178
- # => cadba131-f6d6-4cfc-9892-16ee738ba54c
179
- message.message_id
180
- # => "cadba131-f6d6-4cfc-9892-16ee738ba54c"
181
- ```
445
+ messages << Mail.new do
446
+ from 'sheldon@bigbangtheory.com'
447
+ to 'Leonard Hofstadter <leonard@bigbangtheory.com>'
448
+ subject 'Re: Come on, Sheldon. It will be fun.'
449
+ body 'That\'s what you said about the Green Lantern movie. You' \
450
+ 'were 114 minutes of wrong.'
451
+ end
182
452
 
183
- ## Using with [TMail](http://rubygems.org/gems/tmail) library
453
+ messages << Mail.new do
454
+ from 'sheldon@bigbangtheory.com'
455
+ to 'Penny <penny@bigbangtheory.com>'
456
+ subject 'Re: You cleaned my apartment???'
457
+ body 'I couldn\'t sleep knowing that just outside my bedroom is ' \
458
+ 'our living room and just outside our living room is that ' \
459
+ 'hallway and immediately adjacent to that hallway is this!'
460
+ tag 'confidential'
461
+ end
184
462
 
185
- Postmark gem also supports `tmail` library, which can be used by Ruby 1.8.7
186
- users working on legacy projects. Please note that TMail is not supported since
187
- 2010, so please consider using new ruby [mail](http://rubygems.org/gems/mail)
188
- library for all your new projects.
463
+ client.deliver_messages(messages)
464
+ # => [{:to=>"leonard@bigbangtheory.com", :submitted_at=>"2013-05-10T01:59:29.830486-04:00", :message_id=>"8ad0e8b0-xxxx-xxxx-951d-223c581bb467", :error_code=>0, :message=>"OK"}, {:to=>"penny@bigbangtheory.com", :submitted_at=>"2013-05-10T01:59:29.830486-04:00", :message_id=>"33c6240c-xxxx-xxxx-b0df-40bdfcf4e0f7", :error_code=>0, :message=>"OK"}]
465
+ ```
189
466
 
190
- Make sure you have a [sender signature](https://postmarkapp.com/signatures) for
191
- every From email you specify. From can also accept array of addresses.
467
+ After delivering a batch you can check on each message’s delivery status:
192
468
 
193
469
  ``` ruby
194
- require 'rubygems'
195
- require 'postmark'
196
- require 'tmail'
197
- require 'json'
470
+ messages.first.delivered?
471
+ # => true
198
472
 
199
- Postmark.api_key = 'your-postmark-api-key'
200
-
201
- message = TMail::Mail.new
202
- message.from = "leonard@bigbangtheory.com"
203
- message.to = "Sheldon Cooper <sheldon@bigbangtheory.com>"
204
- message.subject = "Hi Sheldon!"
205
- message.content_type = "text/html"
206
- message.body = "Hello my friend!"
473
+ messages.all?(&:delivered)
474
+ # => true
475
+ ```
207
476
 
208
- # You can set customer headers if you like:
209
- message["CUSTOM-HEADER"] = "my custom header value"
477
+ Or even get a related Postmark response:
210
478
 
211
- # Added a tag:
212
- message.tag = "my-tracking-tag"
479
+ ``` ruby
480
+ messages.first.postmark_response
481
+ # => {"To"=>"leonard@bigbangtheory.com", "SubmittedAt"=>"2013-05-10T01:59:29.830486-04:00", "MessageID"=>"8ad0e8b0-xxxx-xxxx-951d-223c581bb467", "ErrorCode"=>0, "Message"=>"OK"}
482
+ ```
213
483
 
214
- # Add attachments:
215
- message.postmark_attachments = [File.open("/path"), File.open("/path")]
484
+ ## Accessing Postmark Message-ID
216
485
 
217
- # Add attachments with content generated on the fly:
218
- message.postmark_attachments = [{
219
- "Name" => "September 2011.pdf",
220
- "Content" => [pdf_content].pack("m"),
221
- "ContentType" => "application/pdf"
222
- }]
486
+ You might want to save identifiers of messages you send. Postmark provides you
487
+ with a unique Message-ID, which you can
488
+ [use to retrieve bounces](http://blog.postmarkapp.com/post/24970994681/using-messageid-to-retrieve-bounces)
489
+ later. This example shows you how to access the Message-ID of a sent email message.
223
490
 
224
- # Or specify a reply-to address (can also be an array of addresses):
225
- message.reply_to = "penny@bigbangtheory.com"
491
+ ``` ruby
492
+ message = Mail.new
493
+ # ...
494
+ message.deliver
226
495
 
227
- Postmark.send_through_postmark(message)
496
+ message['Message-ID']
497
+ # => cadba131-f6d6-4cfc-9892-16ee738ba54c
498
+ message.message_id
499
+ # => "cadba131-f6d6-4cfc-9892-16ee738ba54c"
228
500
  ```
229
501
 
230
- ## Exploring Other API Features
502
+ # Exploring Other Gem Features
231
503
 
232
- You can retrieve various information about your server state using the [Public
233
- bounces API](http://developer.postmarkapp.com/bounces).
504
+ To provide an interface similar to ActiveRecord for bounces, the Postmark gem adds
505
+ `Postmark::Bounce` class. This class uses the shared `Postmark::ApiClient` instance
506
+ configured through the Postmark module.
234
507
 
235
508
  ``` ruby
236
509
  require 'rubygems'
@@ -241,10 +514,6 @@ require 'json'
241
514
  Postmark.response_parser_class = :Json
242
515
  Postmark.api_key = 'your-postmark-api-key'
243
516
 
244
- # Delivery stats
245
- Postmark.delivery_stats
246
- # => {"InactiveMails"=>1, "Bounces"=>[{"Name"=>"All", "Count"=>1}, {"Type"=>"HardBounce", "Name"=>"Hard bounce", "Count"=>1}]}
247
-
248
517
  # Get bounces information: (array of bounce objects)
249
518
  Postmark::Bounce.all
250
519
  # => [#<Postmark::Bounce:0x007ff09c04ae18 @id=580516117, @email="sheldon@bigbangtheory.com", @bounced_at=2012-10-21 00:01:56 +0800, @type="HardBounce", @name=nil, @details="smtp;550 5.1.1 The email account that you tried to reach does not exist. Please try double-checking the recipient's email address for typos or unnecessary spaces. Learn more at http://support.google.com/mail/bin/answer.py?answer=6596 c13si5382730vcw.23", @tag=nil, @dump_available=false, @inactive=true, @can_activate=true, @message_id="876d40fe-ab2a-4925-9d6f-8d5e4f4926f5", @subject="Re: What, to you, is a large crowd?">]
@@ -260,24 +529,15 @@ bounce.activate # reactivate hard bounce
260
529
  # => #<Postmark::Bounce:0x007ff09c04ae18 @id=580516117, @email="sheldon@bigbangtheory.com", @bounced_at=2012-10-21 00:01:56 +0800, @type="HardBounce", @name=nil, @details="smtp;550 5.1.1 The email account that you tried to reach does not exist. Please try double-checking the recipient's email address for typos or unnecessary spaces. Learn more at http://support.google.com/mail/bin/answer.py?answer=6596 c13si5382730vcw.23", @tag=nil, @dump_available=false, @inactive=true, @can_activate=true, @message_id="876d40fe-ab2a-4925-9d6f-8d5e4f4926f5", @subject="Re: What, to you, is a large crowd?">
261
530
  ```
262
531
 
263
- ## Security
264
-
265
- To use SSL encryption when sending email configure the library as follows:
266
-
267
- ``` ruby
268
- Postmark.secure = true
269
- ```
270
-
271
532
  ## Requirements
272
533
 
273
- The gem relies on Mail or TMail for building the message. You will also need
274
- postmark account, server and sender signature set up to use it.
275
- If you plan using it in a rails project, check out the
534
+ You will need a Postmark account, server and sender signature set up to use it.
535
+ If you plan using it in a Rails project, check out the
276
536
  [postmark-rails](https://github.com/wildbit/postmark-rails/) gem, which
277
537
  is meant to integrate with ActionMailer.
278
538
 
279
539
  The plugin will try to use ActiveSupport Json if it is already included. If not,
280
- it will attempt using the built-in ruby Json library.
540
+ it will attempt using the built-in Ruby Json library.
281
541
 
282
542
  You can also explicitly specify which one to be used, using
283
543