multi_mail 0.0.2 → 0.1.0

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.
Files changed (60) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +10 -0
  3. data/README.md +283 -59
  4. data/Rakefile +38 -19
  5. data/lib/multi_mail/cloudmailin/receiver.rb +3 -13
  6. data/lib/multi_mail/mailgun/message.rb +67 -0
  7. data/lib/multi_mail/mailgun/receiver.rb +20 -13
  8. data/lib/multi_mail/mailgun/sender.rb +79 -2
  9. data/lib/multi_mail/mandrill/message.rb +74 -0
  10. data/lib/multi_mail/mandrill/receiver.rb +36 -16
  11. data/lib/multi_mail/mandrill/sender.rb +77 -2
  12. data/lib/multi_mail/message/base.rb +40 -0
  13. data/lib/multi_mail/postmark/receiver.rb +1 -1
  14. data/lib/multi_mail/postmark/sender.rb +35 -5
  15. data/lib/multi_mail/receiver/base.rb +31 -2
  16. data/lib/multi_mail/receiver.rb +1 -4
  17. data/lib/multi_mail/sender/base.rb +23 -1
  18. data/lib/multi_mail/sendgrid/message.rb +74 -0
  19. data/lib/multi_mail/sendgrid/receiver.rb +72 -23
  20. data/lib/multi_mail/sendgrid/sender.rb +63 -2
  21. data/lib/multi_mail/service.rb +48 -56
  22. data/lib/multi_mail/simple/receiver.rb +4 -4
  23. data/lib/multi_mail/version.rb +1 -1
  24. data/lib/multi_mail.rb +16 -1
  25. data/multi_mail.gemspec +4 -1
  26. data/spec/fixtures/empty.gif +0 -0
  27. data/spec/fixtures/mailgun/raw/invalid.txt +13 -0
  28. data/spec/fixtures/mailgun/raw/missing.txt +13 -0
  29. data/spec/fixtures/mailgun/raw/spam.txt +13 -0
  30. data/spec/fixtures/mailgun/raw/valid.txt +13 -0
  31. data/spec/fixtures/mandrill/invalid.txt +15 -0
  32. data/spec/fixtures/mandrill/missing.txt +14 -0
  33. data/spec/fixtures/mandrill/multiple.txt +15 -0
  34. data/spec/fixtures/mandrill/valid.txt +10 -5
  35. data/spec/fixtures/postmark/valid.txt +13 -13
  36. data/spec/fixtures/sendgrid/encoding.txt +90 -0
  37. data/spec/fixtures/sendgrid/spam.txt +94 -0
  38. data/spec/fixtures/sendgrid/valid.txt +136 -0
  39. data/spec/mailgun/message_spec.rb +251 -0
  40. data/spec/mailgun/receiver_spec.rb +35 -20
  41. data/spec/mailgun/sender_spec.rb +175 -0
  42. data/spec/mandrill/message_spec.rb +305 -0
  43. data/spec/mandrill/receiver_spec.rb +90 -46
  44. data/spec/mandrill/sender_spec.rb +138 -0
  45. data/spec/message/base_spec.rb +81 -0
  46. data/spec/postmark/receiver_spec.rb +4 -4
  47. data/spec/postmark/sender_spec.rb +118 -0
  48. data/spec/receiver/base_spec.rb +16 -9
  49. data/spec/sender/base_spec.rb +24 -10
  50. data/spec/sendgrid/message_spec.rb +265 -0
  51. data/spec/sendgrid/receiver_spec.rb +77 -0
  52. data/spec/sendgrid/sender_spec.rb +140 -0
  53. data/spec/service_spec.rb +18 -1
  54. data/spec/simple/receiver_spec.rb +1 -1
  55. data/spec/spec_helper.rb +46 -4
  56. metadata +226 -143
  57. data/lib/multi_mail/sender.rb +0 -46
  58. data/lib/multi_mail/simple/sender.rb +0 -14
  59. data/spec/sender_spec.rb +0 -13
  60. data/spec/simple/sender_spec.rb +0 -0
@@ -1,11 +1,10 @@
1
1
  module MultiMail
2
2
  module Receiver
3
3
  # Mailgun's incoming email receiver.
4
- class Mailgun < MultiMail::Service
4
+ class Mailgun
5
5
  include MultiMail::Receiver::Base
6
6
 
7
- requires :mailgun_api_key
8
- recognizes :http_post_format
7
+ recognizes :mailgun_api_key, :http_post_format
9
8
 
10
9
  # Initializes a Mailgun incoming email receiver.
11
10
  #
@@ -25,9 +24,11 @@ module MultiMail
25
24
  # @raise [IndexError] if the request is missing parameters
26
25
  # @see http://documentation.mailgun.net/user_manual.html#securing-webhooks
27
26
  def valid?(params)
28
- params.fetch('signature') == OpenSSL::HMAC.hexdigest(
29
- OpenSSL::Digest::Digest.new('sha256'), @mailgun_api_key,
30
- '%s%s' % [params.fetch('timestamp'), params.fetch('token')])
27
+ if @mailgun_api_key
28
+ params.fetch('signature') == signature(params)
29
+ else
30
+ super
31
+ end
31
32
  end
32
33
 
33
34
  # Transforms the content of Mailgun's webhook into a list of messages.
@@ -39,12 +40,10 @@ module MultiMail
39
40
  def transform(params)
40
41
  case @http_post_format
41
42
  when 'parsed', '', nil
42
- headers = Multimap.new
43
- JSON.parse(params['message-headers']).each do |key,value|
44
- headers[key] = value
45
- end
46
-
43
+ # Mail changes `self`.
44
+ headers = self.class.multimap(JSON.load(params['message-headers']))
47
45
  this = self
46
+
48
47
  message = Mail.new do
49
48
  headers headers
50
49
 
@@ -71,7 +70,7 @@ module MultiMail
71
70
  end
72
71
 
73
72
  if params.key?('attachment-count')
74
- 1.upto(params['attachment-count'].to_i).each do |n|
73
+ 1.upto(params['attachment-count'].to_i) do |n|
75
74
  attachment = params["attachment-#{n}"]
76
75
  add_file(this.class.add_file_arguments(attachment))
77
76
  end
@@ -99,7 +98,8 @@ module MultiMail
99
98
 
100
99
  [message]
101
100
  when 'raw'
102
- [Mail.new(params['body-mime'])]
101
+ message = self.class.condense(Mail.new(params['body-mime']))
102
+ [message]
103
103
  else
104
104
  raise ArgumentError, "Can't handle Mailgun #{@http_post_format} HTTP POST format"
105
105
  end
@@ -117,6 +117,13 @@ module MultiMail
117
117
  def spam?(message)
118
118
  message['X-Mailgun-Sflag'] && message['X-Mailgun-Sflag'].value == 'Yes'
119
119
  end
120
+
121
+ private
122
+
123
+ def signature(params)
124
+ data = "#{params.fetch('timestamp')}#{params.fetch('token')}"
125
+ OpenSSL::HMAC.hexdigest('sha256', @mailgun_api_key, data)
126
+ end
120
127
  end
121
128
  end
122
129
  end
@@ -1,13 +1,90 @@
1
+ require 'multi_mail/mailgun/message'
2
+
1
3
  module MultiMail
2
4
  module Sender
3
- class Mailgun < MultiMail::Service
5
+ # Mailgun's outgoing mail sender.
6
+ class Mailgun
4
7
  include MultiMail::Sender::Base
5
8
 
6
- #requires :
9
+ requires :api_key, :domain
10
+
11
+ attr_reader :api_key, :domain
7
12
 
13
+ # Initializes a Mailgun outgoing email sender.
14
+ #
8
15
  # @param [Hash] options required and optional arguments
16
+ # @option options [String] :api_key a Mailgun API key
9
17
  def initialize(options = {})
10
18
  super
19
+ @api_key = settings.delete(:api_key)
20
+ @domain = settings.delete(:domain)
21
+ end
22
+
23
+ # Returns the additional parameters for the API call.
24
+ #
25
+ # @return [Hash] the additional parameters for the API call
26
+ def parameters
27
+ parameters = settings.dup
28
+ parameters.delete(:return_response)
29
+
30
+ [:opens, :clicks].each do |sym|
31
+ if tracking.key?(sym)
32
+ parameter = :"o:tracking-#{sym}"
33
+ case tracking[sym]
34
+ when 'yes', 'no', 'htmlonly'
35
+ parameters[parameter] = tracking[sym]
36
+ when true
37
+ parameters[parameter] = 'yes'
38
+ when false
39
+ parameters[parameter] = 'no'
40
+ end # ignore nil
41
+ end
42
+ end
43
+
44
+ parameters
45
+ end
46
+
47
+ # Delivers a message via the Mailgun API.
48
+ #
49
+ # @param [Mail::Message] mail a message
50
+ def deliver!(mail)
51
+ message = MultiMail::Message::Mailgun.new(mail).to_mailgun_hash.merge(parameters)
52
+
53
+ connection = Faraday.new do |conn|
54
+ conn.basic_auth 'api', api_key
55
+ conn.request :multipart
56
+ conn.request :url_encoded
57
+ conn.adapter Faraday.default_adapter
58
+ end
59
+
60
+ response = connection.post("https://api.mailgun.net/v2/#{domain}/messages", message)
61
+
62
+ case response.status
63
+ when 401
64
+ raise InvalidAPIKey, response.body
65
+ when 400
66
+ body = JSON.load(response.body)
67
+ case body['message']
68
+ when "'from' parameter is missing"
69
+ raise MissingSender, body['message']
70
+ when "'to' parameter is missing"
71
+ raise MissingRecipients, body['message']
72
+ when "Need at least one of 'text' or 'html' parameters specified"
73
+ raise MissingBody, body['message']
74
+ else
75
+ raise InvalidMessage, body['message']
76
+ end
77
+ when 200
78
+ body = JSON.load(response.body)
79
+ else
80
+ raise response.body
81
+ end
82
+
83
+ if settings[:return_response]
84
+ body
85
+ else
86
+ self
87
+ end
11
88
  end
12
89
  end
13
90
  end
@@ -0,0 +1,74 @@
1
+ module MultiMail
2
+ module Message
3
+ # @see https://mandrillapp.com/api/docs/messages.ruby.html#method-send
4
+ class Mandrill < MultiMail::Message::Base
5
+ # Returns the To header in Mandrill format.
6
+ #
7
+ # @return [Array<Hash>] the To header in Mandrill format
8
+ def mandrill_to
9
+ if to
10
+ to.each_with_index.map do |value,index|
11
+ {
12
+ 'email' => value,
13
+ 'name' => self[:to].display_names[index]
14
+ }
15
+ end
16
+ else
17
+ []
18
+ end
19
+ end
20
+
21
+ # Returns the message headers in Mandrill format.
22
+ #
23
+ # @return [Hash] the message headers in Mandrill format
24
+ def mandrill_headers
25
+ headers = {}
26
+ header_fields.each do |field|
27
+ key = field.name.downcase
28
+ # Mandrill only allows Reply-To and X-* headers currently.
29
+ # https://mandrillapp.com/api/docs/messages.ruby.html
30
+ if key == 'reply-to' || key.start_with?('x-')
31
+ headers[field.name] = field.value
32
+ end
33
+ end
34
+ headers
35
+ end
36
+
37
+ # Returns the message's attachments in Mandrill format.
38
+ #
39
+ # @return [Array<Faraday::UploadIO>] the attachments in Mandrill format
40
+ def mandrill_attachments
41
+ attachments.map do |attachment|
42
+ {
43
+ 'type' => attachment.content_type,
44
+ 'name' => attachment.filename,
45
+ 'content' => Base64.encode64(attachment.body.decoded)
46
+ }
47
+ end
48
+ end
49
+
50
+ # Returns the message as parameters to POST to Mandrill.
51
+ #
52
+ # @return [Hash] the message as parameters to POST to Mandrill
53
+ def to_mandrill_hash
54
+ images, attachments = mandrill_attachments.partition do |attachment|
55
+ attachment['type'].start_with?('image/')
56
+ end
57
+
58
+ hash = {
59
+ 'html' => body_html,
60
+ 'text' => body_text,
61
+ 'subject' => subject,
62
+ 'from_email' => from && from.first,
63
+ 'from_name' => from && self[:from].display_names.first,
64
+ 'to' => mandrill_to,
65
+ 'headers' => mandrill_headers,
66
+ 'attachments' => attachments,
67
+ 'images' => images,
68
+ }
69
+
70
+ normalize(hash)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -5,10 +5,10 @@ module MultiMail
5
5
  # Mandrill uses an HTTP header to ensure a request originates from Mandrill.
6
6
  #
7
7
  # @see http://help.mandrill.com/entries/23704122-Authenticating-webhook-requests
8
- class Mandrill < MultiMail::Service
8
+ class Mandrill
9
9
  include MultiMail::Receiver::Base
10
10
 
11
- recognizes :spamassassin_threshold
11
+ recognizes :spamassassin_threshold, :mandrill_webhook_key, :mandrill_webhook_url
12
12
 
13
13
  # Initializes a Mandrill incoming email receiver.
14
14
  #
@@ -18,6 +18,22 @@ module MultiMail
18
18
  def initialize(options = {})
19
19
  super
20
20
  @spamassassin_threshold = options[:spamassassin_threshold] || 5
21
+ @mandrill_webhook_key = options[:mandrill_webhook_key]
22
+ @mandrill_webhook_url = options[:mandrill_webhook_url]
23
+ end
24
+
25
+ # Returns whether a request originates from Mandrill.
26
+ #
27
+ # @param [Hash] params the content of Mandrill's webhook
28
+ # @return [Boolean] whether the request originates from Mailgun
29
+ # @raise [IndexError] if the request is missing parameters
30
+ # @see http://help.mandrill.com/entries/23704122-Authenticating-webhook-requests
31
+ def valid?(params)
32
+ if @mandrill_webhook_url && @mandrill_webhook_key
33
+ params.fetch('env').fetch('HTTP_X_MANDRILL_SIGNATURE') == signature(params)
34
+ else
35
+ super
36
+ end
21
37
  end
22
38
 
23
39
  # Transforms the content of Mandrill's webhook into a list of messages.
@@ -27,23 +43,15 @@ module MultiMail
27
43
  # @see http://help.mandrill.com/entries/22092308-What-is-the-format-of-inbound-email-webhooks-
28
44
  def transform(params)
29
45
  # JSON is necessarily UTF-8.
30
- JSON.parse(params['mandrill_events']).select do |event|
46
+ JSON.load(params['mandrill_events']).select do |event|
31
47
  event.fetch('event') == 'inbound'
32
48
  end.map do |event|
33
49
  msg = event['msg']
34
50
 
35
- headers = Multimap.new
36
- msg['headers'].each do |key,value|
37
- if Array === value
38
- value.each do |v|
39
- headers[key] = v
40
- end
41
- else
42
- headers[key] = value
43
- end
44
- end
45
-
51
+ # Mail changes `self`.
52
+ headers = self.class.multimap(msg['headers'])
46
53
  this = self
54
+
47
55
  message = Mail.new do
48
56
  headers headers
49
57
 
@@ -88,8 +96,8 @@ module MultiMail
88
96
 
89
97
  # Extra Mandrill parameters. Discard `sender` and `tags`, which are
90
98
  # null according to the docs, `matched_rules` within `spam_report`,
91
- # `detail` within `spf`, which is just a human-readable version of
92
- # `result`, and `raw_msg`.
99
+ # and `detail` within `spf`, which is just a human-readable version of
100
+ # `result`.
93
101
  message['ts'] = event['ts']
94
102
  message['email'] = msg['email']
95
103
  message['dkim-signed'] = msg['dkim']['signed'].to_s
@@ -108,6 +116,18 @@ module MultiMail
108
116
  def spam?(message)
109
117
  message['spam_report-score'] && message['spam_report-score'].value.to_f > @spamassassin_threshold
110
118
  end
119
+
120
+ private
121
+
122
+ def signature(params)
123
+ data = @mandrill_webhook_url
124
+ params.sort.each do |key,value|
125
+ unless key == 'env'
126
+ data += "#{key}#{value}"
127
+ end
128
+ end
129
+ Base64.encode64(OpenSSL::HMAC.digest('sha1', @mandrill_webhook_key, data)).strip
130
+ end
111
131
  end
112
132
  end
113
133
  end
@@ -1,13 +1,88 @@
1
+ require 'multi_mail/mandrill/message'
2
+
1
3
  module MultiMail
2
4
  module Sender
3
- class Mandrill < MultiMail::Service
5
+ # Mandrill's outgoing mail sender.
6
+ class Mandrill
4
7
  include MultiMail::Sender::Base
5
8
 
6
- #requires :
9
+ requires :api_key
10
+
11
+ attr_reader :api_key, :async, :ip_pool, :send_at
7
12
 
13
+ # Initializes a Mandrill outgoing email sender.
14
+ #
8
15
  # @param [Hash] options required and optional arguments
16
+ # @option options [String] :api_key a Mandrill API key
17
+ # @see https://mandrillapp.com/api/docs/index.ruby.html
9
18
  def initialize(options = {})
10
19
  super
20
+ @api_key = settings.delete(:api_key)
21
+ @async = settings.delete(:async) || false
22
+ @ip_pool = settings.delete(:ip_pool)
23
+ @send_at = settings.delete(:send_at)
24
+ end
25
+
26
+ # Returns the additional parameters for the API call.
27
+ #
28
+ # @return [Hash] the additional parameters for the API call
29
+ def parameters
30
+ parameters = settings.dup
31
+ parameters.delete(:return_response)
32
+
33
+ [:opens, :clicks].each do |sym|
34
+ if tracking.key?(sym)
35
+ parameter = :"track_#{sym}"
36
+ case tracking[sym]
37
+ when true, false, nil
38
+ parameters[parameter] = tracking[sym]
39
+ when 'yes'
40
+ parameters[parameter] = true
41
+ when 'no'
42
+ parameters[parameter] = false
43
+ end # ignore "htmlonly"
44
+ end
45
+ end
46
+
47
+ parameters
48
+ end
49
+
50
+ # Delivers a message via the Mandrill API.
51
+ #
52
+ # @param [Mail::Message] mail a message
53
+ # @see https://bitbucket.org/mailchimp/mandrill-api-ruby/src/d0950a6f9c4fac1dd2d5198a4f72c12c626ab149/lib/mandrill/api.rb?at=master#cl-738
54
+ # @see https://bitbucket.org/mailchimp/mandrill-api-ruby/src/d0950a6f9c4fac1dd2d5198a4f72c12c626ab149/lib/mandrill.rb?at=master#cl-32
55
+ def deliver!(mail)
56
+ message = MultiMail::Message::Mandrill.new(mail).to_mandrill_hash.merge(parameters)
57
+
58
+ response = Faraday.post('https://mandrillapp.com/api/1.0/messages/send.json', JSON.dump({
59
+ :key => api_key,
60
+ :message => message,
61
+ :async => async,
62
+ :ip_pool => ip_pool,
63
+ :send_at => send_at,
64
+ }))
65
+
66
+ body = JSON.load(response.body)
67
+
68
+ unless response.status == 200
69
+ if body['status'] == 'error'
70
+ case body['name']
71
+ when 'Invalid_Key'
72
+ raise InvalidAPIKey, body['message']
73
+ else
74
+ raise body['message']
75
+ end
76
+ else
77
+ raise body['message']
78
+ end
79
+ end
80
+
81
+ if settings[:return_response]
82
+ body
83
+ else
84
+ self
85
+ end
11
86
  end
12
87
  end
13
88
  end
@@ -0,0 +1,40 @@
1
+ module MultiMail
2
+ module Message
3
+ class Base < Mail::Message
4
+ # @see https://github.com/wildbit/postmark-gem/blob/master/lib/postmark/message_extensions/mail.rb
5
+ def html?
6
+ !!content_type && content_type.include?('text/html')
7
+ end
8
+
9
+ def body_html
10
+ if html_part
11
+ html_part.body.decoded
12
+ elsif html?
13
+ body.decoded
14
+ end
15
+ end
16
+
17
+ def body_text
18
+ if text_part
19
+ text_part.body.decoded
20
+ elsif !html?
21
+ body.decoded
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def normalize(hash)
28
+ hash.delete_if do |_,value|
29
+ value.nil? || value.empty? || Array === value && value.all?{|v| v.nil?}
30
+ end
31
+
32
+ hash.keys.each do |key| # based on Hash#symbolize_keys! from Rails
33
+ hash[(key.to_sym rescue key) || key] = hash.delete(key)
34
+ end
35
+
36
+ hash
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,7 +1,7 @@
1
1
  module MultiMail
2
2
  module Receiver
3
3
  # Postmark's incoming email receiver.
4
- class Postmark < MultiMail::Service
4
+ class Postmark
5
5
  include MultiMail::Receiver::Base
6
6
 
7
7
  def transform(params)
@@ -1,13 +1,43 @@
1
+ begin
2
+ require 'postmark'
3
+ rescue LoadError
4
+ raise 'The postmark gem is not available. In order to use the Postmark sender, you must: gem install postmark'
5
+ end
6
+
1
7
  module MultiMail
2
8
  module Sender
3
- class Postmark < MultiMail::Service
9
+ # Postmark's outgoing mail sender.
10
+ class Postmark
4
11
  include MultiMail::Sender::Base
5
12
 
6
- #requires :
13
+ # @see https://github.com/wildbit/postmark-gem#communicating-with-the-api
14
+ requires :api_key
15
+
16
+ # Delivers a message via the Postmark API.
17
+ #
18
+ # @param [Mail::Message] mail a message
19
+ # @see https://github.com/wildbit/postmark-gem#using-postmark-with-the-mail-library
20
+ def deliver!(mail)
21
+ mail.delivery_method Mail::Postmark, settings
7
22
 
8
- # @param [Hash] options required and optional arguments
9
- def initialize(options = {})
10
- super
23
+ if settings[:return_response]
24
+ mail.deliver!
25
+ else
26
+ mail.deliver
27
+ end
28
+ rescue ::Postmark::InvalidApiKeyError => e
29
+ raise InvalidAPIKey, e.message
30
+ rescue ::Postmark::InvalidMessageError => e
31
+ case e.message
32
+ when "Invalid 'From' value."
33
+ raise MissingSender, e.message
34
+ when 'Zero recipients specified'
35
+ raise MissingRecipients, e.message
36
+ when 'Provide either email TextBody or HtmlBody or both.'
37
+ raise MissingBody, e.message
38
+ else
39
+ raise InvalidMessage, e.message
40
+ end
11
41
  end
12
42
  end
13
43
  end
@@ -7,10 +7,18 @@ module MultiMail
7
7
  module Base
8
8
  def self.included(subclass)
9
9
  subclass.class_eval do
10
+ extend MultiMail::Service
10
11
  extend MultiMail::Receiver::Base::ClassMethods
11
12
  end
12
13
  end
13
14
 
15
+ # Initializes an incoming email receiver.
16
+ #
17
+ # @param [Hash] options required and optional arguments
18
+ def initialize(options = {})
19
+ self.class.validate_options(options)
20
+ end
21
+
14
22
  # Ensures a request is authentic, parses it into a params hash, and
15
23
  # transforms it into a list of messages.
16
24
  #
@@ -66,6 +74,24 @@ module MultiMail
66
74
  end
67
75
  end
68
76
 
77
+ # Converts a hash or array to a multimap.
78
+ #
79
+ # @param [Hash,Array] object a hash or array
80
+ # @return [Multimap] a multimap
81
+ def multimap(object)
82
+ multimap = Multimap.new
83
+ object.each do |key,value|
84
+ if Array === value
85
+ value.each do |v|
86
+ multimap[key] = v
87
+ end
88
+ else
89
+ multimap[key] = value
90
+ end
91
+ end
92
+ multimap
93
+ end
94
+
69
95
  # Parses raw POST data into a params hash.
70
96
  #
71
97
  # @param [String,Hash] raw raw POST data or a params hash
@@ -74,7 +100,7 @@ module MultiMail
74
100
  case raw
75
101
  when String
76
102
  begin
77
- JSON.parse(raw)
103
+ JSON.load(raw)
78
104
  rescue JSON::ParserError
79
105
  params = CGI.parse(raw)
80
106
 
@@ -107,7 +133,10 @@ module MultiMail
107
133
 
108
134
  params
109
135
  when Rack::Request
110
- raw.params
136
+ env = raw.env.dup
137
+ env.delete('rack.input')
138
+ env.delete('rack.errors')
139
+ {'env' => env}.merge(raw.params)
111
140
  when Hash
112
141
  raw
113
142
  else
@@ -14,7 +14,7 @@ module MultiMail
14
14
  #
15
15
  # @param [Hash] attributes required arguments
16
16
  # @option attributes [String,Symbol] :provider a provider
17
- # @return [MultiMail::Service] an incoming email receiver
17
+ # @return an incoming email receiver
18
18
  # @raise [ArgumentError] if the provider does not exist
19
19
  # @see Fog::Storage::new
20
20
  def self.new(attributes)
@@ -38,9 +38,6 @@ module MultiMail
38
38
  when :simple
39
39
  require 'multi_mail/simple/receiver'
40
40
  MultiMail::Receiver::Simple.new(attributes)
41
- when :mock
42
- # for testing
43
- MultiMail::Receiver::Mock.new(attributes)
44
41
  else
45
42
  raise ArgumentError.new("#{provider} is not a recognized provider")
46
43
  end
@@ -1,7 +1,29 @@
1
1
  module MultiMail
2
2
  module Sender
3
- # Abstract class for outgoing email services.
3
+ # Abstract class for outgoing email senders.
4
4
  module Base
5
+ def self.included(subclass)
6
+ subclass.class_eval do
7
+ extend MultiMail::Service
8
+ end
9
+ end
10
+
11
+ attr_reader :settings, :tracking
12
+
13
+ # Initializes an outgoing email sender.
14
+ #
15
+ # @param [Hash] options required and optional arguments
16
+ def initialize(options = {})
17
+ @settings = {}
18
+
19
+ options.keys.each do |key| # based on Hash#symbolize_keys! from Rails
20
+ settings[(key.to_sym rescue key) || key] = options[key]
21
+ end
22
+
23
+ self.class.validate_options(settings, false)
24
+
25
+ @tracking = settings.delete(:track) || {}
26
+ end
5
27
  end
6
28
  end
7
29
  end