mailgun-ruby 1.1.8 → 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.
Files changed (39) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +6 -5
  3. data/Gemfile +1 -1
  4. data/README.md +24 -2
  5. data/docs/Domains.md +0 -0
  6. data/docs/Webhooks.md +1 -1
  7. data/docs/railgun/Overview.md +11 -0
  8. data/docs/railgun/Parameters.md +83 -0
  9. data/lib/mailgun/client.rb +27 -6
  10. data/lib/mailgun/events/events.rb +1 -1
  11. data/lib/mailgun/exceptions/exceptions.rb +2 -0
  12. data/lib/mailgun/messages/batch_message.rb +1 -0
  13. data/lib/mailgun/messages/message_builder.rb +61 -6
  14. data/lib/mailgun/suppressions.rb +4 -1
  15. data/lib/mailgun/version.rb +1 -1
  16. data/lib/mailgun/webhooks/webhooks.rb +1 -1
  17. data/lib/railgun/mailer.rb +105 -13
  18. data/lib/railgun/message.rb +2 -1
  19. data/lib/railgun/railtie.rb +3 -2
  20. data/mailgun.gemspec +11 -11
  21. data/spec/integration/email_validation_spec.rb +8 -0
  22. data/spec/integration/events_spec.rb +1 -1
  23. data/spec/integration/mailer_spec.rb +67 -0
  24. data/spec/integration/mailgun_spec.rb +4 -1
  25. data/spec/integration/suppressions_spec.rb +18 -2
  26. data/spec/spec_helper.rb +3 -1
  27. data/spec/unit/connection/test_client.rb +16 -0
  28. data/spec/unit/events/events_spec.rb +19 -0
  29. data/spec/unit/mailgun_spec.rb +24 -2
  30. data/spec/unit/messages/batch_message_spec.rb +56 -40
  31. data/spec/unit/messages/message_builder_spec.rb +165 -17
  32. data/spec/unit/messages/sample_data/unknown.type +0 -0
  33. data/spec/unit/railgun/content_type_spec.rb +71 -0
  34. data/spec/unit/railgun/mailer_spec.rb +388 -0
  35. data/vcr_cassettes/mailer_invalid_domain.yml +109 -0
  36. data/vcr_cassettes/message_deliver.yml +149 -0
  37. data/vcr_cassettes/suppressions.yml +66 -15
  38. metadata +50 -26
  39. data/.ruby-version +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 03bb73706b0d179b3f48cfa58f68acc8b0d716e7
4
- data.tar.gz: 3fc860e152511f367033a5954ba95720a474fd01
2
+ SHA256:
3
+ metadata.gz: eeb68d33e0686c3074e829fa7c33c1a9300c31bd9db3b7fde74abfdbdd681a31
4
+ data.tar.gz: dc0cd7912abb804476731ba487186a362595f7806d88f50bbbe2296da8eb0b5b
5
5
  SHA512:
6
- metadata.gz: 0d1f7e536083087eb9c2423c8cb3cd54624b5d0a7fbfb96eb8740b06c2947c14d94542e6ef22439d0f0711d9627b5219e09b9ccab7408b454e20ad1531a07d56
7
- data.tar.gz: 9e03a0244f385c15497856fe9c421d3d330a38309798313c88f3139a86b6fc163cf6234654e15a1c1faceca40b70ad590ff5caf02c3cefc2d225a4eb3bc2b852
6
+ metadata.gz: 9469121fbdd8341af7cc6ee395d35d8f487f556b67d8b6a0d2b6cd874f4bb1828384f9aff7c6751377fa56182b7c890947e40444f09d583d97aca8f90c41c642
7
+ data.tar.gz: 2f2b70e53e187c28ea3fcf3d3abc3f208886baca01732af0bb233c154bd171565790279620fe392259f3fa74151276d4e281389606d35ce60047b6f9a922a7cf
data/.travis.yml CHANGED
@@ -1,10 +1,11 @@
1
1
  language: ruby
2
2
  sudo: false
3
3
  rvm:
4
- - 2.0.0
5
- - 2.1
6
- - 2.2
7
- - 2.3.1
4
+ - 2.2.2
5
+ - 2.2.10
6
+ - 2.3.7
7
+ - 2.4.4
8
+ - 2.5.1
8
9
  script:
9
10
  - bundle install
10
11
  - bundle exec rake spec
@@ -17,7 +18,7 @@ deploy:
17
18
  gemspec: mailgun.gemspec
18
19
  on:
19
20
  tags: true
20
- condition: "$TRAVIS_RUBY_VERSION == 2.3.1"
21
+ condition: "$TRAVIS_RUBY_VERSION == 2.5.1"
21
22
  notifications:
22
23
  slack:
23
24
  rooms:
data/Gemfile CHANGED
@@ -3,4 +3,4 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in mailgun.gemspec
4
4
  gemspec
5
5
 
6
- gem 'json', '~> 1.8', platform: :mri_19
6
+ gem 'json', '~> 2.1', platform: :mri_19
data/README.md CHANGED
@@ -19,7 +19,7 @@ gem install mailgun-ruby
19
19
  Gemfile:
20
20
 
21
21
  ```ruby
22
- gem 'mailgun-ruby', '~>1.1.6'
22
+ gem 'mailgun-ruby', '~>1.2.5'
23
23
  ```
24
24
 
25
25
  Usage
@@ -27,7 +27,7 @@ Usage
27
27
  Here's how to send a message using the library:
28
28
 
29
29
  ```ruby
30
- require 'mailgun'
30
+ require 'mailgun-ruby'
31
31
 
32
32
  # First, instantiate the Mailgun Client with your API key
33
33
  mg_client = Mailgun::Client.new 'your-api-key'
@@ -56,6 +56,12 @@ domain = 'example.com'
56
56
  result = mg_client.get("#{domain}/events", {:event => 'delivered'})
57
57
  ```
58
58
 
59
+ If you're using the EU domains, make sure you specify it when creating the client:
60
+
61
+ ```
62
+ mg_client = Mailgun::Client.new 'your-api-key', 'api.eu.mailgun.net'
63
+ ```
64
+
59
65
  Rails
60
66
  -----
61
67
 
@@ -74,9 +80,25 @@ and replace `api-myapikey` and `mydomain.com` with your secret API key and domai
74
80
  config.action_mailer.mailgun_settings = {
75
81
  api_key: 'api-myapikey',
76
82
  domain: 'mydomain.com',
83
+ # api_host: 'api.eu.mailgun.net' # Uncomment this line for EU region domains
77
84
  }
78
85
  ```
79
86
 
87
+ To specify Mailgun options such as campaign or tags:
88
+ ```ruby
89
+ class UserMailer < ApplicationMailer
90
+ def welcome_email
91
+ mail(to: params[:to], subject: "Welcome!").tap do |message|
92
+ message.mailgun_options = {
93
+ "tag" => ["abtest-option-a", "beta-user"],
94
+ "tracking-opens" => true,
95
+ "tracking-clicks" => "htmlonly"
96
+ }
97
+ end
98
+ end
99
+ end
100
+ ```
101
+
80
102
  To get the Mailgun `message_id` after ActionMailer has successfully delivered the email:
81
103
 
82
104
  ```ruby
data/docs/Domains.md CHANGED
File without changes
data/docs/Webhooks.md CHANGED
@@ -31,7 +31,7 @@ hook.create 'my.perfect.domain', 'deliver', 'https://the.webhook.url/'
31
31
  hook.remove 'my.perfect.domain', 'deliver'
32
32
 
33
33
  # Remove all webhooks for a domain
34
- hook.remove 'my.perfect.domain'
34
+ hook.remove_all 'my.perfect.domain'
35
35
  ```
36
36
 
37
37
  More Documentation
@@ -0,0 +1,11 @@
1
+ Overview
2
+ ========
3
+
4
+ Railgun is a Rails add-on that allows ActionMailer to send via the Mailgun API.
5
+
6
+ See [railgun-sample](https://github.com/pirogoeth/railgun-sample/) for examples of integrating Railgun with your Rails app.
7
+
8
+
9
+ ## Table of Contents
10
+
11
+ - [Parameters](/docs/railgun/Parameters.md)
@@ -0,0 +1,83 @@
1
+ Parameters
2
+ ==========
3
+
4
+ When sending messages via Railgun, it is often useful to set options, headers, and variables
5
+ that should be added to the `POST` request against the messages endpoint.
6
+
7
+
8
+ ## Options
9
+
10
+ See [Mailgun Docs | Sending](https://documentation.mailgun.com/en/latest/api-sending.html#sending) for available options.
11
+
12
+ ---
13
+
14
+ To set options on a message:
15
+
16
+ ```ruby
17
+ # app/controllers/some_controller.rb
18
+
19
+ message = YourMailer.your_message(@args)
20
+
21
+ message.mailgun_options ||= {
22
+ "tracking-opens" => "true",
23
+ "tracking-clicks" => "htmlonly",
24
+ "tag" => "some,tags",
25
+ }
26
+ ```
27
+
28
+
29
+ ## Variables
30
+
31
+ See [Mailgun Docs | Attaching Data to Messages](https://documentation.mailgun.com/en/latest/user_manual.html#attaching-data-to-messages) for more information.
32
+
33
+ ---
34
+
35
+ To set variables on a message:
36
+
37
+ ```ruby
38
+ # app/controllers/some_controller.rb
39
+
40
+ message = YourMailer.your_message(@args)
41
+
42
+ message.mailgun_variables ||= {
43
+ "user_info" => {"id" => "1", "name" => "tstark"},
44
+ }
45
+ ```
46
+
47
+
48
+ ## Headers
49
+
50
+ See [Mailgun Docs | Sending](https://documentation.mailgun.com/en/latest/api-sending.html#sending) for more information.
51
+
52
+ ---
53
+
54
+ To set headers on a message *from a controller*:
55
+
56
+ ```ruby
57
+ # app/controllers/some_controller.rb
58
+
59
+ message = YourMailer.your_message(@args)
60
+
61
+ message.mailgun_headers ||= {
62
+ "X-Sent-From-Rails" => "true",
63
+ }
64
+ ```
65
+
66
+ To set headers on a message *from a mailer*:
67
+
68
+ ```ruby
69
+ # app/mailers/your_mailer.rb
70
+
71
+ class YourMailer < ApplicationMailer
72
+ # ...
73
+
74
+ def your_message(args)
75
+ headers({
76
+ "X-Sent-From-Rails" => "true",
77
+ })
78
+
79
+ mail to: "some-address@example.org", ...
80
+ end
81
+
82
+ end
83
+ ```
@@ -14,13 +14,20 @@ module Mailgun
14
14
  api_host = 'api.mailgun.net',
15
15
  api_version = 'v3',
16
16
  ssl = true,
17
- test_mode = false)
17
+ test_mode = false,
18
+ timeout = nil,
19
+ proxy_url = nil)
20
+
21
+ rest_client_params = {
22
+ user: 'api',
23
+ password: api_key,
24
+ user_agent: "mailgun-sdk-ruby/#{Mailgun::VERSION}"
25
+ }
26
+ rest_client_params[:timeout] = timeout if timeout
18
27
 
19
28
  endpoint = endpoint_generator(api_host, api_version, ssl)
20
- @http_client = RestClient::Resource.new(endpoint,
21
- user: 'api',
22
- password: api_key,
23
- user_agent: "mailgun-sdk-ruby/#{Mailgun::VERSION}")
29
+ RestClient.proxy = proxy_url
30
+ @http_client = RestClient::Resource.new(endpoint, rest_client_params)
24
31
  @test_mode = test_mode
25
32
  end
26
33
 
@@ -59,11 +66,13 @@ module Mailgun
59
66
  # containing required parameters for the requested resource.
60
67
  # @return [Mailgun::Response] A Mailgun::Response object.
61
68
  def send_message(working_domain, data)
69
+ perform_data_validation(working_domain, data)
70
+
62
71
  if test_mode? then
63
72
  Mailgun::Client.deliveries << data
64
73
  return Response.from_hash(
65
74
  {
66
- :body => '{"id": "test-mode-mail@localhost", "message": "Queued. Thank you."}',
75
+ :body => "{\"id\": \"test-mode-mail-#{SecureRandom.uuid}@localhost\", \"message\": \"Queued. Thank you.\"}",
67
76
  :code => 200,
68
77
  }
69
78
  )
@@ -195,5 +204,17 @@ module Mailgun
195
204
  CommunicationError.new(e.message)
196
205
  end
197
206
 
207
+ def perform_data_validation(working_domain, data)
208
+ message = data.respond_to?(:message) ? data.message : data
209
+ fail ParameterError.new('Missing working domain', working_domain) unless working_domain
210
+ fail ParameterError.new(
211
+ 'Missing `to` recipient, message should containg at least 1 recipient',
212
+ working_domain
213
+ ) if message.fetch('to', []).empty? && message.fetch(:to, []).empty?
214
+ fail ParameterError.new(
215
+ 'Missing a `from` sender, message should containg at least 1 `from` sender',
216
+ working_domain
217
+ ) if message.fetch('from', []).empty? && message.fetch(:from, []).empty?
218
+ end
198
219
  end
199
220
  end
@@ -101,7 +101,7 @@ module Mailgun
101
101
  # Returns a String of the partial URI if the given url follows the regular API format
102
102
  # Returns nil in other cases (e.g. when given nil, or an irrelevant url)
103
103
  def extract_endpoint_from(url = nil)
104
- URI.parse(url).path[/api.mailgun.net\/v[\d]\/#{@domain}\/events\/(.+)/,1]
104
+ URI.parse(url).path[/\/v[\d]\/#{@domain}\/events\/(.+)/,1]
105
105
  rescue URI::InvalidURIError
106
106
  nil
107
107
  end
@@ -34,6 +34,7 @@ module Mailgun
34
34
 
35
35
  # Public: fallback if there is no response code on the object
36
36
  NOCODE = 000
37
+ FORBIDDEN = 'Forbidden'
37
38
 
38
39
  # Public: initialization of new error given a message and/or object
39
40
  #
@@ -51,6 +52,7 @@ module Mailgun
51
52
  rescue NoMethodError
52
53
  api_message = "Unknown API error"
53
54
  end
55
+ api_message = api_message + ' - Invalid Domain or API key' if api_message == FORBIDDEN
54
56
 
55
57
  message = message || ''
56
58
  message = message + ': ' + api_message
@@ -111,6 +111,7 @@ module Mailgun
111
111
  # This method resets the message object to prepare for the next batch
112
112
  # of recipients.
113
113
  def reset_message
114
+ @recipient_variables = {}
114
115
  @message.delete('recipient-variables')
115
116
  @message.delete(:to)
116
117
  @message.delete(:cc)
@@ -1,3 +1,4 @@
1
+ require 'mime/types'
1
2
  require 'time'
2
3
 
3
4
  module Mailgun
@@ -197,7 +198,9 @@ module Mailgun
197
198
  # @param [Boolean] tracking Boolean true or false.
198
199
  # @return [void]
199
200
  def track_opens(mode)
200
- set_multi_simple('o:tracking-opens', bool_lookup(mode))
201
+ value = bool_lookup(mode)
202
+ set_single('o:tracking-opens', value)
203
+ set_multi_simple('o:tracking', value)
201
204
  end
202
205
 
203
206
  # Deprecated: 'set_open_tracking' is deprecated. Please use 'track_opens' instead.
@@ -211,7 +214,9 @@ module Mailgun
211
214
  # @param [String] mode True, False, or HTML (for HTML only tracking)
212
215
  # @return [void]
213
216
  def track_clicks(mode)
214
- set_multi_simple('o:tracking-clicks', bool_lookup(mode))
217
+ value = bool_lookup(mode)
218
+ set_single('o:tracking-clicks', value)
219
+ set_multi_simple('o:tracking', value)
215
220
  end
216
221
 
217
222
  # Depreciated: 'set_click_tracking. is deprecated. Please use 'track_clicks' instead.
@@ -269,8 +274,12 @@ module Mailgun
269
274
  # @return [void]
270
275
  def variable(name, data)
271
276
  fail(Mailgun::ParameterError, 'Variable name must be specified') if name.to_s.empty?
272
- jsondata = make_json data
273
- set_single("v:#{name}", jsondata)
277
+ begin
278
+ jsondata = make_json data
279
+ set_single("v:#{name}", jsondata)
280
+ rescue Mailgun::ParameterError
281
+ set_single("v:#{name}", data)
282
+ end
274
283
  end
275
284
 
276
285
  # Add custom parameter to the message. A custom parameter is any parameter that
@@ -303,6 +312,38 @@ module Mailgun
303
312
  message_id data
304
313
  end
305
314
 
315
+ # Set name of a template stored via template API. See Templates for more information
316
+ # https://documentation.mailgun.com/en/latest/api-templates.html
317
+ #
318
+ # @param [String] tag A defined template name to use. Passing nil or
319
+ # empty string will delete template key and value from @message hash.
320
+ # @return [void]
321
+ def template(template_name = nil)
322
+ key = 'template'
323
+ return @message.delete(key) if template_name.to_s.empty?
324
+ set_single(key, template_name)
325
+ end
326
+
327
+ # Set specific template version.
328
+ #
329
+ # @param [String] tag A defined template name to use. Passing nil or
330
+ # empty string will delete template key and value from @message hash.
331
+ # @return [void]
332
+ def template_version(version = nil)
333
+ key = 't:version'
334
+ return @message.delete(key) if version.to_s.empty?
335
+ set_single(key, version)
336
+ end
337
+
338
+ # Turn off or on template rendering in the text part
339
+ # of the message in case of template sending.
340
+ #
341
+ # @param [Boolean] tracking Boolean true or false.
342
+ # @return [void]
343
+ def template_text(mode)
344
+ set_single('t:text', bool_lookup(mode))
345
+ end
346
+
306
347
  private
307
348
 
308
349
  # Sets a single value in the message hash where "multidict" features are not needed.
@@ -342,6 +383,7 @@ module Mailgun
342
383
  def bool_lookup(value)
343
384
  return 'yes' if %w(true yes yep).include? value.to_s.downcase
344
385
  return 'no' if %w(false no nope).include? value.to_s.downcase
386
+ warn 'WARN: for bool type actions next values are prefered: true yes yep | false no nope | htmlonly'
345
387
  value
346
388
  end
347
389
 
@@ -379,10 +421,17 @@ module Mailgun
379
421
  def parse_address(address, vars)
380
422
  return address unless vars.is_a? Hash
381
423
  fail(Mailgun::ParameterError, 'Email address not specified') unless address.is_a? String
424
+ if vars['full_name'] != nil && (vars['first'] != nil || vars['last'] != nil)
425
+ fail(Mailgun::ParameterError, 'Must specify at most one of full_name or first/last. Vars passed: #{vars}')
426
+ end
382
427
 
383
- full_name = "#{vars['first']} #{vars['last']}".strip
428
+ if vars['full_name']
429
+ full_name = vars['full_name']
430
+ elsif vars['first'] || vars['last']
431
+ full_name = "#{vars['first']} #{vars['last']}".strip
432
+ end
384
433
 
385
- return "'#{full_name}' <#{address}>" if defined?(full_name)
434
+ return "'#{full_name}' <#{address}>" if full_name
386
435
  address
387
436
  end
388
437
 
@@ -402,6 +451,12 @@ module Mailgun
402
451
  'Unable to access attachment file object.'
403
452
  ) unless attachment.respond_to?(:read)
404
453
 
454
+ if attachment.respond_to?(:path) && !attachment.respond_to?(:content_type)
455
+ mime_types = MIME::Types.type_for(attachment.path)
456
+ content_type = mime_types.empty? ? 'application/octet-stream' : mime_types[0].content_type
457
+ attachment.instance_eval "def content_type; '#{content_type}'; end"
458
+ end
459
+
405
460
  unless filename.nil?
406
461
  attachment.instance_variable_set :@original_filename, filename
407
462
  attachment.instance_eval 'def original_filename; @original_filename; end'
@@ -157,7 +157,10 @@ module Mailgun
157
157
 
158
158
  unsubscribe.each do |k, v|
159
159
  # Hash values MUST be strings.
160
- if not v.is_a? String then
160
+ # However, unsubscribes contain an array of tags
161
+ if v.is_a? Array
162
+ unsubscribe[k] = v.map(&:to_s)
163
+ elsif !v.is_a? String
161
164
  unsubscribe[k] = v.to_s
162
165
  end
163
166
  end
@@ -1,4 +1,4 @@
1
1
  # It's the version. Yeay!
2
2
  module Mailgun
3
- VERSION = '1.1.8'
3
+ VERSION = '1.2.5'
4
4
  end
@@ -46,7 +46,7 @@ module Mailgun
46
46
  # Returns a Boolean of whether the webhook was created
47
47
  def create(domain, action, url = '')
48
48
  res = @client.post("domains/#{domain}/webhooks", id: action, url: url)
49
- res.to_h['webhook']['url'] == url && res.to_h[message] == 'Webhook has been created'
49
+ res.to_h['webhook']['url'] == url && res.to_h['message'] == 'Webhook has been created'
50
50
  end
51
51
  alias_method :add, :create
52
52
  alias_method :add_webhook, :create
@@ -1,4 +1,5 @@
1
1
  require 'action_mailer'
2
+ require 'json'
2
3
  require 'mailgun'
3
4
  require 'rails'
4
5
  require 'railgun/errors'
@@ -9,6 +10,9 @@ module Railgun
9
10
  # Mailgun.
10
11
  class Mailer
11
12
 
13
+ # List of the headers that will be ignored when copying headers from `mail.header_fields`
14
+ IGNORED_HEADERS = %w[ to from subject reply-to mime-version template ]
15
+
12
16
  # [Hash] config ->
13
17
  # Requires *at least* `api_key` and `domain` keys.
14
18
  attr_accessor :config, :domain, :settings
@@ -23,7 +27,14 @@ module Railgun
23
27
  raise Railgun::ConfigurationError.new("Config requires `#{k}` key", @config) unless @config.has_key?(k)
24
28
  end
25
29
 
26
- @mg_client = Mailgun::Client.new(config[:api_key])
30
+ @mg_client = Mailgun::Client.new(
31
+ config[:api_key],
32
+ config[:api_host] || 'api.mailgun.net',
33
+ config[:api_version] || 'v3',
34
+ config[:api_ssl].nil? ? true : config[:api_ssl],
35
+ false,
36
+ config[:timeout],
37
+ )
27
38
  @domain = @config[:domain]
28
39
 
29
40
  # To avoid exception in mail gem v2.6
@@ -36,8 +47,11 @@ module Railgun
36
47
  end
37
48
 
38
49
  def deliver!(mail)
50
+ @mg_domain = set_mg_domain(mail)
51
+ mail[:domain] = nil if mail[:domain].present?
52
+
39
53
  mg_message = Railgun.transform_for_mailgun(mail)
40
- response = @mg_client.send_message(@domain, mg_message)
54
+ response = @mg_client.send_message(@mg_domain, mg_message)
41
55
 
42
56
  if response.code == 200 then
43
57
  mg_id = response.to_h['id']
@@ -47,7 +61,15 @@ module Railgun
47
61
  end
48
62
 
49
63
  def mailgun_client
50
- @mg_obj
64
+ @mg_client
65
+ end
66
+
67
+ private
68
+
69
+ # Set @mg_domain from mail[:domain] header if present, then remove it to prevent being sent.
70
+ def set_mg_domain(mail)
71
+ return mail[:domain].value if mail[:domain]
72
+ domain
51
73
  end
52
74
 
53
75
  end
@@ -58,24 +80,60 @@ module Railgun
58
80
  # After prefixing them with the proper option type, they are added to
59
81
  # the message hash where they will then be sent to the API as JSON.
60
82
  #
83
+ # It is important to note that headers set in `mailgun_headers` on the message
84
+ # WILL overwrite headers set via `mail.headers()`.
85
+ #
61
86
  # @param [Mail::Message] mail message to transform
62
87
  #
63
88
  # @return [Hash] transformed message hash
64
89
  def transform_for_mailgun(mail)
65
90
  message = build_message_object(mail)
66
91
 
67
- # v:* attributes (variables)
68
- mail.mailgun_variables.try(:each) do |k, v|
69
- message["v:#{k}"] = v
70
- end
71
-
72
92
  # o:* attributes (options)
73
93
  mail.mailgun_options.try(:each) do |k, v|
74
- message["o:#{k}"] = v
94
+ message["o:#{k}"] = v.dup
75
95
  end
76
96
 
97
+ # t:* attributes (options)
98
+ mail.mailgun_template_variables.try(:each) do |k, v|
99
+ message["t:#{k}"] = v.dup
100
+ end
101
+
102
+ # support for using ActionMailer's `headers()` inside of the mailer
103
+ # note: this will filter out parameters such as `from`, `to`, and so forth
104
+ # as they are accepted as POST parameters on the message endpoint.
105
+
106
+ msg_headers = Hash.new
107
+
77
108
  # h:* attributes (headers)
78
- mail.mailgun_headers.try(:each) do |k, v|
109
+
110
+ # Let's set all of these headers on the [Mail::Message] so that
111
+ # the are created inside of a [Mail::Header] instance and processed there.
112
+ mail.headers(mail.mailgun_headers || {})
113
+ mail.header_fields.each do |field|
114
+ header = field.name.downcase
115
+ if msg_headers.include? header
116
+ msg_headers[header] = [msg_headers[header], field.value].flatten
117
+ else
118
+ msg_headers[header] = field.value
119
+ end
120
+ end
121
+
122
+ msg_headers.each do |k, v|
123
+ if Railgun::Mailer::IGNORED_HEADERS.include? k.downcase
124
+ Rails.logger.debug("[railgun] ignoring header (using envelope instead): #{k}")
125
+ next
126
+ end
127
+
128
+ # Cover cases like `cc`, `bcc` where parameters are valid
129
+ # headers BUT they are submitted as separate POST params
130
+ # and already exist on the message because of the call to
131
+ # `build_message_object`.
132
+ if message.include? k.downcase
133
+ Rails.logger.debug("[railgun] ignoring header (already set): #{k}")
134
+ next
135
+ end
136
+
79
137
  message["h:#{k}"] = v
80
138
  end
81
139
 
@@ -84,7 +142,12 @@ module Railgun
84
142
 
85
143
  # reject blank values
86
144
  message.delete_if do |k, v|
87
- v.nil? or (v.respond_to?(:empty) and v.empty?)
145
+ return true if v.nil?
146
+
147
+ # if it's an array remove empty elements
148
+ v.delete_if { |i| i.respond_to?(:empty?) && i.empty? } if v.is_a?(Array)
149
+
150
+ v.respond_to?(:empty?) && v.empty?
88
151
  end
89
152
 
90
153
  return message
@@ -101,6 +164,7 @@ module Railgun
101
164
 
102
165
  mb.from mail[:from]
103
166
  mb.reply_to(mail[:reply_to].to_s) if mail[:reply_to].present?
167
+ mb.template(mail[:template].to_s) if mail[:template].present?
104
168
  mb.subject mail.subject
105
169
  mb.body_html extract_body_html(mail)
106
170
  mb.body_text extract_body_text(mail)
@@ -120,6 +184,11 @@ module Railgun
120
184
  end
121
185
  end
122
186
 
187
+ # v:* attributes (variables)
188
+ mail.mailgun_variables.try(:each) do |name, value|
189
+ mb.variable(name, value)
190
+ end
191
+
123
192
  return mb.message if mail.attachments.empty?
124
193
 
125
194
  mail.attachments.each do |attach|
@@ -138,7 +207,7 @@ module Railgun
138
207
  # @return [String]
139
208
  def extract_body_html(mail)
140
209
  begin
141
- (mail.html_part || mail).body.decoded || nil
210
+ retrieve_html_part(mail).body.decoded || nil
142
211
  rescue
143
212
  nil
144
213
  end
@@ -152,10 +221,33 @@ module Railgun
152
221
  # @return [String]
153
222
  def extract_body_text(mail)
154
223
  begin
155
- (mail.text_part || mail).body.decoded || nil
224
+ retrieve_text_part(mail).body.decoded || nil
156
225
  rescue
157
226
  nil
158
227
  end
159
228
  end
160
229
 
230
+ # Returns the mail object from the Mail::Message object if text part exists,
231
+ # (decomposing multipart into individual format if necessary)
232
+ # otherwise nil.
233
+ #
234
+ # @param [Mail::Message] mail message to transform
235
+ #
236
+ # @return [Mail::Message] mail message with its content-type = text/plain
237
+ def retrieve_text_part(mail)
238
+ return mail.text_part if mail.multipart?
239
+ (mail.mime_type =~ /^text\/plain$/i) && mail
240
+ end
241
+
242
+ # Returns the mail object from the Mail::Message object if html part exists,
243
+ # (decomposing multipart into individual format if necessary)
244
+ # otherwise nil.
245
+ #
246
+ # @param [Mail::Message] mail message to transform
247
+ #
248
+ # @return [Mail::Message] mail message with its content-type = text/html
249
+ def retrieve_html_part(mail)
250
+ return mail.html_part if mail.multipart?
251
+ (mail.mime_type =~ /^text\/html$/i) && mail
252
+ end
161
253
  end
@@ -11,7 +11,8 @@ module Mail
11
11
  attr_accessor :mailgun_variables,
12
12
  :mailgun_options,
13
13
  :mailgun_recipient_variables,
14
- :mailgun_headers
14
+ :mailgun_headers,
15
+ :mailgun_template_variables
15
16
 
16
17
  end
17
18
  end
@@ -2,8 +2,9 @@ require 'railgun/mailer'
2
2
 
3
3
  module Railgun
4
4
  class Railtie < ::Rails::Railtie
5
- config.before_configuration do
6
- ActionMailer::Base.add_delivery_method :mailgun, Railgun::Mailer
5
+ ActiveSupport.on_load(:action_mailer) do
6
+ add_delivery_method :mailgun, Railgun::Mailer
7
+ ActiveSupport.run_load_hooks(:mailgun_mailer, Railgun::Mailer)
7
8
  end
8
9
  end
9
10
  end