mailgun-ruby 1.1.8 → 1.2.3

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