mandrill_mailer 0.4.8 → 0.4.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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