postageapp 1.2.0 → 1.2.5
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/.gitignore +1 -0
- data/.travis.yml +14 -1
- data/README.md +1 -1
- data/generators/postageapp/postageapp_generator.rb +3 -3
- data/generators/postageapp/templates/postageapp_tasks.rake +57 -24
- data/lib/generators/postageapp/postageapp_generator.rb +6 -6
- data/lib/postageapp.rb +3 -1
- data/lib/postageapp/configuration.rb +52 -40
- data/lib/postageapp/failed_request.rb +4 -4
- data/lib/postageapp/mailer.rb +3 -86
- data/lib/postageapp/mailer/mailer_4.rb +7 -10
- data/lib/postageapp/request.rb +124 -11
- data/lib/postageapp/response.rb +4 -1
- data/lib/postageapp/utils.rb +27 -22
- data/lib/postageapp/version.rb +1 -1
- data/postageapp.gemspec +15 -5
- data/script/with +116 -0
- data/test/configuration_test.rb +1 -1
- data/test/failed_request_test.rb +32 -17
- data/test/gemfiles/Gemfile.rails-2.3.x +5 -1
- data/test/gemfiles/Gemfile.rails-3.0.x +6 -1
- data/test/gemfiles/Gemfile.rails-3.1.x +1 -1
- data/test/gemfiles/Gemfile.rails-3.2.x +2 -1
- data/test/gemfiles/Gemfile.rails-4.0.x +2 -1
- data/test/gemfiles/Gemfile.rails-4.1.x +2 -1
- data/test/gemfiles/Gemfile.rails-4.2.x +2 -1
- data/test/gemfiles/Gemfile.ruby +1 -0
- data/test/helper.rb +12 -5
- data/test/live_test.rb +36 -17
- data/test/mailer_2_test.rb +4 -5
- data/test/mailer_3_test.rb +8 -6
- data/test/mailer_4_test.rb +14 -6
- data/test/request_test.rb +8 -6
- data/test/response_test.rb +2 -1
- metadata +35 -14
- checksums.yaml +0 -7
@@ -18,13 +18,15 @@
|
|
18
18
|
#
|
19
19
|
# Postage::Mailer introduces a few mailer methods specific to Postage:
|
20
20
|
#
|
21
|
-
# * postageapp_template
|
21
|
+
# * postageapp_template - template name that is defined in your PostageApp project
|
22
22
|
# * postageapp_variables - extra variables you want to send along with the message
|
23
23
|
#
|
24
24
|
# Sending email
|
25
25
|
#
|
26
|
-
#
|
27
|
-
#
|
26
|
+
# # Create a PostageApp::Request object
|
27
|
+
# request = Notifier.signup_notification(user)
|
28
|
+
# # Deliver the message and return a PostageApp::Response
|
29
|
+
# response = request.deliver_now
|
28
30
|
|
29
31
|
class PostageApp::Mailer < ActionMailer::Base
|
30
32
|
CONTENT_TYPE_MAP = {
|
@@ -128,9 +130,6 @@ class PostageApp::Mailer < ActionMailer::Base
|
|
128
130
|
@_mail_was_called = true
|
129
131
|
m = @_message
|
130
132
|
|
131
|
-
# At the beginning, do not consider class default for parts order neither content_type
|
132
|
-
content_type = headers[:content_type]
|
133
|
-
|
134
133
|
# Call all the procs (if any)
|
135
134
|
class_default = self.class.default
|
136
135
|
default_values = class_default.merge(self.class.default) do |k,v|
|
@@ -140,9 +139,6 @@ class PostageApp::Mailer < ActionMailer::Base
|
|
140
139
|
# Handle defaults
|
141
140
|
headers = headers.reverse_merge(default_values)
|
142
141
|
|
143
|
-
# Apply charset at the beginning so all fields are properly quoted
|
144
|
-
charset = headers[:charset]
|
145
|
-
|
146
142
|
# Set configure delivery behavior
|
147
143
|
wrap_delivery_behavior!(
|
148
144
|
headers.delete(:delivery_method),
|
@@ -201,7 +197,7 @@ class PostageApp::Request
|
|
201
197
|
|
202
198
|
# Either doing an actual send, or passing it along to Mail::TestMailer
|
203
199
|
# Probably not the best way as we're skipping way too many intermediate methods
|
204
|
-
def
|
200
|
+
def deliver_now
|
205
201
|
inform_interceptors
|
206
202
|
|
207
203
|
if (perform_deliveries)
|
@@ -212,6 +208,7 @@ class PostageApp::Request
|
|
212
208
|
end
|
213
209
|
end
|
214
210
|
end
|
211
|
+
alias_method :deliver, :deliver_now
|
215
212
|
|
216
213
|
# Not 100% on this, but I need to assign this so I can properly handle deliver method
|
217
214
|
def delivery_method(method = nil, settings = nil)
|
data/lib/postageapp/request.rb
CHANGED
@@ -7,12 +7,12 @@ class PostageApp::Request
|
|
7
7
|
}
|
8
8
|
|
9
9
|
# Unique ID (UID) for the request
|
10
|
-
|
10
|
+
attr_writer :uid
|
11
11
|
|
12
12
|
# The API method being called (example: send_message)
|
13
13
|
# This controls the url of the request (example: https://api.postageapp.com/v.1.0/send_message.json)
|
14
14
|
attr_accessor :method
|
15
|
-
|
15
|
+
|
16
16
|
# A list of arguments in a Hash format passed along with the request
|
17
17
|
attr_accessor :arguments
|
18
18
|
|
@@ -30,9 +30,9 @@ class PostageApp::Request
|
|
30
30
|
end
|
31
31
|
|
32
32
|
# Creates a new Request with the given API call method and arguments.
|
33
|
-
def initialize(method, arguments =
|
33
|
+
def initialize(method, arguments = nil)
|
34
34
|
@method = method
|
35
|
-
@arguments = arguments.dup
|
35
|
+
@arguments = arguments ? arguments.dup : { }
|
36
36
|
|
37
37
|
@uid = @arguments.delete('uid')
|
38
38
|
@api_key = @arguments.delete('api_key') || PostageApp.configuration.api_key
|
@@ -110,18 +110,131 @@ class PostageApp::Request
|
|
110
110
|
hash
|
111
111
|
end
|
112
112
|
|
113
|
-
|
114
|
-
|
115
|
-
self.arguments and self.arguments['content'] and (self.arguments['content']['text/html'] or self.arguments['content']['text/plain'])
|
113
|
+
def content
|
114
|
+
self.arguments['content'] ||= { }
|
116
115
|
end
|
117
116
|
|
118
|
-
#
|
117
|
+
# -- Mail::Message Emulation ----------------------------------------------
|
118
|
+
|
119
119
|
def html_part
|
120
|
-
self.
|
120
|
+
self.content['text/html']
|
121
121
|
end
|
122
122
|
|
123
|
-
# Emulates Mail::Message#text_part
|
124
123
|
def text_part
|
125
|
-
self.
|
124
|
+
self.content['text/plain']
|
125
|
+
end
|
126
|
+
|
127
|
+
def find_first_mime_type(type)
|
128
|
+
self.content[type]
|
129
|
+
end
|
130
|
+
|
131
|
+
def mime_type
|
132
|
+
self.content.keys.first
|
133
|
+
end
|
134
|
+
|
135
|
+
def header
|
136
|
+
self.arguments['headers'] ||= { }
|
137
|
+
end
|
138
|
+
|
139
|
+
def reply_to
|
140
|
+
self.header['reply-to']
|
141
|
+
end
|
142
|
+
|
143
|
+
def cc
|
144
|
+
self.header['cc']
|
145
|
+
end
|
146
|
+
|
147
|
+
def attachments
|
148
|
+
self.arguments['attachments']
|
149
|
+
end
|
150
|
+
|
151
|
+
def multipart?
|
152
|
+
self.content.keys.length > 1
|
153
|
+
end
|
154
|
+
|
155
|
+
# Getter and setter for headers. You can specify headers in the following
|
156
|
+
# formats:
|
157
|
+
# headers['Custom-Header'] = 'Custom Value'
|
158
|
+
# headers 'Custom-Header-1' => 'Custom Value 1',
|
159
|
+
# 'Custom-Header-2' => 'Custom Value 2'
|
160
|
+
def headers(value = nil)
|
161
|
+
_headers = self.arguments['headers'] ||= { }
|
162
|
+
|
163
|
+
case (value)
|
164
|
+
when Hash
|
165
|
+
value.each do |k, v|
|
166
|
+
_headers[k.to_s] = v.to_s
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
_headers
|
171
|
+
end
|
172
|
+
|
173
|
+
def [](key)
|
174
|
+
case (key)
|
175
|
+
when :to, 'to'
|
176
|
+
self.to
|
177
|
+
when :from, 'from'
|
178
|
+
self.from
|
179
|
+
when :bcc, 'bcc'
|
180
|
+
# Not supported via API at this time
|
181
|
+
[ ]
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def to
|
186
|
+
out = self.arguments_to_send.dig('arguments', 'recipients')
|
187
|
+
|
188
|
+
case (out)
|
189
|
+
when Hash
|
190
|
+
out
|
191
|
+
else
|
192
|
+
[ out ].flatten
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def to=(list)
|
197
|
+
self.arguments['recipients'] = list
|
198
|
+
end
|
199
|
+
|
200
|
+
def from
|
201
|
+
[ self.arguments_to_send.dig('arguments', 'headers', 'from') ].flatten
|
202
|
+
end
|
203
|
+
|
204
|
+
def from=(address)
|
205
|
+
_headers = self.arguments['headers'] ||= { }
|
206
|
+
|
207
|
+
_headers['from'] = address.to_s
|
208
|
+
end
|
209
|
+
|
210
|
+
def bcc
|
211
|
+
# Not supported natively via API at this time
|
212
|
+
[ ]
|
213
|
+
end
|
214
|
+
|
215
|
+
def bcc=(list)
|
216
|
+
# Not supported natively via API at this time
|
217
|
+
end
|
218
|
+
|
219
|
+
def subject
|
220
|
+
self.arguments_to_send.dig('arguments', 'headers', 'subject')
|
221
|
+
end
|
222
|
+
|
223
|
+
def subject=(subject)
|
224
|
+
_headers = self.arguments['headers'] ||= { }
|
225
|
+
|
226
|
+
_headers['subject'] = subject.to_s
|
227
|
+
end
|
228
|
+
|
229
|
+
# # Emulation of Mail::Message interface
|
230
|
+
# def body
|
231
|
+
# _content = self.arguments && self.arguments['content']
|
232
|
+
|
233
|
+
# _content and (_content['text/html'] or _content['text/plain'])
|
234
|
+
# end
|
235
|
+
|
236
|
+
def body
|
237
|
+
out = self.arguments_to_send.dig('arguments', 'content')
|
238
|
+
out.is_a?(Hash) ? out.values.join("\n\n") : out.to_s
|
126
239
|
end
|
127
240
|
end
|
data/lib/postageapp/response.rb
CHANGED
@@ -13,6 +13,8 @@ class PostageApp::Response
|
|
13
13
|
# The data payload of the response. This is usually the return value of the
|
14
14
|
# request we're looking for
|
15
15
|
attr_reader :data
|
16
|
+
|
17
|
+
attr_reader :exception
|
16
18
|
|
17
19
|
# Takes in Net::HTTPResponse object as the attribute.
|
18
20
|
# If something goes wrong Response will be thought of as failed
|
@@ -27,8 +29,9 @@ class PostageApp::Response
|
|
27
29
|
|
28
30
|
@data = hash['data']
|
29
31
|
|
30
|
-
rescue
|
32
|
+
rescue => e
|
31
33
|
@status = 'fail'
|
34
|
+
@exception = '[%s] %s' % [ e.class, e ]
|
32
35
|
end
|
33
36
|
|
34
37
|
# Little helper that checks for the Response status
|
data/lib/postageapp/utils.rb
CHANGED
@@ -1,37 +1,42 @@
|
|
1
1
|
class Hash
|
2
|
-
#
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
2
|
+
# Ruby 2.3.0 adds the dig method so this needs to be conditional.
|
3
|
+
unless ((instance_methods & [ :dig ]).any?)
|
4
|
+
# Example usage:
|
5
|
+
# @hash.dig(:k1) # same as @hash[:k1]
|
6
|
+
# @hash.dig(:k1, :k2) # same as @hash[:k1] && @hash[:k1][:k2]
|
7
|
+
# @hash.dig(:k1, :k2, k3) # same as @hash[:k1] && @hash[:k1][:k2] && @hash[:k1][:k2][:k3]
|
8
|
+
def dig(*path)
|
9
|
+
path.inject(self) do |location, key|
|
10
|
+
location.respond_to?(:keys) ? location[key] : nil
|
11
|
+
end
|
9
12
|
end
|
10
13
|
end
|
11
14
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
unless ((instance_methods & [ :recursive_stringify_keys! ]).any?)
|
16
|
+
# Destructively convert all keys to strings.
|
17
|
+
def recursive_stringify_keys!
|
18
|
+
keys.each do |key|
|
19
|
+
value = delete(key)
|
16
20
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
self[key.to_s] =
|
22
|
+
case (value)
|
23
|
+
when Hash
|
24
|
+
value.recursive_stringify_keys!
|
25
|
+
else
|
26
|
+
value
|
27
|
+
end
|
28
|
+
end
|
25
29
|
|
26
|
-
|
30
|
+
self
|
31
|
+
end
|
27
32
|
end
|
28
33
|
end
|
29
34
|
|
30
35
|
class Net::HTTP
|
31
36
|
# Getting rid of the 'warning: peer certificate won't be verified in this SSL session'
|
32
|
-
alias_method :
|
37
|
+
alias_method :__initialize, :initialize
|
33
38
|
def initialize(*args)
|
34
|
-
|
39
|
+
__initialize(*args)
|
35
40
|
|
36
41
|
@ssl_context = OpenSSL::SSL::SSLContext.new
|
37
42
|
@ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
data/lib/postageapp/version.rb
CHANGED
data/postageapp.gemspec
CHANGED
@@ -1,16 +1,25 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
3
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
|
4
4
|
require 'postageapp/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "postageapp"
|
8
8
|
s.version = PostageApp::VERSION
|
9
|
-
s.authors = [
|
10
|
-
|
9
|
+
s.authors = [
|
10
|
+
"Oleg Khabarov",
|
11
|
+
"Scott Tadman",
|
12
|
+
"The Working Group Inc."
|
13
|
+
]
|
14
|
+
s.email = [
|
15
|
+
"oleg@khabarov.ca",
|
16
|
+
"tadman@postageapp.com"
|
17
|
+
]
|
18
|
+
|
11
19
|
s.homepage = "http://github.com/postageapp/postageapp-ruby"
|
12
|
-
|
13
|
-
s.
|
20
|
+
|
21
|
+
s.summary = "Gem for communicating with the PostageApp email API"
|
22
|
+
s.description = "Gem that interfaces with PostageApp service to send emails from Ruby applications"
|
14
23
|
s.license = 'MIT'
|
15
24
|
|
16
25
|
s.files = `git ls-files`.split("\n")
|
@@ -20,4 +29,5 @@ Gem::Specification.new do |s|
|
|
20
29
|
s.required_ruby_version = '>= 1.9.3'
|
21
30
|
|
22
31
|
s.add_dependency 'json'
|
32
|
+
s.add_dependency 'mail'
|
23
33
|
end
|
data/script/with
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This script can be used to engage a particular Gemfile for the purposes of
|
4
|
+
# continuous integration testing.
|
5
|
+
#
|
6
|
+
# Usage: script/with <variant> <env|bootstrap|bundle|rake>
|
7
|
+
#
|
8
|
+
# Here <variant> refers to a pre-defined Gemfile located in test/gemfiles
|
9
|
+
# and the commands include:
|
10
|
+
#
|
11
|
+
# * env - Display the current BUNDLE_GEMFILE environment
|
12
|
+
# * bootstrap - Install the necessary hard dependencies (rubygems, bundler)
|
13
|
+
# * bundle - Execute arbitrary Bundler command
|
14
|
+
# * rake - Execute arbitrary rake command via `bundle exec`
|
15
|
+
#
|
16
|
+
# Examples:
|
17
|
+
# * script/with rails-3.0.x bootstrap
|
18
|
+
# * script/with rails-3.0.x bundle install
|
19
|
+
# * script/with rails-3.0.x rake test
|
20
|
+
|
21
|
+
# == Constants ==============================================================
|
22
|
+
|
23
|
+
GEMFILE_DIR = File.expand_path('../test/gemfiles', File.dirname(__FILE__))
|
24
|
+
GEMFILE_ROOT = File.expand_path('../Gemfile', File.dirname(__FILE__))
|
25
|
+
|
26
|
+
# == Support Methods ========================================================
|
27
|
+
|
28
|
+
def dependencies(gemfile)
|
29
|
+
dependencies = { }
|
30
|
+
|
31
|
+
File.open(gemfile) do |f|
|
32
|
+
f.each do |line|
|
33
|
+
if (line.match(/\Agem\s+['"]bundler['"]\s*,\s*['"]([^'"]+)['"]/))
|
34
|
+
dependencies[:bundler] = $1
|
35
|
+
elsif (line.match(/\A\#\s*rubygems\s+(\S+)/))
|
36
|
+
dependencies[:rubygems] = $1
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
dependencies
|
42
|
+
end
|
43
|
+
|
44
|
+
def gemfile_list
|
45
|
+
Dir.entries(GEMFILE_DIR).reject do |name|
|
46
|
+
name[/\A\./] or name[/\.lock\z/]
|
47
|
+
end.collect do |name|
|
48
|
+
name.sub(/\AGemfile\./, '')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def with_variant(variant, &block)
|
53
|
+
case (variant)
|
54
|
+
when 'current'
|
55
|
+
if (ENV['BUNDLE_GEMFILE'])
|
56
|
+
yield(dependencies(ENV['BUNDLE_GEMFILE']))
|
57
|
+
else
|
58
|
+
yield(dependencies(GEMFILE_ROOT))
|
59
|
+
end
|
60
|
+
when 'each', 'all'
|
61
|
+
gemfile_list.each do |_variant|
|
62
|
+
with_variant(_variant, &block)
|
63
|
+
end
|
64
|
+
else
|
65
|
+
ENV['BUNDLE_GEMFILE'] = File.expand_path('Gemfile.%s' % variant, GEMFILE_DIR)
|
66
|
+
|
67
|
+
yield(dependencies(ENV['BUNDLE_GEMFILE']))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def shell(*args)
|
72
|
+
if (ENV['WITH_VERBOSE'])
|
73
|
+
puts args.join(' ')
|
74
|
+
end
|
75
|
+
|
76
|
+
unless (system(*args))
|
77
|
+
exit($?)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# == Main ===================================================================
|
82
|
+
|
83
|
+
variant = ARGV[0]
|
84
|
+
|
85
|
+
unless (variant)
|
86
|
+
puts "Usage: with <gemspec variant> <env|bootstrap|bundle|rake> ..."
|
87
|
+
|
88
|
+
|
89
|
+
puts
|
90
|
+
puts "Available variants:"
|
91
|
+
gemfile_list.each do |name|
|
92
|
+
puts name
|
93
|
+
end
|
94
|
+
|
95
|
+
exit(-1)
|
96
|
+
end
|
97
|
+
|
98
|
+
with_variant(variant) do |dependencies|
|
99
|
+
bundle_version_args = dependencies[:bundler] ? [ '_%s_' % dependencies[:bundler] ] : [ ]
|
100
|
+
|
101
|
+
case (ARGV[1])
|
102
|
+
when 'env'
|
103
|
+
puts 'BUNDLE_GEMFILE=%s' % ENV['BUNDLE_GEMFILE']
|
104
|
+
when 'bootstrap'
|
105
|
+
if (dependencies[:rubygems])
|
106
|
+
shell('gem', 'update', '--system', dependencies[:rubygems])
|
107
|
+
end
|
108
|
+
if (dependencies[:bundler])
|
109
|
+
shell('gem', 'install', 'bundler', '-v', dependencies[:bundler])
|
110
|
+
end
|
111
|
+
when 'bundle'
|
112
|
+
shell("bundle", *bundle_version_args, *ARGV.to_a[2, ARGV.length])
|
113
|
+
when 'rake'
|
114
|
+
shell("bundle", *bundle_version_args, "exec", "rake", *ARGV.to_a[2, ARGV.length])
|
115
|
+
end
|
116
|
+
end
|