sparkpost_rails 1.3.0 → 1.5.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: 925047803a24a8f98ba5f888f81c4a1ad472f9fc
4
- data.tar.gz: 171842fb600f86f2e65143102ccdba962ac80d17
2
+ SHA256:
3
+ metadata.gz: bd9cf39367215bffb11029280b7526e52553b8484767d2823fc59448b9352c80
4
+ data.tar.gz: 662a03effd5a0332400c843e9a047f65dc34e823baabfa63ce3366867a3f4b69
5
5
  SHA512:
6
- metadata.gz: a783aaf00991597cc6f3b5558db1b43f285e9196cc9cebf22e21f87ba54c5b5cc7cfcc20158cfa1ef9309c70f9ff2da7e6f0fb30f1a6f6f1ae0606bcfa8187ea
7
- data.tar.gz: 6ef6d2888ebf9d6b1864ab77df43aaae54d59abd28ab173621b173b9e3d52877931f6415b27efbda4b9454b730026b1b6804e0f665d9543a084f65caa6ee0d54
6
+ metadata.gz: 5c62bb17ff1685deb530b442e4c022bf4eb88208d8ea988ad6b7baa4c9ee79d0e24180ae099db14d3635e9973367de9ce402dbd3f93418ba1c085a2732e828f4
7
+ data.tar.gz: 202997cc9e44bc84bfc4412b1d5f2f0e6b09246089cf3372506fe3606ce3ea383aec09ae99fbc7db31af27e96cd314f6555894cef2d19317d7cf0ad668519b4d
data/README.md CHANGED
@@ -4,159 +4,154 @@
4
4
  SparkPost Rails
5
5
  ===============
6
6
 
7
- This gem provides seamless integration of SparkPost with ActionMailer. It provides a delivery_method based upon the SparkPost API, and
8
- makes getting setup and sending email via SparkPost in a Rails app pretty painless.
7
+ This gem provides seamless integration of SparkPost with ActionMailer. It provides a `delivery_method` based upon the SparkPost API, and makes getting setup and sending email via SparkPost in a Rails app pretty painless.
9
8
 
10
9
  Getting Started
11
10
  ---------------
12
11
 
13
12
  Add the gem to your Gemfile
14
13
 
15
- ```
14
+ ```ruby
16
15
  gem 'sparkpost_rails'
17
16
  ```
18
17
 
19
18
  Then run the bundle command to install it.
20
19
 
21
- By default, the gem will look for your SparkPost API key in your environment, with the key 'SPARKPOST_API_KEY'. You can override this
22
- setting by identifying a different key in the initializer (`config/initializers/sparkpost_rails.rb`):
20
+ By default, the gem will look for your SparkPost API key in your environment, with the key `SPARKPOST_API_KEY`. You can override this setting by identifying a different key in the initializer (`config/initializers/sparkpost_rails.rb`):
23
21
 
24
- ```
22
+ ```ruby
25
23
  SparkPostRails.configure do |c|
26
24
  c.api_key = 'YOUR API KEY'
27
25
  end
28
26
  ```
27
+ Note that an initializer file is not required to use this gem. If an initializer is not provided, default values will be used. See ["Additional Configuration"](#additional-configuration) below for a list of all the default settings.
29
28
 
30
29
  In each environment configuration file from which you want to send emails via Sparkpost, (i.e. `config/environments/production.rb`) add
31
30
 
32
- ```
31
+ ```ruby
33
32
  config.action_mailer.delivery_method = :sparkpost
34
33
  ```
35
34
 
36
35
  Additional Configuration
37
36
  ------------------------
38
- You can establish values for a number of SparkPost settings in the initializer. These values will be used for every message sent
39
- from your application. You can override these settings on individual messages.
37
+ You can establish values for a number of SparkPost settings in the initializer. These values will be used for every message sent from your application. You can override these settings on individual messages.
40
38
 
41
- ```
39
+ ```ruby
42
40
  SparkPostRails.configure do |c|
43
- c.sandbox = true
44
- c.track_opens = true
45
- c.track_clicks = true
46
- c.return_path = 'BOUNCE-EMAIL@YOUR-DOMAIN.COM'
47
- c.campaign_id = 'YOUR-CAMPAIGN'
48
- c.transactional = true
49
- c.ip_pool = "MY-POOL"
50
- c.inline_css = true
51
- c.html_content_only = true
52
- c.subaccount = "123"
41
+ c.api_endpoint = "https://api.eu.sparkpost.com/api/" # default: "https://api.sparkpost.com/api/"
42
+ c.sandbox = true # default: false
43
+ c.track_opens = true # default: false
44
+ c.track_clicks = true # default: false
45
+ c.return_path = 'BOUNCE-EMAIL@YOUR-DOMAIN.COM' # default: nil
46
+ c.campaign_id = 'YOUR-CAMPAIGN' # default: nil
47
+ c.transactional = true # default: false
48
+ c.ip_pool = "MY-POOL" # default: nil
49
+ c.inline_css = true # default: false
50
+ c.html_content_only = true # default: false
51
+ c.subaccount = "123" # default: nil
53
52
  end
54
53
  ```
55
54
 
56
- The default values for these optional configuration settings are:
57
-
58
- ```
59
- sandbox = false
60
- track_opens = false
61
- track_clicks = false
62
- return_path = nil
63
- campaign_id = nil
64
- transactional = false
65
- ip_pool = nil
66
- inline_css = false
67
- html_content_only = false
68
- subaccount = nil
69
- ```
70
-
71
55
  Usage
72
56
  -----
73
- When calling the deliver! method on the mail object returned from your mailer, SparkPostRails provides the response data directly back
74
- from SparkPost as a hash.
57
+ When calling the `deliver!` method on the mail object returned from your mailer, `SparkPostRails` provides the response data directly back from SparkPost as a hash.
75
58
 
76
- ```
59
+ ```ruby
77
60
  result = MyMailer.welcome_message(user).deliver!
78
61
  ```
79
62
 
80
63
  Example:
81
64
 
65
+ ```ruby
66
+ {
67
+ "total_rejected_recipients" => 0,
68
+ "total_accepted_recipients" => 1,
69
+ "id" => "00000000000000"
70
+ }
82
71
  ```
83
- {"total_rejected_recipients"=>0, "total_accepted_recipients"=>1, "id"=>"00000000000000"}
84
- ```
85
72
 
86
- If the SparkPost API reponds with an error condition, SparkPostRails will raise a SparkPostRails::DeliveryException, which will include all the message
87
- data returned by the API.
73
+ If the SparkPost API responds with an error condition, SparkPostRails will raise a `SparkPostRails::DeliveryException`, which will include all the message data returned by the API.
74
+
75
+ SparkPostRails will support multiple recipients, multiple CC, multiple BCC, ReplyTo address, file attachments, inline images, multi-part (HTML and plaintext) messages - all utilizing the standard `ActionMailer` methodologies.
88
76
 
89
- SparkPostRails will support multiple recipients, multilple CC, multiple BCC, ReplyTo address, file attachments, inline images, multi-part (HTML and plaintext) messages -
90
- all utilizing the standard ActionMailer methodologies.
77
+ Handling Errors
78
+ ---------------
79
+ If you are using `ActiveJob` and wish to do something special when the SparkPost API responds with an error condition you can do so by rescuing these exceptions via `ActionMailer::DeliveryJob`. Simply add an initializer:
91
80
 
81
+ `config/initializers/action_mailer.rb`
92
82
 
93
- SparkPost Specific Features
83
+ ```ruby
84
+ ActionMailer::DeliveryJob.rescue_from(SparkPostRails::DeliveryException) do |exception|
85
+ # do something special with the error
86
+ # do something special with the error
87
+ end
88
+ ```
89
+
90
+ SparkPost-Specific Features
94
91
  ---------------------------
95
92
 
96
93
  ### Configuration Settings
97
- You can specifiy values for any or all of the configuration settings listed above on an individual message. Simply add a hash of these values
98
- to the mail message in a field named "sparkpost_data":
99
-
100
- ```
101
- data = { track_opens: true,
102
- track_clicks: false,
103
- campaign_id: "My Campaign",
104
- transactional: true,
105
- ip_pool = "SPECIAL_POOL",
106
- api_key = "MESSAGE_SPECIFIC_API_KEY"
107
- subaccount = "123"
108
- }
94
+ You can specify values for any or all of the configuration settings listed above on an individual message. Simply add a hash of these values to the mail message in a field named `sparkpost_data`:
95
+
96
+ ```ruby
97
+ data = {
98
+ track_opens: true,
99
+ track_clicks: false,
100
+ campaign_id: 'My Campaign',
101
+ transactional: true,
102
+ ip_pool: 'SPECIAL_POOL',
103
+ api_key: 'MESSAGE_SPECIFIC_API_KEY',
104
+ subaccount: '123'
105
+ }
109
106
 
110
107
  mail(to: to_email, subject: "Test", body: "test", sparkpost_data: data)
111
108
  ```
112
109
 
113
- Additionally, return_path can be overriden on a specific email by setting that field on the mail message itself:
110
+ Additionally, `return_path` can be overridden on a specific email by setting that field on the mail message itself:
114
111
 
115
- ```
112
+ ```ruby
116
113
  mail(to: to_email, subject: "Test", body: "test", return_path: "bounces@example.com")
117
114
  ```
118
115
 
119
116
  ### Transmission Specific Settings
120
117
 
121
- For an individual transmisison you can specifiy that SparkPost should ignore customer supression rules - if your SparkPost account allows for this
122
- feature. Simply include the flag in the "sparkpost_data" field on the message:
118
+ For an individual transmisison you can specifiy that SparkPost should ignore customer supression rules - if your SparkPost account allows for this feature. Simply include the flag in the `sparkpost_data` field on the message:
123
119
 
124
- ```
120
+ ```ruby
125
121
  data = { skip_suppression: true }
126
122
 
127
123
  mail(to: to_email, subject: "Test", body: "test", sparkpost_data: data)
128
124
  ```
129
125
 
130
- To schedule the generation of messages for a future date and time, specify a start time in the date parameter of the mail. Date must be in the future and less than 1 year from today. If date is in the past or too far in the future, no date will be passed, and no delivery schedule will be set.
126
+ To schedule the generation of messages for a future date and time, specify a start time in the `date` parameter of the mail. The `date` must be in the future and less than 1 year from today. If `date` is in the past or too far in the future, no date will be passed, and no delivery schedule will be set.
131
127
 
132
- ```
133
- start_time = DateTime.now + 4.hours
128
+ ```ruby
129
+ start_time = DateTime.now + 4.hours
134
130
 
135
131
  mail(to: to_email, subject: "Test", body: "test", date: start_time)
136
132
  ```
137
133
 
138
- You can set a description for a transmission via the "sparkpost_data" as well. The maximum length of the decription is 1024 characters - values
139
- longer than the maxium will be truncated.
134
+ You can set a `description` for a transmission via the `sparkpost_data` as well. The maximum length of the `decription` is 1024 characters - values longer than the maxium will be truncated.
140
135
 
141
- ```
136
+ ```ruby
142
137
  data = { description: "My Important Message" }
143
138
 
144
139
  mail(to: to_email, subject: "Test", body: "test", sparkpost_data: data)
145
140
  ```
146
141
 
147
- By default, content from single-part messages is sent at plain-text. If you are only intending to send HTML email, with no plain-text part, you can specify this
148
- as shown below. You can also set this in the configuration to ensure that all single-part emails are sent as html.
142
+ By default, content from single-part messages is sent at plain-text. If you are only intending to send HTML email, with no plain-text part, you can specify this as shown below. You can also set this in the configuration to ensure that all single-part emails are sent as HTML.
149
143
 
150
- ```
144
+ ```ruby
151
145
  data = { html_content_only: true }
152
146
 
153
147
  mail(to: to_email, subject: "Test", body: "<h1>test</h1>", sparkpost_data: data)
154
148
  ```
155
149
 
156
150
  ### Subaccounts
151
+
157
152
  SparkPostRails supports sending messages via subaccounts in two ways. The default API key set in the configuration can be overriden on a message-by-message basis with a subaccount API key.
158
153
 
159
- ```
154
+ ```ruby
160
155
  data = { api_key: "SUBACCOUNT_API_KEY" }
161
156
 
162
157
  mail(subject: "Test", body: "test", sparkpost_data: data)
@@ -164,57 +159,72 @@ mail(subject: "Test", body: "test", sparkpost_data: data)
164
159
 
165
160
  Subaccounts can also be leveraged using the subaccount ID with the master API key.
166
161
 
167
- ```
162
+ ```ruby
168
163
  data = { subaccount: "123" }
169
164
 
170
165
  mail(subject: "Test", body: "test", sparkpost_data: data)
171
166
  ```
172
167
 
173
168
  ### Recipient Lists
174
- SparkPostRails supports using SparkPost stored recipient lists. Simply add the list_id to the sparkpost_data hash on the mail message:
169
+ SparkPostRails supports using SparkPost stored recipient lists. Simply add the `list_id` to the `sparkpost_data` hash on the mail message:
175
170
 
176
- ```
177
- data = { list_id: "MY-LIST"}
171
+ ```ruby
172
+ data = { recipient_list_id: "MY-LIST"}
178
173
 
179
174
  mail(subject: "Test", body: "test", sparkpost_data: data)
180
175
  ```
181
176
 
182
- **NOTE**: If you supply a recipient list id, all To:, CC:, and BCC: data specified on the mail message will be ignored. The SparkPost API does
183
- not support utilizing both a recipient list and inline recipients.
177
+ **NOTE**: If you supply a recipient `list_id`, all `To:`, `CC:`, and `BCC:` data specified on the mail message will be ignored. The SparkPost API does not support utilizing both a recipient list and inline recipients.
184
178
 
185
179
 
186
180
  ### Substitution Data
187
- You can leverage SparkPost's substitution engine through the gem as well. To supply substitution data, simply add your hash of substitution data
188
- to your sparkpost_data hash, with the key :substitution_data.
181
+ You can leverage SparkPost's substitution engine through the gem as well. To supply substitution data, simply add your hash of substitution data to your `sparkpost_data` hash, with the key `substitution_data`.
189
182
 
190
- ```
191
- sub_data = {first_name: "Sam",
192
- last_name: "Test}
183
+ ```ruby
184
+ sub_data = {
185
+ first_name: "Sam",
186
+ last_name: "Test
187
+ }
193
188
 
194
189
  data = { substitution_data: sub_data }
195
190
 
196
191
  mail(to: to_email, subject: "Test", body: "test", sparkpost_data: data)
197
192
  ```
198
193
 
199
- ### Using SparkPost Templates
200
- If you would rather leverage SparkPost's powerful templates rather than building ActionMailer views, SparkPostRails can support that as well. Simply
201
- add your template id to the sparkpost_data hash:
194
+ ### Recipient-Specific Data
195
+ When sending to multiple recipients, you can pass an array of data to complement each recipient. Simply pass an array called `recipients` containing an array of the additional data (e.g. `substitution_data`).
202
196
 
197
+ ```ruby
198
+ recipients = ['recipient1@email.com', 'recipient2@email.com']
199
+ sparkpost_data = {
200
+ recipients: [
201
+ { substitution_data: { name: 'Recipient1' } },
202
+ { substitution_data: { name: 'Recipient2' } }
203
+ ]
204
+ }
205
+ mail(to: recipients, sparkpost_data: sparkpost_data)
203
206
  ```
207
+
208
+
209
+ ### Using SparkPost Templates
210
+ You can leverage SparkPost's powerful templates rather than building ActionMailer views using SparkPostRails. Add your `template_id` to the `sparkpost_data` hash. By default, `ActionMailer` finds a template to use within views. A workaround to prevent this default action is to explicitly pass a block with an empty `text` part:
211
+
212
+ ```ruby
204
213
  data = { template_id: "MY-TEMPLATE" }
205
214
 
206
- mail(to: to_email, sparkpost_data: data)
215
+ mail(to: to_email, sparkpost_data: data) do |format|
216
+ format.text { render text: "" }
217
+ end
207
218
  ```
208
219
 
209
- **NOTE**: All inline-content that may exist in your mail message will be ignored, as the SparkPost API does not accept that data when a template id is
210
- supplied. This includes Subject, From, ReplyTo, Attachments, and Inline Images.
220
+ **NOTE**: All inline-content that may exist in your mail message will be ignored, as the SparkPost API does not accept that data when a template id is supplied. This includes `Subject`, `From`, `ReplyTo`, Attachments, and Inline Images.
211
221
 
212
222
  ###Other Mail Headers
213
- If you need to identify custom mail headers for your messages, utilize the ActionMailer header[] method. The gem will pass all approprite headers through
214
- to the api. Note, per the SparkPost API documentation, "Headers such as 'Content-Type' and 'Content-Transfer-Encoding' are not allowed here as they are auto
215
- generated upon construction of the email."
223
+ If you need to identify custom mail headers for your messages, use the `ActionMailer` `header[]` method. The gem will pass all appropriate headers through to the API. Note, per the SparkPost API documentation
216
224
 
217
- ```
225
+ > Headers such as 'Content-Type' and 'Content-Transfer-Encoding' are not allowed here as they are auto-generated upon construction of the email.
226
+
227
+ ```ruby
218
228
  headers["Priority"] = "urgent"
219
229
  headers["Sensitivity"] = "private"
220
230
 
@@ -1,3 +1,4 @@
1
+ require "sparkpost_rails/data_options"
1
2
  require "sparkpost_rails/delivery_method"
2
3
  require "sparkpost_rails/exceptions"
3
4
  require "sparkpost_rails/railtie"
@@ -7,6 +8,10 @@ module SparkPostRails
7
8
  attr_accessor :configuration
8
9
  end
9
10
 
11
+ def self.configuration
12
+ @configuration ||= Configuration.new
13
+ end
14
+
10
15
  def self.configure
11
16
  self.configuration ||= Configuration.new
12
17
  yield(configuration)
@@ -14,6 +19,7 @@ module SparkPostRails
14
19
 
15
20
  class Configuration
16
21
  attr_accessor :api_key
22
+ attr_accessor :api_endpoint
17
23
  attr_accessor :sandbox
18
24
 
19
25
  attr_accessor :track_opens
@@ -40,6 +46,8 @@ module SparkPostRails
40
46
  @api_key = ""
41
47
  end
42
48
 
49
+ @api_endpoint = "https://api.sparkpost.com/api/"
50
+
43
51
  @sandbox = false
44
52
 
45
53
  @track_opens = false
@@ -0,0 +1,25 @@
1
+ module SparkPostRails
2
+ module DataOptions
3
+
4
+ def self.included(base)
5
+ base.class_eval do
6
+ prepend InstanceMethods
7
+ end
8
+ end
9
+
10
+ module InstanceMethods
11
+
12
+ def mail(headers={}, &block)
13
+ headers = headers.clone
14
+ sparkpost_data = headers.delete(:sparkpost_data)
15
+ sparkpost_data ||= {}
16
+ super(headers, &block).tap do |message|
17
+ message.singleton_class.class_eval { attr_accessor "sparkpost_data" }
18
+ message.sparkpost_data = sparkpost_data
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end
@@ -14,6 +14,7 @@ module SparkPostRails
14
14
  sparkpost_data = find_sparkpost_data_from mail
15
15
 
16
16
  prepare_recipients_from mail, sparkpost_data
17
+ prepare_recipients_data_from sparkpost_data
17
18
 
18
19
  if sparkpost_data.has_key?(:template_id)
19
20
  prepare_template_content_from sparkpost_data
@@ -28,6 +29,7 @@ module SparkPostRails
28
29
  end
29
30
 
30
31
  prepare_substitution_data_from sparkpost_data
32
+ prepare_metadata_from sparkpost_data
31
33
  prepare_description_from sparkpost_data
32
34
  prepare_options_from mail, sparkpost_data
33
35
  prepare_additional_mail_headers_from mail
@@ -41,11 +43,7 @@ module SparkPostRails
41
43
 
42
44
  private
43
45
  def find_sparkpost_data_from mail
44
- if mail[:sparkpost_data]
45
- eval(mail[:sparkpost_data].value)
46
- else
47
- Hash.new
48
- end
46
+ mail.sparkpost_data
49
47
  end
50
48
 
51
49
  def prepare_recipients_from mail, sparkpost_data
@@ -67,14 +65,15 @@ module SparkPostRails
67
65
 
68
66
  def prepare_addresses emails, names
69
67
  emails = [emails] unless emails.is_a?(Array)
70
- emails.each_with_index.map {|email, index| prepare_address(email, index, names) }
68
+ header_to = emails.join(",")
69
+ emails.each_with_index.map {|email, index| prepare_address(email, index, names, header_to) }
71
70
  end
72
71
 
73
- def prepare_address email, index, names
72
+ def prepare_address email, index, names, header_to
74
73
  if !names[index].nil?
75
- { address: { email: email, name: names[index] } }
74
+ { address: { email: email, name: names[index], header_to: header_to } }
76
75
  else
77
- { address: { email: email } }
76
+ { address: { email: email, header_to: header_to } }
78
77
  end
79
78
  end
80
79
 
@@ -95,6 +94,17 @@ module SparkPostRails
95
94
  end
96
95
  end
97
96
 
97
+ # See https://developers.sparkpost.com/api/#/introduction/substitutions-reference/links-and-substitution-expressions-within-substitution-values
98
+ def prepare_recipients_data_from sparkpost_data
99
+ if (recipients_data = sparkpost_data[:recipients])
100
+ @data[:recipients].each_with_index do |recipient, index|
101
+ if (recipient_data = recipients_data[index])
102
+ recipient.merge!(recipient_data)
103
+ end
104
+ end
105
+ end
106
+ end
107
+
98
108
  def prepare_template_content_from sparkpost_data
99
109
  @data[:content][:template_id] = sparkpost_data[:template_id]
100
110
 
@@ -106,6 +116,12 @@ module SparkPostRails
106
116
  end
107
117
  end
108
118
 
119
+ def prepare_metadata_from sparkpost_data
120
+ if sparkpost_data[:metadata]
121
+ @data[:metadata] = sparkpost_data[:metadata]
122
+ end
123
+ end
124
+
109
125
  def prepare_from_address_from mail
110
126
  if !mail[:from].display_names.first.nil?
111
127
  from = { email: mail.from.first, name: mail[:from].display_names.first }
@@ -135,7 +151,7 @@ module SparkPostRails
135
151
  emails << copy[:address][:email]
136
152
  end
137
153
 
138
- @data[:content][:headers] = { cc: emails }
154
+ @data[:content][:headers] = { cc: emails.join(",") }
139
155
  end
140
156
  end
141
157
 
@@ -166,9 +182,9 @@ module SparkPostRails
166
182
  inline_images = Array.new
167
183
 
168
184
  mail.attachments.each do |attachment|
169
- #We decode and reencode here to ensure that attachments are
185
+ #We decode and reencode here to ensure that attachments are
170
186
  #Base64 encoded without line breaks as required by the API.
171
- attachment_data = { name: attachment.inline? ? attachment.url : attachment.filename,
187
+ attachment_data = { name: attachment.inline? ? attachment.cid : attachment.filename,
172
188
  type: attachment.content_type,
173
189
  data: Base64.strict_encode64(attachment.body.decoded) }
174
190
 
@@ -361,15 +377,13 @@ module SparkPostRails
361
377
  end
362
378
 
363
379
  def post_to_api
364
- url = "https://api.sparkpost.com/api/v1/transmissions"
380
+ uri = URI.join(SparkPostRails.configuration.api_endpoint, 'v1/transmissions')
365
381
 
366
- uri = URI.parse(url)
367
382
  http = Net::HTTP.new(uri.host, uri.port)
368
383
  http.use_ssl = true
369
384
 
370
385
  request = Net::HTTP::Post.new(uri.path, @headers)
371
386
  request.body = JSON.generate(@data)
372
-
373
387
  http.request(request)
374
388
  end
375
389
 
@@ -1,4 +1,17 @@
1
1
  module SparkPostRails
2
2
  class DeliveryException < StandardError
3
+ attr_reader :service_message, :service_description, :service_code
4
+
5
+ def initialize(message)
6
+ errors = [*message].first
7
+
8
+ if errors.is_a?(Hash)
9
+ @service_message = errors['message']
10
+ @service_description = errors['description']
11
+ @service_code = errors['code']
12
+ end
13
+
14
+ super(message)
15
+ end
3
16
  end
4
17
  end
@@ -5,5 +5,11 @@ module SparkPostRails
5
5
  ActionMailer::Base.add_delivery_method :sparkpost, SparkPostRails::DeliveryMethod, return_response: true
6
6
  end
7
7
  end
8
+
9
+ initializer "sparkpost_rails.extend_with_data_options" do
10
+ ActiveSupport.on_load :action_mailer do
11
+ ActionMailer::Base.send :include, SparkPostRails::DataOptions
12
+ end
13
+ end
8
14
  end
9
15
  end
@@ -1,4 +1,4 @@
1
1
  module SparkPostRails
2
- VERSION = "1.3.0"
2
+ VERSION = "1.5.3"
3
3
  end
4
4
 
@@ -48,7 +48,7 @@ describe SparkPostRails::DeliveryMethod do
48
48
  expect(images.length).to eq(1)
49
49
  image = images[0]
50
50
  expect(image).to include(:name, :type=>"image/png; filename=image_0.png", :data=>"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")
51
- expect(image[:name]).to match(/cid:.*@.*/)
51
+ expect(image[:name]).to match(/.*@.*/)
52
52
  end
53
53
 
54
54
  end
@@ -13,7 +13,8 @@ describe SparkPostRails::DeliveryMethod do
13
13
  test_email = Mailer.test_email bcc: "bcc@example.com"
14
14
  @delivery_method.deliver!(test_email)
15
15
 
16
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to@example.com"}}, {address: {email: "bcc@example.com", header_to: "to@example.com"}}])
16
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", header_to: anything}}),
17
+ {address: {email: "bcc@example.com", header_to: "to@example.com"}}])
17
18
  expect(@delivery_method.data[:content]).not_to include(:headers)
18
19
  end
19
20
 
@@ -21,7 +22,9 @@ describe SparkPostRails::DeliveryMethod do
21
22
  test_email = Mailer.test_email to: "Joe Test <to1@example.com>, Sam Test <to2@example.com>", bcc: "Brock Test <bcc@example.com>"
22
23
  @delivery_method.deliver!(test_email)
23
24
 
24
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to1@example.com", name: "Joe Test"}}, {address: {email: "to2@example.com", name: "Sam Test"}}, {address: {email: "bcc@example.com", name: "Brock Test", header_to: "to1@example.com"}}])
25
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", name: "Joe Test", header_to: anything}}),
26
+ a_hash_including({address: {email: "to2@example.com", name: "Sam Test", header_to: anything}}),
27
+ {address: {email: "bcc@example.com", name: "Brock Test", header_to: "to1@example.com"}}])
25
28
  expect(@delivery_method.data[:content]).not_to include(:headers)
26
29
  end
27
30
  end
@@ -31,7 +34,10 @@ describe SparkPostRails::DeliveryMethod do
31
34
  test_email = Mailer.test_email bcc: "bcc1@example.com, bcc2@example.com"
32
35
  @delivery_method.deliver!(test_email)
33
36
 
34
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to@example.com"}}, {address: {email: "bcc1@example.com", header_to: "to@example.com"}}, {address: {email: "bcc2@example.com", header_to: "to@example.com"}}])
37
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", header_to: anything}}),
38
+ {address: {email: "bcc1@example.com", header_to: "to@example.com"}},
39
+ {address: {email: "bcc2@example.com", header_to: "to@example.com"}}])
40
+
35
41
  expect(@delivery_method.data[:content]).not_to include(:headers)
36
42
  end
37
43
 
@@ -39,7 +45,9 @@ describe SparkPostRails::DeliveryMethod do
39
45
  test_email = Mailer.test_email to: "Joe Test <to@example.com>", bcc: "Brock Test <bcc1@example.com>, Brack Test <bcc2@example.com>"
40
46
  @delivery_method.deliver!(test_email)
41
47
 
42
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to@example.com", name: "Joe Test"}}, {address: {email: "bcc1@example.com", name: "Brock Test", header_to: "to@example.com"}}, {address: {email: "bcc2@example.com", name: "Brack Test", header_to: "to@example.com"}}])
48
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", name: "Joe Test", header_to: anything}}),
49
+ {address: {email: "bcc1@example.com", name: "Brock Test", header_to: "to@example.com"}},
50
+ {address: {email: "bcc2@example.com", name: "Brack Test", header_to: "to@example.com"}}])
43
51
  expect(@delivery_method.data[:content]).not_to include(:headers)
44
52
  end
45
53
 
@@ -47,7 +55,9 @@ describe SparkPostRails::DeliveryMethod do
47
55
  test_email = Mailer.test_email to: "Joe Test <to@example.com>", bcc: "Brock Test <bcc1@example.com>, bcc2@example.com"
48
56
  @delivery_method.deliver!(test_email)
49
57
 
50
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to@example.com", name: "Joe Test"}}, {address: {email: "bcc1@example.com", name: "Brock Test", header_to: "to@example.com"}}, {address: {email: "bcc2@example.com", header_to: "to@example.com"}}])
58
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", name: "Joe Test", header_to: anything}}),
59
+ {address: {email: "bcc1@example.com", name: "Brock Test", header_to: "to@example.com"}},
60
+ {address: {email: "bcc2@example.com", header_to: "to@example.com"}}])
51
61
  expect(@delivery_method.data[:content]).not_to include(:headers)
52
62
  end
53
63
  end
@@ -57,24 +67,35 @@ describe SparkPostRails::DeliveryMethod do
57
67
  test_email = Mailer.test_email to: "to1@example.com, to2@example.com", cc: "cc@example.com", bcc: "bcc@example.com"
58
68
  @delivery_method.deliver!(test_email)
59
69
 
60
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to1@example.com"}}, {address: {email: "to2@example.com"}}, {address: {email: "cc@example.com", header_to: "to1@example.com"}}, {address: {email: "bcc@example.com", header_to: "to1@example.com"}}])
61
- expect(@delivery_method.data[:content][:headers]).to eq({cc: ["cc@example.com"]})
70
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", header_to: anything}}),
71
+ a_hash_including({address: {email: "to2@example.com", header_to: anything}}),
72
+ {address: {email: "cc@example.com", header_to: "to1@example.com"}},
73
+ {address: {email: "bcc@example.com", header_to: "to1@example.com"}}])
74
+ expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc@example.com"})
62
75
  end
63
76
 
64
77
  it "handles name and email" do
65
78
  test_email = Mailer.test_email to: "Joe Test <to1@example.com>, Sam Test <to2@example.com>", cc: "Carl Test <cc@example.com>", bcc: "Brock Test <bcc@example.com>"
66
79
  @delivery_method.deliver!(test_email)
67
80
 
68
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to1@example.com", name: "Joe Test"}}, {address: {email: "to2@example.com", name: "Sam Test"}}, {address: {email: "cc@example.com", name: "Carl Test", header_to: "to1@example.com"}}, {address: {email: "bcc@example.com", name: "Brock Test", header_to: "to1@example.com"}}])
69
- expect(@delivery_method.data[:content][:headers]).to eq({cc: ["cc@example.com"]})
81
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", name: "Joe Test", header_to: anything}}),
82
+ a_hash_including({address: {email: "to2@example.com", name: "Sam Test", header_to: anything}}),
83
+ {address: {email: "cc@example.com", name: "Carl Test", header_to: "to1@example.com"}},
84
+ {address: {email: "bcc@example.com", name: "Brock Test", header_to: "to1@example.com"}}])
85
+ expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc@example.com"})
70
86
  end
71
87
 
72
88
  it "handles mix of email only and name/email" do
73
89
  test_email = Mailer.test_email to: "Joe Test <to1@example.com>, to2@example.com", cc: "cc1@example.com, Chris Test <cc2@example.com>", bcc: "Brock Test <bcc1@example.com>, bcc2@example.com"
74
90
  @delivery_method.deliver!(test_email)
75
91
 
76
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to1@example.com", name: "Joe Test"}}, {address: {email: "to2@example.com"}}, {address: {email: "cc1@example.com", header_to: "to1@example.com"}}, {address: {email: "cc2@example.com", name: "Chris Test", header_to: "to1@example.com"}}, {address: {email: "bcc1@example.com", name: "Brock Test", header_to: "to1@example.com"}}, {address: {email: "bcc2@example.com", header_to: "to1@example.com"}}])
77
- expect(@delivery_method.data[:content][:headers]).to eq({cc: ["cc1@example.com", "cc2@example.com"]})
92
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", name: "Joe Test", header_to: anything}}),
93
+ a_hash_including({address: {email: "to2@example.com", header_to: anything}}),
94
+ {address: {email: "cc1@example.com", header_to: "to1@example.com"}},
95
+ {address: {email: "cc2@example.com", name: "Chris Test", header_to: "to1@example.com"}},
96
+ {address: {email: "bcc1@example.com", name: "Brock Test", header_to: "to1@example.com"}},
97
+ {address: {email: "bcc2@example.com", header_to: "to1@example.com"}}])
98
+ expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc1@example.com,cc2@example.com"})
78
99
  end
79
100
  end
80
101
  end
@@ -12,16 +12,18 @@ describe SparkPostRails::DeliveryMethod do
12
12
  test_email = Mailer.test_email cc: "cc@example.com"
13
13
  @delivery_method.deliver!(test_email)
14
14
 
15
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to@example.com"}}, {address: {email: "cc@example.com", header_to: "to@example.com"}}])
16
- expect(@delivery_method.data[:content][:headers]).to eq({cc: ["cc@example.com"]})
15
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", header_to: anything}}),
16
+ {address: {email: "cc@example.com", header_to: "to@example.com"}}])
17
+ expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc@example.com"})
17
18
  end
18
19
 
19
20
  it "handles name and email" do
20
21
  test_email = Mailer.test_email to: "Joe Test <to@example.com>", cc: "Carl Test <cc@example.com>"
21
22
  @delivery_method.deliver!(test_email)
22
23
 
23
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to@example.com", name: "Joe Test"}}, {address: {email: "cc@example.com", name: "Carl Test", header_to: "to@example.com"}}])
24
- expect(@delivery_method.data[:content][:headers]).to eq({cc: ["cc@example.com"]})
24
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", name: "Joe Test", header_to: anything}}),
25
+ {address: {email: "cc@example.com", name: "Carl Test", header_to: "to@example.com"}}])
26
+ expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc@example.com"})
25
27
  end
26
28
  end
27
29
 
@@ -30,24 +32,30 @@ describe SparkPostRails::DeliveryMethod do
30
32
  test_email = Mailer.test_email cc: "cc1@example.com, cc2@example.com"
31
33
  @delivery_method.deliver!(test_email)
32
34
 
33
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to@example.com"}}, {address: {email: "cc1@example.com", header_to: "to@example.com"}}, {address: {email: "cc2@example.com", header_to: "to@example.com"}}])
34
- expect(@delivery_method.data[:content][:headers]).to eq({cc: ["cc1@example.com", "cc2@example.com"]})
35
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", header_to: anything}}),
36
+ {address: {email: "cc1@example.com", header_to: "to@example.com"}},
37
+ {address: {email: "cc2@example.com", header_to: "to@example.com"}}])
38
+ expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc1@example.com,cc2@example.com"})
35
39
  end
36
40
 
37
41
  it "handles name and email" do
38
42
  test_email = Mailer.test_email to: "Joe Test <to@example.com>", cc: "Carl Test <cc1@example.com>, Chris Test <cc2@example.com>"
39
43
  @delivery_method.deliver!(test_email)
40
44
 
41
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to@example.com", name: "Joe Test"}}, {address: {email: "cc1@example.com", name: "Carl Test", header_to: "to@example.com"}}, {address: {email: "cc2@example.com", name: "Chris Test", header_to: "to@example.com"}}])
42
- expect(@delivery_method.data[:content][:headers]).to eq({cc: ["cc1@example.com", "cc2@example.com"]})
45
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", name: "Joe Test", header_to: anything}}),
46
+ {address: {email: "cc1@example.com", name: "Carl Test", header_to: "to@example.com"}},
47
+ {address: {email: "cc2@example.com", name: "Chris Test", header_to: "to@example.com"}}])
48
+ expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc1@example.com,cc2@example.com"})
43
49
  end
44
50
 
45
51
  it "handles mix of email only and name/email" do
46
52
  test_email = Mailer.test_email to: "Joe Test <to@example.com>", cc: "Carl Test <cc1@example.com>, cc2@example.com"
47
53
  @delivery_method.deliver!(test_email)
48
54
 
49
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to@example.com", name: "Joe Test"}}, {address: {email: "cc1@example.com", name: "Carl Test", header_to: "to@example.com"}}, {address: {email: "cc2@example.com", header_to: "to@example.com"}}])
50
- expect(@delivery_method.data[:content][:headers]).to eq({cc: ["cc1@example.com", "cc2@example.com"]})
55
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", name: "Joe Test", header_to: anything}}),
56
+ {address: {email: "cc1@example.com", name: "Carl Test", header_to: "to@example.com"}},
57
+ {address: {email: "cc2@example.com", header_to: "to@example.com"}}])
58
+ expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc1@example.com,cc2@example.com"})
51
59
  end
52
60
  end
53
61
 
@@ -56,16 +64,20 @@ describe SparkPostRails::DeliveryMethod do
56
64
  test_email = Mailer.test_email to: "to1@example.com, to2@example.com", cc: "cc@example.com"
57
65
  @delivery_method.deliver!(test_email)
58
66
 
59
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to1@example.com"}}, {address: {email: "to2@example.com"}}, {address: {email: "cc@example.com", header_to: "to1@example.com"}}])
60
- expect(@delivery_method.data[:content][:headers]).to eq({cc: ["cc@example.com"]})
67
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", header_to: anything}}),
68
+ a_hash_including({address: {email: "to2@example.com", header_to: anything}}),
69
+ {address: {email: "cc@example.com", header_to: "to1@example.com"}}])
70
+ expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc@example.com"})
61
71
  end
62
72
 
63
73
  it "handles name and email" do
64
74
  test_email = Mailer.test_email to: "Joe Test <to1@example.com>, Sam Test <to2@example.com>", cc: "Carl Test <cc@example.com>"
65
75
  @delivery_method.deliver!(test_email)
66
76
 
67
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to1@example.com", name: "Joe Test"}}, {address: {email: "to2@example.com", name: "Sam Test"}}, {address: {email: "cc@example.com", name: "Carl Test", header_to: "to1@example.com"}}])
68
- expect(@delivery_method.data[:content][:headers]).to eq({cc: ["cc@example.com"]})
77
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", name: "Joe Test", header_to: anything}}),
78
+ a_hash_including({address: {email: "to2@example.com", name: "Sam Test", header_to: anything}}),
79
+ {address: {email: "cc@example.com", name: "Carl Test", header_to: "to1@example.com"}}])
80
+ expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc@example.com"})
69
81
  end
70
82
  end
71
83
 
@@ -74,24 +86,33 @@ describe SparkPostRails::DeliveryMethod do
74
86
  test_email = Mailer.test_email to: "to1@example.com, to2@example.com", cc: "cc1@example.com, cc2@example.com"
75
87
  @delivery_method.deliver!(test_email)
76
88
 
77
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to1@example.com"}}, {address: {email: "to2@example.com"}}, {address: {email: "cc1@example.com", header_to: "to1@example.com"}}, {address: {email: "cc2@example.com", header_to: "to1@example.com"}}])
78
- expect(@delivery_method.data[:content][:headers]).to eq({cc: ["cc1@example.com", "cc2@example.com"]})
89
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", header_to: anything}}),
90
+ a_hash_including({address: {email: "to2@example.com", header_to: anything}}),
91
+ {address: {email: "cc1@example.com", header_to: "to1@example.com"}},
92
+ {address: {email: "cc2@example.com", header_to: "to1@example.com"}}])
93
+ expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc1@example.com,cc2@example.com"})
79
94
  end
80
95
 
81
96
  it "handles name and email" do
82
97
  test_email = Mailer.test_email to: "Joe Test <to1@example.com>, Sam Test <to2@example.com>", cc: "Carl Test <cc1@example.com>, Chris Test <cc2@example.com>"
83
98
  @delivery_method.deliver!(test_email)
84
99
 
85
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to1@example.com", name: "Joe Test"}}, {address: {email: "to2@example.com", name: "Sam Test"}}, {address: {email: "cc1@example.com", name: "Carl Test", header_to: "to1@example.com"}}, {address: {email: "cc2@example.com", name: "Chris Test", header_to: "to1@example.com"}}])
86
- expect(@delivery_method.data[:content][:headers]).to eq({cc: ["cc1@example.com", "cc2@example.com"]})
100
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", name: "Joe Test", header_to: anything}}),
101
+ a_hash_including({address: {email: "to2@example.com", name: "Sam Test", header_to: anything}}),
102
+ {address: {email: "cc1@example.com", name: "Carl Test", header_to: "to1@example.com"}},
103
+ {address: {email: "cc2@example.com", name: "Chris Test", header_to: "to1@example.com"}}])
104
+ expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc1@example.com,cc2@example.com"})
87
105
  end
88
106
 
89
107
  it "handles mix of email only and name/email for to recipients" do
90
108
  test_email = Mailer.test_email to: "Joe Test <to1@example.com>, to2@example.com", cc: "cc1@example.com, Chris Test <cc2@example.com>"
91
109
  @delivery_method.deliver!(test_email)
92
110
 
93
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to1@example.com", name: "Joe Test"}}, {address: {email: "to2@example.com"}}, {address: {email: "cc1@example.com", header_to: "to1@example.com"}}, {address: {email: "cc2@example.com", name: "Chris Test", header_to: "to1@example.com"}}])
94
- expect(@delivery_method.data[:content][:headers]).to eq({cc: ["cc1@example.com", "cc2@example.com"]})
111
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", name: "Joe Test", header_to: anything}}),
112
+ a_hash_including({address: {email: "to2@example.com", header_to: anything}}),
113
+ {address: {email: "cc1@example.com", header_to: "to1@example.com"}},
114
+ {address: {email: "cc2@example.com", name: "Chris Test", header_to: "to1@example.com"}}])
115
+ expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc1@example.com,cc2@example.com"})
95
116
  end
96
117
  end
97
118
  end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe "SparkPostRails configuration" do
4
+ let(:delivery_method) { SparkPostRails::DeliveryMethod.new }
5
+
6
+ describe "#configuration" do
7
+ it "creates a new configuration + defaults if #configure is never called", skip_configure: true do
8
+ config = SparkPostRails.configuration
9
+ expect(config).to_not be_nil
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe SparkPostRails::DeliveryException do
4
+ subject { SparkPostRails::DeliveryException.new(error) }
5
+
6
+ describe 'string' do
7
+ let(:error) { 'Some delivery error' }
8
+
9
+ it 'preserves original message' do
10
+ begin
11
+ raise subject
12
+ rescue SparkPostRails::DeliveryException => err
13
+ expect(error).to eq(err.message)
14
+ end
15
+ end
16
+ end
17
+
18
+ describe 'array with error details' do
19
+ let(:error) { [{ 'message' => 'Message generation rejected', 'description' => 'recipient address suppressed due to customer policy', 'code' => '1902' }] }
20
+
21
+ it 'assigns message' do
22
+ expect(subject.service_message).to eq('Message generation rejected')
23
+ end
24
+
25
+ it 'assigns description' do
26
+ expect(subject.service_description).to eq('recipient address suppressed due to customer policy')
27
+ end
28
+
29
+ it 'assigns code' do
30
+ expect(subject.service_code).to eq('1902')
31
+ end
32
+
33
+ it 'preserves original message' do
34
+ begin
35
+ raise subject
36
+ rescue SparkPostRails::DeliveryException => err
37
+ expect(error.to_s).to eq(err.message)
38
+ end
39
+ end
40
+ end
41
+
42
+ describe 'array with partial details' do
43
+ let(:error) { [{ 'message' => 'end of world' }] }
44
+
45
+ it 'assigns message' do
46
+ expect(subject.service_message).to eq('end of world')
47
+ end
48
+
49
+ it 'assigns description' do
50
+ expect(subject.service_description).to be nil
51
+ end
52
+
53
+ it 'assigns code' do
54
+ expect(subject.service_code).to be nil
55
+ end
56
+
57
+ it 'preserves original message' do
58
+ begin
59
+ raise subject
60
+ rescue SparkPostRails::DeliveryException => err
61
+ expect(error.to_s).to eq(err.message)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -18,7 +18,7 @@ describe SparkPostRails::DeliveryMethod do
18
18
  test_email = Mailer.test_email cc: "Carl Test <cc@example.com>", headers: {"Priority" => "urgent", "Sensitivity" => "private"}
19
19
  @delivery_method.deliver!(test_email)
20
20
 
21
- expect(@delivery_method.data[:content][:headers]).to eq({cc: ["cc@example.com"], "Priority" => "urgent", "Sensitivity" => "private"})
21
+ expect(@delivery_method.data[:content][:headers]).to eq({cc: "cc@example.com", "Priority" => "urgent", "Sensitivity" => "private"})
22
22
  end
23
23
 
24
24
  it "does not pass inappropriate headers through to the API" do
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe SparkPostRails::DeliveryMethod do
4
+ subject { described_class.new }
5
+ let(:metadata) { {item_1: 'test data 1', item_2: 'test data 2'} }
6
+
7
+ describe 'Metadata' do
8
+ context 'template-based message' do
9
+ context 'when metadata is passed' do
10
+ it 'includes metadata' do
11
+ test_email = Mailer.test_email sparkpost_data: { template_id: 'test_template', metadata: metadata }
12
+ subject.deliver!(test_email)
13
+ expect(subject.data[:metadata]).to eq(metadata)
14
+ end
15
+ end
16
+
17
+ context "when metadata isn't passed" do
18
+ it "doesn't include metadata" do
19
+ test_email = Mailer.test_email sparkpost_data: { template_id: 'test_template' }
20
+ subject.deliver!(test_email)
21
+ expect(subject.data).to_not have_key(:metadata)
22
+ end
23
+ end
24
+ end
25
+
26
+ context 'inline-content message' do
27
+ context 'when metadata is passed' do
28
+ it 'includes metadata' do
29
+ test_email = Mailer.test_email sparkpost_data: { metadata: metadata }
30
+ subject.deliver!(test_email)
31
+ expect(subject.data[:metadata]).to eq(metadata)
32
+ end
33
+ end
34
+
35
+ context "when metadata isn't passed" do
36
+ it "doesn't include metadata" do
37
+ test_email = Mailer.test_email sparkpost_data: { metadata: nil }
38
+ subject.deliver!(test_email)
39
+ expect(subject.data).to_not have_key(:metadata)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe SparkPostRails::DeliveryMethod do
4
+
5
+ before(:each) do
6
+ @delivery_method = SparkPostRails::DeliveryMethod.new
7
+ end
8
+
9
+ context "Recipient Data" do
10
+
11
+ it "accepts data matching all recipients" do
12
+ recipients = ['recipient1@email.com', 'recipient2@email.com']
13
+ recipients_data = [
14
+ {substitution_data: {name: "Recipient1"}},
15
+ {substitution_data: {name: "Recipient2"}}
16
+ ]
17
+
18
+ test_email = Mailer.test_email to: recipients, sparkpost_data: {recipients: recipients_data}
19
+
20
+ @delivery_method.deliver!(test_email)
21
+
22
+ actual_recipients = @delivery_method.data[:recipients]
23
+ expect(actual_recipients.length).to eq(recipients.length)
24
+ expect(actual_recipients).to match(recipients.each_with_index.map { |recipient, index| recipients_data[index].merge(address: {email: recipient, header_to: anything}) })
25
+ end
26
+
27
+ end
28
+ end
29
+
30
+
@@ -13,14 +13,14 @@ describe SparkPostRails::DeliveryMethod do
13
13
  test_email = Mailer.test_email
14
14
  @delivery_method.deliver!(test_email)
15
15
 
16
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to@example.com"}}])
16
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", header_to: anything}})])
17
17
  end
18
18
 
19
19
  it "handles name and email" do
20
20
  test_email = Mailer.test_email to: "Joe Test <to@example.com>"
21
21
  @delivery_method.deliver!(test_email)
22
22
 
23
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to@example.com", name: "Joe Test"}}])
23
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to@example.com", name: "Joe Test", header_to: anything}})])
24
24
  end
25
25
  end
26
26
 
@@ -29,21 +29,33 @@ describe SparkPostRails::DeliveryMethod do
29
29
  test_email = Mailer.test_email to: "to1@example.com, to2@example.com"
30
30
  @delivery_method.deliver!(test_email)
31
31
 
32
- expect(@delivery_method.data[:recipients]).to eq([{address: {email: "to1@example.com"}}, {address: {email: "to2@example.com"}}])
32
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({address: {email: "to1@example.com", header_to: anything}}),
33
+ a_hash_including({address: {email: "to2@example.com", header_to: anything}})])
33
34
  end
34
35
 
35
36
  it "handles name and email" do
36
37
  test_email = Mailer.test_email to: "Sam Test <to1@example.com>, Joe Test <to2@example.com>"
37
38
  @delivery_method.deliver!(test_email)
38
39
 
39
- expect(@delivery_method.data[:recipients]).to eq([{:address=>{:email=>"to1@example.com", :name=>"Sam Test"}}, {:address=>{:email=>"to2@example.com", :name=>"Joe Test"}}])
40
+ expect(@delivery_method.data[:recipients]).to match([a_hash_including({:address=>{:email=>"to1@example.com", :name=>"Sam Test", header_to: anything}}),
41
+ a_hash_including({:address=>{:email=>"to2@example.com", :name=>"Joe Test", header_to: anything}})])
40
42
  end
41
43
 
42
44
  it "handles mix of email only and name/email" do
43
45
  test_email = Mailer.test_email to: "Sam Test <to1@example.com>, to2@example.com"
44
46
  @delivery_method.deliver!(test_email)
45
47
 
46
- expect(@delivery_method.data[:recipients]).to eq([{:address=>{:email=>"to1@example.com", :name=>"Sam Test"}}, {:address=>{:email=>"to2@example.com"}}])
48
+ expect(@delivery_method.data[:recipients]).to match_array([a_hash_including({:address=>{:email=>"to1@example.com", :name=>"Sam Test", header_to: anything}}),
49
+ a_hash_including({:address=>{:email=>"to2@example.com", header_to: anything}})])
50
+ end
51
+
52
+ it "compiles list of email addresses to populate :header_to for each recipient" do
53
+ expected_header_to = "a@a.com,b@b.com"
54
+ test_email = Mailer.test_email to: "a <a@a.com>, b@b.com"
55
+ @delivery_method.deliver!(test_email)
56
+
57
+ expect(@delivery_method.data[:recipients].first[:address][:header_to]).to eql(expected_header_to)
58
+ expect(@delivery_method.data[:recipients].second[:address][:header_to]).to eql(expected_header_to)
47
59
  end
48
60
  end
49
61
  end
@@ -15,7 +15,8 @@ describe SparkPostRails::DeliveryMethod do
15
15
  end
16
16
 
17
17
  it "raises exception on error" do
18
- stub_request(:any, "https://api.sparkpost.com/api/v1/transmissions").
18
+ uri = URI.join(SparkPostRails.configuration.api_endpoint, 'v1/transmissions')
19
+ stub_request(:any, uri.to_s).
19
20
  to_return(body: "{\"errors\":[{\"message\":\"required field is missing\",\"description\":\"recipients or list_id required\",\"code\":\"1400\"}]}", status: 403)
20
21
 
21
22
  test_email = Mailer.test_email
@@ -6,13 +6,19 @@ require "sparkpost_rails"
6
6
  RSpec.configure do |config|
7
7
 
8
8
  config.before(:all) do
9
- SparkPostRails.configure do |c|
10
- c.api_key = "TESTKEY1234"
11
- end
9
+ ActionMailer::Base.send :include, SparkPostRails::DataOptions
12
10
  end
13
11
 
14
- config.before(:each) do
15
- stub_request(:any, "https://api.sparkpost.com/api/v1/transmissions").
12
+ config.before(:each) do |example|
13
+ if example.metadata[:skip_configure]
14
+ SparkPostRails.configuration = nil # Reset configuration
15
+ else
16
+ SparkPostRails.configure do |c|
17
+ c.api_key = "TESTKEY1234"
18
+ end
19
+ end
20
+ uri = URI.join(SparkPostRails.configuration.api_endpoint, 'v1/transmissions')
21
+ stub_request(:any, uri.to_s).
16
22
  to_return(body: "{\"results\":{\"total_rejected_recipients\":0,\"total_accepted_recipients\":1,\"id\":\"00000000000000000\"}}", status: 200)
17
23
  end
18
24
 
@@ -23,7 +29,7 @@ class Mailer < ActionMailer::Base
23
29
  def test_email options = {}
24
30
  data = {
25
31
  from: "from@example.com",
26
- to: "to@example.com",
32
+ to: options[:to] || "to@example.com",
27
33
  subject: "Test Email",
28
34
  text_part: "Hello, Testing!"
29
35
  }
@@ -65,14 +71,14 @@ class Mailer < ActionMailer::Base
65
71
  if data.has_key?(:html_part)
66
72
 
67
73
  mail(data) do |format|
68
- format.text {render text: data[:text_part]}
69
- format.html {render text: data[:html_part]}
74
+ format.text {render plain: data[:text_part]}
75
+ format.html {render plain: data[:html_part]}
70
76
  end
71
77
 
72
78
  else
73
79
 
74
80
  mail(data) do |format|
75
- format.text {render text: data[:text_part]}
81
+ format.text {render plain: data[:text_part]}
76
82
  end
77
83
 
78
84
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sparkpost_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Kimball
@@ -9,22 +9,28 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-04-25 00:00:00.000000000 Z
12
+ date: 2020-10-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - "~>"
18
+ - - ">="
19
19
  - !ruby/object:Gem::Version
20
20
  version: '4.0'
21
+ - - "<"
22
+ - !ruby/object:Gem::Version
23
+ version: '6.1'
21
24
  type: :runtime
22
25
  prerelease: false
23
26
  version_requirements: !ruby/object:Gem::Requirement
24
27
  requirements:
25
- - - "~>"
28
+ - - ">="
26
29
  - !ruby/object:Gem::Version
27
30
  version: '4.0'
31
+ - - "<"
32
+ - !ruby/object:Gem::Version
33
+ version: '6.1'
28
34
  - !ruby/object:Gem::Dependency
29
35
  name: rspec
30
36
  requirement: !ruby/object:Gem::Requirement
@@ -65,6 +71,7 @@ files:
65
71
  - LICENSE
66
72
  - README.md
67
73
  - lib/sparkpost_rails.rb
74
+ - lib/sparkpost_rails/data_options.rb
68
75
  - lib/sparkpost_rails/delivery_method.rb
69
76
  - lib/sparkpost_rails/exceptions.rb
70
77
  - lib/sparkpost_rails/railtie.rb
@@ -74,14 +81,18 @@ files:
74
81
  - spec/campaign_id_spec.rb
75
82
  - spec/cc_recipients_spec.rb
76
83
  - spec/click_tracking_spec.rb
84
+ - spec/configuration_spec.rb
77
85
  - spec/delivery_schedule_spec.rb
78
86
  - spec/description_spec.rb
87
+ - spec/exceptions_spec.rb
79
88
  - spec/from_spec.rb
80
89
  - spec/headers_spec.rb
81
90
  - spec/inline_content_spec.rb
82
91
  - spec/inline_css_spec.rb
83
92
  - spec/ip_pool_spec.rb
93
+ - spec/metadata_spec.rb
84
94
  - spec/open_tracking_spec.rb
95
+ - spec/recipient_specific_data_spec.rb
85
96
  - spec/recipients_list_spec.rb
86
97
  - spec/recipients_spec.rb
87
98
  - spec/reply_to_spec.rb
@@ -113,34 +124,37 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
124
  - !ruby/object:Gem::Version
114
125
  version: '0'
115
126
  requirements: []
116
- rubyforge_project:
117
- rubygems_version: 2.2.2
127
+ rubygems_version: 3.1.4
118
128
  signing_key:
119
129
  specification_version: 4
120
130
  summary: SparkPost for Rails
121
131
  test_files:
122
- - spec/inline_content_spec.rb
132
+ - spec/reply_to_spec.rb
133
+ - spec/open_tracking_spec.rb
134
+ - spec/recipient_specific_data_spec.rb
135
+ - spec/response_spec.rb
136
+ - spec/click_tracking_spec.rb
137
+ - spec/substitution_data_spec.rb
138
+ - spec/spec_helper.rb
123
139
  - spec/from_spec.rb
140
+ - spec/ip_pool_spec.rb
141
+ - spec/metadata_spec.rb
124
142
  - spec/subaccount_api_spec.rb
143
+ - spec/return_path_spec.rb
144
+ - spec/campaign_id_spec.rb
145
+ - spec/inline_content_spec.rb
146
+ - spec/skip_suppression_spec.rb
125
147
  - spec/recipients_spec.rb
148
+ - spec/description_spec.rb
149
+ - spec/inline_css_spec.rb
150
+ - spec/exceptions_spec.rb
151
+ - spec/delivery_schedule_spec.rb
152
+ - spec/attachments_spec.rb
126
153
  - spec/headers_spec.rb
127
- - spec/transactional_spec.rb
128
154
  - spec/bcc_recipients_spec.rb
129
- - spec/ip_pool_spec.rb
130
- - spec/attachments_spec.rb
131
- - spec/cc_recipients_spec.rb
132
155
  - spec/recipients_list_spec.rb
133
- - spec/template_spec.rb
134
- - spec/campaign_id_spec.rb
135
- - spec/reply_to_spec.rb
136
- - spec/spec_helper.rb
137
- - spec/description_spec.rb
138
- - spec/click_tracking_spec.rb
139
- - spec/skip_suppression_spec.rb
140
- - spec/substitution_data_spec.rb
141
- - spec/inline_css_spec.rb
142
- - spec/response_spec.rb
143
- - spec/open_tracking_spec.rb
156
+ - spec/transactional_spec.rb
157
+ - spec/cc_recipients_spec.rb
144
158
  - spec/sandbox_mode_spec.rb
145
- - spec/return_path_spec.rb
146
- - spec/delivery_schedule_spec.rb
159
+ - spec/template_spec.rb
160
+ - spec/configuration_spec.rb