mandrill_mailer 0.4.8 → 0.4.9

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.
@@ -0,0 +1,212 @@
1
+ # MandrilMailer class for sending transactional emails through mandril.
2
+ # Only template based emails are supported at this time.
3
+
4
+ # Example usage:
5
+
6
+ # class InvitationMailer < MandrillMailer::MessageMailer
7
+ # default from: 'support@codeschool.com'
8
+
9
+ # def invite(invitation)
10
+ # invitees = invitation.invitees.map { |invitee| { email: invitee.email, name: invitee.name } }
11
+ # mandrill_mail html: "<p>Example HTML content</p>",
12
+ # text: "Example text content",
13
+ # subject: I18n.t('invitation_mailer.invite.subject'),
14
+ # to: invitees,
15
+ # # to: invitation.email
16
+ # # to: { email: invitation.email, name: invitation.recipient_name }
17
+ # vars: {
18
+ # 'OWNER_NAME' => invitation.owner_name,
19
+ # 'PROJECT_NAME' => invitation.project_name
20
+ # },
21
+ # recipient_vars: invitation.invitees.map do |invitee| # invitation.invitees is an Array
22
+ # { invitee.email =>
23
+ # {
24
+ # 'INVITEE_NAME' => invitee.name,
25
+ # 'INVITATION_URL' => new_invitation_url(invitee.email, secret: invitee.secret_code)
26
+ # }
27
+ # }
28
+ # end,
29
+ # attachments: [{file: File.read(File.expand_path('assets/some_image.png')), filename: 'My Image.png', mimetype: 'image/png'}],
30
+ # important: true,
31
+ # inline_css: true
32
+ # end
33
+ # end
34
+
35
+ # #default:
36
+ # :from - set the default from email address for the mailer
37
+
38
+ # .mandrill_mail
39
+ # :html(required) - HTML codes for the Message
40
+ # :text - Text for the Message
41
+
42
+ # :subject(required) - Subject of the email
43
+
44
+ # :to(required) - Accepts an email String, a Hash with :name and :email keys
45
+ # or an Array of Hashes with :name and :email keys
46
+ # examples:
47
+ # 1)
48
+ # 'example@domain.com`
49
+ # 2)
50
+ # { email: 'someone@email.com', name: 'Bob Bertly' }
51
+ # 3)
52
+ # [{ email: 'someone@email.com', name: 'Bob Bertly' },
53
+ # { email: 'other@email.com', name: 'Claire Nayo' }]
54
+ #
55
+
56
+ # :vars - A Hash of merge tags made available to the email. Use them in the
57
+ # email by wrapping them in '*||*' vars: {'OWNER_NAME' => 'Suzy'} is used
58
+ # by doing: *|OWNER_NAME|* in the email template within Mandrill
59
+ #
60
+ # :recipient_vars - Similar to :vars, this is a Hash of merge tags specific to a particular recipient.
61
+ # Use this if you are sending batch transactions and hence need to send multiple emails at one go.
62
+ # ex. [{'someone@email.com' => {'INVITEE_NAME' => 'Roger'}}, {'another@email.com' => {'INVITEE_NAME' => 'Tommy'}}]
63
+
64
+ # :attachments - An array of file objects with the following keys:
65
+ # file: This is the actual file, it will be converted to byte data in the mailer
66
+ # filename: The name of the file
67
+ # mimetype: This is the mimetype of the file. Ex. png = image/png, pdf = application/pdf, txt = text/plain etc
68
+
69
+ # :images - An array of embedded images to add to the message:
70
+ # file: This is the actual file, it will be converted to byte data in the mailer
71
+ # filename: The Content ID of the image - use <img src="cid:THIS_VALUE"> to reference the image in your HTML content
72
+ # mimetype: The MIME type of the image - must start with "image/"
73
+
74
+ # :headers - Extra headers to add to the message (currently only Reply-To and X-* headers are allowed) {"...": "..."}
75
+
76
+ # :bcc - Add an email to bcc to
77
+
78
+ # :tags - Array of Strings to tag the message with. Stats are
79
+ # accumulated using tags, though we only store the first 100 we see,
80
+ # so this should not be unique or change frequently. Tags should be
81
+ # 50 characters or less. Any tags starting with an underscore are
82
+ # reserved for internal use and will cause errors.
83
+
84
+ # :google_analytics_domains - Array of Strings indicating for which any
85
+ # matching URLs will automatically have Google Analytics parameters appended
86
+ # to their query string automatically.
87
+
88
+ # :google_analytics_campaign - String indicating the value to set for
89
+ # the utm_campaign tracking parameter. If this isn't provided the email's
90
+ # from address will be used instead.
91
+
92
+ # :inline_css - whether or not to automatically inline all CSS styles provided in the
93
+ # message HTML - only for HTML documents less than 256KB in size
94
+
95
+ # :important - whether or not this message is important, and should be delivered ahead of non-important messages
96
+ require 'base64'
97
+ require 'mandrill_mailer/core_mailer'
98
+
99
+ module MandrillMailer
100
+ 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
+ # Public: Triggers the stored Mandrill params to be sent to the Mandrill api
108
+ def deliver
109
+ mandrill = Mandrill::API.new(api_key)
110
+ check_required_options(message)
111
+ mandrill.messages.send(message, async, ip_pool, send_at)
112
+ end
113
+
114
+ # Public: Build the hash needed to send to the mandrill api
115
+ #
116
+ # args - The Hash options used to refine the selection:
117
+ # :template - Template name in Mandrill
118
+ # :subject - Subject of the email
119
+ # :to - Email to send the mandrill email to
120
+ # :vars - Global merge vars used in the email for dynamic data
121
+ # :recipient_vars - Merge vars used in the email for recipient-specific dynamic data
122
+ # :bcc - bcc email for the mandrill email
123
+ # :tags - Tags for the email
124
+ # :google_analytics_domains - Google analytics domains
125
+ # :google_analytics_campaign - Google analytics campaign
126
+ # :inline_css - whether or not to automatically inline all CSS styles provided in the message HTML
127
+ # :important - whether or not this message is important
128
+ # :async - whether or not this message should be sent asynchronously
129
+ # :ip_pool - name of the dedicated IP pool that should be used to send the message
130
+ # :send_at - when this message should be sent
131
+ #
132
+ # Examples
133
+ #
134
+ # mandrill_mail template: 'Group Invite',
135
+ # subject: I18n.t('invitation_mailer.invite.subject'),
136
+ # to: invitation.email,
137
+ # vars: {
138
+ # 'OWNER_NAME' => invitation.owner_name,
139
+ # 'INVITATION_URL' => new_invitation_url(email: invitation.email, secret: invitation.secret)
140
+ # }
141
+ #
142
+ # Returns the the mandrill mailer class (this is so you can chain #deliver like a normal mailer)
143
+ def mandrill_mail(args)
144
+ # format the :to param to what Mandrill expects if a string or array is passed
145
+ args[:to] = format_to_params(args[:to])
146
+
147
+ self.async = args.delete(:async)
148
+ self.ip_pool = args.delete(:ip_pool)
149
+ if args.has_key?(:send_at)
150
+ self.send_at = args.delete(:send_at).getutc.strftime('%Y-%m-%d %H:%M:%S')
151
+ end
152
+
153
+ # Construct message hash
154
+ self.message = {
155
+ "text" => args[:text],
156
+ "html" => args[:html],
157
+ "view_content_link" => args[:view_content_link],
158
+ "subject" => args[:subject],
159
+ "from_email" => args[:from] || self.class.defaults[:from],
160
+ "from_name" => args[:from_name] || self.class.defaults[:from_name] || self.class.defaults[:from],
161
+ "to" => args[:to],
162
+ "headers" => args[:headers],
163
+ "important" => args[:important],
164
+ "track_opens" => args.fetch(:track_opens, true),
165
+ "track_clicks" => args.fetch(:track_clicks, true),
166
+ "auto_text" => true,
167
+ "inline_css" => args[:inline_css],
168
+ "url_strip_qs" => args.fetch(:url_strip_qs, true),
169
+ "preserve_recipients" => args[:preserve_recipients],
170
+ "bcc_address" => args[:bcc],
171
+ "global_merge_vars" => mandrill_args(args[:vars]),
172
+ "merge_vars" => mandrill_rcpt_args(args[:recipient_vars]),
173
+ "tags" => args[:tags],
174
+ "subaccount" => args[:subaccount],
175
+ "google_analytics_domains" => args[:google_analytics_domains],
176
+ "google_analytics_campaign" => args[:google_analytics_campaign],
177
+ "metadata" => args[:metadata],
178
+ "attachments" => mandrill_attachment_args(args[:attachments]),
179
+ "images" => mandrill_images_args(args[:images])
180
+ }
181
+
182
+ unless MandrillMailer.config.interceptor_params.nil?
183
+ unless MandrillMailer.config.interceptor_params.is_a?(Hash)
184
+ raise InvalidInterceptorParams.new "The interceptor_params config must be a Hash"
185
+ end
186
+ self.message.merge!(MandrillMailer.config.interceptor_params.stringify_keys)
187
+ end
188
+
189
+ # return self so we can chain deliver after the method call, like a normal mailer.
190
+ return self
191
+ end
192
+
193
+ # Public: Data hash (deprecated)
194
+ def data
195
+ {
196
+ "key" => api_key,
197
+ "message" => message,
198
+ "async" => async,
199
+ "ip_pool" => ip_pool,
200
+ "send_at" => send_at
201
+ }
202
+ end
203
+
204
+
205
+ def check_required_options(options)
206
+ names = ['text', 'html', 'from', 'subject', 'to']
207
+ names.each do |name|
208
+ warn("Mandrill Mailer Warn: missing required option: #{name}") unless options.has_key?(name)
209
+ end
210
+ end
211
+ end
212
+ end
@@ -99,93 +99,10 @@
99
99
 
100
100
  # :important - whether or not this message is important, and should be delivered ahead of non-important messages
101
101
  require 'base64'
102
+ require 'mandrill_mailer/core_mailer'
102
103
 
103
104
  module MandrillMailer
104
- class TemplateMailer
105
- # include Rails.application.routes.url_helpers
106
- include ActionView::Helpers::NumberHelper
107
-
108
-
109
- class InvalidEmail < StandardError; end
110
- class InvalidMailerMethod < StandardError; end
111
- class InvalidInterceptorParams < StandardError; end
112
-
113
- # Public: Defaults for the mailer. Currently the only option is from:
114
- #
115
- # options - The Hash options used to refine the selection (default: {}):
116
- # :from - Default from email address
117
- #
118
- # Examples
119
- #
120
- # default from: 'foo@bar.com'
121
- #
122
- # Returns options
123
- def self.defaults
124
- @defaults || super_defaults
125
- end
126
-
127
- def self.super_defaults
128
- superclass.defaults if superclass.respond_to?(:defaults)
129
- end
130
-
131
- def self.default(args)
132
- @defaults ||= {}
133
- @defaults[:from] ||= 'example@email.com'
134
- @defaults.merge!(args)
135
- end
136
- class << self
137
- attr_writer :defaults
138
- end
139
-
140
- # Public: setup a way to test mailer methods
141
- #
142
- # mailer_method - Name of the mailer method the test setup is for
143
- #
144
- # block - Block of code to execute to perform the test. The mailer
145
- # and options are passed to the block. The options have to
146
- # contain at least the :email to send the test to.
147
- #
148
- # Examples
149
- #
150
- # test_setup_for :invite do |mailer, options|
151
- # invitation = OpenStruct.new({
152
- # email: options[:email],
153
- # owner_name: 'foobar',
154
- # secret: rand(9000000..1000000).to_s
155
- # })
156
- # mailer.invite(invitation).deliver
157
- # end
158
- #
159
- # Returns the duplicated String.
160
- def self.test_setup_for(mailer_method, &block)
161
- @mailer_methods ||= {}
162
- @mailer_methods[mailer_method] = block
163
- end
164
-
165
- # Public: Executes a test email
166
- #
167
- # mailer_method - Method to execute
168
- #
169
- # options - The Hash options used to refine the selection (default: {}):
170
- # :email - The email to send the test to.
171
- #
172
- # Examples
173
- #
174
- # InvitationMailer.test(:invite, email: 'benny@envylabs.com')
175
- #
176
- # Returns the duplicated String.
177
- def self.test(mailer_method, options={})
178
- unless options[:email]
179
- raise InvalidEmail.new 'Please specify a :email option(email to send the test to)'
180
- end
181
-
182
- if @mailer_methods[mailer_method]
183
- @mailer_methods[mailer_method].call(self.new, options)
184
- else
185
- raise InvalidMailerMethod.new "The mailer method: #{mailer_method} does not have test setup"
186
- end
187
-
188
- end
105
+ class TemplateMailer < MandrillMailer::CoreMailer
189
106
 
190
107
  # Public: The name of the template to use
191
108
  attr_accessor :template_name
@@ -193,31 +110,10 @@ module MandrillMailer
193
110
  # Public: Template content
194
111
  attr_accessor :template_content
195
112
 
196
- # Public: Other information on the message to send
197
- attr_accessor :message
198
-
199
- # Public: Enable background sending mode
200
- attr_accessor :async
201
-
202
- # Public: Name of the dedicated IP pool that should be used to send the message
203
- attr_accessor :ip_pool
204
-
205
- # Public: When message should be sent
206
- attr_accessor :send_at
207
-
208
- # Public: Triggers the stored Mandril params to be sent to the Mandrill api
209
- #
210
- # text - The String to be duplicated.
211
- # count - The Integer number of times to duplicate the text.
212
- #
213
- # Examples
214
- #
215
- # multiplex('Tom', 4)
216
- # # => 'TomTomTomTom'
217
- #
218
- # Returns the duplicated String.
113
+ # Public: Triggers the stored Mandrill params to be sent to the Mandrill api
219
114
  def deliver
220
115
  mandrill = Mandrill::API.new(api_key)
116
+ check_required_options(message)
221
117
  mandrill.messages.send_template(template_name, template_content, message, async, ip_pool, send_at)
222
118
  end
223
119
 
@@ -263,9 +159,9 @@ module MandrillMailer
263
159
 
264
160
  # Set the template content
265
161
  self.template_content = mandrill_args(args.delete(:template_content))
266
-
267
162
  self.async = args.delete(:async)
268
163
  self.ip_pool = args.delete(:ip_pool)
164
+
269
165
  if args.has_key?(:send_at)
270
166
  self.send_at = args.delete(:send_at).getutc.strftime('%Y-%m-%d %H:%M:%S')
271
167
  end
@@ -295,7 +191,6 @@ module MandrillMailer
295
191
  "attachments" => mandrill_attachment_args(args[:attachments]),
296
192
  "images" => mandrill_images_args(args[:images])
297
193
  }
298
-
299
194
  unless MandrillMailer.config.interceptor_params.nil?
300
195
  unless MandrillMailer.config.interceptor_params.is_a?(Hash)
301
196
  raise InvalidInterceptorParams.new "The interceptor_params config must be a Hash"
@@ -320,130 +215,12 @@ module MandrillMailer
320
215
  }
321
216
  end
322
217
 
323
- def from
324
- self.message && self.message['from_email']
325
- end
326
-
327
- def to
328
- self.message && self.message['to']
329
- end
330
-
331
- def to=(values)
332
- self.message && self.message['to'] = format_to_params(values)
333
- end
334
-
335
- def bcc
336
- self.message && self.message['bcc_address']
337
- end
338
-
339
- protected
340
-
341
- def mandrill_attachment_args(args)
342
- return unless args
343
- args.map do |attachment|
344
- attachment.symbolize_keys!
345
- type = attachment[:mimetype]
346
- name = attachment[:filename]
347
- file = attachment[:file]
348
- {"type" => type, "name" => name, "content" => Base64.encode64(file)}
349
- end
350
- end
351
-
352
- def mandrill_images_args(args)
353
- return unless args
354
- args.map do |attachment|
355
- attachment.symbolize_keys!
356
- type = attachment[:mimetype]
357
- name = attachment[:filename]
358
- file = attachment[:file]
359
- {"type" => type, "name" => name, "content" => Base64.encode64(file)}
360
- end
361
- end
362
-
363
- # Makes this class act as a singleton without it actually being a singleton
364
- # This keeps the syntax the same as the orginal mailers so we can swap quickly if something
365
- # goes wrong.
366
- def self.method_missing(method, *args)
367
- return super unless respond_to?(method)
368
- new.method(method).call(*args)
369
- end
370
-
371
- def self.respond_to?(method, include_private = false)
372
- super || instance_methods.include?(method.to_sym)
373
- end
374
-
375
- # Proxy route helpers to rails if Rails exists. Doing routes this way
376
- # makes it so this gem doesn't need to be a rails engine
377
- def method_missing(method, *args)
378
- return super unless defined?(Rails) && Rails.application.routes.url_helpers.respond_to?(method)
379
- # Check to see if one of the args is an open struct. If it is, we'll assume it's the
380
- # test stub and try to call a path or url attribute.
381
- if args.any? {|arg| arg.kind_of?(MandrillMailer::Mock)}
382
- # take the first OpenStruct found in args and look for .url or.path
383
- args.each do |arg|
384
- if arg.kind_of?(MandrillMailer::Mock)
385
- break arg.url || arg.path
386
- end
387
- end
388
- else
389
- options = args.extract_options!.merge({host: MandrillMailer.config.default_url_options[:host], protocol: MandrillMailer.config.default_url_options[:protocol]})
390
- args << options
391
- Rails.application.routes.url_helpers.method(method).call(*args)
392
- end
393
- end
394
-
395
- def image_path(image)
396
- if defined? Rails
397
- ActionController::Base.helpers.asset_path(image)
398
- else
399
- method_missing(:image_path, image)
400
- end
401
- end
402
-
403
- def image_url(image)
404
- "#{root_url}#{image_path(image).split('/').reject!(&:empty?).join('/')}"
405
- end
406
-
407
- # convert a normal hash into the format mandrill needs
408
- def mandrill_args(args)
409
- return [] unless args
410
- args.map do |k,v|
411
- {'name' => k, 'content' => v}
412
- end
413
- end
414
-
415
- def mandrill_rcpt_args(args)
416
- return [] unless args
417
- args.map do |item|
418
- rcpt = item.keys[0]
419
- {'rcpt' => rcpt, 'vars' => mandrill_args(item.fetch(rcpt))}
218
+ def check_required_options(options)
219
+ names = ['to', 'from', 'subject']
220
+
221
+ names.each do |name|
222
+ warn("Mandrill Mailer Warn: missing required option: #{name}") unless options.has_key?(name)
420
223
  end
421
224
  end
422
-
423
- # ensure only true or false is returned given arg
424
- def format_boolean(arg)
425
- arg ? true : false
426
- end
427
-
428
- # handle if to params is an array of either hashes or strings or the single string
429
- def format_to_params(to_params)
430
- if to_params.kind_of? Array
431
- to_params.map do |p|
432
- to_params_item(p)
433
- end
434
- else
435
- [to_params_item(to_params)]
436
- end
437
- end
438
-
439
- # single to params item
440
- def to_params_item(item)
441
- return {"email" => item, "name" => item} unless item.kind_of? Hash
442
- item
443
- end
444
-
445
- def api_key
446
- MandrillMailer.config.api_key
447
- end
448
225
  end
449
226
  end