courrier 0.10.0 → 1.0.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +16 -24
  4. data/README.md +173 -121
  5. data/courrier.gemspec +4 -4
  6. data/lib/courrier/configuration.rb +2 -28
  7. data/lib/courrier/email/provider.rb +12 -4
  8. data/lib/courrier/email/providers/base.rb +2 -0
  9. data/lib/courrier/email/providers/brevo.rb +36 -0
  10. data/lib/courrier/email/providers/cloudflare.rb +37 -0
  11. data/lib/courrier/email/providers/lettermint.rb +33 -0
  12. data/lib/courrier/email/providers/loops.rb +2 -0
  13. data/lib/courrier/email/providers/mailgun.rb +2 -0
  14. data/lib/courrier/email/providers/mailjet.rb +2 -0
  15. data/lib/courrier/email/providers/postmark.rb +2 -0
  16. data/lib/courrier/email/providers/ses.rb +77 -0
  17. data/lib/courrier/email/providers/smtp2go.rb +29 -0
  18. data/lib/courrier/email/providers/userlist.rb +2 -0
  19. data/lib/courrier/email/request.rb +1 -1
  20. data/lib/courrier/email/transformer.rb +9 -9
  21. data/lib/courrier/email.rb +30 -37
  22. data/lib/courrier/errors.rb +0 -2
  23. data/lib/courrier/subscriber/brevo.rb +39 -0
  24. data/lib/courrier/test.rb +38 -0
  25. data/lib/courrier/test_helper.rb +65 -0
  26. data/lib/courrier/version.rb +1 -1
  27. data/lib/courrier.rb +2 -2
  28. metadata +19 -31
  29. data/app/controllers/courrier/previews/cleanups_controller.rb +0 -13
  30. data/app/controllers/courrier/previews_controller.rb +0 -23
  31. data/app/views/courrier/previews/index.html.erb +0 -171
  32. data/config/routes.rb +0 -8
  33. data/lib/courrier/configuration/inbox.rb +0 -21
  34. data/lib/courrier/email/providers/inbox/default.html.erb +0 -126
  35. data/lib/courrier/email/providers/inbox.rb +0 -83
  36. data/lib/courrier/engine.rb +0 -7
  37. data/lib/courrier/jobs/email_delivery_job.rb +0 -23
  38. data/lib/courrier/railtie.rb +0 -23
  39. data/lib/courrier/tasks/courrier.rake +0 -13
  40. data/lib/generators/courrier/email_generator.rb +0 -42
  41. data/lib/generators/courrier/install_generator.rb +0 -11
  42. data/lib/generators/courrier/templates/email/password_reset.rb.tt +0 -29
  43. data/lib/generators/courrier/templates/email/welcome.rb.tt +0 -43
  44. data/lib/generators/courrier/templates/email.rb.tt +0 -13
  45. data/lib/generators/courrier/templates/initializer.rb.tt +0 -43
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "courrier/email/providers/base"
4
- require "courrier/email/providers/inbox"
4
+ require "courrier/email/providers/brevo"
5
+ require "courrier/email/providers/cloudflare"
6
+ require "courrier/email/providers/lettermint"
5
7
  require "courrier/email/providers/logger"
6
8
  require "courrier/email/providers/loops"
7
9
  require "courrier/email/providers/mailgun"
@@ -9,7 +11,9 @@ require "courrier/email/providers/mailjet"
9
11
  require "courrier/email/providers/mailpace"
10
12
  require "courrier/email/providers/postmark"
11
13
  require "courrier/email/providers/resend"
14
+ require "courrier/email/providers/ses"
12
15
  require "courrier/email/providers/sendgrid"
16
+ require "courrier/email/providers/smtp2go"
13
17
  require "courrier/email/providers/sparkpost"
14
18
  require "courrier/email/providers/userlist"
15
19
 
@@ -17,15 +21,19 @@ module Courrier
17
21
  class Email
18
22
  class Provider
19
23
  PROVIDERS = {
20
- inbox: Courrier::Email::Providers::Inbox,
24
+ brevo: Courrier::Email::Providers::Brevo,
25
+ cloudflare: Courrier::Email::Providers::Cloudflare,
21
26
  logger: Courrier::Email::Providers::Logger,
27
+ lettermint: Courrier::Email::Providers::Lettermint,
22
28
  loops: Courrier::Email::Providers::Loops,
23
29
  mailgun: Courrier::Email::Providers::Mailgun,
24
30
  mailjet: Courrier::Email::Providers::Mailjet,
25
31
  mailpace: Courrier::Email::Providers::Mailpace,
26
32
  postmark: Courrier::Email::Providers::Postmark,
27
33
  resend: Courrier::Email::Providers::Resend,
34
+ ses: Courrier::Email::Providers::Ses,
28
35
  sendgrid: Courrier::Email::Providers::Sendgrid,
36
+ smtp2go: Courrier::Email::Providers::Smtp2go,
29
37
  sparkpost: Courrier::Email::Providers::Sparkpost,
30
38
  userlist: Courrier::Email::Providers::Userlist
31
39
  }
@@ -72,7 +80,7 @@ module Courrier
72
80
  end
73
81
 
74
82
  def api_key_required_providers?
75
- !%w[logger inbox].include?(@provider.to_s)
83
+ !%w[logger].include?(@provider.to_s)
76
84
  end
77
85
 
78
86
  def api_key_blank?
@@ -80,7 +88,7 @@ module Courrier
80
88
  end
81
89
 
82
90
  def production?
83
- defined?(Rails) && Rails.env.production?
91
+ ENV["RACK_ENV"] == "production"
84
92
  end
85
93
  end
86
94
  end
@@ -6,6 +6,8 @@ module Courrier
6
6
  class Email
7
7
  module Providers
8
8
  class Base
9
+ def self.config_options = []
10
+
9
11
  def initialize(api_key: nil, options: {}, provider_options: {}, context_options: {}, custom_headers: {})
10
12
  @api_key = api_key
11
13
  @options = options
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Courrier
4
+ class Email
5
+ module Providers
6
+ class Brevo < Base
7
+ ENDPOINT_URL = "https://api.brevo.com/v3/smtp/email"
8
+
9
+ def body
10
+ {
11
+ "sender" => email_address(@options.from),
12
+ "to" => email_addresses(@options.to),
13
+ "cc" => email_addresses(@options.cc),
14
+ "bcc" => email_addresses(@options.bcc),
15
+ "replyTo" => email_address(@options.reply_to),
16
+ "subject" => @options.subject,
17
+ "htmlContent" => @options.html,
18
+ "textContent" => @options.text
19
+ }.compact
20
+ end
21
+
22
+ private
23
+
24
+ def default_headers = {"api-key" => @api_key}
25
+
26
+ def email_addresses(addresses)
27
+ addresses&.split(",")&.map { |address| email_address(address) }
28
+ end
29
+
30
+ def email_address(address)
31
+ {"email" => address.strip} if address
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Courrier
4
+ class Email
5
+ module Providers
6
+ class Cloudflare < Base
7
+ def self.config_options = %w[account_id]
8
+
9
+ def body
10
+ {
11
+ "from" => @options.from,
12
+ "to" => @options.to,
13
+ "reply_to" => @options.reply_to,
14
+ "cc" => @options.cc,
15
+ "bcc" => @options.bcc,
16
+ "subject" => @options.subject,
17
+ "text" => @options.text,
18
+ "html" => @options.html
19
+ }.compact
20
+ end
21
+
22
+ private
23
+
24
+ def endpoint_url
25
+ account_id = @provider_options.account_id
26
+ raise Courrier::ArgumentError, "Cloudflare requires an `account_id`" unless account_id
27
+
28
+ "https://api.cloudflare.com/client/v4/accounts/#{account_id}/email/sending/send"
29
+ end
30
+
31
+ def default_headers
32
+ {"Authorization" => "Bearer #{@api_key}"}
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Courrier
4
+ class Email
5
+ module Providers
6
+ class Lettermint < Base
7
+ def self.config_options = %w[route]
8
+
9
+ ENDPOINT_URL = "https://api.lettermint.co/v1/send"
10
+
11
+ def body
12
+ {
13
+ "route" => @provider_options.route,
14
+ "from" => @options.from,
15
+ "to" => @options.to.to_s.split(",").map(&:strip),
16
+ "cc" => @options.cc&.split(",")&.map(&:strip),
17
+ "bcc" => @options.bcc&.split(",")&.map(&:strip),
18
+ "reply_to" => @options.reply_to&.split(",")&.map(&:strip),
19
+ "subject" => @options.subject,
20
+ "html" => @options.html,
21
+ "text" => @options.text
22
+ }.compact
23
+ end
24
+
25
+ private
26
+
27
+ def default_headers
28
+ {"x-lettermint-token" => @api_key}
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -4,6 +4,8 @@ module Courrier
4
4
  class Email
5
5
  module Providers
6
6
  class Loops < Base
7
+ def self.config_options = %w[transactional_id]
8
+
7
9
  ENDPOINT_URL = "https://app.loops.so/api/v1/transactional"
8
10
 
9
11
  def body
@@ -4,6 +4,8 @@ module Courrier
4
4
  class Email
5
5
  module Providers
6
6
  class Mailgun < Base
7
+ def self.config_options = %w[domain]
8
+
7
9
  ENDPOINT_URL = "https://api.mailgun.net/v3/%{domain}/messages"
8
10
 
9
11
  def body
@@ -4,6 +4,8 @@ module Courrier
4
4
  class Email
5
5
  module Providers
6
6
  class Mailjet < Base
7
+ def self.config_options = %w[api_secret]
8
+
7
9
  ENDPOINT_URL = "https://api.mailjet.com/v3.1/send"
8
10
 
9
11
  def body
@@ -4,6 +4,8 @@ module Courrier
4
4
  class Email
5
5
  module Providers
6
6
  class Postmark < Base
7
+ def self.config_options = %w[message_stream]
8
+
7
9
  ENDPOINT_URL = "https://api.postmarkapp.com/email"
8
10
 
9
11
  def body
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Courrier
4
+ class Email
5
+ module Providers
6
+ class Ses < Base
7
+ def self.config_options = %w[region access_key_id secret_access_key session_token]
8
+
9
+ def deliver
10
+ uri = URI.parse("https://email.#{@provider_options[:region]}.amazonaws.com/v2/email/outbound-emails")
11
+
12
+ request = Net::HTTP::Post.new(uri)
13
+ default_headers.merge(@custom_headers).each { |name, value| request[name] = value }
14
+
15
+ sign!(request)
16
+
17
+ request.body = JSON.dump(body)
18
+
19
+ options = {use_ssl: uri.scheme == "https"}
20
+ response = Net::HTTP.start(uri.hostname, uri.port, options) { it.request(request) }
21
+
22
+ Courrier::Email::Result.new(response: response)
23
+ rescue => error
24
+ Courrier::Email::Result.new(error: error)
25
+ end
26
+
27
+ def body
28
+ {
29
+ "FromEmailAddress" => @options.from,
30
+ "Destination" => {
31
+ "ToAddresses" => Array(@options.to),
32
+ "CcAddresses" => @options.cc ? Array(@options.cc) : nil,
33
+ "BccAddresses" => @options.bcc ? Array(@options.bcc) : nil
34
+ }.compact,
35
+
36
+ "ReplyToAddresses" => @options.reply_to ? Array(@options.reply_to) : nil,
37
+ "Content" => {
38
+ "Simple" => {
39
+ "Subject" => {"Data" => @options.subject},
40
+
41
+ "Body" => {
42
+ "Text" => {"Data" => @options.text},
43
+ "Html" => {"Data" => @options.html}
44
+ }.compact
45
+ }
46
+ }
47
+ }.compact
48
+ end
49
+
50
+ private
51
+
52
+ def default_headers
53
+ {
54
+ "Content-Type" => "application/json",
55
+ "Accept" => "application/json"
56
+ }
57
+ end
58
+
59
+ def sign!(request)
60
+ require "aws-sigv4"
61
+
62
+ Aws::Sigv4::Signer.new(
63
+ service: "ses",
64
+ region: @provider_options[:region],
65
+ access_key_id: @provider_options[:access_key_id],
66
+ secret_access_key: @provider_options[:secret_access_key],
67
+ session_token: @provider_options[:session_token]
68
+ ).sign_request(
69
+ http_method: request.method,
70
+ url: request.uri.to_s,
71
+ headers: request.to_hash
72
+ ).headers.each { |name, value| request[name] = value }
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Courrier
4
+ class Email
5
+ module Providers
6
+ class Smtp2go < Base
7
+ ENDPOINT_URL = "https://api.smtp2go.com/v3/email/send"
8
+
9
+ def body
10
+ {
11
+ "sender" => @options.from,
12
+ "to" => @options.to.to_s.split(",").map(&:strip),
13
+ "cc" => @options.cc&.split(",")&.map(&:strip),
14
+ "bcc" => @options.bcc&.split(",")&.map(&:strip),
15
+ "subject" => @options.subject,
16
+ "html_body" => @options.html,
17
+ "text_body" => @options.text
18
+ }.compact
19
+ end
20
+
21
+ private
22
+
23
+ def default_headers
24
+ {"X-Smtp2go-Api-Key" => @api_key}
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -4,6 +4,8 @@ module Courrier
4
4
  class Email
5
5
  module Providers
6
6
  class Userlist < Base
7
+ def self.config_options = %w[theme]
8
+
7
9
  ENDPOINT_URL = "https://push.userlist.com/messages"
8
10
 
9
11
  def body
@@ -25,7 +25,7 @@ module Courrier
25
25
  options = {use_ssl: uri.scheme == "https"}
26
26
 
27
27
  begin
28
- response = Net::HTTP.start(uri.hostname, uri.port, options) { _1.request(request) }
28
+ response = Net::HTTP.start(uri.hostname, uri.port, options) { it.request(request) }
29
29
 
30
30
  Result.new(response: response)
31
31
  rescue => error
@@ -11,30 +11,30 @@ module Courrier
11
11
 
12
12
  def to_text
13
13
  Nokogiri::HTML(@content)
14
- .then { remove_unwanted_elements(_1) }
15
- .then { process_links(_1) }
16
- .then { preserve_line_breaks(_1) }
17
- .then { clean_up(_1) }
14
+ .then { remove_unwanted_elements(it) }
15
+ .then { process_links(it) }
16
+ .then { preserve_line_breaks(it) }
17
+ .then { clean_up(it) }
18
18
  end
19
19
 
20
20
  private
21
21
 
22
22
  BLOCK_ELEMENTS = %w[p div h1 h2 h3 h4 h5 h6 pre blockquote li]
23
23
 
24
- def remove_unwanted_elements(html) = html.tap { _1.css("script, style").remove }
24
+ def remove_unwanted_elements(html) = html.tap { it.css("script, style").remove }
25
25
 
26
26
  def process_links(html)
27
27
  html.tap do |document|
28
28
  document.css("a")
29
- .select { valid?(_1) }
30
- .reject { _1.text.strip.empty? || _1.text.strip == _1["href"] }
31
- .each { _1.content = "#{_1.text.strip} (#{_1["href"]})" }
29
+ .select { valid?(it) }
30
+ .reject { it.text.strip.empty? || it.text.strip == it["href"] }
31
+ .each { it.content = "#{it.text.strip} (#{it["href"]})" }
32
32
  end
33
33
  end
34
34
 
35
35
  def preserve_line_breaks(html)
36
36
  html.tap do |document|
37
- document.css(BLOCK_ELEMENTS.join(",")).each { _1.after("\n") }
37
+ document.css(BLOCK_ELEMENTS.join(",")).each { it.after("\n") }
38
38
  end
39
39
  end
40
40
 
@@ -3,7 +3,6 @@
3
3
  require "erb"
4
4
 
5
5
  require "courrier/email/address"
6
- require "courrier/jobs/email_delivery_job" if defined?(Rails)
7
6
  require "courrier/email/layouts"
8
7
  require "courrier/markdown"
9
8
  require "courrier/email/options"
@@ -52,29 +51,41 @@ module Courrier
52
51
  self.layouts = options
53
52
  end
54
53
 
54
+ def before_deliver(&block)
55
+ (@before_deliver ||= []) << block
56
+ end
57
+
58
+ def after_deliver(&block)
59
+ (@after_deliver ||= []) << block
60
+ end
61
+
62
+ def before_deliver_callbacks
63
+ return @before_deliver if @before_deliver
64
+ return superclass.before_deliver_callbacks if superclass.respond_to?(:before_deliver_callbacks)
65
+
66
+ []
67
+ end
68
+
69
+ def after_deliver_callbacks
70
+ return @after_deliver if @after_deliver
71
+ return superclass.after_deliver_callbacks if superclass.respond_to?(:after_deliver_callbacks)
72
+
73
+ []
74
+ end
75
+
55
76
  def deliver(**options)
56
77
  new(options).deliver_now
57
78
  end
58
79
  alias_method :deliver_now, :deliver
59
80
 
60
- def deliver_later(**options)
61
- new(options).deliver_later
62
- end
63
-
64
81
  def inherited(subclass)
65
82
  super
66
-
67
- # If you read this and know how to move this Rails-specific logic somewhere
68
- # else, e.g. `lib/courrier/railtie.rb`, open a PR ❤️
69
- if defined?(Rails) && Rails.application
70
- subclass.include Rails.application.routes.url_helpers
71
- end
72
83
  end
73
84
  end
74
85
 
75
86
  def initialize(options = {})
76
- @provider = options[:provider] || ENV["COURRIER_PROVIDER"] || self.class.provider || Courrier.configuration&.email&.[](:provider)
77
- @api_key = options[:api_key] || ENV["COURRIER_API_KEY"] || self.class.api_key || Courrier.configuration&.email&.[](:api_key)
87
+ @provider = options[:provider] || self.class.provider || Courrier.configuration&.email&.[](:provider)
88
+ @api_key = options[:api_key] || self.class.api_key || Courrier.configuration&.email&.[](:api_key)
78
89
 
79
90
  @default_url_options = self.class.default_url_options.merge(options[:default_url_options] || {})
80
91
  @context_options = options.except(:provider, :api_key, :from, :to, :reply_to, :cc, :bcc, :subject, :text, :html)
@@ -100,6 +111,8 @@ module Courrier
100
111
  return nil
101
112
  end
102
113
 
114
+ return nil if self.class.before_deliver_callbacks.any? { |callback| callback.call(self) == false }
115
+
103
116
  Provider.new(
104
117
  provider: @provider,
105
118
  api_key: @api_key,
@@ -107,33 +120,13 @@ module Courrier
107
120
  provider_options: Courrier.configuration&.providers&.[](@provider.to_s.downcase.to_sym),
108
121
  context_options: @context_options,
109
122
  custom_headers: self.class.headers
110
- ).deliver
111
- end
112
- alias_method :deliver_now, :deliver
123
+ ).deliver.tap do |result|
124
+ Test.record(self, result)
113
125
 
114
- def deliver_later
115
- if delivery_disabled?
116
- Courrier.configuration&.logger&.info "[Courrier] Email delivery skipped: delivery is disabled via environment variable"
117
-
118
- return nil
126
+ self.class.after_deliver_callbacks.each { |callback| callback.call(self, result) }
119
127
  end
120
-
121
- data = {
122
- email_class: self.class.name,
123
- provider: @provider,
124
- api_key: @api_key,
125
- options: @options.to_h,
126
- provider_options: Courrier.configuration&.providers&.[](@provider.to_s.downcase.to_sym),
127
- context_options: @context_options
128
- }
129
-
130
- job = Courrier::Jobs::EmailDeliveryJob
131
- job = job.set(**self.class.queue_options) if self.class.queue_options.any?
132
-
133
- job.perform_later(data)
134
- rescue => error
135
- raise Courrier::BackgroundDeliveryError, "Failed to enqueue email: #{error.message}"
136
128
  end
129
+ alias_method :deliver_now, :deliver
137
130
 
138
131
  private
139
132
 
@@ -8,6 +8,4 @@ module Courrier
8
8
  class ArgumentError < ::ArgumentError; end
9
9
 
10
10
  class NotImplementedError < ::NotImplementedError; end
11
-
12
- class BackgroundDeliveryError < StandardError; end
13
11
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require "courrier/subscriber/base"
5
+
6
+ module Courrier
7
+ class Subscriber
8
+ class Brevo < Base
9
+ ENDPOINT_URL = "https://api.brevo.com/v3/contacts"
10
+
11
+ def create(email)
12
+ body = {
13
+ "email" => email,
14
+ "updateEnabled" => true
15
+ }
16
+
17
+ list_ids = Array(Courrier.configuration.subscriber[:list_ids]).compact
18
+ body["listIds"] = list_ids unless list_ids.empty?
19
+
20
+ request(:post, ENDPOINT_URL, body)
21
+ end
22
+
23
+ def destroy(email)
24
+ identifier = URI.encode_www_form_component(email)
25
+
26
+ request(:delete, "#{ENDPOINT_URL}/#{identifier}")
27
+ end
28
+
29
+ private
30
+
31
+ def headers
32
+ {
33
+ "api-key" => @api_key,
34
+ "Content-Type" => "application/json"
35
+ }
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Courrier
4
+ module Test
5
+ Delivery = Data.define(:email_class, :to, :from, :reply_to, :cc, :bcc, :subject, :body, :headers, :provider, :result, :timestamp) do
6
+ def success?
7
+ result.success?
8
+ end
9
+ end
10
+
11
+ class << self
12
+ def deliveries
13
+ @deliveries ||= []
14
+ end
15
+
16
+ def clear!
17
+ @deliveries = []
18
+ end
19
+
20
+ def record(email, result)
21
+ deliveries << Delivery.new(
22
+ email_class: email.class.name,
23
+ to: email.options.to,
24
+ from: email.options.from,
25
+ reply_to: email.options.reply_to,
26
+ cc: email.options.cc,
27
+ bcc: email.options.bcc,
28
+ subject: email.options.subject,
29
+ body: {text: email.options.text, html: email.options.html},
30
+ headers: email.class.headers,
31
+ provider: email.provider,
32
+ result: result,
33
+ timestamp: Time.now
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Courrier
4
+ module TestHelper
5
+ def assert_emails_delivered(count)
6
+ actual = Test.deliveries.size
7
+
8
+ assert_equal count, actual, "Expected #{count} email(s) to be delivered, but #{actual} were delivered"
9
+ end
10
+
11
+ def assert_no_emails_delivered
12
+ assert_emails_delivered(0)
13
+ end
14
+
15
+ def assert_email_delivered(email_class = nil, to: nil, from: nil, subject: nil, provider: nil)
16
+ deliveries = Test.deliveries
17
+
18
+ matching = deliveries.find do |delivery|
19
+ match_email_class(email_class, delivery.email_class) &&
20
+ match_recipient(to, delivery.to) &&
21
+ match_recipient(from, delivery.from) &&
22
+ match_subject(subject, delivery.subject) &&
23
+ match_provider(provider, delivery.provider)
24
+ end
25
+
26
+ assert matching, assertion_message(email_class, to: to, from: from, subject: subject, provider: provider, deliveries: deliveries)
27
+ end
28
+
29
+ private
30
+
31
+ def match_email_class(expected, actual)
32
+ return true if expected.nil?
33
+
34
+ expected == actual || (expected.is_a?(Class) && actual == expected.name)
35
+ end
36
+
37
+ def match_recipient(expected, actual)
38
+ return true if expected.nil?
39
+
40
+ actual.to_s.include?(expected.to_s)
41
+ end
42
+
43
+ def match_subject(expected, actual)
44
+ return true if expected.nil?
45
+
46
+ actual.to_s.include?(expected.to_s)
47
+ end
48
+
49
+ def match_provider(expected, actual)
50
+ return true if expected.nil?
51
+
52
+ actual.to_s == expected.to_s
53
+ end
54
+
55
+ def assertion_message(email_class, to:, from:, subject:, provider:, deliveries:)
56
+ "Expected email matching #{[].tap do |criteria|
57
+ criteria << "email_class=#{email_class}" if email_class
58
+ criteria << "to=#{to}" if to
59
+ criteria << "from=#{from}" if from
60
+ criteria << "subject=#{subject}" if subject
61
+ criteria << "provider=#{provider}" if provider
62
+ end.join(", ")} but none found. #{deliveries.size} email(s) delivered."
63
+ end
64
+ end
65
+ end
@@ -1,3 +1,3 @@
1
1
  module Courrier
2
- VERSION = "0.10.0"
2
+ VERSION = "1.0.0"
3
3
  end
data/lib/courrier.rb CHANGED
@@ -5,8 +5,8 @@ require "courrier/errors"
5
5
  require "courrier/configuration"
6
6
  require "courrier/email"
7
7
  require "courrier/subscriber"
8
- require "courrier/engine" if defined?(Rails)
9
- require "courrier/railtie" if defined?(Rails)
8
+ require "courrier/test"
9
+ require "courrier/test_helper"
10
10
 
11
11
  module Courrier
12
12
  end