postageapp 1.2.0 → 1.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,13 +18,15 @@
18
18
  #
19
19
  # Postage::Mailer introduces a few mailer methods specific to Postage:
20
20
  #
21
- # * postageapp_template - template name that is defined in your PostageApp project
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
- # request = Notifier.signup_notification(user) # creates PostageApp::Request object
27
- # response = request.deliver # attempts to deliver the message and creates a PostageApp::Response
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 deliver
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)
@@ -7,12 +7,12 @@ class PostageApp::Request
7
7
  }
8
8
 
9
9
  # Unique ID (UID) for the request
10
- attr_accessor :uid
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
- # Emulation of Mail::Message interface
114
- def body
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
- # Emulates Mail::Message#html_part
117
+ # -- Mail::Message Emulation ----------------------------------------------
118
+
119
119
  def html_part
120
- self.arguments and self.arguments['content'] and self.arguments['content']['text/html']
120
+ self.content['text/html']
121
121
  end
122
122
 
123
- # Emulates Mail::Message#text_part
124
123
  def text_part
125
- self.arguments and self.arguments['content'] and self.arguments['content']['text/plain']
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
@@ -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
@@ -1,37 +1,42 @@
1
1
  class Hash
2
- # Example usage:
3
- # @hash.dig(:k1) # same as @hash[:k1]
4
- # @hash.dig(:k1, :k2) # same as @hash[:k1] && @hash[:k1][:k2]
5
- # @hash.dig(:k1, :k2, k3) # same as @hash[:k1] && @hash[:k1][:k2] && @hash[:k1][:k2][:k3]
6
- def dig(*path)
7
- path.inject(self) do |location, key|
8
- location.respond_to?(:keys) ? location[key] : nil
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
- # Destructively convert all keys to strings.
13
- def recursive_stringify_keys!
14
- keys.each do |key|
15
- value = delete(key)
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
- self[key.to_s] =
18
- case (value)
19
- when Hash
20
- value.recursive_stringify_keys!
21
- else
22
- value
23
- end
24
- end
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
- self
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 :old_initialize, :initialize
37
+ alias_method :__initialize, :initialize
33
38
  def initialize(*args)
34
- old_initialize(*args)
39
+ __initialize(*args)
35
40
 
36
41
  @ssl_context = OpenSSL::SSL::SSLContext.new
37
42
  @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
@@ -1,3 +1,3 @@
1
1
  module PostageApp
2
- VERSION = "1.2.0"
2
+ VERSION = '1.2.5'.freeze
3
3
  end
@@ -1,16 +1,25 @@
1
1
  # encoding: utf-8
2
2
 
3
- $:.unshift File.expand_path('../lib', __FILE__)
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 = [ "Oleg Khabarov", "Scott Tadman", "The Working Group Inc." ]
10
- s.email = [ "oleg@khabarov.ca", "tadman@postageapp.com" ]
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
- s.summary = "Easier way to send email from web apps"
13
- s.description = "Gem that interfaces with PostageApp.com service to send emails from web apps"
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
@@ -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