multi_mail 0.0.1
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.
- data/.gitignore +7 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +40 -0
- data/Rakefile +100 -0
- data/USAGE +1 -0
- data/lib/multi_mail/mailgun/receiver.rb +84 -0
- data/lib/multi_mail/mandrill/receiver.rb +71 -0
- data/lib/multi_mail/receiver/base.rb +65 -0
- data/lib/multi_mail/receiver.rb +43 -0
- data/lib/multi_mail/sender.rb +40 -0
- data/lib/multi_mail/service.rb +63 -0
- data/lib/multi_mail/version.rb +3 -0
- data/lib/multi_mail.rb +16 -0
- data/multi_mail.gemspec +25 -0
- data/spec/fixtures/mailgun/invalid.txt +8 -0
- data/spec/fixtures/mailgun/missing.txt +8 -0
- data/spec/fixtures/mailgun/spam.txt +8 -0
- data/spec/fixtures/mailgun/valid.txt +8 -0
- data/spec/mailgun/receiver_spec.rb +75 -0
- data/spec/mandrill/receiver_spec.rb +64 -0
- data/spec/multi_mail_spec.rb +4 -0
- data/spec/receiver/base_spec.rb +51 -0
- data/spec/receiver_spec.rb +13 -0
- data/spec/sender_spec.rb +13 -0
- data/spec/service_spec.rb +60 -0
- data/spec/spec_helper.rb +27 -0
- metadata +182 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Open North Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# MultiMail: easily switch between email APIs
|
2
|
+
|
3
|
+
[](https://gemnasium.com/opennorth/multi_mail)
|
4
|
+
[](https://codeclimate.com/github/opennorth/multi_mail)
|
5
|
+
|
6
|
+
Many providers – including [Cloudmailin](http://www.cloudmailin.com/), [Mailgun](http://www.mailgun.com/), [Mandrill](http://mandrill.com/), [Postmark](http://postmarkapp.com/) and [SendGrid](http://sendgrid.com/) – offer APIs to send, receive, parse and forward email. MultiMail lets you easily switch between these APIs.
|
7
|
+
|
8
|
+
## Usage
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
require 'multi_mail'
|
12
|
+
|
13
|
+
service = MultiMail::Receiver.new({
|
14
|
+
:provider => 'mailgun',
|
15
|
+
:mailgun_api_key => 'key-xxxxxxxxxxxxxxxxxxxxxxx-x-xxxxxx',
|
16
|
+
})
|
17
|
+
|
18
|
+
message = service.process data # raw POST data or params hash
|
19
|
+
```
|
20
|
+
|
21
|
+
`message` is an array of [Mail::Message](https://github.com/mikel/mail) instances.
|
22
|
+
|
23
|
+
## Supported APIs
|
24
|
+
|
25
|
+
Incoming email:
|
26
|
+
|
27
|
+
* [Mailgun](http://www.mailgun.com/)
|
28
|
+
* [Mandrill](http://mandrill.com/)
|
29
|
+
|
30
|
+
[Attachment parsing](https://github.com/mikel/mail#attaching-and-detaching-files) on incoming email is not implemented yet. No outgoing email services are implemented yet.
|
31
|
+
|
32
|
+
## Bugs? Questions?
|
33
|
+
|
34
|
+
This gem's main repository is on GitHub: [http://github.com/opennorth/multi_mail](http://github.com/opennorth/multi_mail), where your contributions, forks, bug reports, feature requests, and feedback are greatly welcomed.
|
35
|
+
|
36
|
+
## Copyright
|
37
|
+
|
38
|
+
This gem re-uses code from [fog](https://github.com/fog/fog), released under the MIT license.
|
39
|
+
|
40
|
+
Copyright (c) 2012 Open North Inc., released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
begin
|
10
|
+
require 'yard'
|
11
|
+
YARD::Rake::YardocTask.new
|
12
|
+
rescue LoadError
|
13
|
+
task :yard do
|
14
|
+
abort 'YARD is not available. In order to run yard, you must: gem install yard'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'yaml'
|
19
|
+
|
20
|
+
def credentials
|
21
|
+
@credentials ||= YAML.load_file File.expand_path(File.join(File.dirname(__FILE__), 'api_keys.yml'))
|
22
|
+
end
|
23
|
+
|
24
|
+
namespace :mailgun do
|
25
|
+
require 'json'
|
26
|
+
require 'rest-client'
|
27
|
+
|
28
|
+
desc 'Create a Mailgun catch-all route forwarding to a postbin'
|
29
|
+
task :postbin do
|
30
|
+
|
31
|
+
bin_name = ENV['BIN_NAME'] || JSON.load(RestClient.post('http://requestb.in/api/v1/bins', {}))['name']
|
32
|
+
bin_url = "http://requestb.in/#{bin_name}"
|
33
|
+
|
34
|
+
base_url = "https://api:#{credentials[:mailgun_api_key]}@api.mailgun.net/v2"
|
35
|
+
action = %(forward("#{bin_url}"))
|
36
|
+
|
37
|
+
domain = ENV['DOMAIN'] || "#{SecureRandom.base64(4).tr('+/=lIO0', 'pqrsxyz')}.mailgun.com"
|
38
|
+
|
39
|
+
if JSON.load(RestClient.get("#{base_url}/domains"))['items'].empty?
|
40
|
+
puts "Creating the #{domain} domain..."
|
41
|
+
RestClient.post("#{base_url}/domains", :name => domain)
|
42
|
+
end
|
43
|
+
|
44
|
+
route = JSON.load(RestClient.get("#{base_url}/routes"))['items'].find do |route|
|
45
|
+
route['expression'] == 'catch_all()'
|
46
|
+
end
|
47
|
+
|
48
|
+
if route
|
49
|
+
unless route['action'] == action
|
50
|
+
puts "Updating the catch_all() route..."
|
51
|
+
JSON.load(RestClient.put("#{base_url}/routes/#{route['id']}", :action => action))
|
52
|
+
end
|
53
|
+
else
|
54
|
+
puts "Creating a catch_all() route..."
|
55
|
+
JSON.load(RestClient.post("#{base_url}/routes", :expression => 'catch_all()', :action => action))
|
56
|
+
end
|
57
|
+
|
58
|
+
puts "#{bin_url}?inspect"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
namespace :mandrill do
|
63
|
+
require 'mandrill'
|
64
|
+
|
65
|
+
def mandrill_api
|
66
|
+
@mandrill_api ||= Mandrill::API.new credentials[:mandrill_api_key]
|
67
|
+
end
|
68
|
+
|
69
|
+
def mandrill_domains
|
70
|
+
@mandrill_domains ||= mandrill_api.inbound.domains.each_with_object({}) do |domain,domains|
|
71
|
+
domains[domain['domain']] = domain
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def mandrill_domain
|
76
|
+
@mandrill_domain ||= ENV['DOMAIN'] || mandrill_domains.keys.first
|
77
|
+
end
|
78
|
+
|
79
|
+
desc 'Create a Mandrill catch-all route forwarding to a postbin'
|
80
|
+
task :validate do
|
81
|
+
if mandrill_domains.empty?
|
82
|
+
abort 'Add an inbound domain'
|
83
|
+
elsif mandrill_domains.size > 1 && ENV['DOMAIN'].nil?
|
84
|
+
abort "ENV['DOMAIN'] must be one of #{mandrill_domains.keys.join ', '}"
|
85
|
+
end
|
86
|
+
|
87
|
+
if ENV['DOMAIN'] && !mandrill_domains.keys.include?(ENV['DOMAIN'])
|
88
|
+
abort "#{ENV['DOMAIN']} must be one of #{mandrill_domains.keys.join ', '}"
|
89
|
+
end
|
90
|
+
|
91
|
+
unless mandrill_domains[mandrill_domain]['valid_mx']
|
92
|
+
puts "The MX for #{mandrill_domain} is not valid"
|
93
|
+
end
|
94
|
+
|
95
|
+
routes = mandrill_api.inbound.routes mandrill_domain
|
96
|
+
if routes.empty? || routes.none?{|route| route['pattern'] == '*'}
|
97
|
+
puts "Add a catchall (*) route for #{mandrill_domain}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/USAGE
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
See README.md for full usage details.
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module MultiMail
|
2
|
+
module Receiver
|
3
|
+
class Mailgun < MultiMail::Service
|
4
|
+
include MultiMail::Receiver::Base
|
5
|
+
|
6
|
+
requires :mailgun_api_key
|
7
|
+
|
8
|
+
# @param [Hash] options required and optional arguments
|
9
|
+
# @option opts [String] :mailgun_api_key a Mailgun API key
|
10
|
+
def initialize(options = {})
|
11
|
+
super
|
12
|
+
@mailgun_api_key = options[:mailgun_api_key]
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [Hash] params the content of Mailgun's webhook
|
16
|
+
# @return [Boolean] whether the request originates from Mailgun
|
17
|
+
# @raises [KeyError] if the request is missing parameters
|
18
|
+
# @see http://documentation.mailgun.net/user_manual.html#securing-webhooks
|
19
|
+
def valid?(params)
|
20
|
+
params.fetch('signature') == OpenSSL::HMAC.hexdigest(
|
21
|
+
OpenSSL::Digest::Digest.new('sha256'), @mailgun_api_key,
|
22
|
+
'%s%s' % [params.fetch('timestamp'), params.fetch('token')])
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param [Hash] params the content of Mailgun's webhook
|
26
|
+
# @return [Array<Mail::Message>] messages
|
27
|
+
# @note Mailgun sends the message headers both individually and in the
|
28
|
+
# `message-headers` parameter. Only `message-headers` is documented.
|
29
|
+
# @todo parse attachments properly
|
30
|
+
def transform(params)
|
31
|
+
headers = Multimap.new
|
32
|
+
JSON.parse(params['message-headers']).each do |key,value|
|
33
|
+
headers[key] = value
|
34
|
+
end
|
35
|
+
|
36
|
+
message = Mail.new do
|
37
|
+
headers headers
|
38
|
+
|
39
|
+
# The following are redundant with `message-headers`:
|
40
|
+
#
|
41
|
+
# from params['from']
|
42
|
+
# sender params['sender']
|
43
|
+
# to params['recipient']
|
44
|
+
# subject params['subject']
|
45
|
+
|
46
|
+
text_part do
|
47
|
+
body params['body-plain']
|
48
|
+
end
|
49
|
+
|
50
|
+
html_part do
|
51
|
+
content_type 'text/html; charset=UTF-8'
|
52
|
+
body params['body-html']
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Extra Mailgun parameters.
|
57
|
+
[ 'stripped-text',
|
58
|
+
'stripped-signature',
|
59
|
+
'stripped-html',
|
60
|
+
'attachment-count',
|
61
|
+
'attachment-x',
|
62
|
+
'content-id-map',
|
63
|
+
].each do |key|
|
64
|
+
if !params[key].nil? && !params[key].empty?
|
65
|
+
message[key] = params[key]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
[message]
|
70
|
+
end
|
71
|
+
|
72
|
+
# @param [Mail::Message] message a message
|
73
|
+
# @return [Boolean] whether the message is spam
|
74
|
+
# @see http://documentation.mailgun.net/user_manual.html#spam-filter
|
75
|
+
# @note You must enable spam filtering for each domain in Mailgun's [Control
|
76
|
+
# Panel](https://mailgun.net/cp/domains).
|
77
|
+
# @note We may also inspect `X-Mailgun-SScore` and `X-Mailgun-Spf`, whose
|
78
|
+
# possible values are "Pass", "Neutral", "Fail" and "SoftFail".
|
79
|
+
def spam?(message)
|
80
|
+
message['X-Mailgun-Sflag'].value == 'Yes'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module MultiMail
|
2
|
+
module Receiver
|
3
|
+
class Mandrill < MultiMail::Service
|
4
|
+
include MultiMail::Receiver::Base
|
5
|
+
|
6
|
+
requires :mandrill_api_key
|
7
|
+
|
8
|
+
# @param [Hash] options required and optional arguments
|
9
|
+
# @option opts [String] :mandrill_api_key a Mandrill API key
|
10
|
+
def initialize(options = {})
|
11
|
+
super
|
12
|
+
@mandrill_api_key = options[:mandrill_api_key]
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [Hash] params the content of Mandrill's webhook
|
16
|
+
# @return [Boolean] whether the request originates from Mandrill
|
17
|
+
def valid?(params)
|
18
|
+
JSON.parse(params['mandrill_events']).all? do |event|
|
19
|
+
event.fetch('event') == 'inbound'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param [Mail::Message] message a message
|
24
|
+
# @return [Boolean] whether the message is spam
|
25
|
+
def spam?(message)
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param [Hash] params the content of Mandrill's webhook
|
30
|
+
# @return [Array<Mail::Message>] messages
|
31
|
+
# @todo parse attachments properly
|
32
|
+
def transform(params)
|
33
|
+
JSON.parse(params['mandrill_events']).map do |event|
|
34
|
+
message = Mail.new do
|
35
|
+
headers event['msg']['headers'].reject{|k,_| k=='Received'} # @todo
|
36
|
+
|
37
|
+
# The following are redundant with `message-headers`:
|
38
|
+
#
|
39
|
+
# address = Mail::Address.new event['msg']['from_email']
|
40
|
+
# address.display_name = event['msg']['from_name']
|
41
|
+
#
|
42
|
+
# from address.format
|
43
|
+
# to event['msg']['to'].flatten.compact
|
44
|
+
# subject event['msg']['subject']
|
45
|
+
|
46
|
+
text_part do
|
47
|
+
body event['msg']['text']
|
48
|
+
end
|
49
|
+
|
50
|
+
html_part do
|
51
|
+
content_type 'text/html; charset=UTF-8' # unsure about charset
|
52
|
+
body event['msg']['html']
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Extra Mandrill parameters. Discard `raw_msg`.
|
57
|
+
[ 'email',
|
58
|
+
'tags',
|
59
|
+
'sender',
|
60
|
+
].each do |key|
|
61
|
+
if !event['msg'][key].nil? && !event['msg'][key].empty?
|
62
|
+
message[key] = event['msg'][key]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
message
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module MultiMail
|
2
|
+
module Receiver
|
3
|
+
# Abstract class for incoming email services.
|
4
|
+
#
|
5
|
+
# The `transform` instance method must be implemented in sub-classes. The
|
6
|
+
# `valid?` and `spam?` instance methods may be implemented in sub-classes.
|
7
|
+
module Base
|
8
|
+
def self.included(subclass)
|
9
|
+
subclass.class_eval do
|
10
|
+
extend MultiMail::Receiver::Base::ClassMethods
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param [String,Hash] raw raw POST data or a params hash
|
15
|
+
# @return [Array<Mail::Message>] messages
|
16
|
+
# @raises [ForgedRequest] if the request is not authentic
|
17
|
+
def process(raw)
|
18
|
+
params = self.class.parse raw
|
19
|
+
if valid? params
|
20
|
+
transform params
|
21
|
+
else
|
22
|
+
raise ForgedRequest
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param [Hash] params the content of the provider's webhook
|
27
|
+
# @return [Boolean] whether the request is authentic
|
28
|
+
def valid?(params)
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param [Hash] params the content of the provider's webhook
|
33
|
+
# @return [Array<Mail::Message>] messages
|
34
|
+
def transform(params)
|
35
|
+
raise NotImplementedError
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param [Mail::Message] message a message
|
39
|
+
# @return [Boolean] whether the message is spam
|
40
|
+
def spam?(message)
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
44
|
+
module ClassMethods
|
45
|
+
# @param [String,Hash] raw raw POST data or a params hash
|
46
|
+
def parse(raw)
|
47
|
+
case raw
|
48
|
+
when String
|
49
|
+
params = CGI.parse raw
|
50
|
+
params.each do |key,value|
|
51
|
+
if Array === value && value.size == 1
|
52
|
+
params[key] = value.first
|
53
|
+
end
|
54
|
+
end
|
55
|
+
params
|
56
|
+
when Hash
|
57
|
+
raw
|
58
|
+
else
|
59
|
+
raise ArgumentError, "Can't handle #{raw.class.name} input"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module MultiMail
|
2
|
+
# @see http://rdoc.info/gems/fog/Fog/Storage
|
3
|
+
module Receiver
|
4
|
+
autoload :Base, 'multi_mail/receiver/base'
|
5
|
+
|
6
|
+
# @example
|
7
|
+
# require 'multi_mail'
|
8
|
+
# service = MultiMail::Receiver.new({
|
9
|
+
# :provider => 'mailgun',
|
10
|
+
# :mailgun_api_key => 'key-xxxxxxxxxxxxxxxxxxxxxxx-x-xxxxxx',
|
11
|
+
# })
|
12
|
+
#
|
13
|
+
# @param [Hash] attributes required arguments
|
14
|
+
# @option opts [String,Symbol] :provider a provider
|
15
|
+
# @raises [ArgumentError] if the provider does not exist
|
16
|
+
# @see Fog::Storage::new
|
17
|
+
def self.new(attributes)
|
18
|
+
attributes = attributes.dup # prevent delete from having side effects
|
19
|
+
case provider = attributes.delete(:provider).to_s.downcase.to_sym
|
20
|
+
when :cloudmailin
|
21
|
+
require 'multi_mail/cloudmailin/receiver'
|
22
|
+
MultiMail::Receiver::Cloudmailin.new(attributes)
|
23
|
+
when :mailgun
|
24
|
+
require 'multi_mail/mailgun/receiver'
|
25
|
+
MultiMail::Receiver::Mailgun.new(attributes)
|
26
|
+
when :mandrill
|
27
|
+
require 'multi_mail/mandrill/receiver'
|
28
|
+
MultiMail::Receiver::Mandrill.new(attributes)
|
29
|
+
when :postmark
|
30
|
+
require 'multi_mail/postmark/receiver'
|
31
|
+
MultiMail::Receiver::Postmark.new(attributes)
|
32
|
+
when :sendgrid
|
33
|
+
require 'multi_mail/sendgrid/receiver'
|
34
|
+
MultiMail::Receiver::SendGrid.new(attributes)
|
35
|
+
when :mock
|
36
|
+
# for testing
|
37
|
+
MultiMail::Receiver::Mock.new(attributes)
|
38
|
+
else
|
39
|
+
raise ArgumentError.new("#{provider} is not a recognized provider")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module MultiMail
|
2
|
+
# @see http://rdoc.info/gems/fog/Fog/Storage
|
3
|
+
module Sender
|
4
|
+
autoload :Base, 'multi_mail/sender/base'
|
5
|
+
|
6
|
+
# @example
|
7
|
+
# require 'multi_mail'
|
8
|
+
# service = MultiMail::Sender.new({
|
9
|
+
# :provider => 'mailgun',
|
10
|
+
# :mailgun_api_key => 'key-xxxxxxxxxxxxxxxxxxxxxxx-x-xxxxxx',
|
11
|
+
# })
|
12
|
+
#
|
13
|
+
# @param [Hash] attributes required arguments
|
14
|
+
# @option opts [String,Symbol] :provider a provider
|
15
|
+
# @raises [ArgumentError] if the provider does not exist
|
16
|
+
# @see Fog::Storage::new
|
17
|
+
def self.new(attributes)
|
18
|
+
attributes = attributes.dup # prevent delete from having side effects
|
19
|
+
case provider = attributes.delete(:provider).to_s.downcase.to_sym
|
20
|
+
when :mailgun
|
21
|
+
require 'multi_mail/mailgun/sender'
|
22
|
+
MultiMail::Sender::Mailgun.new(attributes)
|
23
|
+
when :mandrill
|
24
|
+
require 'multi_mail/mandrill/sender'
|
25
|
+
MultiMail::Sender::Mandrill.new(attributes)
|
26
|
+
when :postmark
|
27
|
+
require 'multi_mail/postmark/sender'
|
28
|
+
MultiMail::Sender::Postmark.new(attributes)
|
29
|
+
when :sendgrid
|
30
|
+
require 'multi_mail/sendgrid/sender'
|
31
|
+
MultiMail::Sender::SendGrid.new(attributes)
|
32
|
+
when :mock
|
33
|
+
# for testing
|
34
|
+
MultiMail::Sender::Mock.new(attributes)
|
35
|
+
else
|
36
|
+
raise ArgumentError.new("#{provider} is not a recognized provider")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module MultiMail
|
2
|
+
# @see http://rdoc.info/gems/fog/Fog/Service
|
3
|
+
class Service
|
4
|
+
# @param [Hash] options optional arguments
|
5
|
+
def initialize(options = {})
|
6
|
+
self.class.validate_options(options)
|
7
|
+
end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# Appends the given arguments to the list of required arguments.
|
11
|
+
#
|
12
|
+
# @param args one or more required arguments
|
13
|
+
# @see Fog::Service::requires
|
14
|
+
def requires(*args)
|
15
|
+
requirements.concat(args)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [Array] a list of required arguments
|
19
|
+
# @see Fog::Service::requirements
|
20
|
+
def requirements
|
21
|
+
@requirements ||= []
|
22
|
+
end
|
23
|
+
|
24
|
+
# Appends the given arguments to the list of optional arguments.
|
25
|
+
#
|
26
|
+
# @param args one or more optional arguments
|
27
|
+
# @see Fog::Service::recognizes
|
28
|
+
def recognizes(*args)
|
29
|
+
recognized.concat(args)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Array] a list of optional arguments
|
33
|
+
# @see Fog::Service::recognized
|
34
|
+
def recognized
|
35
|
+
@recognized ||= []
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param [Hash] options arguments
|
39
|
+
# @raises [ArgumentError] if can't find required arguments or can't
|
40
|
+
# recognize optional arguments
|
41
|
+
# @see Fog::Service::validate_options
|
42
|
+
def validate_options(options)
|
43
|
+
keys = []
|
44
|
+
for key, value in options
|
45
|
+
unless value.nil?
|
46
|
+
keys << key
|
47
|
+
end
|
48
|
+
end
|
49
|
+
missing = requirements - keys
|
50
|
+
unless missing.empty?
|
51
|
+
raise ArgumentError, "Missing required arguments: #{missing.join(', ')}"
|
52
|
+
end
|
53
|
+
|
54
|
+
unless recognizes.empty?
|
55
|
+
unrecognized = options.keys - requirements - recognized
|
56
|
+
unless unrecognized.empty?
|
57
|
+
raise ArgumentError, "Unrecognized arguments: #{unrecognized.join(', ')}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/multi_mail.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'json'
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
require 'mail'
|
6
|
+
require 'multimap'
|
7
|
+
|
8
|
+
module MultiMail
|
9
|
+
# @see http://rdoc.info/gems/fog/Fog/Errors
|
10
|
+
class Error < StandardError; end
|
11
|
+
class ForgedRequest < MultiMail::Error; end
|
12
|
+
|
13
|
+
autoload :Service, 'multi_mail/service'
|
14
|
+
autoload :Receiver, 'multi_mail/receiver'
|
15
|
+
autoload :Sender, 'multi_mail/sender'
|
16
|
+
end
|
data/multi_mail.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "multi_mail/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "multi_mail"
|
7
|
+
s.version = MultiMail::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Open North"]
|
10
|
+
s.email = ["info@opennorth.ca"]
|
11
|
+
s.homepage = "http://github.com/opennorth/multi_mail"
|
12
|
+
s.summary = %q{Easily switch between email APIs}
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_runtime_dependency 'mail', '~> 2.4.4' # Rails 3.2.9
|
20
|
+
s.add_runtime_dependency 'multimap', '~> 1.1.2'
|
21
|
+
s.add_development_dependency 'rspec', '~> 2.10'
|
22
|
+
s.add_development_dependency 'rest-client', '~> 1.6.7'
|
23
|
+
s.add_development_dependency 'mandrill-api', '~> 1.0.12'
|
24
|
+
s.add_development_dependency 'rake'
|
25
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
User-Agent: Python-urllib/2.7
|
3
|
+
Host: requestb.in
|
4
|
+
Content-Type: application/x-www-form-urlencoded
|
5
|
+
Content-Length: 5027
|
6
|
+
Connection: keep-alive
|
7
|
+
|
8
|
+
recipient=foo%2Bbar%40multimail.mailgun.org&sender=james%40opennorth.ca&subject=Test&from=James+McKinney+%3Cjames%40opennorth.ca%3E&Received=by+luna.mailgun.net+with+SMTP+mgrt+8774320450185%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+%2B0000&X-Envelope-From=%3Cjames%40opennorth.ca%3E&Received=from+mail-ia0-f172.google.com+%28mail-ia0-f172.google.com+%5B209.85.210.172%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d786f7.7f6ce4283bb0-in1%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+-0000+%28UTC%29&Received=by+mail-ia0-f172.google.com+with+SMTP+id+z13so5461278iaz.17+for+%3Cfoo%2Bbar%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+14%3A34%3A31+-0800+%28PST%29&X-Google-Dkim-Signature=v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Acontent-transfer-encoding%3Asubject%3Adate+%3Amessage-id%3Ato%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3D47DEQpj8HBSa%2B%2FTImW%2B5JCeuQeRkm5NMpJWZG3hSuFU%3D%3B+b%3Dp26tTn1Fhc%2FrjEkAhod8wKiSRChEdBRx2XKnFNxcDnCydK3PBFBT92aLQIBMeZeSz4+Wy0KgeSI8mEd841jjsp8B98EvsVFr7tN4woLT%2BDwA2RK7shJozGGrX3hcH3Guf1hHPcy+ETaw7ErqdaTEKdzT2B8y5YR3yQZ%2FuPfk%2B%2F5xJI1%2FDT%2BzK9FUaJ0Z7OQaiYSb8638Vz22+IDx%2BO1gEvKhEnpXhdClx6oWpQ5ZQcG6tsmI7YSMTOGDshJfSs%2FwhKsjMqAA3tvVwvRS6+BCzb9sd0VKdzeqeK2O1kG66UvCqNECknsqZfeaDgz2hTjlPVrg5txNwX4UrwB8D3txLI+o0Vg%3D%3D&X-Received=by+10.50.179.71+with+SMTP+id+de7mr13979750igc.84.1356302070921%3B+Sun%2C+23+Dec+2012+14%3A34%3A30+-0800+%28PST%29&Return-Path=%3Cjames%40opennorth.ca%3E&Received=from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+i10sm21051124igb.12.2012.12.23.14.34.28+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+14%3A34%3A29+-0800+%28PST%29&From=James+McKinney+%3Cjames%40opennorth.ca%3E&Content-Type=text%2Fplain&Content-Transfer-Encoding=7bit&Subject=Test&Date=Sun%2C+23+Dec+2012+17%3A34%3A26+-0500&Message-Id=%3C68CEEB79-97ED-4B9E-B192-B5311862622A%40opennorth.ca%3E&To=foo%2Bbar%40multimail.mailgun.org&Mime-Version=1.0+%28Apple+Message+framework+v1283%29&X-Mailer=Apple+Mail+%282.1283%29&X-Gm-Message-State=ALoCoQmgb8lQCLkNxQv2Pi0pHTsSKWtQfZoz6TZedmlACkDdgLlkdyhh4stBcJRimFZ3R%2FzeLXMc&X-Mailgun-Sflag=No&X-Mailgun-Sscore=-1.9&X-Mailgun-Spf=Pass&message-headers=%5B%5B%22Received%22%2C+%22by+luna.mailgun.net+with+SMTP+mgrt+8774320450185%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+%2B0000%22%5D%2C+%5B%22X-Envelope-From%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+mail-ia0-f172.google.com+%28mail-ia0-f172.google.com+%5B209.85.210.172%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d786f7.7f6ce4283bb0-in1%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+-0000+%28UTC%29%22%5D%2C+%5B%22Received%22%2C+%22by+mail-ia0-f172.google.com+with+SMTP+id+z13so5461278iaz.17+for+%3Cfoo%2Bbar%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+14%3A34%3A31+-0800+%28PST%29%22%5D%2C+%5B%22X-Google-Dkim-Signature%22%2C+%22v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Acontent-transfer-encoding%3Asubject%3Adate+%3Amessage-id%3Ato%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3D47DEQpj8HBSa%2B%2FTImW%2B5JCeuQeRkm5NMpJWZG3hSuFU%3D%3B+b%3Dp26tTn1Fhc%2FrjEkAhod8wKiSRChEdBRx2XKnFNxcDnCydK3PBFBT92aLQIBMeZeSz4+Wy0KgeSI8mEd841jjsp8B98EvsVFr7tN4woLT%2BDwA2RK7shJozGGrX3hcH3Guf1hHPcy+ETaw7ErqdaTEKdzT2B8y5YR3yQZ%2FuPfk%2B%2F5xJI1%2FDT%2BzK9FUaJ0Z7OQaiYSb8638Vz22+IDx%2BO1gEvKhEnpXhdClx6oWpQ5ZQcG6tsmI7YSMTOGDshJfSs%2FwhKsjMqAA3tvVwvRS6+BCzb9sd0VKdzeqeK2O1kG66UvCqNECknsqZfeaDgz2hTjlPVrg5txNwX4UrwB8D3txLI+o0Vg%3D%3D%22%5D%2C+%5B%22X-Received%22%2C+%22by+10.50.179.71+with+SMTP+id+de7mr13979750igc.84.1356302070921%3B+Sun%2C+23+Dec+2012+14%3A34%3A30+-0800+%28PST%29%22%5D%2C+%5B%22Return-Path%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+i10sm21051124igb.12.2012.12.23.14.34.28+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+14%3A34%3A29+-0800+%28PST%29%22%5D%2C+%5B%22From%22%2C+%22James+McKinney+%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Content-Type%22%2C+%22text%2Fplain%22%5D%2C+%5B%22Content-Transfer-Encoding%22%2C+%227bit%22%5D%2C+%5B%22Subject%22%2C+%22Test%22%5D%2C+%5B%22Date%22%2C+%22Sun%2C+23+Dec+2012+17%3A34%3A26+-0500%22%5D%2C+%5B%22Message-Id%22%2C+%22%3C68CEEB79-97ED-4B9E-B192-B5311862622A%40opennorth.ca%3E%22%5D%2C+%5B%22To%22%2C+%22foo%2Bbar%40multimail.mailgun.org%22%5D%2C+%5B%22Mime-Version%22%2C+%221.0+%28Apple+Message+framework+v1283%29%22%5D%2C+%5B%22X-Mailer%22%2C+%22Apple+Mail+%282.1283%29%22%5D%2C+%5B%22X-Gm-Message-State%22%2C+%22ALoCoQmgb8lQCLkNxQv2Pi0pHTsSKWtQfZoz6TZedmlACkDdgLlkdyhh4stBcJRimFZ3R%2FzeLXMc%22%5D%2C+%5B%22X-Mailgun-Sflag%22%2C+%22No%22%5D%2C+%5B%22X-Mailgun-Sscore%22%2C+%22-1.9%22%5D%2C+%5B%22X-Mailgun-Spf%22%2C+%22Pass%22%5D%5D×tamp=1356302072&token=3ye06wh177qi0rogrkrw25fj8an42ncxpyw72h7d4gj946hpb2&signature=xxx
|
@@ -0,0 +1,8 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
User-Agent: Python-urllib/2.7
|
3
|
+
Host: requestb.in
|
4
|
+
Content-Type: application/x-www-form-urlencoded
|
5
|
+
Content-Length: 5027
|
6
|
+
Connection: keep-alive
|
7
|
+
|
8
|
+
recipient=foo%2Bbar%40multimail.mailgun.org&sender=james%40opennorth.ca&subject=Test&from=James+McKinney+%3Cjames%40opennorth.ca%3E&Received=by+luna.mailgun.net+with+SMTP+mgrt+8774320450185%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+%2B0000&X-Envelope-From=%3Cjames%40opennorth.ca%3E&Received=from+mail-ia0-f172.google.com+%28mail-ia0-f172.google.com+%5B209.85.210.172%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d786f7.7f6ce4283bb0-in1%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+-0000+%28UTC%29&Received=by+mail-ia0-f172.google.com+with+SMTP+id+z13so5461278iaz.17+for+%3Cfoo%2Bbar%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+14%3A34%3A31+-0800+%28PST%29&X-Google-Dkim-Signature=v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Acontent-transfer-encoding%3Asubject%3Adate+%3Amessage-id%3Ato%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3D47DEQpj8HBSa%2B%2FTImW%2B5JCeuQeRkm5NMpJWZG3hSuFU%3D%3B+b%3Dp26tTn1Fhc%2FrjEkAhod8wKiSRChEdBRx2XKnFNxcDnCydK3PBFBT92aLQIBMeZeSz4+Wy0KgeSI8mEd841jjsp8B98EvsVFr7tN4woLT%2BDwA2RK7shJozGGrX3hcH3Guf1hHPcy+ETaw7ErqdaTEKdzT2B8y5YR3yQZ%2FuPfk%2B%2F5xJI1%2FDT%2BzK9FUaJ0Z7OQaiYSb8638Vz22+IDx%2BO1gEvKhEnpXhdClx6oWpQ5ZQcG6tsmI7YSMTOGDshJfSs%2FwhKsjMqAA3tvVwvRS6+BCzb9sd0VKdzeqeK2O1kG66UvCqNECknsqZfeaDgz2hTjlPVrg5txNwX4UrwB8D3txLI+o0Vg%3D%3D&X-Received=by+10.50.179.71+with+SMTP+id+de7mr13979750igc.84.1356302070921%3B+Sun%2C+23+Dec+2012+14%3A34%3A30+-0800+%28PST%29&Return-Path=%3Cjames%40opennorth.ca%3E&Received=from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+i10sm21051124igb.12.2012.12.23.14.34.28+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+14%3A34%3A29+-0800+%28PST%29&From=James+McKinney+%3Cjames%40opennorth.ca%3E&Content-Type=text%2Fplain&Content-Transfer-Encoding=7bit&Subject=Test&Date=Sun%2C+23+Dec+2012+17%3A34%3A26+-0500&Message-Id=%3C68CEEB79-97ED-4B9E-B192-B5311862622A%40opennorth.ca%3E&To=foo%2Bbar%40multimail.mailgun.org&Mime-Version=1.0+%28Apple+Message+framework+v1283%29&X-Mailer=Apple+Mail+%282.1283%29&X-Gm-Message-State=ALoCoQmgb8lQCLkNxQv2Pi0pHTsSKWtQfZoz6TZedmlACkDdgLlkdyhh4stBcJRimFZ3R%2FzeLXMc&X-Mailgun-Sflag=No&X-Mailgun-Sscore=-1.9&X-Mailgun-Spf=Pass&message-headers=%5B%5B%22Received%22%2C+%22by+luna.mailgun.net+with+SMTP+mgrt+8774320450185%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+%2B0000%22%5D%2C+%5B%22X-Envelope-From%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+mail-ia0-f172.google.com+%28mail-ia0-f172.google.com+%5B209.85.210.172%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d786f7.7f6ce4283bb0-in1%3B+Sun%2C+23+Dec+2012+22%3A34%3A31+-0000+%28UTC%29%22%5D%2C+%5B%22Received%22%2C+%22by+mail-ia0-f172.google.com+with+SMTP+id+z13so5461278iaz.17+for+%3Cfoo%2Bbar%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+14%3A34%3A31+-0800+%28PST%29%22%5D%2C+%5B%22X-Google-Dkim-Signature%22%2C+%22v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Acontent-transfer-encoding%3Asubject%3Adate+%3Amessage-id%3Ato%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3D47DEQpj8HBSa%2B%2FTImW%2B5JCeuQeRkm5NMpJWZG3hSuFU%3D%3B+b%3Dp26tTn1Fhc%2FrjEkAhod8wKiSRChEdBRx2XKnFNxcDnCydK3PBFBT92aLQIBMeZeSz4+Wy0KgeSI8mEd841jjsp8B98EvsVFr7tN4woLT%2BDwA2RK7shJozGGrX3hcH3Guf1hHPcy+ETaw7ErqdaTEKdzT2B8y5YR3yQZ%2FuPfk%2B%2F5xJI1%2FDT%2BzK9FUaJ0Z7OQaiYSb8638Vz22+IDx%2BO1gEvKhEnpXhdClx6oWpQ5ZQcG6tsmI7YSMTOGDshJfSs%2FwhKsjMqAA3tvVwvRS6+BCzb9sd0VKdzeqeK2O1kG66UvCqNECknsqZfeaDgz2hTjlPVrg5txNwX4UrwB8D3txLI+o0Vg%3D%3D%22%5D%2C+%5B%22X-Received%22%2C+%22by+10.50.179.71+with+SMTP+id+de7mr13979750igc.84.1356302070921%3B+Sun%2C+23+Dec+2012+14%3A34%3A30+-0800+%28PST%29%22%5D%2C+%5B%22Return-Path%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+i10sm21051124igb.12.2012.12.23.14.34.28+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+14%3A34%3A29+-0800+%28PST%29%22%5D%2C+%5B%22From%22%2C+%22James+McKinney+%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Content-Type%22%2C+%22text%2Fplain%22%5D%2C+%5B%22Content-Transfer-Encoding%22%2C+%227bit%22%5D%2C+%5B%22Subject%22%2C+%22Test%22%5D%2C+%5B%22Date%22%2C+%22Sun%2C+23+Dec+2012+17%3A34%3A26+-0500%22%5D%2C+%5B%22Message-Id%22%2C+%22%3C68CEEB79-97ED-4B9E-B192-B5311862622A%40opennorth.ca%3E%22%5D%2C+%5B%22To%22%2C+%22foo%2Bbar%40multimail.mailgun.org%22%5D%2C+%5B%22Mime-Version%22%2C+%221.0+%28Apple+Message+framework+v1283%29%22%5D%2C+%5B%22X-Mailer%22%2C+%22Apple+Mail+%282.1283%29%22%5D%2C+%5B%22X-Gm-Message-State%22%2C+%22ALoCoQmgb8lQCLkNxQv2Pi0pHTsSKWtQfZoz6TZedmlACkDdgLlkdyhh4stBcJRimFZ3R%2FzeLXMc%22%5D%2C+%5B%22X-Mailgun-Sflag%22%2C+%22No%22%5D%2C+%5B%22X-Mailgun-Sscore%22%2C+%22-1.9%22%5D%2C+%5B%22X-Mailgun-Spf%22%2C+%22Pass%22%5D%5D×tamp=1356302072&token=3ye06wh177qi0rogrkrw25fj8an42ncxpyw72h7d4gj946hpb2
|
@@ -0,0 +1,8 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
User-Agent: Python-urllib/2.7
|
3
|
+
Host: requestb.in
|
4
|
+
Content-Type: application/x-www-form-urlencoded
|
5
|
+
Content-Length: 5402
|
6
|
+
Connection: keep-alive
|
7
|
+
|
8
|
+
recipient=bar%2Bfoo%40multimail.mailgun.org&sender=james%40opennorth.ca&subject=&from=James+McKinney+%3Cjames%40opennorth.ca%3E&Received=by+luna.mailgun.net+with+SMTP+mgrt+8777239695241%3B+Sun%2C+23+Dec+2012+22%3A48%3A57+%2B0000&X-Envelope-From=%3Cjames%40opennorth.ca%3E&Received=from+mail-ie0-f171.google.com+%28mail-ie0-f171.google.com+%5B209.85.223.171%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d78a58.7f0a74469eb0-in3%3B+Sun%2C+23+Dec+2012+22%3A48%3A56+-0000+%28UTC%29&Received=by+mail-ie0-f171.google.com+with+SMTP+id+17so8387368iea.16+for+%3Cbar%2Bfoo%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+14%3A48%3A56+-0800+%28PST%29&X-Google-Dkim-Signature=v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Acontent-transfer-encoding%3Asubject%3Adate+%3Amessage-id%3Ato%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3Did84uDPa9SMZAVd%2Bg2pYg3Hl9aPfnapoGlOygxrIuV4%3D%3B+b%3DW5NwO9jAkVJVUPLJtrQMhre2Ry9XT8kuN8dNyin46tqI%2Bs3yvH8gc52K7mmXabo32i+du9Drxck9YIRyfHh%2Fza%2FeJS%2FkhHqo6L5QUiR7pG8DQI1Y7proRdDJqIa0aR92Tqj61ND+a5lAAi5r3bACZ81BBeAQvEqspKSuXSHyBUFdpDUP2cu50M1X3d7oxRdbV%2FC%2B0Z3hvh84+OoJ30XgT%2FO9zcn4XZ5B%2FcdQ%2BAsoCmBWotAwd3XT3yFcFE4uVNuuJkoRsRXZYn%2FeqnJvq+SlNW%2BHbW0QElLamOIfMxWh25mAMMSIsH5jfyG0ITnMfsXOENCoZEXsw3uvXss6StpL8Q+Suyw%3D%3D&X-Received=by+10.50.158.170+with+SMTP+id+wv10mr14044075igb.75.1356302936324%3B+Sun%2C+23+Dec+2012+14%3A48%3A56+-0800+%28PST%29&Return-Path=%3Cjames%40opennorth.ca%3E&Received=from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+xn10sm21091101igb.4.2012.12.23.14.48.53+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+14%3A48%3A54+-0800+%28PST%29&From=James+McKinney+%3Cjames%40opennorth.ca%3E&Content-Type=text%2Fplain%3B+charset%3D%22us-ascii%22&Content-Transfer-Encoding=7bit&Subject=&Date=Sun%2C+23+Dec+2012+17%3A48%3A51+-0500&Message-Id=%3CA95D0B9A-7F74-40A6-8CDE-70E287117BB5%40opennorth.ca%3E&To=bar%2Bfoo%40multimail.mailgun.org&Mime-Version=1.0+%28Apple+Message+framework+v1283%29&X-Mailer=Apple+Mail+%282.1283%29&X-Gm-Message-State=ALoCoQmOpQVgvzMb%2FDJNG97pU0B1TdWlpBAwUrLSpAxDtQMx8mpQpxFwbALmQmN7O2ggEeyGdC66&X-Mailgun-Sflag=Yes&X-Mailgun-Sscore=998.1&X-Mailgun-Spf=Pass&message-headers=%5B%5B%22Received%22%2C+%22by+luna.mailgun.net+with+SMTP+mgrt+8777239695241%3B+Sun%2C+23+Dec+2012+22%3A48%3A57+%2B0000%22%5D%2C+%5B%22X-Envelope-From%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+mail-ie0-f171.google.com+%28mail-ie0-f171.google.com+%5B209.85.223.171%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d78a58.7f0a74469eb0-in3%3B+Sun%2C+23+Dec+2012+22%3A48%3A56+-0000+%28UTC%29%22%5D%2C+%5B%22Received%22%2C+%22by+mail-ie0-f171.google.com+with+SMTP+id+17so8387368iea.16+for+%3Cbar%2Bfoo%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+14%3A48%3A56+-0800+%28PST%29%22%5D%2C+%5B%22X-Google-Dkim-Signature%22%2C+%22v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Acontent-transfer-encoding%3Asubject%3Adate+%3Amessage-id%3Ato%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3Did84uDPa9SMZAVd%2Bg2pYg3Hl9aPfnapoGlOygxrIuV4%3D%3B+b%3DW5NwO9jAkVJVUPLJtrQMhre2Ry9XT8kuN8dNyin46tqI%2Bs3yvH8gc52K7mmXabo32i+du9Drxck9YIRyfHh%2Fza%2FeJS%2FkhHqo6L5QUiR7pG8DQI1Y7proRdDJqIa0aR92Tqj61ND+a5lAAi5r3bACZ81BBeAQvEqspKSuXSHyBUFdpDUP2cu50M1X3d7oxRdbV%2FC%2B0Z3hvh84+OoJ30XgT%2FO9zcn4XZ5B%2FcdQ%2BAsoCmBWotAwd3XT3yFcFE4uVNuuJkoRsRXZYn%2FeqnJvq+SlNW%2BHbW0QElLamOIfMxWh25mAMMSIsH5jfyG0ITnMfsXOENCoZEXsw3uvXss6StpL8Q+Suyw%3D%3D%22%5D%2C+%5B%22X-Received%22%2C+%22by+10.50.158.170+with+SMTP+id+wv10mr14044075igb.75.1356302936324%3B+Sun%2C+23+Dec+2012+14%3A48%3A56+-0800+%28PST%29%22%5D%2C+%5B%22Return-Path%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+xn10sm21091101igb.4.2012.12.23.14.48.53+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+14%3A48%3A54+-0800+%28PST%29%22%5D%2C+%5B%22From%22%2C+%22James+McKinney+%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Content-Type%22%2C+%22text%2Fplain%3B+charset%3D%5C%22us-ascii%5C%22%22%5D%2C+%5B%22Content-Transfer-Encoding%22%2C+%227bit%22%5D%2C+%5B%22Subject%22%2C+%22%22%5D%2C+%5B%22Date%22%2C+%22Sun%2C+23+Dec+2012+17%3A48%3A51+-0500%22%5D%2C+%5B%22Message-Id%22%2C+%22%3CA95D0B9A-7F74-40A6-8CDE-70E287117BB5%40opennorth.ca%3E%22%5D%2C+%5B%22To%22%2C+%22bar%2Bfoo%40multimail.mailgun.org%22%5D%2C+%5B%22Mime-Version%22%2C+%221.0+%28Apple+Message+framework+v1283%29%22%5D%2C+%5B%22X-Mailer%22%2C+%22Apple+Mail+%282.1283%29%22%5D%2C+%5B%22X-Gm-Message-State%22%2C+%22ALoCoQmOpQVgvzMb%2FDJNG97pU0B1TdWlpBAwUrLSpAxDtQMx8mpQpxFwbALmQmN7O2ggEeyGdC66%22%5D%2C+%5B%22X-Mailgun-Sflag%22%2C+%22Yes%22%5D%2C+%5B%22X-Mailgun-Sscore%22%2C+%22998.1%22%5D%2C+%5B%22X-Mailgun-Spf%22%2C+%22Pass%22%5D%5D×tamp=1356302937&token=358k6n1x3wgth57c4c05qwhw61mr5ip9zws0mzk6du4d582g50&signature=58032e7c84f180ca3627226f443d7237e789ff30da23898af1aa8cb589e81632&body-plain=XJS%2AC4JDBQADN1.NSBN3%2A2IDNEN%2AGTUBE-STANDARD-ANTI-UBE-TEST-EMAIL%2AC.34X%0D%0A%0D%0A&stripped-html=%3Cp%3EXJS%2AC4JDBQADN1.NSBN3%2A2IDNEN%2AGTUBE-STANDARD-ANTI-UBE-TEST-EMAIL%2AC.34X%3C%2Fp%3E&stripped-text=XJS%2AC4JDBQADN1.NSBN3%2A2IDNEN%2AGTUBE-STANDARD-ANTI-UBE-TEST-EMAIL%2AC.34X&stripped-signature=
|
@@ -0,0 +1,8 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
User-Agent: Python-urllib/2.7
|
3
|
+
Host: requestb.in
|
4
|
+
Content-Type: application/x-www-form-urlencoded
|
5
|
+
Content-Length: 6108
|
6
|
+
Connection: keep-alive
|
7
|
+
|
8
|
+
recipient=foo%2Bbar%40multimail.mailgun.org&sender=james%40opennorth.ca&subject=Test&from=James+McKinney+%3Cjames%40opennorth.ca%3E&Received=by+luna.mailgun.net+with+SMTP+mgrt+8757958553753%3B+Mon%2C+24+Dec+2012+05%3A31%3A14+%2B0000&X-Envelope-From=%3Cjames%40opennorth.ca%3E&Received=from+mail-ia0-f172.google.com+%28mail-ia0-f172.google.com+%5B209.85.210.172%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d7e8a1.59309b0-in1%3B+Mon%2C+24+Dec+2012+05%3A31%3A13+-0000+%28UTC%29&Received=by+mail-ia0-f172.google.com+with+SMTP+id+z13so5721887iaz.3+for+%3Cfoo%2Bbar%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+21%3A31%3A13+-0800+%28PST%29&X-Google-Dkim-Signature=v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Asubject%3Adate%3Amessage-id%3Ato+%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3D6upuK3YAruhelVsnSVxaSLj4p3tANY1XJwtf3oISt3o%3D%3B+b%3DewYc7cjB7NYk%2Bj3pL3xXRm60pVzerzE6P3mX%2FLWa3CmRWbHZlQSs95483ShvqSkRhz+IXVr6AFDA%2BxHu7CcaR5Ge1jn9EKmoa6nTd7DMjHBX4bdnyQQcjdQJAwQeArXcmihNBPU+7pDyPkqMCzqEL2H1CExVODhOkgZYURXwpYDBsQdy7XEgdTRueJIKtgrTyrTuxz%2FupjIC+RaSk24GKJHMvGvLso%2F%2BNPhIbbih1cIXnx7vG2F8NM%2BFrmtHrMlRxs%2B3BLKza4zfHRTIg+mtUjPlJmCa%2BfdnYYUd0yhtgIsx2iODI8Rw%2FqdH76ZKWxC6h%2BGShu%2Br36uX8sI2mIexBY+qrPg%3D%3D&X-Received=by+10.50.33.212+with+SMTP+id+t20mr14433982igi.108.1356327073551%3B+Sun%2C+23+Dec+2012+21%3A31%3A13+-0800+%28PST%29&Return-Path=%3Cjames%40opennorth.ca%3E&Received=from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+c3sm22042054igj.1.2012.12.23.21.31.10+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+21%3A31%3A12+-0800+%28PST%29&From=James+McKinney+%3Cjames%40opennorth.ca%3E&Content-Type=multipart%2Falternative%3B+boundary%3D%22Apple-Mail%3D_062FA552-79D4-4FFA-95A1-647F73FD11A1%22&Subject=Test&Date=Mon%2C+24+Dec+2012+00%3A31%3A08+-0500&Message-Id=%3C79F1A964-ABCE-4131-82F8-0E5073AE1A65%40opennorth.ca%3E&To=foo%2Bbar%40multimail.mailgun.org&Mime-Version=1.0+%28Apple+Message+framework+v1283%29&X-Mailer=Apple+Mail+%282.1283%29&X-Gm-Message-State=ALoCoQm%2FYGSXiuENvTKhfHsnlafwStjOWi4IXZEsxEQWK1k8rjhvh%2BDPhjxtztKZ2agv3AQp5aYn&X-Mailgun-Sflag=No&X-Mailgun-Sscore=-1.9&X-Mailgun-Spf=Pass&message-headers=%5B%5B%22Received%22%2C+%22by+luna.mailgun.net+with+SMTP+mgrt+8757958553753%3B+Mon%2C+24+Dec+2012+05%3A31%3A14+%2B0000%22%5D%2C+%5B%22X-Envelope-From%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+mail-ia0-f172.google.com+%28mail-ia0-f172.google.com+%5B209.85.210.172%5D%29+by+mxa.mailgun.org+with+ESMTP+id+50d7e8a1.59309b0-in1%3B+Mon%2C+24+Dec+2012+05%3A31%3A13+-0000+%28UTC%29%22%5D%2C+%5B%22Received%22%2C+%22by+mail-ia0-f172.google.com+with+SMTP+id+z13so5721887iaz.3+for+%3Cfoo%2Bbar%40multimail.mailgun.org%3E%3B+Sun%2C+23+Dec+2012+21%3A31%3A13+-0800+%28PST%29%22%5D%2C+%5B%22X-Google-Dkim-Signature%22%2C+%22v%3D1%3B+a%3Drsa-sha256%3B+c%3Drelaxed%2Frelaxed%3B+d%3Dgoogle.com%3B+s%3D20120113%3B+h%3Dx-received%3Afrom%3Acontent-type%3Asubject%3Adate%3Amessage-id%3Ato+%3Amime-version%3Ax-mailer%3Ax-gm-message-state%3B+bh%3D6upuK3YAruhelVsnSVxaSLj4p3tANY1XJwtf3oISt3o%3D%3B+b%3DewYc7cjB7NYk%2Bj3pL3xXRm60pVzerzE6P3mX%2FLWa3CmRWbHZlQSs95483ShvqSkRhz+IXVr6AFDA%2BxHu7CcaR5Ge1jn9EKmoa6nTd7DMjHBX4bdnyQQcjdQJAwQeArXcmihNBPU+7pDyPkqMCzqEL2H1CExVODhOkgZYURXwpYDBsQdy7XEgdTRueJIKtgrTyrTuxz%2FupjIC+RaSk24GKJHMvGvLso%2F%2BNPhIbbih1cIXnx7vG2F8NM%2BFrmtHrMlRxs%2B3BLKza4zfHRTIg+mtUjPlJmCa%2BfdnYYUd0yhtgIsx2iODI8Rw%2FqdH76ZKWxC6h%2BGShu%2Br36uX8sI2mIexBY+qrPg%3D%3D%22%5D%2C+%5B%22X-Received%22%2C+%22by+10.50.33.212+with+SMTP+id+t20mr14433982igi.108.1356327073551%3B+Sun%2C+23+Dec+2012+21%3A31%3A13+-0800+%28PST%29%22%5D%2C+%5B%22Return-Path%22%2C+%22%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Received%22%2C+%22from+%5B192.168.0.104%5D+%28%5B69.165.204.186%5D%29+by+mx.google.com+with+ESMTPS+id+c3sm22042054igj.1.2012.12.23.21.31.10+%28version%3DTLSv1%2FSSLv3+cipher%3DOTHER%29%3B+Sun%2C+23+Dec+2012+21%3A31%3A12+-0800+%28PST%29%22%5D%2C+%5B%22From%22%2C+%22James+McKinney+%3Cjames%40opennorth.ca%3E%22%5D%2C+%5B%22Content-Type%22%2C+%22multipart%2Falternative%3B+boundary%3D%5C%22Apple-Mail%3D_062FA552-79D4-4FFA-95A1-647F73FD11A1%5C%22%22%5D%2C+%5B%22Subject%22%2C+%22Test%22%5D%2C+%5B%22Date%22%2C+%22Mon%2C+24+Dec+2012+00%3A31%3A08+-0500%22%5D%2C+%5B%22Message-Id%22%2C+%22%3C79F1A964-ABCE-4131-82F8-0E5073AE1A65%40opennorth.ca%3E%22%5D%2C+%5B%22To%22%2C+%22foo%2Bbar%40multimail.mailgun.org%22%5D%2C+%5B%22Mime-Version%22%2C+%221.0+%28Apple+Message+framework+v1283%29%22%5D%2C+%5B%22X-Mailer%22%2C+%22Apple+Mail+%282.1283%29%22%5D%2C+%5B%22X-Gm-Message-State%22%2C+%22ALoCoQm%2FYGSXiuENvTKhfHsnlafwStjOWi4IXZEsxEQWK1k8rjhvh%2BDPhjxtztKZ2agv3AQp5aYn%22%5D%2C+%5B%22X-Mailgun-Sflag%22%2C+%22No%22%5D%2C+%5B%22X-Mailgun-Sscore%22%2C+%22-1.9%22%5D%2C+%5B%22X-Mailgun-Spf%22%2C+%22Pass%22%5D%5D×tamp=1356327074&token=1u0u68zfxyfyur1cglwzwumutta-fxqfirgbfptaxf3xaq5o41&signature=00b70fb2f2a7008628e0d15bca063f0046bfaa24798fc79b89b48ce765138410&body-plain=bold+text%0D%0A%0D%0A%3E+multiline%0D%0A%3E+quoted%0D%0A%3E+text%0D%0A%0D%0A%0D%0A--%0D%0ASignature+block&body-html=%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody+style%3D%22word-wrap%3A+break-word%3B+-webkit-nbsp-mode%3A+space%3B+-webkit-line-break%3A+after-white-space%3B+%22%3E%3Cb%3Ebold+text%3C%2Fb%3E%3Cdiv%3E%3Cbr%3E%3C%2Fdiv%3E%3Cdiv%3E%3Cblockquote+type%3D%22cite%22%3Emultiline%3C%2Fblockquote%3E%3Cblockquote+type%3D%22cite%22%3Equoted%3C%2Fblockquote%3E%3Cblockquote+type%3D%22cite%22%3Etext%3C%2Fblockquote%3E%3C%2Fdiv%3E%3Cdiv%3E%3Cbr%3E%3C%2Fdiv%3E%3Cdiv%3E--%3C%2Fdiv%3E%3Cdiv%3ESignature+block%3C%2Fdiv%3E%3C%2Fbody%3E%3C%2Fhtml%3E&stripped-html=%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody+style%3D%22word-wrap%3A+break-word%3B+-webkit-nbsp-mode%3A+space%3B+-webkit-line-break%3A+after-white-space%3B+%22%3E%3Cb%3Ebold+text%3C%2Fb%3E%3Cdiv%3E%3Cbr%3E%3C%2Fdiv%3E%3Cdiv%3E%3Cbr%3E%3C%2Fdiv%3E%3Cdiv%3E--%3C%2Fdiv%3E%3Cdiv%3ESignature+block%3C%2Fdiv%3E%3C%2Fbody%3E%3C%2Fhtml%3E&stripped-text=bold+text&stripped-signature=--%0D%0ASignature+block
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require 'multi_mail/mailgun/receiver'
|
3
|
+
|
4
|
+
describe MultiMail::Receiver::Mailgun do
|
5
|
+
describe '#initialize' do
|
6
|
+
it 'should raise an error if :mailgun_api_key is missing' do
|
7
|
+
expect{ MultiMail::Receiver.new :provider => :mailgun }.to raise_error(ArgumentError)
|
8
|
+
expect{ MultiMail::Receiver.new :provider => :mailgun, :mailgun_api_key => nil }.to raise_error(ArgumentError)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'after initialization' do
|
13
|
+
def params(fixture)
|
14
|
+
MultiMail::Receiver::Mailgun.parse(response('mailgun', fixture))
|
15
|
+
end
|
16
|
+
|
17
|
+
before :all do
|
18
|
+
@service = MultiMail::Receiver.new({
|
19
|
+
:provider => :mailgun,
|
20
|
+
:mailgun_api_key => credentials[:mailgun_api_key],
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#valid?' do
|
25
|
+
it 'should return true if the response is valid' do
|
26
|
+
@service.valid?(params('valid')).should == true
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should return false if the response is invalid' do
|
30
|
+
@service.valid?(params('invalid')).should == false
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should raise an error if parameters are missing' do
|
34
|
+
expect{ @service.valid?(params('missing')) }.to raise_error(KeyError)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#transform' do
|
39
|
+
it 'should return a mail message' do
|
40
|
+
message = @service.transform(params('valid'))[0]
|
41
|
+
|
42
|
+
# Headers
|
43
|
+
message.date.should == DateTime.parse('Mon, 24 Dec 2012 00:31:08 -0500')
|
44
|
+
message.from.should == ['james@opennorth.ca']
|
45
|
+
message.to.should == ['foo+bar@multimail.mailgun.org']
|
46
|
+
message.subject.should == 'Test'
|
47
|
+
|
48
|
+
# Body
|
49
|
+
message.multipart?.should == true
|
50
|
+
message.parts.size.should == 2
|
51
|
+
message.parts[0].content_type.should == 'text/plain'
|
52
|
+
message.parts[0].body.should == "bold text\n\n> multiline\n> quoted\n> text\n\n\n--\nSignature block"
|
53
|
+
message.parts[1].content_type.should == 'text/html; charset=UTF-8'
|
54
|
+
message.parts[1].body.should == %(<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><b>bold text</b><div><br></div><div><blockquote type="cite">multiline</blockquote><blockquote type="cite">quoted</blockquote><blockquote type="cite">text</blockquote></div><div><br></div><div>--</div><div>Signature block</div></body></html>)
|
55
|
+
|
56
|
+
# Extra Mailgun parameters
|
57
|
+
message['stripped-text'].value.should == 'bold text'
|
58
|
+
message['stripped-signature'].value.should == "--\r\nSignature block"
|
59
|
+
message['stripped-html'].value.should == '<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><b>bold text</b><div><br></div><div><br></div><div>--</div><div>Signature block</div></body></html>'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#spam?' do
|
64
|
+
it 'should return true if the response is spam' do
|
65
|
+
message = @service.transform(params('spam'))[0]
|
66
|
+
@service.spam?(message).should == true
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should return false if the response is ham' do
|
70
|
+
message = @service.transform(params('valid'))[0]
|
71
|
+
@service.spam?(message).should == false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require 'multi_mail/mandrill/receiver'
|
3
|
+
|
4
|
+
describe MultiMail::Receiver::Mandrill do
|
5
|
+
describe '#initialize' do
|
6
|
+
it 'should raise an error if :mandrill_api_key is missing' do
|
7
|
+
expect{ MultiMail::Receiver.new :provider => :mandrill }.to raise_error(ArgumentError)
|
8
|
+
expect{ MultiMail::Receiver.new :provider => :mandrill, :mandrill_api_key => nil }.to raise_error(ArgumentError)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'after initialization' do
|
13
|
+
def params(fixture)
|
14
|
+
MultiMail::Receiver::Mandrill.parse(response('mandrill', fixture))
|
15
|
+
end
|
16
|
+
|
17
|
+
before :all do
|
18
|
+
@service = MultiMail::Receiver.new({
|
19
|
+
:provider => :mandrill,
|
20
|
+
:mandrill_api_key => credentials[:mandrill_api_key],
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#valid?' do
|
25
|
+
it 'should return true if the response is valid' do
|
26
|
+
@service.valid?(params('valid')).should == true
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should return false if the response is invalid' do
|
30
|
+
@service.valid?(params('invalid')).should == false
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should raise an error if parameters are missing' do
|
34
|
+
expect{ @service.valid?(params('missing')) }.to raise_error(KeyError)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#transform' do
|
39
|
+
it 'should return a mail message' do
|
40
|
+
message = @service.transform(params('valid'))[0]
|
41
|
+
|
42
|
+
# Headers
|
43
|
+
message.date.should == DateTime.parse('Thu, 27 Dec 2012 15:25:37 -0500')
|
44
|
+
message.from.should == ['james@opennorth.ca']
|
45
|
+
message.to.should == ['foo+bar@govkit.org']
|
46
|
+
message.subject.should == 'Test'
|
47
|
+
|
48
|
+
# Body
|
49
|
+
message.multipart?.should == true
|
50
|
+
message.parts.size.should == 2
|
51
|
+
message.parts[0].content_type.should == 'text/plain'
|
52
|
+
message.parts[0].body.should == "bold text\n\n\n> multiline\n> quoted\n> text\n\n--\nSignature block\n"
|
53
|
+
message.parts[1].content_type.should == 'text/html; charset=UTF-8'
|
54
|
+
p message.parts[1].body.decoded
|
55
|
+
message.parts[1].body.should == %(<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><div><b>bold text</b></div><div><br></div><div><blockquote type="cite"></blockquote></div><div><blockquote type="cite">multiline</blockquote></div><blockquote type="cite"><div>quoted</div><div>text</div></blockquote><br><div>--</div><div>Signature block</div></body></html>\n)
|
56
|
+
|
57
|
+
# Extra Mandrill parameters
|
58
|
+
message['email'].value.should == 'foo+bar@govkit.org'
|
59
|
+
message['tags'].should be_nil
|
60
|
+
message['sender'].should be_nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe MultiMail::Receiver::Base do
|
4
|
+
let :klass do
|
5
|
+
Class.new(MultiMail::Service) do
|
6
|
+
include MultiMail::Receiver::Base
|
7
|
+
|
8
|
+
def valid?(params)
|
9
|
+
params['foo'] == 1
|
10
|
+
end
|
11
|
+
|
12
|
+
def transform(params)
|
13
|
+
[Mail.new]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#process' do
|
19
|
+
before :all do
|
20
|
+
@service = klass.new
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should parse the request' do
|
24
|
+
klass.should_receive(:parse).with('foo' => 1).once.and_return('foo' => 1)
|
25
|
+
@service.process('foo' => 1)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should transform the request if the request is valid' do
|
29
|
+
@service.should_receive(:transform).with('foo' => 1).once
|
30
|
+
@service.process('foo' => 1)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'raise an error if the request is invalid' do
|
34
|
+
expect{ @service.process('foo' => 0) }.to raise_error(MultiMail::ForgedRequest)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#parse' do
|
39
|
+
it 'should parse raw POST data' do
|
40
|
+
klass.parse('foo=1&bar=1&bar=1').should == {'foo' => '1', 'bar' => ['1', '1']}
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should pass-through a hash' do
|
44
|
+
klass.parse('foo' => 1).should == {'foo' => 1}
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should raise an error if the argument is invalid' do
|
48
|
+
expect{ klass.parse(1) }.to raise_error(ArgumentError, "Can't handle Fixnum input")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe MultiMail::Receiver do
|
4
|
+
describe '#new' do
|
5
|
+
it 'should not raise an error if the provider is recognized' do
|
6
|
+
expect{ MultiMail::Receiver.new :provider => :mailgun, :mailgun_api_key => 1 }.to_not raise_error
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should raise an error if the provider is not recognized' do
|
10
|
+
expect{ MultiMail::Receiver.new :provider => :foo }.to raise_error(ArgumentError)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/spec/sender_spec.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe MultiMail::Sender do
|
4
|
+
describe '#new' do
|
5
|
+
it 'should not raise an error if the provider is recognized' do
|
6
|
+
expect{ MultiMail::Sender.new :provider => :mailgun, :mailgun_api_key => 1 }.to_not raise_error
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should raise an error if the provider is not recognized' do
|
10
|
+
expect{ MultiMail::Sender.new :provider => :foo }.to raise_error(ArgumentError)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe MultiMail::Service do
|
4
|
+
let :klass do
|
5
|
+
Class.new(MultiMail::Service) do
|
6
|
+
requires :required_argument1, :required_argument2
|
7
|
+
recognizes :optional_argument1, :optional_argument2
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#initialize' do
|
12
|
+
it 'should validate options' do
|
13
|
+
klass.should_receive(:validate_options).with(:required_argument1 => 1, :required_argument2 => 1).once
|
14
|
+
klass.new(:required_argument1 => 1, :required_argument2 => 1)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#requires' do
|
19
|
+
it 'should return required arguments' do
|
20
|
+
klass.requirements.should == [:required_argument1, :required_argument2]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#recognizes' do
|
25
|
+
it 'should return optional arguments' do
|
26
|
+
klass.recognized.should == [:optional_argument1, :optional_argument2]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#validate_options' do
|
31
|
+
it 'should not raise an error if the arguments are valid' do
|
32
|
+
expect{
|
33
|
+
klass.validate_options({
|
34
|
+
:required_argument1 => 1,
|
35
|
+
:required_argument2 => 1,
|
36
|
+
})
|
37
|
+
}.to_not raise_error
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should raise an error if a required argument is missing' do
|
41
|
+
expect{
|
42
|
+
klass.validate_options({
|
43
|
+
:optional_argument1 => 1,
|
44
|
+
:optional_argument2 => 1,
|
45
|
+
})
|
46
|
+
}.to raise_error(ArgumentError, "Missing required arguments: required_argument1, required_argument2")
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should raise an error if an argument is not recognized' do
|
50
|
+
expect{
|
51
|
+
klass.validate_options({
|
52
|
+
:required_argument1 => 1,
|
53
|
+
:required_argument2 => 1,
|
54
|
+
:foo => 1,
|
55
|
+
:bar => 1,
|
56
|
+
})
|
57
|
+
}.to raise_error(ArgumentError, "Unrecognized arguments: foo, bar")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'rspec'
|
6
|
+
require File.dirname(__FILE__) + '/../lib/multi_mail'
|
7
|
+
|
8
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
9
|
+
# in spec/support/ and its subdirectories.
|
10
|
+
Dir[File.expand_path("../support/**/*.rb", __FILE__)].each {|f| require f}
|
11
|
+
|
12
|
+
# @return [Hash] required arguments to initialize services
|
13
|
+
# @todo warn if unable to authenticate with details in api_keys.yml
|
14
|
+
def credentials
|
15
|
+
@credentials ||= YAML.load_file(File.expand_path('../../api_keys.yml', __FILE__))
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param [String] provider a provider
|
19
|
+
# @param [String] fixture one of "valid", "invalid" or "spam"
|
20
|
+
# @return [String] the provider's baked response
|
21
|
+
# @see FakeWeb::Responder#baked_response
|
22
|
+
def response(provider, fixture)
|
23
|
+
io = File.open(File.expand_path("../fixtures/#{provider}/#{fixture}.txt", __FILE__), 'r')
|
24
|
+
socket = Net::BufferedIO.new(io)
|
25
|
+
response = Net::HTTPResponse.read_new(socket)
|
26
|
+
response.reading_body(socket, true) {}
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: multi_mail
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Open North
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-27 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mail
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.4.4
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.4.4
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: multimap
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.1.2
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.1.2
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.10'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.10'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rest-client
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.6.7
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 1.6.7
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: mandrill-api
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.0.12
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.0.12
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rake
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
description:
|
111
|
+
email:
|
112
|
+
- info@opennorth.ca
|
113
|
+
executables: []
|
114
|
+
extensions: []
|
115
|
+
extra_rdoc_files: []
|
116
|
+
files:
|
117
|
+
- .gitignore
|
118
|
+
- .travis.yml
|
119
|
+
- Gemfile
|
120
|
+
- LICENSE
|
121
|
+
- README.md
|
122
|
+
- Rakefile
|
123
|
+
- USAGE
|
124
|
+
- lib/multi_mail.rb
|
125
|
+
- lib/multi_mail/mailgun/receiver.rb
|
126
|
+
- lib/multi_mail/mandrill/receiver.rb
|
127
|
+
- lib/multi_mail/receiver.rb
|
128
|
+
- lib/multi_mail/receiver/base.rb
|
129
|
+
- lib/multi_mail/sender.rb
|
130
|
+
- lib/multi_mail/service.rb
|
131
|
+
- lib/multi_mail/version.rb
|
132
|
+
- multi_mail.gemspec
|
133
|
+
- spec/fixtures/mailgun/invalid.txt
|
134
|
+
- spec/fixtures/mailgun/missing.txt
|
135
|
+
- spec/fixtures/mailgun/spam.txt
|
136
|
+
- spec/fixtures/mailgun/valid.txt
|
137
|
+
- spec/mailgun/receiver_spec.rb
|
138
|
+
- spec/mandrill/receiver_spec.rb
|
139
|
+
- spec/multi_mail_spec.rb
|
140
|
+
- spec/receiver/base_spec.rb
|
141
|
+
- spec/receiver_spec.rb
|
142
|
+
- spec/sender_spec.rb
|
143
|
+
- spec/service_spec.rb
|
144
|
+
- spec/spec_helper.rb
|
145
|
+
homepage: http://github.com/opennorth/multi_mail
|
146
|
+
licenses: []
|
147
|
+
post_install_message:
|
148
|
+
rdoc_options: []
|
149
|
+
require_paths:
|
150
|
+
- lib
|
151
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
152
|
+
none: false
|
153
|
+
requirements:
|
154
|
+
- - ! '>='
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: '0'
|
157
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
158
|
+
none: false
|
159
|
+
requirements:
|
160
|
+
- - ! '>='
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '0'
|
163
|
+
requirements: []
|
164
|
+
rubyforge_project:
|
165
|
+
rubygems_version: 1.8.24
|
166
|
+
signing_key:
|
167
|
+
specification_version: 3
|
168
|
+
summary: Easily switch between email APIs
|
169
|
+
test_files:
|
170
|
+
- spec/fixtures/mailgun/invalid.txt
|
171
|
+
- spec/fixtures/mailgun/missing.txt
|
172
|
+
- spec/fixtures/mailgun/spam.txt
|
173
|
+
- spec/fixtures/mailgun/valid.txt
|
174
|
+
- spec/mailgun/receiver_spec.rb
|
175
|
+
- spec/mandrill/receiver_spec.rb
|
176
|
+
- spec/multi_mail_spec.rb
|
177
|
+
- spec/receiver/base_spec.rb
|
178
|
+
- spec/receiver_spec.rb
|
179
|
+
- spec/sender_spec.rb
|
180
|
+
- spec/service_spec.rb
|
181
|
+
- spec/spec_helper.rb
|
182
|
+
has_rdoc:
|