mailgun-ruby 1.1.8 → 1.2.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 03bb73706b0d179b3f48cfa58f68acc8b0d716e7
4
- data.tar.gz: 3fc860e152511f367033a5954ba95720a474fd01
2
+ SHA256:
3
+ metadata.gz: 4fec3197ad0202b588d430e3bbbc96e822961e0213a9b45882e5f6490bd9b5bf
4
+ data.tar.gz: 8db91a5a020812f436900efb0d181baede311ed6a6d8390a28fa907a32099897
5
5
  SHA512:
6
- metadata.gz: 0d1f7e536083087eb9c2423c8cb3cd54624b5d0a7fbfb96eb8740b06c2947c14d94542e6ef22439d0f0711d9627b5219e09b9ccab7408b454e20ad1531a07d56
7
- data.tar.gz: 9e03a0244f385c15497856fe9c421d3d330a38309798313c88f3139a86b6fc163cf6234654e15a1c1faceca40b70ad590ff5caf02c3cefc2d225a4eb3bc2b852
6
+ metadata.gz: 164c3e148cd16078ad96c2f60620720e754a412dc8ae69bb0248b1e0dc599e5f570314e5b668a356ff90e3e12e1e72879276c9ccf91901c9502ad98cf56a7781
7
+ data.tar.gz: be6d57fca9b0224ddc99a17831bef724e05a6e63dfef177c9d478bec8efefe943bf3f07886902da015474d0e5c2a33ddf797cf7ba11db1c7ce2eda8648b0ea35
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.3'
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,18 @@ 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
+
20
+ rest_client_params = {
21
+ user: 'api',
22
+ password: api_key,
23
+ user_agent: "mailgun-sdk-ruby/#{Mailgun::VERSION}"
24
+ }
25
+ rest_client_params[:timeout] = timeout if timeout
18
26
 
19
27
  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}")
28
+ @http_client = RestClient::Resource.new(endpoint, rest_client_params)
24
29
  @test_mode = test_mode
25
30
  end
26
31
 
@@ -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
@@ -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,7 @@ 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
+ set_single('o:tracking-opens', bool_lookup(mode))
201
202
  end
202
203
 
203
204
  # Deprecated: 'set_open_tracking' is deprecated. Please use 'track_opens' instead.
@@ -211,7 +212,7 @@ module Mailgun
211
212
  # @param [String] mode True, False, or HTML (for HTML only tracking)
212
213
  # @return [void]
213
214
  def track_clicks(mode)
214
- set_multi_simple('o:tracking-clicks', bool_lookup(mode))
215
+ set_single('o:tracking-clicks', bool_lookup(mode))
215
216
  end
216
217
 
217
218
  # Depreciated: 'set_click_tracking. is deprecated. Please use 'track_clicks' instead.
@@ -379,10 +380,17 @@ module Mailgun
379
380
  def parse_address(address, vars)
380
381
  return address unless vars.is_a? Hash
381
382
  fail(Mailgun::ParameterError, 'Email address not specified') unless address.is_a? String
383
+ if vars['full_name'] != nil && (vars['first'] != nil || vars['last'] != nil)
384
+ fail(Mailgun::ParameterError, 'Must specify at most one of full_name or first/last. Vars passed: #{vars}')
385
+ end
382
386
 
383
- full_name = "#{vars['first']} #{vars['last']}".strip
387
+ if vars['full_name']
388
+ full_name = vars['full_name']
389
+ elsif vars['first'] || vars['last']
390
+ full_name = "#{vars['first']} #{vars['last']}".strip
391
+ end
384
392
 
385
- return "'#{full_name}' <#{address}>" if defined?(full_name)
393
+ return "'#{full_name}' <#{address}>" if full_name
386
394
  address
387
395
  end
388
396
 
@@ -402,6 +410,12 @@ module Mailgun
402
410
  'Unable to access attachment file object.'
403
411
  ) unless attachment.respond_to?(:read)
404
412
 
413
+ if attachment.respond_to?(:path) && !attachment.respond_to?(:content_type)
414
+ mime_types = MIME::Types.type_for(attachment.path)
415
+ content_type = mime_types.empty? ? 'application/octet-stream' : mime_types[0].content_type
416
+ attachment.instance_eval "def content_type; '#{content_type}'; end"
417
+ end
418
+
405
419
  unless filename.nil?
406
420
  attachment.instance_variable_set :@original_filename, filename
407
421
  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.3'
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 ]
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
@@ -47,7 +58,7 @@ module Railgun
47
58
  end
48
59
 
49
60
  def mailgun_client
50
- @mg_obj
61
+ @mg_client
51
62
  end
52
63
 
53
64
  end
@@ -58,6 +69,9 @@ module Railgun
58
69
  # After prefixing them with the proper option type, they are added to
59
70
  # the message hash where they will then be sent to the API as JSON.
60
71
  #
72
+ # It is important to note that headers set in `mailgun_headers` on the message
73
+ # WILL overwrite headers set via `mail.headers()`.
74
+ #
61
75
  # @param [Mail::Message] mail message to transform
62
76
  #
63
77
  # @return [Hash] transformed message hash
@@ -66,16 +80,49 @@ module Railgun
66
80
 
67
81
  # v:* attributes (variables)
68
82
  mail.mailgun_variables.try(:each) do |k, v|
69
- message["v:#{k}"] = v
83
+ message["v:#{k}"] = JSON.dump(v)
70
84
  end
71
85
 
72
86
  # o:* attributes (options)
73
87
  mail.mailgun_options.try(:each) do |k, v|
74
- message["o:#{k}"] = v
88
+ message["o:#{k}"] = v.dup
75
89
  end
76
90
 
91
+ # support for using ActionMailer's `headers()` inside of the mailer
92
+ # note: this will filter out parameters such as `from`, `to`, and so forth
93
+ # as they are accepted as POST parameters on the message endpoint.
94
+
95
+ msg_headers = Hash.new
96
+
77
97
  # h:* attributes (headers)
78
- mail.mailgun_headers.try(:each) do |k, v|
98
+
99
+ # Let's set all of these headers on the [Mail::Message] so that
100
+ # the are created inside of a [Mail::Header] instance and processed there.
101
+ mail.headers(mail.mailgun_headers || {})
102
+ mail.header_fields.each do |field|
103
+ header = field.name.downcase
104
+ if msg_headers.include? header
105
+ msg_headers[header] = [msg_headers[header], field.value].flatten
106
+ else
107
+ msg_headers[header] = field.value
108
+ end
109
+ end
110
+
111
+ msg_headers.each do |k, v|
112
+ if Railgun::Mailer::IGNORED_HEADERS.include? k.downcase
113
+ Rails.logger.debug("[railgun] ignoring header (using envelope instead): #{k}")
114
+ next
115
+ end
116
+
117
+ # Cover cases like `cc`, `bcc` where parameters are valid
118
+ # headers BUT they are submitted as separate POST params
119
+ # and already exist on the message because of the call to
120
+ # `build_message_object`.
121
+ if message.include? k.downcase
122
+ Rails.logger.debug("[railgun] ignoring header (already set): #{k}")
123
+ next
124
+ end
125
+
79
126
  message["h:#{k}"] = v
80
127
  end
81
128
 
@@ -84,7 +131,12 @@ module Railgun
84
131
 
85
132
  # reject blank values
86
133
  message.delete_if do |k, v|
87
- v.nil? or (v.respond_to?(:empty) and v.empty?)
134
+ return true if v.nil?
135
+
136
+ # if it's an array remove empty elements
137
+ v.delete_if { |i| i.respond_to?(:empty?) && i.empty? } if v.is_a?(Array)
138
+
139
+ v.respond_to?(:empty?) && v.empty?
88
140
  end
89
141
 
90
142
  return message
@@ -138,7 +190,7 @@ module Railgun
138
190
  # @return [String]
139
191
  def extract_body_html(mail)
140
192
  begin
141
- (mail.html_part || mail).body.decoded || nil
193
+ retrieve_html_part(mail).body.decoded || nil
142
194
  rescue
143
195
  nil
144
196
  end
@@ -152,10 +204,34 @@ module Railgun
152
204
  # @return [String]
153
205
  def extract_body_text(mail)
154
206
  begin
155
- (mail.text_part || mail).body.decoded || nil
207
+ retrieve_text_part(mail).body.decoded || nil
156
208
  rescue
157
209
  nil
158
210
  end
159
211
  end
160
212
 
213
+ # Returns the mail object from the Mail::Message object if text part exists,
214
+ # (decomposing multipart into individual format if necessary)
215
+ # otherwise nil.
216
+ #
217
+ # @param [Mail::Message] mail message to transform
218
+ #
219
+ # @return [Mail::Message] mail message with its content-type = text/plain
220
+ def retrieve_text_part(mail)
221
+ return mail.text_part if mail.multipart?
222
+ (mail.mime_type =~ /^text\/plain$/i) && mail
223
+ end
224
+
225
+ # Returns the mail object from the Mail::Message object if html part exists,
226
+ # (decomposing multipart into individual format if necessary)
227
+ # otherwise nil.
228
+ #
229
+ # @param [Mail::Message] mail message to transform
230
+ #
231
+ # @return [Mail::Message] mail message with its content-type = text/html
232
+ def retrieve_html_part(mail)
233
+ return mail.html_part if mail.multipart?
234
+ (mail.mime_type =~ /^text\/html$/i) && mail
235
+ end
236
+
161
237
  end