dm_courier 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2b8943c2c5c0940d2bd7682f47f6c0a28e990a95
4
+ data.tar.gz: 770b51e01c169f6d68d7144d53dff1e5f6eac861
5
+ SHA512:
6
+ metadata.gz: 5cae0696f9e1d6a629eb7723bd4f8dd992b0b3edf0ff803065fcd8f032aa5de9f56d7b7eacde1b180ebc8b3c7e169ace7ddf9faadeb2e54fa5c27ab53f8494df
7
+ data.tar.gz: 78365d346b2c0d932d3df221edc56bde790f848afe0606e32e0dd3234628ce22c74c488009a7e682bbe6b6d08a1fe283d9c77829ddea859fc0d9e1a421d91a94
data/.codeclimate.yml ADDED
@@ -0,0 +1,25 @@
1
+ ---
2
+ engines:
3
+ duplication:
4
+ enabled: true
5
+ config:
6
+ languages:
7
+ - ruby
8
+ - javascript
9
+ - python
10
+ - php
11
+ fixme:
12
+ enabled: true
13
+ rubocop:
14
+ enabled: true
15
+ ratings:
16
+ paths:
17
+ - "**.inc"
18
+ - "**.js"
19
+ - "**.jsx"
20
+ - "**.module"
21
+ - "**.php"
22
+ - "**.py"
23
+ - "**.rb"
24
+ exclude_paths:
25
+ - spec/
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ tmp/*
data/.hound.yml ADDED
@@ -0,0 +1,4 @@
1
+ fail_on_violations: true
2
+ ruby:
3
+ enabled: true
4
+ config_file: .rubocop.yml
data/.rubocop.yml ADDED
@@ -0,0 +1,292 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.2
3
+ Exclude:
4
+ - "Gemfile"
5
+ UseCache: false
6
+
7
+ LineLength:
8
+ Max: 120
9
+
10
+ Style/CollectionMethods:
11
+ Description: Preferred collection methods.
12
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#map-find-select-reduce-size
13
+ Enabled: true
14
+ PreferredMethods:
15
+ collect: map
16
+ collect!: map!
17
+
18
+ Style/DotPosition:
19
+ Description: Checks the position of the dot in multi-line method calls.
20
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains
21
+ Enabled: true
22
+ EnforcedStyle: leading
23
+ SupportedStyles:
24
+ - leading
25
+ - trailing
26
+
27
+ Style/FileName:
28
+ Description: Use snake_case for source file names.
29
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-files
30
+ Enabled: true
31
+
32
+ Style/GuardClause:
33
+ Description: Check for conditionals that can be replaced with guard clauses
34
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals
35
+ Enabled: false
36
+ MinBodyLength: 1
37
+
38
+ Style/IfUnlessModifier:
39
+ Description: Favor modifier if/unless usage when you have a single-line body.
40
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier
41
+ Enabled: false
42
+ MaxLineLength: 120
43
+
44
+ Style/OptionHash:
45
+ Description: Don't use option hashes when you can use keyword arguments.
46
+ Enabled: false
47
+
48
+ Style/PercentLiteralDelimiters:
49
+ Description: Use `%`-literal delimiters consistently
50
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#percent-literal-braces
51
+ Enabled: false
52
+ PreferredDelimiters:
53
+ "%": "()"
54
+ "%i": "()"
55
+ "%q": "()"
56
+ "%Q": "()"
57
+ "%r": "{}"
58
+ "%s": "()"
59
+ "%w": "()"
60
+ "%W": "()"
61
+ "%x": "()"
62
+
63
+ Style/PredicateName:
64
+ Description: Check the names of predicate methods.
65
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark
66
+ Enabled: true
67
+ NamePrefix:
68
+ - is_
69
+ - has_
70
+ - have_
71
+ NamePrefixBlacklist:
72
+ - is_
73
+ Exclude:
74
+ - spec/**/*
75
+
76
+ Style/RaiseArgs:
77
+ Description: Checks the arguments passed to raise/fail.
78
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#exception-class-messages
79
+ Enabled: false
80
+ EnforcedStyle: exploded
81
+ SupportedStyles:
82
+ - compact
83
+ - exploded
84
+
85
+ Style/SignalException:
86
+ Description: Checks for proper usage of fail and raise.
87
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#fail-method
88
+ Enabled: false
89
+ EnforcedStyle: semantic
90
+ SupportedStyles:
91
+ - only_raise
92
+ - only_fail
93
+ - semantic
94
+
95
+ Style/SingleLineBlockParams:
96
+ Description: Enforces the names of some block params.
97
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#reduce-blocks
98
+ Enabled: false
99
+ Methods:
100
+ - reduce:
101
+ - a
102
+ - e
103
+ - inject:
104
+ - a
105
+ - e
106
+ Style/CollectionMethods:
107
+ Description: Preferred collection methods.
108
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#map-find-select-reduce-size
109
+ Enabled: true
110
+ PreferredMethods:
111
+ collect: map
112
+ collect!: map!
113
+ reduce: inject
114
+
115
+ Style/SingleLineMethods:
116
+ Description: Avoid single-line methods.
117
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-single-line-methods
118
+ Enabled: false
119
+ AllowIfMethodIsEmpty: true
120
+
121
+ Style/StringLiterals:
122
+ Description: Checks if uses of quotes match the configured preference.
123
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#consistent-string-literals
124
+ Enabled: true
125
+ EnforcedStyle: double_quotes
126
+ SupportedStyles:
127
+ - single_quotes
128
+ - double_quotes
129
+
130
+ Style/StringLiteralsInInterpolation:
131
+ Description: Checks if uses of quotes inside expressions in interpolated strings
132
+ match the configured preference.
133
+ Enabled: true
134
+ EnforcedStyle: single_quotes
135
+ SupportedStyles:
136
+ - single_quotes
137
+ - double_quotes
138
+
139
+ Style/TrailingCommaInArguments:
140
+ Description: 'Checks for trailing comma in argument lists.'
141
+ StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas'
142
+ Enabled: false
143
+ EnforcedStyleForMultiline: no_comma
144
+ SupportedStyles:
145
+ - comma
146
+ - consistent_comma
147
+ - no_comma
148
+
149
+ Style/TrailingCommaInLiteral:
150
+ Description: 'Checks for trailing comma in array and hash literals.'
151
+ StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas'
152
+ Enabled: false
153
+ EnforcedStyleForMultiline: no_comma
154
+ SupportedStyles:
155
+ - comma
156
+ - consistent_comma
157
+ - no_comma
158
+
159
+ Metrics/AbcSize:
160
+ Description: A calculated magnitude based on number of assignments, branches, and
161
+ conditions.
162
+ Enabled: false
163
+ Max: 15
164
+
165
+ Metrics/ClassLength:
166
+ Description: Avoid classes longer than 100 lines of code.
167
+ Enabled: false
168
+ CountComments: false
169
+ Max: 100
170
+
171
+ Metrics/ModuleLength:
172
+ CountComments: false
173
+ Max: 100
174
+ Description: Avoid modules longer than 100 lines of code.
175
+ Enabled: false
176
+
177
+ Metrics/CyclomaticComplexity:
178
+ Description: A complexity metric that is strongly correlated to the number of test
179
+ cases needed to validate a method.
180
+ Enabled: false
181
+ Max: 6
182
+
183
+ Metrics/MethodLength:
184
+ Description: Avoid methods longer than 10 lines of code.
185
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#short-methods
186
+ Enabled: false
187
+ CountComments: false
188
+ Max: 10
189
+
190
+ Metrics/ParameterLists:
191
+ Description: Avoid parameter lists longer than three or four parameters.
192
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#too-many-params
193
+ Enabled: false
194
+ Max: 5
195
+ CountKeywordArgs: true
196
+
197
+ Metrics/PerceivedComplexity:
198
+ Description: A complexity metric geared towards measuring complexity for a human
199
+ reader.
200
+ Enabled: false
201
+ Max: 7
202
+
203
+ Lint/AssignmentInCondition:
204
+ Description: Don't use assignment in conditions.
205
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition
206
+ Enabled: false
207
+ AllowSafeAssignment: true
208
+
209
+ Style/InlineComment:
210
+ Description: Avoid inline comments.
211
+ Enabled: true
212
+
213
+ Style/AccessorMethodName:
214
+ Description: Check the naming of accessor methods for get_/set_.
215
+ Enabled: true
216
+
217
+ Style/Alias:
218
+ Description: Use alias_method instead of alias.
219
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#alias-method
220
+ Enabled: true
221
+
222
+ Style/Documentation:
223
+ Description: Document classes and non-namespace modules.
224
+ Enabled: false
225
+
226
+ Style/DoubleNegation:
227
+ Description: Checks for uses of double negation (!!).
228
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-bang-bang
229
+ Enabled: false
230
+
231
+ Style/EachWithObject:
232
+ Description: Prefer `each_with_object` over `inject` or `reduce`.
233
+ Enabled: false
234
+
235
+ Style/EmptyLiteral:
236
+ Description: Prefer literals to Array.new/Hash.new/String.new.
237
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#literal-array-hash
238
+ Enabled: true
239
+
240
+ Style/ModuleFunction:
241
+ Description: Checks for usage of `extend self` in modules.
242
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#module-function
243
+ Enabled: true
244
+
245
+ Style/OneLineConditional:
246
+ Description: Favor the ternary operator(?:) over if/then/else/end constructs.
247
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#ternary-operator
248
+ Enabled: true
249
+
250
+ Style/PerlBackrefs:
251
+ Description: Avoid Perl-style regex back references.
252
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers
253
+ Enabled: true
254
+
255
+ Style/Send:
256
+ Description: Prefer `Object#__send__` or `Object#public_send` to `send`, as `send`
257
+ may overlap with existing methods.
258
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#prefer-public-send
259
+ Enabled: false
260
+
261
+ Style/SpecialGlobalVars:
262
+ Description: Avoid Perl-style global variables.
263
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms
264
+ Enabled: true
265
+
266
+ Style/VariableInterpolation:
267
+ Description: Don't interpolate global, instance and class variables directly in
268
+ strings.
269
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#curlies-interpolate
270
+ Enabled: false
271
+
272
+ Style/WhenThen:
273
+ Description: Use when x then ... for one-line cases.
274
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#one-line-cases
275
+ Enabled: false
276
+
277
+ Lint/EachWithObjectArgument:
278
+ Description: Check for immutable argument given to each_with_object.
279
+ Enabled: true
280
+
281
+ Lint/HandleExceptions:
282
+ Description: Don't suppress exception.
283
+ StyleGuide: https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions
284
+ Enabled: false
285
+
286
+ Lint/LiteralInCondition:
287
+ Description: Checks of literals used in conditions.
288
+ Enabled: false
289
+
290
+ Lint/LiteralInInterpolation:
291
+ Description: Checks for literals used in interpolation.
292
+ Enabled: false
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.2.0
data/.travis.yml ADDED
@@ -0,0 +1,15 @@
1
+ language: ruby
2
+ sudo: false
3
+ rvm:
4
+ - 2.2
5
+ - 2.3.0
6
+ notifications:
7
+ email:
8
+ on_success: change
9
+ on_failure: always
10
+ addons:
11
+ code_climate:
12
+ repo_token: e3eac13dabf5af3f7cd993b869ae5bce1cee735332537815badda3fd4ee2fc28
13
+ script:
14
+ - bundle exec rubocop
15
+ - bundle exec rake spec
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Jaison Erick
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,95 @@
1
+
2
+ # DM Courier
3
+
4
+ [![Build Status](https://travis-ci.org/sumoners/dm_courier.svg?branch=master)](https://travis-ci.org/sumoners/dm_courier)
5
+ [![Gem Version](https://badge.fury.io/rb/dm_courier.svg)](http://badge.fury.io/rb/dm_courier)
6
+ [![security](https://hakiri.io/github/sumoners/dm_courier/master.svg)](https://hakiri.io/github/sumoners/dm_courier/master)
7
+ [![Test Coverage](https://codeclimate.com/github/sumoners/dm_courier/badges/coverage.svg)](https://codeclimate.com/github/sumoners/dm_courier/coverage)
8
+ [![Code Climate](https://codeclimate.com/github/sumoners/dm_courier/badges/gpa.svg)](https://codeclimate.com/github/sumoners/dm_courier)
9
+
10
+ Stick with just one Gem and be free to choose your email delivery service. Rails
11
+ Courier allows you to change easily the deliery method anytime you want.
12
+
13
+ ## Rails Setup
14
+
15
+ First, add the gem to your Gemfile and run the `bundle` command to install it.
16
+
17
+ ```ruby
18
+ gem 'dm_courier'
19
+ ```
20
+
21
+ Second, set the delivery method in `config/environments/production.rb`.
22
+
23
+ ```ruby
24
+ config.action_mailer.delivery_method = :dm_courier
25
+ ```
26
+
27
+ Third, create an initializer such as `config/initializers/dm_courier.rb` and
28
+ paste in the following code:
29
+
30
+ ```ruby
31
+ DMCourier.configure do |config|
32
+ config.api_key = ENV['DM_COURIER_APIKEY']
33
+ config.service_name = :mandrill # Choose here the service you want to use
34
+ end
35
+ ```
36
+
37
+ NOTE: If you don't already have an environment variable for delivery service API
38
+ key, don't forget to create one.
39
+
40
+ ### Available configuration options
41
+
42
+ Any option with Mailer Support can be used inside the mailer like:
43
+
44
+ ```ruby
45
+ class MyMailer < ActionMailer::Base
46
+ default track_opens: false
47
+
48
+ def notify_user(email)
49
+ mail(auto_html: false, inline_css: true)
50
+ end
51
+ end
52
+ ```
53
+
54
+ Option | Mailer Support | Description
55
+ -----------|----------------|-------------
56
+ `api_key` | false | Your service API key<br />**Default:** `ENV['DM_COURIER_API_KEY']`
57
+ `service_name` | false | Your service API name.<br />**Default:** `ENV['DM_COURIER_SERVICE']`
58
+ `async` | false | If the message with be sent asynchronous (depends on the service support)<br />**Default:** false<br />**Services:** mandrill
59
+ `from` | true | A default from address for all emails<br />**Services:** all
60
+ `auto_html` | true | whether or not to automatically generate an HTML part for messages that are not given HTML<br />**Services:** mandrill
61
+ `auto_text` | true | whether or not to automatically generate a text part for messages that are not given text<br />**Services:** mandrill
62
+ `important` | true | whether or not this message is important, and should be delivered ahead of non-important messages<br />**Default**: false<br />**Services:** mandrill
63
+ `inline_css` | true | whether or not to automatically inline all CSS styles provided in the message HTML - only for HTML documents less than 256KB in size<br />**Services:** mandrill, sparkpost
64
+ `track_clicks` | true | whether or not to turn on click tracking for the message<br />**Services:** mandrill, sparkpost
65
+ `track_opens` | true | whether or not to turn on open tracking for the message<br />**Services:** mandrill, sparkpost
66
+ `track_url_without_query_string` | true | whether or not to strip the query string from URLs when aggregating tracked URL data<br />**Services:** mandrill
67
+ `log_content` | true | set to false to remove content logging for sensitive emails<br />**Services:** mandrill
68
+ `bcc_address` | true | an optional address to receive an exact copy of each recipient's email<br />**Services:** mandrill
69
+ `return_path_domain` | true | a custom domain to use for the messages's return-path<br />**Services:** mandrill, sparkpost
70
+ `signing_domain` | true | a custom domain to use for SPF/DKIM signing (for "via" or "on behalf of" in email clients)<br />**Services:** mandrill
71
+ `subaccount` | true | the unique id of a subaccount - must already exist or will fail with an error<br />**Services:** mandrill
72
+ `tracking_domain` | true | a custom domain to use for tracking opens and clicks<br />**Services:** mandrill
73
+ `tags` | true | an array of string to tag the message with. Stats are accumulated using tags, though we only store the first 100 we see, so this should not be unique or change frequently. Tags should be 50 characters or less. Any tags starting with an underscore are reserved for internal use and will cause errors.<br />**Services:** mandrill
74
+
75
+ ## Services
76
+
77
+ ### Mandrill
78
+
79
+ ### SparkPost
80
+
81
+ The options were choosen to create an abstraction layer above the original
82
+ service API. Because of that some options have different names:
83
+
84
+ - **return_path_domain**: Is called **return_path** on SparkPost
85
+ - **track_opens**: Is called **open_tracking** on SparkPost
86
+ - **track_clicks**: Is called **click_tracking** on SparkPost
87
+
88
+ > TODO: Sparkpost service implementation does not support BCC emails yet.
89
+
90
+ ## Development & Feedback
91
+
92
+ Questions or problems? Please use the issue tracker. If you would like to
93
+ contribute to this project, fork this repository. Pull requests appreciated.
94
+
95
+ This gem is based on the [mandrill_dm](https://github.com/mandrill_dm) gem.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler"
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ Bundler.require(:default, :development)
6
+
7
+ task default: :spec
8
+ RSpec::Core::RakeTask.new
@@ -0,0 +1,41 @@
1
+ require "dm_courier/default"
2
+
3
+ module DMCourier
4
+ module Configurable
5
+ attr_accessor :api_key, :service_name, :async, :auto_html, :auto_text, :important,
6
+ :inline_css, :track_clicks, :track_opens, :track_url_without_query_string,
7
+ :log_content, :bcc_address, :return_path_domain, :signing_domain,
8
+ :subaccount, :tracking_domain, :tags, :from
9
+
10
+ class << self
11
+ def keys
12
+ @keys ||= [:api_key, :service_name, :async, :auto_html, :auto_text, :important,
13
+ :inline_css, :track_clicks, :track_opens, :track_url_without_query_string,
14
+ :log_content, :bcc_address, :return_path_domain, :signing_domain,
15
+ :subaccount, :tracking_domain, :tags, :from]
16
+ end
17
+ end
18
+
19
+ def configure
20
+ yield self
21
+ end
22
+
23
+ def reset!
24
+ DMCourier::Configurable.keys.each do |key|
25
+ instance_variable_set(:"@#{key}", DMCourier::Default.options[key])
26
+ end
27
+
28
+ self
29
+ end
30
+ alias setup reset!
31
+
32
+ def same_options?(opts)
33
+ opts.hash == options.hash
34
+ end
35
+
36
+ def options
37
+ Hash[DMCourier::Configurable
38
+ .keys.map { |key| [key, instance_variable_get(:"@#{key}")] }]
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,81 @@
1
+ module DMCourier
2
+ module Default
3
+ class << self
4
+ def options
5
+ Hash[DMCourier::Configurable.keys.map { |key| [key, send(key)] }]
6
+ end
7
+
8
+ def api_key
9
+ ENV["DM_COURIER_API_KEY"]
10
+ end
11
+
12
+ def service_name
13
+ ENV["DM_COURIER_SERVICE"]
14
+ end
15
+
16
+ def async
17
+ false
18
+ end
19
+
20
+ def auto_html
21
+ nil
22
+ end
23
+
24
+ def auto_text
25
+ nil
26
+ end
27
+
28
+ def important
29
+ nil
30
+ end
31
+
32
+ def inline_css
33
+ nil
34
+ end
35
+
36
+ def track_clicks
37
+ nil
38
+ end
39
+
40
+ def track_opens
41
+ nil
42
+ end
43
+
44
+ def track_url_without_query_string
45
+ nil
46
+ end
47
+
48
+ def log_content
49
+ nil
50
+ end
51
+
52
+ def bcc_address
53
+ nil
54
+ end
55
+
56
+ def return_path_domain
57
+ nil
58
+ end
59
+
60
+ def signing_domain
61
+ nil
62
+ end
63
+
64
+ def subaccount
65
+ nil
66
+ end
67
+
68
+ def tracking_domain
69
+ nil
70
+ end
71
+
72
+ def tags
73
+ nil
74
+ end
75
+
76
+ def from
77
+ nil
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,22 @@
1
+ require "dm_courier/service_locator"
2
+
3
+ module DMCourier
4
+ class DeliveryMethod
5
+ include DMCourier::ServiceLocator
6
+ include DMCourier::Configurable
7
+
8
+ attr_reader :response
9
+
10
+ def initialize(options = {})
11
+ DMCourier::Configurable.keys.each do |key|
12
+ instance_variable_set(:"@#{key}", options[key] ||
13
+ DMCourier.instance_variable_get(:"@#{key}"))
14
+ end
15
+ end
16
+ alias settings options
17
+
18
+ def deliver!(mail)
19
+ @response = service.new(mail, options).deliver!
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,4 @@
1
+ module DMCourier
2
+ class Error < StandardError; end
3
+ class InvalidService < Error; end
4
+ end
@@ -0,0 +1,95 @@
1
+ require "base64"
2
+
3
+ module DMCourier
4
+ module Services
5
+ module MessageHelper
6
+ def extract_params(params)
7
+ json = {}
8
+
9
+ json
10
+ .merge!(Hash[
11
+ params[:nil_true_false].map { |name, key| [name.to_sym, nil_true_false?(key.to_sym)] }
12
+ ]) if params.key?(:nil_true_false)
13
+
14
+ json
15
+ .merge!(Hash[
16
+ params[:string].map { |name, key| [name.to_sym, return_string_value(key.to_sym)] }
17
+ ]) if params.key?(:string)
18
+
19
+ json
20
+ end
21
+
22
+ def from_email
23
+ from.address if from_address
24
+ end
25
+
26
+ def from_name
27
+ from.display_name if from_address
28
+ end
29
+
30
+ def from
31
+ Mail::Address.new(from_address)
32
+ end
33
+
34
+ def from_address
35
+ mail[:from] ? mail[:from].formatted.first : options[:from]
36
+ end
37
+
38
+ def text_part
39
+ return mail.text_part.body.decoded if mail.multipart? && mail.text_part
40
+ nil
41
+ end
42
+
43
+ def html_part
44
+ mail.html_part ? mail.html_part.body.decoded : mail.body.decoded
45
+ end
46
+
47
+ def subject
48
+ mail.subject
49
+ end
50
+
51
+ def return_string_value(field)
52
+ value = fallback_options(field)
53
+ value ? value.to_s : nil
54
+ end
55
+
56
+ def nil_true_false?(field)
57
+ value = fallback_options(field)
58
+ return nil if value.nil?
59
+ value.to_s == "true" ? true : false
60
+ end
61
+
62
+ def fallback_options(field)
63
+ mail[field] || options[field]
64
+ end
65
+
66
+ def attachments(filter = {})
67
+ Enumerator.new do |y|
68
+ attachments = mail.attachments
69
+ attachments = if filter[:inline]
70
+ attachments.select(&:inline?)
71
+ else
72
+ attachments.reject(&:inline?)
73
+ end if filter.key?(:inline)
74
+
75
+ attachments.map do |attachment|
76
+ y.yield(name: attachment.inline? ? attachment.cid : attachment.filename,
77
+ type: attachment.mime_type,
78
+ content: Base64.encode64(attachment.body.decoded),
79
+ inline: attachment.inline?)
80
+ end
81
+ end
82
+ end
83
+
84
+ def attachments?(filter = {})
85
+ found = mail.attachments && !mail.attachments.empty?
86
+
87
+ if found && filter.key?(:inline)
88
+ found &&= mail.attachments.any? { |a| a.inline? == filter[:inline] }
89
+ end
90
+
91
+ found
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,11 @@
1
+ require "dm_courier"
2
+
3
+ module DMCourier
4
+ class Railtie < Rails::Railtie
5
+ initializer "dm_courier.add_delivery_method" do
6
+ ActiveSupport.on_load :action_mailer do
7
+ ActionMailer::Base.add_delivery_method :dm_courier, DMCourier::DeliveryMethod
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,43 @@
1
+ require "dm_courier/services/mandrill"
2
+ require "dm_courier/services/sparkpost"
3
+
4
+ module DMCourier
5
+ module ServiceLocator
6
+ def service
7
+ @service ||= begin
8
+ raise DMCourier::InvalidService unless @service_name
9
+
10
+ constantize(@service_name)
11
+ end
12
+ rescue NameError => e
13
+ raise DMCourier::InvalidService, e
14
+ end
15
+
16
+ def constantize(service_name)
17
+ camel_cased_word = "DMCourier::Services::#{service_name.to_s.tr('_', ' ').split.map(&:capitalize).join('')}"
18
+ names = camel_cased_word.split("::")
19
+
20
+ Object.const_get(camel_cased_word) if names.empty?
21
+
22
+ names.shift if names.size > 1 && names.first.empty?
23
+
24
+ names.inject(Object) do |constant, name|
25
+ if constant == Object
26
+ constant.const_get(name)
27
+ else
28
+ candidate = constant.const_get(name)
29
+ next candidate if constant.const_defined?(name, false)
30
+ next candidate unless Object.const_defined?(name)
31
+
32
+ constant = constant.ancestors.inject do |const, ancestor|
33
+ break const if ancestor == Object
34
+ break ancestor if ancestor.const_defined?(name, false)
35
+ const
36
+ end
37
+
38
+ constant.const_get(name, false)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,111 @@
1
+ require "mandrill"
2
+ require "base64"
3
+
4
+ require "dm_courier/message_helper"
5
+
6
+ module DMCourier
7
+ module Services
8
+ class Mandrill
9
+ include DMCourier::Services::MessageHelper
10
+
11
+ attr_reader :api_key, :async, :mail, :options
12
+
13
+ def initialize(mail, options = {})
14
+ @mail = mail
15
+ @api_key = options.fetch(:api_key)
16
+ @async = options.fetch(:async, false)
17
+ @options = options
18
+ end
19
+
20
+ def name
21
+ :mandrill
22
+ end
23
+
24
+ def deliver!
25
+ mandrill_api = ::Mandrill::API.new(api_key)
26
+ mandrill_api.messages.send(mandrill_message, async)
27
+ end
28
+
29
+ def mandrill_message
30
+ message = extract_params(nil_true_false: { auto_html: :auto_html,
31
+ auto_text: :auto_text,
32
+ important: :important,
33
+ inline_css: :inline_css,
34
+ track_clicks: :track_clicks,
35
+ track_opens: :track_opens,
36
+ url_strip_qs: :track_url_without_query_string,
37
+ view_content_link: :log_content },
38
+ string: { bcc_address: :bcc_address,
39
+ return_path_domain: :return_path_domain,
40
+ signing_domain: :signing_domain,
41
+ subaccount: :subaccount,
42
+ tracking_domain: :tracking_domain })
43
+
44
+ message[:important] ||= false
45
+
46
+ message.merge!(from_email: from_email,
47
+ from_name: from_name,
48
+ headers: headers,
49
+ html: html_part,
50
+ subject: subject,
51
+ tags: tags,
52
+ text: text_part,
53
+ to: to)
54
+
55
+ message[:attachments] = regular_attachments if attachments?(inline: false)
56
+ message[:images] = inline_attachments if attachments?(inline: true)
57
+ message
58
+ end
59
+
60
+ private
61
+
62
+ def headers
63
+ headers = {}
64
+ value = mail["Reply-To"] || options[:reply_to]
65
+ headers["Reply-To"] = value.to_s if value
66
+ headers
67
+ end
68
+
69
+ def tags
70
+ mail[:tags].to_s.split(", ").map { |tag| tag } +
71
+ options[:tags].to_s.split(", ").map { |tag| tag }
72
+ end
73
+
74
+ def to
75
+ %w(to cc bcc)
76
+ .map { |field| hash_addresses(mail[field]) }
77
+ .reject(&:nil?)
78
+ .flatten
79
+ end
80
+
81
+ def hash_addresses(address_field)
82
+ return nil unless address_field
83
+
84
+ address_field.formatted.map do |address|
85
+ address_obj = Mail::Address.new(address)
86
+ {
87
+ email: address_obj.address,
88
+ name: address_obj.display_name,
89
+ type: address_field.name.downcase
90
+ }
91
+ end
92
+ end
93
+
94
+ def regular_attachments
95
+ attachments(inline: false).map do |attachment|
96
+ { name: attachment[:name],
97
+ type: attachment[:type],
98
+ content: attachment[:content] }
99
+ end
100
+ end
101
+
102
+ def inline_attachments
103
+ attachments(inline: true).map do |attachment|
104
+ { name: attachment[:name],
105
+ type: attachment[:type],
106
+ content: attachment[:content] }
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,107 @@
1
+ require "sparkpost"
2
+ require "base64"
3
+
4
+ require "monkey_patch/sparkpost/request"
5
+ require "dm_courier/message_helper"
6
+
7
+ module DMCourier
8
+ module Services
9
+ class Sparkpost
10
+ include DMCourier::Services::MessageHelper
11
+
12
+ attr_reader :api_key, :mail, :options
13
+
14
+ def initialize(mail, options = {})
15
+ @mail = mail
16
+ @api_key = options.fetch(:api_key)
17
+ @options = options
18
+ end
19
+
20
+ def name
21
+ :sparkpost
22
+ end
23
+
24
+ def deliver!
25
+ sparkpost = ::SparkPost::Client.new(api_key)
26
+ transmission = sparkpost.transmission
27
+
28
+ transmission.send(:request, transmission.endpoint, api_key, sparkpost_message)
29
+ end
30
+
31
+ def sparkpost_message
32
+ parameters = extract_params(nil_true_false: { inline_css: :inline_css,
33
+ click_tracking: :track_clicks,
34
+ open_tracking: :track_opens },
35
+ string: { return_path: :return_path_domain })
36
+
37
+ message = { options: {} }
38
+ message[:options][:inline_css] = parameters[:inline_css] unless parameters[:inline_css].nil?
39
+ message[:options][:click_tracking] = parameters[:click_tracking] unless parameters[:click_tracking].nil?
40
+ message[:options][:open_tracking] = parameters[:open_tracking] unless parameters[:open_tracking].nil?
41
+
42
+ message[:return_path] = parameters[:return_path] unless parameters[:return_path].nil?
43
+
44
+ from = { email: from_email }
45
+ from[:name] = from_name if from_name
46
+
47
+ message[:content] = {
48
+ from: from,
49
+ subject: subject
50
+ }
51
+
52
+ message[:content][:reply_to] = reply_to if reply_to
53
+ message[:content][:html] = html_part if html_part
54
+ message[:content][:text] = text_part if text_part
55
+
56
+ message[:recipients] = recipients
57
+
58
+ message[:content][:attachments] = regular_attachments if attachments?(inline: false)
59
+ message[:content][:inline_images] = inline_attachments if attachments?(inline: true)
60
+ message
61
+ end
62
+
63
+ private
64
+
65
+ def reply_to
66
+ value = mail["Reply-To"] || options[:reply_to]
67
+ value.to_s if value
68
+ end
69
+
70
+ def recipients
71
+ %w(to cc)
72
+ .map { |field| hash_addresses(mail[field]) }
73
+ .reject(&:nil?)
74
+ .flatten
75
+ end
76
+
77
+ def hash_addresses(address_field)
78
+ return nil unless address_field
79
+
80
+ address_field.formatted.map do |address|
81
+ address_obj = Mail::Address.new(address)
82
+ {
83
+ address: { email: address_obj.address }.tap do |hash|
84
+ hash[:name] = address_obj.display_name if address_obj.display_name
85
+ end
86
+ }
87
+ end
88
+ end
89
+
90
+ def regular_attachments
91
+ attachments(inline: false).map do |attachment|
92
+ { name: attachment[:name],
93
+ type: attachment[:type],
94
+ content: attachment[:content] }
95
+ end
96
+ end
97
+
98
+ def inline_attachments
99
+ attachments(inline: true).map do |attachment|
100
+ { name: attachment[:name],
101
+ type: attachment[:type],
102
+ content: attachment[:content] }
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,3 @@
1
+ module DMCourier
2
+ VERSION = "0.1.1".freeze
3
+ end
data/lib/dm_courier.rb ADDED
@@ -0,0 +1,20 @@
1
+ require "dm_courier/version"
2
+ require "dm_courier/errors"
3
+ require "dm_courier/configurable"
4
+ require "dm_courier/delivery_method"
5
+
6
+ require "dm_courier/railtie" if defined? Rails
7
+
8
+ module DMCourier
9
+ class << self
10
+ include DMCourier::Configurable
11
+
12
+ def delivery_method
13
+ return @delivery_method if defined?(@delivery_method) &&
14
+ @delivery_method.same_options?(options)
15
+ @delivery_method = DMCourier::DeliveryMethod.new(options)
16
+ end
17
+ end
18
+ end
19
+
20
+ DMCourier.setup
@@ -0,0 +1,21 @@
1
+ require "net/http"
2
+ require "json"
3
+
4
+ module SparkPost
5
+ module Request
6
+ def request(url, api_key, data)
7
+ uri = URI.parse(url)
8
+ http = Net::HTTP.new(uri.host, uri.port)
9
+ http.use_ssl = true
10
+ headers = {
11
+ "User-Agent" => "ruby-sparkpost/" + VERSION,
12
+ "Content-Type" => "application/json",
13
+ "Authorization" => api_key
14
+ }
15
+ req = Net::HTTP::Post.new(uri.path, headers)
16
+ req.body = JSON.generate(data)
17
+
18
+ process_response(http.request(req))
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,39 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "dm_courier/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "dm_courier"
7
+ spec.version = DMCourier::VERSION.dup
8
+ spec.authors = ["Jaison Erick"]
9
+ spec.email = ["jaisonreis@gmail.com"]
10
+
11
+ spec.summary = "A delivery method that abstract the most common email delivery APIs"
12
+ spec.description = "A delivery method that abstract the most common email delivery APIs"
13
+ spec.homepage = "http://github.com/sumoners/dm_courier"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`
17
+ .split("\x0")
18
+ .reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.required_ruby_version = ">= 2.2.0"
25
+ spec.required_rubygems_version = ">= 2.4.5"
26
+
27
+ spec.add_dependency "mandrill-api", "~> 1.0.53"
28
+ spec.add_dependency "sparkpost", "~> 0.1.2"
29
+ spec.add_dependency "http", "0.9.8"
30
+
31
+ spec.add_development_dependency "codeclimate-test-reporter", "~> 0.5"
32
+ spec.add_development_dependency "bundler", "~> 1.7"
33
+ spec.add_development_dependency "rspec", "~> 3.4"
34
+ spec.add_development_dependency "mail", "~> 2.6"
35
+ spec.add_development_dependency "rake"
36
+ spec.add_development_dependency "simplecov", "~> 0.11"
37
+ spec.add_development_dependency "rubocop", "~> 0.36"
38
+ spec.add_development_dependency "pry"
39
+ end
metadata ADDED
@@ -0,0 +1,221 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dm_courier
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Jaison Erick
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-03-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mandrill-api
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.53
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.0.53
27
+ - !ruby/object:Gem::Dependency
28
+ name: sparkpost
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: http
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.9.8
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.8
55
+ - !ruby/object:Gem::Dependency
56
+ name: codeclimate-test-reporter
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.4'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.4'
97
+ - !ruby/object:Gem::Dependency
98
+ name: mail
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2.6'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '2.6'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.11'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.11'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rubocop
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '0.36'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '0.36'
153
+ - !ruby/object:Gem::Dependency
154
+ name: pry
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ description: A delivery method that abstract the most common email delivery APIs
168
+ email:
169
+ - jaisonreis@gmail.com
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - ".codeclimate.yml"
175
+ - ".gitignore"
176
+ - ".hound.yml"
177
+ - ".rubocop.yml"
178
+ - ".ruby-version"
179
+ - ".travis.yml"
180
+ - Gemfile
181
+ - LICENSE
182
+ - README.md
183
+ - Rakefile
184
+ - lib/dm_courier.rb
185
+ - lib/dm_courier/configurable.rb
186
+ - lib/dm_courier/default.rb
187
+ - lib/dm_courier/delivery_method.rb
188
+ - lib/dm_courier/errors.rb
189
+ - lib/dm_courier/message_helper.rb
190
+ - lib/dm_courier/railtie.rb
191
+ - lib/dm_courier/service_locator.rb
192
+ - lib/dm_courier/services/mandrill.rb
193
+ - lib/dm_courier/services/sparkpost.rb
194
+ - lib/dm_courier/version.rb
195
+ - lib/monkey_patch/sparkpost/request.rb
196
+ - rails_courier.gemspec
197
+ homepage: http://github.com/sumoners/dm_courier
198
+ licenses:
199
+ - MIT
200
+ metadata: {}
201
+ post_install_message:
202
+ rdoc_options: []
203
+ require_paths:
204
+ - lib
205
+ required_ruby_version: !ruby/object:Gem::Requirement
206
+ requirements:
207
+ - - ">="
208
+ - !ruby/object:Gem::Version
209
+ version: 2.2.0
210
+ required_rubygems_version: !ruby/object:Gem::Requirement
211
+ requirements:
212
+ - - ">="
213
+ - !ruby/object:Gem::Version
214
+ version: 2.4.5
215
+ requirements: []
216
+ rubyforge_project:
217
+ rubygems_version: 2.4.5
218
+ signing_key:
219
+ specification_version: 4
220
+ summary: A delivery method that abstract the most common email delivery APIs
221
+ test_files: []