mandrill_mailer 0.6.1 → 1.0.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -1
- data/README.md +12 -2
- data/lib/mandrill_mailer/arg_formatter.rb +119 -0
- data/lib/mandrill_mailer/core_mailer.rb +38 -73
- data/lib/mandrill_mailer/message_mailer.rb +2 -102
- data/lib/mandrill_mailer/offline.rb +10 -6
- data/lib/mandrill_mailer/template_mailer.rb +9 -97
- data/lib/mandrill_mailer/version.rb +1 -1
- data/spec/arg_formatter_spec.rb +236 -0
- data/spec/core_mailer_spec.rb +294 -38
- data/spec/message_mailer_spec.rb +12 -133
- data/spec/spec_helper.rb +2 -0
- data/spec/template_mailer_spec.rb +24 -442
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 050aa30e1863a63c0e023050c83d720fdf952805
|
4
|
+
data.tar.gz: f2179df6cf50a863c5b70096a8cadbf77ea40dc8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ccb28b3e20348666f2fe2ba80565a24ce06e2550013de771ab7ac7a03a9d69ef7e4773bef4aa5bbef72f93c764494cbc456e428d8c2b0eb0b881f65f52bd6d7
|
7
|
+
data.tar.gz: 31deabd8cace6c79cffe3f8004a3ffcb8e865bac0d71a0361da230790dcd7bae607c060c4ae63cf219c491e5422f7b0dbb476ba149ec5bcfa2a44d9a7229070f
|
data/CHANGELOG.md
CHANGED
@@ -2,7 +2,10 @@
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
4
4
|
|
5
|
-
##
|
5
|
+
## 1.0.0
|
6
|
+
- Update manrill_api gem to 1.0.X
|
7
|
+
- Change how interceptors work to be more flexible and not overwrite data if needed
|
8
|
+
- Make both the template and message mailers compatible with all available attributes in the messages api
|
6
9
|
|
7
10
|
## 0.6.1
|
8
11
|
### Fixed
|
data/README.md
CHANGED
@@ -331,11 +331,21 @@ More info: https://github.com/mperham/sidekiq/wiki/Delayed-Extensions#actionmail
|
|
331
331
|
|
332
332
|
## Using an interceptor
|
333
333
|
You can set a mailer interceptor to override any params used when you deliver an e-mail.
|
334
|
+
The interceptor is a Proc object that gets called with the mail object being sent
|
335
|
+
to the api.
|
334
336
|
|
335
|
-
Example:
|
337
|
+
Example that adds multiple bcc recipients:
|
336
338
|
|
337
339
|
```ruby
|
338
340
|
MandrillMailer.configure do |config|
|
339
|
-
config.
|
341
|
+
config.interceptor = Proc.new {|params|
|
342
|
+
|
343
|
+
params[:to] = [
|
344
|
+
params[:to],
|
345
|
+
{ email: "bccEmailThatWillBeUsedInAll@emailsSent1.com", name: "name", type: "bcc" },
|
346
|
+
{ email: "bccEmailThatWillBeUsedInAll@emailsSent2.com", name: "name", type: "bcc" },
|
347
|
+
{ email: "bccEmailThatWillBeUsedInAll@emailsSent3.com", name: "name", type: "bcc" }
|
348
|
+
].flatten
|
349
|
+
}
|
340
350
|
end
|
341
351
|
```
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module MandrillMailer
|
4
|
+
class ArgFormatter
|
5
|
+
ACCEPTED_MERGE_LANGUAGES = ['mailchimp', 'handlebars'].freeze
|
6
|
+
|
7
|
+
def self.attachment_args(args)
|
8
|
+
return unless args
|
9
|
+
args.map do |attachment|
|
10
|
+
attachment.symbolize_keys!
|
11
|
+
type = attachment[:mimetype] || attachment[:type]
|
12
|
+
name = attachment[:filename] || attachment[:name]
|
13
|
+
file = attachment[:file] || attachment[:content]
|
14
|
+
{"type" => type, "name" => name, "content" => Base64.encode64(file)}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.images_args(args)
|
19
|
+
return unless args
|
20
|
+
attachment_args(args)
|
21
|
+
end
|
22
|
+
|
23
|
+
# convert a normal hash into the format mandrill needs
|
24
|
+
def self.mandrill_args(args)
|
25
|
+
return [] unless args
|
26
|
+
args.map do |k,v|
|
27
|
+
{'name' => k, 'content' => v}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.merge_vars(args)
|
32
|
+
return [] unless args
|
33
|
+
args.map do |item|
|
34
|
+
rcpt = item.keys[0]
|
35
|
+
{'rcpt' => rcpt, 'vars' => mandrill_args(item.fetch(rcpt))}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.rcpt_metadata(args)
|
40
|
+
return [] unless args
|
41
|
+
args.map do |item|
|
42
|
+
rcpt = item.keys[0]
|
43
|
+
{'rcpt' => rcpt, 'values' => item.fetch(rcpt)}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# ensure only true or false is returned given arg
|
48
|
+
def self.boolean(arg)
|
49
|
+
!!arg
|
50
|
+
end
|
51
|
+
|
52
|
+
# handle if to params is an array of either hashes or strings or the single string
|
53
|
+
def self.params(to_params)
|
54
|
+
if to_params.kind_of? Array
|
55
|
+
to_params.map do |p|
|
56
|
+
params_item(p)
|
57
|
+
end
|
58
|
+
else
|
59
|
+
[params_item(to_params)]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# single to params item
|
64
|
+
def self.params_item(item)
|
65
|
+
if item.kind_of? Hash
|
66
|
+
item
|
67
|
+
else
|
68
|
+
{"email" => item, "name" => item}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.format_messages_api_message_data(args)
|
73
|
+
# If a merge_language attribute is given and it's not one of the accepted
|
74
|
+
# languages Raise an error
|
75
|
+
if args[:merge_language] && !ACCEPTED_MERGE_LANGUAGES.include?(args[:merge_language])
|
76
|
+
raise MandrillMailer::CoreMailer::InvalidMergeLanguageError.new("The :merge_language value `#{args[:merge_language]}`is invalid, value must be one of: #{ACCEPTED_MERGE_LANGUAGES.join(', ')}.")
|
77
|
+
end
|
78
|
+
|
79
|
+
{
|
80
|
+
"html" => args[:html],
|
81
|
+
"text" => args[:text],
|
82
|
+
"subject" => args[:subject],
|
83
|
+
"from_email" => args[:from] || defaults[:from],
|
84
|
+
"from_name" => args[:from_name] || defaults[:from_name] || defaults[:from],
|
85
|
+
"to" => params(args[:to]),
|
86
|
+
"headers" => args[:headers],
|
87
|
+
"important" => boolean(args[:important]),
|
88
|
+
"track_opens" => args.fetch(:track_opens, true),
|
89
|
+
"track_clicks" => boolean(args.fetch(:track_clicks, true)),
|
90
|
+
"auto_text" => boolean(args.fetch(:auto_text, true)),
|
91
|
+
"auto_html" => boolean(args[:auto_html]),
|
92
|
+
"inline_css" => boolean(args[:inline_css]),
|
93
|
+
"url_strip_qs" => boolean(args.fetch(:url_strip_qs, true)),
|
94
|
+
"preserve_recipients" => boolean(args[:preserve_recipients]),
|
95
|
+
"view_content_link" => boolean(args[:view_content_link]),
|
96
|
+
"bcc_address" => args[:bcc],
|
97
|
+
"tracking_domain" => args[:tracking_domain],
|
98
|
+
"signing_domain" => args[:signing_domain],
|
99
|
+
"return_path_domain" => args[:return_path_domain],
|
100
|
+
"merge" => boolean(args[:merge]),
|
101
|
+
"merge_language" => args[:merge_language],
|
102
|
+
"global_merge_vars" => mandrill_args(args[:vars] || args[:global_merge_vars]),
|
103
|
+
"merge_vars" => merge_vars(args[:recipient_vars] || args[:merge_vars] || defaults[:merge_vars]),
|
104
|
+
"tags" => args[:tags],
|
105
|
+
"subaccount" => args[:subaccount],
|
106
|
+
"google_analytics_domains" => args[:google_analytics_domains],
|
107
|
+
"google_analytics_campaign" => args[:google_analytics_campaign],
|
108
|
+
"metadata" => args[:metadata],
|
109
|
+
"recipient_metadata" => args[:recipient_metadata],
|
110
|
+
"attachments" => attachment_args(args[:attachments]),
|
111
|
+
"images" => images_args(args[:images])
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.defaults
|
116
|
+
MandrillMailer::CoreMailer.defaults
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -105,13 +105,14 @@
|
|
105
105
|
|
106
106
|
# Required for hash.stringify_keys!
|
107
107
|
require 'active_support/all'
|
108
|
-
require '
|
108
|
+
require 'mandrill_mailer/arg_formatter'
|
109
109
|
|
110
110
|
module MandrillMailer
|
111
111
|
class CoreMailer
|
112
112
|
class InvalidEmail < StandardError; end
|
113
113
|
class InvalidMailerMethod < StandardError; end
|
114
114
|
class InvalidInterceptorParams < StandardError; end
|
115
|
+
class InvalidMergeLanguageError < StandardError; end
|
115
116
|
|
116
117
|
# Public: Other information on the message to send
|
117
118
|
attr_accessor :message
|
@@ -141,7 +142,7 @@ module MandrillMailer
|
|
141
142
|
#
|
142
143
|
# Returns options
|
143
144
|
def self.defaults
|
144
|
-
@defaults || super_defaults
|
145
|
+
@defaults || super_defaults || {}
|
145
146
|
end
|
146
147
|
|
147
148
|
def self.super_defaults
|
@@ -211,14 +212,18 @@ module MandrillMailer
|
|
211
212
|
|
212
213
|
# Public: Triggers the stored Mandrill params to be sent to the Mandrill api
|
213
214
|
def deliver
|
214
|
-
|
215
|
-
|
215
|
+
raise NotImplementedError.new("#{self.class.name}#deliver is not implemented.")
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
def mandrill_mail_handler(args)
|
220
|
+
args
|
216
221
|
end
|
217
222
|
|
218
223
|
# Public: Build the hash needed to send to the mandrill api
|
219
224
|
#
|
220
225
|
# args - The Hash options used to refine the selection:
|
221
|
-
|
226
|
+
#
|
222
227
|
# Examples
|
223
228
|
#
|
224
229
|
# mandrill_mail template: 'Group Invite',
|
@@ -231,19 +236,19 @@ module MandrillMailer
|
|
231
236
|
#
|
232
237
|
# Returns the the mandrill mailer class (this is so you can chain #deliver like a normal mailer)
|
233
238
|
def mandrill_mail(args)
|
234
|
-
|
235
|
-
raise NotImplementedError.new(mesg)
|
236
|
-
end
|
239
|
+
extract_api_options!(args)
|
237
240
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
241
|
+
# Call the mandrill_mail_handler so mailers can handle the args in custom ways
|
242
|
+
mandrill_mail_handler(args)
|
243
|
+
|
244
|
+
# Construct message hash
|
245
|
+
self.message = MandrillMailer::ArgFormatter.format_messages_api_message_data(args)
|
243
246
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
+
# Apply any interceptors that may be present
|
248
|
+
apply_interceptors!(self.message)
|
249
|
+
|
250
|
+
# return self so we can chain deliver after the method call, like a normal mailer.
|
251
|
+
self
|
247
252
|
end
|
248
253
|
|
249
254
|
def from
|
@@ -255,7 +260,7 @@ module MandrillMailer
|
|
255
260
|
end
|
256
261
|
|
257
262
|
def to=(values)
|
258
|
-
self.message && self.message['to'] =
|
263
|
+
self.message && self.message['to'] = MandrillMailer::ArgFormatter.params(values)
|
259
264
|
end
|
260
265
|
|
261
266
|
def bcc
|
@@ -264,20 +269,15 @@ module MandrillMailer
|
|
264
269
|
|
265
270
|
protected
|
266
271
|
|
267
|
-
def
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
file = attachment[:file] || attachment[:content]
|
274
|
-
{"type" => type, "name" => name, "content" => Base64.encode64(file)}
|
272
|
+
def apply_interceptors!(obj)
|
273
|
+
unless MandrillMailer.config.interceptor.nil?
|
274
|
+
unless MandrillMailer.config.interceptor.is_a?(Proc)
|
275
|
+
raise InvalidInterceptorParams.new "The interceptor_params config must be a proc"
|
276
|
+
end
|
277
|
+
MandrillMailer.config.interceptor.call(obj)
|
275
278
|
end
|
276
|
-
end
|
277
279
|
|
278
|
-
|
279
|
-
return unless args
|
280
|
-
mandrill_attachment_args(args)
|
280
|
+
obj
|
281
281
|
end
|
282
282
|
|
283
283
|
# Makes this class act as a singleton without it actually being a singleton
|
@@ -324,56 +324,21 @@ module MandrillMailer
|
|
324
324
|
"#{root_url}#{image_path(image).split('/').reject!(&:empty?).join('/')}"
|
325
325
|
end
|
326
326
|
|
327
|
-
|
328
|
-
|
329
|
-
args = merge_default_merge_vars(args)
|
330
|
-
|
331
|
-
return [] unless args
|
332
|
-
args.map do |k,v|
|
333
|
-
{'name' => k, 'content' => v}
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
|
-
def mandrill_rcpt_args(args)
|
338
|
-
return [] unless args
|
339
|
-
args.map do |item|
|
340
|
-
rcpt = item.keys[0]
|
341
|
-
{ 'rcpt' => rcpt, 'vars' => mandrill_args(item.fetch(rcpt)) }
|
342
|
-
end
|
327
|
+
def api_key
|
328
|
+
MandrillMailer.config.api_key
|
343
329
|
end
|
344
330
|
|
345
|
-
def
|
346
|
-
|
347
|
-
self.class.defaults[:merge_vars].merge(args)
|
348
|
-
else
|
349
|
-
self.class.defaults[:merge_vars]
|
350
|
-
end
|
331
|
+
def mandrill_api
|
332
|
+
@mandrill_api ||= Mandrill::API.new(api_key)
|
351
333
|
end
|
352
334
|
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
end
|
335
|
+
def extract_api_options!(args)
|
336
|
+
self.async = args.delete(:async)
|
337
|
+
self.ip_pool = args.delete(:ip_pool)
|
357
338
|
|
358
|
-
|
359
|
-
|
360
|
-
if to_params.kind_of? Array
|
361
|
-
to_params.map do |p|
|
362
|
-
to_params_item(p)
|
363
|
-
end
|
364
|
-
else
|
365
|
-
[to_params_item(to_params)]
|
339
|
+
if args.has_key?(:send_at)
|
340
|
+
self.send_at = args.delete(:send_at).getutc.strftime('%Y-%m-%d %H:%M:%S')
|
366
341
|
end
|
367
342
|
end
|
368
|
-
|
369
|
-
# single to params item
|
370
|
-
def to_params_item(item)
|
371
|
-
return {"email" => item, "name" => item} unless item.kind_of? Hash
|
372
|
-
item
|
373
|
-
end
|
374
|
-
|
375
|
-
def api_key
|
376
|
-
MandrillMailer.config.api_key
|
377
|
-
end
|
378
343
|
end
|
379
344
|
end
|
@@ -1,6 +1,4 @@
|
|
1
1
|
# MandrilMailer class for sending transactional emails through mandril.
|
2
|
-
# Only template based emails are supported at this time.
|
3
|
-
|
4
2
|
# Example usage:
|
5
3
|
|
6
4
|
# class InvitationMailer < MandrillMailer::MessageMailer
|
@@ -93,111 +91,13 @@
|
|
93
91
|
# message HTML - only for HTML documents less than 256KB in size
|
94
92
|
|
95
93
|
# :important - whether or not this message is important, and should be delivered ahead of non-important messages
|
96
|
-
require 'base64'
|
97
94
|
require 'mandrill_mailer/core_mailer'
|
98
95
|
|
99
96
|
module MandrillMailer
|
100
97
|
class MessageMailer < MandrillMailer::CoreMailer
|
101
|
-
# Public: The name of the template to use
|
102
|
-
attr_accessor :html
|
103
|
-
|
104
|
-
# Public: Template content
|
105
|
-
attr_accessor :text
|
106
|
-
|
107
98
|
# Public: Triggers the stored Mandrill params to be sent to the Mandrill api
|
108
99
|
def deliver
|
109
|
-
|
110
|
-
|
111
|
-
end
|
112
|
-
|
113
|
-
# Public: Build the hash needed to send to the mandrill api
|
114
|
-
#
|
115
|
-
# args - The Hash options used to refine the selection:
|
116
|
-
# :template - Template name in Mandrill
|
117
|
-
# :subject - Subject of the email
|
118
|
-
# :to - Email to send the mandrill email to
|
119
|
-
# :vars - Global merge vars used in the email for dynamic data
|
120
|
-
# :recipient_vars - Merge vars used in the email for recipient-specific dynamic data
|
121
|
-
# :bcc - bcc email for the mandrill email
|
122
|
-
# :tags - Tags for the email
|
123
|
-
# :google_analytics_domains - Google analytics domains
|
124
|
-
# :google_analytics_campaign - Google analytics campaign
|
125
|
-
# :inline_css - whether or not to automatically inline all CSS styles provided in the message HTML
|
126
|
-
# :important - whether or not this message is important
|
127
|
-
# :async - whether or not this message should be sent asynchronously
|
128
|
-
# :ip_pool - name of the dedicated IP pool that should be used to send the message
|
129
|
-
# :send_at - when this message should be sent
|
130
|
-
#
|
131
|
-
# Examples
|
132
|
-
#
|
133
|
-
# mandrill_mail template: 'Group Invite',
|
134
|
-
# subject: I18n.t('invitation_mailer.invite.subject'),
|
135
|
-
# to: invitation.email,
|
136
|
-
# vars: {
|
137
|
-
# 'OWNER_NAME' => invitation.owner_name,
|
138
|
-
# 'INVITATION_URL' => new_invitation_url(email: invitation.email, secret: invitation.secret)
|
139
|
-
# }
|
140
|
-
#
|
141
|
-
# Returns the the mandrill mailer class (this is so you can chain #deliver like a normal mailer)
|
142
|
-
def mandrill_mail(args)
|
143
|
-
# format the :to param to what Mandrill expects if a string or array is passed
|
144
|
-
args[:to] = format_to_params(args[:to])
|
145
|
-
|
146
|
-
self.async = args.delete(:async)
|
147
|
-
self.ip_pool = args.delete(:ip_pool)
|
148
|
-
if args.has_key?(:send_at)
|
149
|
-
self.send_at = args.delete(:send_at).getutc.strftime('%Y-%m-%d %H:%M:%S')
|
150
|
-
end
|
151
|
-
|
152
|
-
# Construct message hash
|
153
|
-
self.message = {
|
154
|
-
"text" => args[:text],
|
155
|
-
"html" => args[:html],
|
156
|
-
"view_content_link" => args[:view_content_link],
|
157
|
-
"subject" => args[:subject],
|
158
|
-
"from_email" => args[:from] || self.class.defaults[:from],
|
159
|
-
"from_name" => args[:from_name] || self.class.defaults[:from_name] || self.class.defaults[:from],
|
160
|
-
"to" => args[:to],
|
161
|
-
"headers" => args[:headers],
|
162
|
-
"important" => args[:important],
|
163
|
-
"track_opens" => args.fetch(:track_opens, true),
|
164
|
-
"track_clicks" => args.fetch(:track_clicks, true),
|
165
|
-
"auto_text" => true,
|
166
|
-
"inline_css" => args[:inline_css],
|
167
|
-
"url_strip_qs" => args.fetch(:url_strip_qs, true),
|
168
|
-
"preserve_recipients" => args[:preserve_recipients],
|
169
|
-
"bcc_address" => args[:bcc],
|
170
|
-
"global_merge_vars" => mandrill_args(args[:vars]),
|
171
|
-
"merge_vars" => mandrill_rcpt_args(args[:recipient_vars] || self.class.defaults[:merge_vars]),
|
172
|
-
"tags" => args[:tags],
|
173
|
-
"subaccount" => args[:subaccount],
|
174
|
-
"google_analytics_domains" => args[:google_analytics_domains],
|
175
|
-
"google_analytics_campaign" => args[:google_analytics_campaign],
|
176
|
-
"metadata" => args[:metadata],
|
177
|
-
"attachments" => mandrill_attachment_args(args[:attachments]),
|
178
|
-
"images" => mandrill_images_args(args[:images])
|
179
|
-
}
|
180
|
-
|
181
|
-
unless MandrillMailer.config.interceptor_params.nil?
|
182
|
-
unless MandrillMailer.config.interceptor_params.is_a?(Hash)
|
183
|
-
raise InvalidInterceptorParams.new "The interceptor_params config must be a Hash"
|
184
|
-
end
|
185
|
-
self.message.merge!(MandrillMailer.config.interceptor_params.stringify_keys)
|
186
|
-
end
|
187
|
-
|
188
|
-
# return self so we can chain deliver after the method call, like a normal mailer.
|
189
|
-
return self
|
190
|
-
end
|
191
|
-
|
192
|
-
# Public: Data hash (deprecated)
|
193
|
-
def data
|
194
|
-
{
|
195
|
-
"key" => api_key,
|
196
|
-
"message" => message,
|
197
|
-
"async" => async,
|
198
|
-
"ip_pool" => ip_pool,
|
199
|
-
"send_at" => send_at
|
200
|
-
}
|
201
|
-
end
|
100
|
+
mandrill_api.messages.send(message, async, ip_pool, send_at)
|
101
|
+
end
|
202
102
|
end
|
203
103
|
end
|