multi_mail 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Dependency Status](https://gemnasium.com/opennorth/multi_mail.png)](https://gemnasium.com/opennorth/multi_mail)
|
4
|
+
[![Code Climate](https://codeclimate.com/badge.png)](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:
|