courrier 1.0.0 → 1.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +5 -0
- data/lib/courrier/context_value.rb +33 -0
- data/lib/courrier/email/options.rb +3 -3
- data/lib/courrier/email/provider.rb +8 -0
- data/lib/courrier/email/providers/base.rb +17 -0
- data/lib/courrier/email/providers/brevo.rb +5 -13
- data/lib/courrier/email/providers/mailersend.rb +28 -0
- data/lib/courrier/email/providers/mailgun.rb +3 -1
- data/lib/courrier/email/providers/mailjet.rb +8 -6
- data/lib/courrier/email/providers/mailkite.rb +32 -0
- data/lib/courrier/email/providers/mailpace.rb +2 -0
- data/lib/courrier/email/providers/mailtrap.rb +28 -0
- data/lib/courrier/email/providers/postmark.rb +2 -0
- data/lib/courrier/email/providers/sendgrid.rb +4 -6
- data/lib/courrier/email/providers/smtpcom.rb +57 -0
- data/lib/courrier/email/providers/sparkpost.rb +23 -8
- data/lib/courrier/email/rendering.rb +83 -0
- data/lib/courrier/email.rb +4 -45
- data/lib/courrier/safe_string.rb +9 -0
- data/lib/courrier/version.rb +1 -1
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 802112a2d410713a25a67fca11af1a9021cf8f5bd15efe55590bb6d41dcf188b
|
|
4
|
+
data.tar.gz: ac7fd7ab96871921876c79e20a623baa5d7a8fd9ab5ad13b23e3ae9e2631cc57
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 350843ff6b853bb92e797d87dd5b485ad095d3f8a70a2129863ea141e11dcf6f0fd90c8a680bf5f731ce38690e0bc0f47dba60ca61cb3ab9089d7b8d524bfaf8
|
|
7
|
+
data.tar.gz: 222f3088acd972aa5f282e4a29d5f7a2035b2373c10804ff97db89651cb09ead6938117734144c182b2b7268ff64285833674782b38d966145635818a49d2a14
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -96,6 +96,7 @@ Courrier.configure do |config|
|
|
|
96
96
|
config.providers.cloudflare.account_id = "your-account-id"
|
|
97
97
|
config.providers.loops.transactional_id = "default-template"
|
|
98
98
|
config.providers.mailgun.domain = "notifications.railsdesigner.com"
|
|
99
|
+
config.providers.smtpcom.channel = "your-sender-channel"
|
|
99
100
|
|
|
100
101
|
config.providers.ses.region = "us-east-1"
|
|
101
102
|
config.providers.ses.access_key_id = "your-access-key-id"
|
|
@@ -196,12 +197,16 @@ Courrier supports these transactional email providers:
|
|
|
196
197
|
- [Cloudflare Email Service](https://developers.cloudflare.com/email-service/)
|
|
197
198
|
- [Lettermint](https://lettermint.co)
|
|
198
199
|
- [Loops](https://loops.so)
|
|
200
|
+
- [MailerSend](https://www.mailersend.com/)
|
|
199
201
|
- [Mailgun](https://mailgun.com)
|
|
202
|
+
- [MailKite](https://mailkite.dev)
|
|
200
203
|
- [MailPace](https://mailpace.com)
|
|
204
|
+
- [Mailtrap](https://mailtrap.io)
|
|
201
205
|
- [Postmark](https://postmarkapp.com)
|
|
202
206
|
- [Resend](https://resend.com)
|
|
203
207
|
- [SendGrid](https://sendgrid.com)
|
|
204
208
|
- [SMTP2GO](https://www.smtp2go.com/)
|
|
209
|
+
- [SMTP.com](https://www.smtp.com/) — requires a `channel`
|
|
205
210
|
- [SparkPost](https://www.sparkpost.com/)
|
|
206
211
|
- [Userlist](https://userlist.com)
|
|
207
212
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cgi"
|
|
4
|
+
|
|
5
|
+
require "courrier/safe_string"
|
|
6
|
+
|
|
7
|
+
module Courrier
|
|
8
|
+
class ContextValue
|
|
9
|
+
def initialize(value, escape:)
|
|
10
|
+
@value = value
|
|
11
|
+
@escape = escape
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_s
|
|
15
|
+
@escape ? CGI.escapeHTML(@value.to_s) : @value.to_s
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def html_safe
|
|
19
|
+
SafeString.new(@value.to_s)
|
|
20
|
+
end
|
|
21
|
+
alias_method :h, :html_safe
|
|
22
|
+
|
|
23
|
+
def method_missing(name, *arguments, &block)
|
|
24
|
+
result = @value.send(name, *arguments, &block)
|
|
25
|
+
|
|
26
|
+
result.is_a?(String) ? ContextValue.new(result, escape: @escape) : result
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def respond_to_missing?(name, include_private = false)
|
|
30
|
+
@value.respond_to?(name, include_private) || super
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -15,9 +15,9 @@ module Courrier
|
|
|
15
15
|
@cc = options.fetch(:cc, nil)
|
|
16
16
|
@bcc = options.fetch(:bcc, nil)
|
|
17
17
|
|
|
18
|
-
@subject = options.fetch(:subject, "")
|
|
19
|
-
@text = options.fetch(:text, "")
|
|
20
|
-
@html = options.fetch(:html, "")
|
|
18
|
+
@subject = options.fetch(:subject, "").to_s
|
|
19
|
+
@text = options.fetch(:text, "").to_s
|
|
20
|
+
@html = options.fetch(:html, "").to_s
|
|
21
21
|
|
|
22
22
|
@auto_generate_text = options.fetch(:auto_generate_text, false)
|
|
23
23
|
|
|
@@ -6,14 +6,18 @@ require "courrier/email/providers/cloudflare"
|
|
|
6
6
|
require "courrier/email/providers/lettermint"
|
|
7
7
|
require "courrier/email/providers/logger"
|
|
8
8
|
require "courrier/email/providers/loops"
|
|
9
|
+
require "courrier/email/providers/mailersend"
|
|
9
10
|
require "courrier/email/providers/mailgun"
|
|
11
|
+
require "courrier/email/providers/mailkite"
|
|
10
12
|
require "courrier/email/providers/mailjet"
|
|
11
13
|
require "courrier/email/providers/mailpace"
|
|
14
|
+
require "courrier/email/providers/mailtrap"
|
|
12
15
|
require "courrier/email/providers/postmark"
|
|
13
16
|
require "courrier/email/providers/resend"
|
|
14
17
|
require "courrier/email/providers/ses"
|
|
15
18
|
require "courrier/email/providers/sendgrid"
|
|
16
19
|
require "courrier/email/providers/smtp2go"
|
|
20
|
+
require "courrier/email/providers/smtpcom"
|
|
17
21
|
require "courrier/email/providers/sparkpost"
|
|
18
22
|
require "courrier/email/providers/userlist"
|
|
19
23
|
|
|
@@ -26,14 +30,18 @@ module Courrier
|
|
|
26
30
|
logger: Courrier::Email::Providers::Logger,
|
|
27
31
|
lettermint: Courrier::Email::Providers::Lettermint,
|
|
28
32
|
loops: Courrier::Email::Providers::Loops,
|
|
33
|
+
mailersend: Courrier::Email::Providers::Mailersend,
|
|
29
34
|
mailgun: Courrier::Email::Providers::Mailgun,
|
|
35
|
+
mailkite: Courrier::Email::Providers::Mailkite,
|
|
30
36
|
mailjet: Courrier::Email::Providers::Mailjet,
|
|
31
37
|
mailpace: Courrier::Email::Providers::Mailpace,
|
|
38
|
+
mailtrap: Courrier::Email::Providers::Mailtrap,
|
|
32
39
|
postmark: Courrier::Email::Providers::Postmark,
|
|
33
40
|
resend: Courrier::Email::Providers::Resend,
|
|
34
41
|
ses: Courrier::Email::Providers::Ses,
|
|
35
42
|
sendgrid: Courrier::Email::Providers::Sendgrid,
|
|
36
43
|
smtp2go: Courrier::Email::Providers::Smtp2go,
|
|
44
|
+
smtpcom: Courrier::Email::Providers::Smtpcom,
|
|
37
45
|
sparkpost: Courrier::Email::Providers::Sparkpost,
|
|
38
46
|
userlist: Courrier::Email::Providers::Userlist
|
|
39
47
|
}
|
|
@@ -40,6 +40,23 @@ module Courrier
|
|
|
40
40
|
|
|
41
41
|
def default_headers = {}
|
|
42
42
|
|
|
43
|
+
def address_list(value, as: :email)
|
|
44
|
+
list = value&.to_s&.split(",")&.map(&:strip)&.reject(&:empty?)
|
|
45
|
+
return unless list && !list.empty?
|
|
46
|
+
|
|
47
|
+
list.map { |address| address_element(address, as) }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def address_line(value) = address_list(value, as: :plain)&.join(", ")
|
|
51
|
+
|
|
52
|
+
def address_element(address, as)
|
|
53
|
+
case as
|
|
54
|
+
when :email then {"email" => address}
|
|
55
|
+
when :address then {"address" => address}
|
|
56
|
+
else address
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
43
60
|
def provider = self.class.name.split("::").last
|
|
44
61
|
end
|
|
45
62
|
end
|
|
@@ -8,11 +8,11 @@ module Courrier
|
|
|
8
8
|
|
|
9
9
|
def body
|
|
10
10
|
{
|
|
11
|
-
"sender" =>
|
|
12
|
-
"to" =>
|
|
13
|
-
"cc" =>
|
|
14
|
-
"bcc" =>
|
|
15
|
-
"replyTo" =>
|
|
11
|
+
"sender" => address_list(@options.from)&.first,
|
|
12
|
+
"to" => address_list(@options.to),
|
|
13
|
+
"cc" => address_list(@options.cc),
|
|
14
|
+
"bcc" => address_list(@options.bcc),
|
|
15
|
+
"replyTo" => address_list(@options.reply_to)&.first,
|
|
16
16
|
"subject" => @options.subject,
|
|
17
17
|
"htmlContent" => @options.html,
|
|
18
18
|
"textContent" => @options.text
|
|
@@ -22,14 +22,6 @@ module Courrier
|
|
|
22
22
|
private
|
|
23
23
|
|
|
24
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
25
|
end
|
|
34
26
|
end
|
|
35
27
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Courrier
|
|
4
|
+
class Email
|
|
5
|
+
module Providers
|
|
6
|
+
class Mailersend < Base
|
|
7
|
+
ENDPOINT_URL = "https://api.mailersend.com/v1/email"
|
|
8
|
+
|
|
9
|
+
def body
|
|
10
|
+
{
|
|
11
|
+
"from" => address_list(@options.from)&.first,
|
|
12
|
+
"to" => address_list(@options.to),
|
|
13
|
+
"cc" => address_list(@options.cc),
|
|
14
|
+
"bcc" => address_list(@options.bcc),
|
|
15
|
+
"reply_to" => address_list(@options.reply_to)&.first,
|
|
16
|
+
"subject" => @options.subject,
|
|
17
|
+
"text" => @options.text,
|
|
18
|
+
"html" => @options.html
|
|
19
|
+
}.compact
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def default_headers = {"Authorization" => "Bearer #{@api_key}"}
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -13,6 +13,8 @@ module Courrier
|
|
|
13
13
|
"from" => @options.from,
|
|
14
14
|
|
|
15
15
|
"to" => @options.to,
|
|
16
|
+
"cc" => address_line(@options.cc),
|
|
17
|
+
"bcc" => address_line(@options.bcc),
|
|
16
18
|
"h:Reply-To" => @options.reply_to,
|
|
17
19
|
|
|
18
20
|
"subject" => @options.subject,
|
|
@@ -33,7 +35,7 @@ module Courrier
|
|
|
33
35
|
|
|
34
36
|
def default_headers
|
|
35
37
|
{
|
|
36
|
-
"Authorization" => "Basic #{
|
|
38
|
+
"Authorization" => "Basic #{["api:#{@api_key}"].pack("m0")}"
|
|
37
39
|
}
|
|
38
40
|
end
|
|
39
41
|
end
|
|
@@ -16,11 +16,9 @@ module Courrier
|
|
|
16
16
|
"Email" => @options.from
|
|
17
17
|
},
|
|
18
18
|
|
|
19
|
-
"To" =>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
],
|
|
19
|
+
"To" => addresses(@options.to),
|
|
20
|
+
"Cc" => addresses(@options.cc),
|
|
21
|
+
"Bcc" => addresses(@options.bcc),
|
|
24
22
|
"ReplyTo" => reply_to_object,
|
|
25
23
|
|
|
26
24
|
"Subject" => @options.subject,
|
|
@@ -35,10 +33,14 @@ module Courrier
|
|
|
35
33
|
|
|
36
34
|
def default_headers
|
|
37
35
|
{
|
|
38
|
-
"Authorization" => "Basic "
|
|
36
|
+
"Authorization" => "Basic #{["#{@api_key}:#{@provider_options.api_secret}"].pack("m0")}"
|
|
39
37
|
}
|
|
40
38
|
end
|
|
41
39
|
|
|
40
|
+
def addresses(value)
|
|
41
|
+
address_list(value, as: :plain)&.map { |address| {"Email" => address} }
|
|
42
|
+
end
|
|
43
|
+
|
|
42
44
|
def reply_to_object
|
|
43
45
|
return if @options.reply_to.nil?
|
|
44
46
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Courrier
|
|
4
|
+
class Email
|
|
5
|
+
module Providers
|
|
6
|
+
class Mailkite < Base
|
|
7
|
+
ENDPOINT_URL = "https://api.mailkite.dev/v1/send"
|
|
8
|
+
|
|
9
|
+
def body
|
|
10
|
+
{
|
|
11
|
+
"from" => @options.from,
|
|
12
|
+
"to" => address_list(@options.to, as: :plain),
|
|
13
|
+
"cc" => address_list(@options.cc, as: :plain),
|
|
14
|
+
"bcc" => address_list(@options.bcc, as: :plain),
|
|
15
|
+
"replyTo" => @options.reply_to,
|
|
16
|
+
"subject" => @options.subject,
|
|
17
|
+
"text" => @options.text,
|
|
18
|
+
"html" => @options.html
|
|
19
|
+
}.compact
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def default_headers
|
|
25
|
+
{
|
|
26
|
+
"Authorization" => "Bearer #{@api_key}"
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Courrier
|
|
4
|
+
class Email
|
|
5
|
+
module Providers
|
|
6
|
+
class Mailtrap < Base
|
|
7
|
+
ENDPOINT_URL = "https://send.api.mailtrap.io/api/send"
|
|
8
|
+
|
|
9
|
+
def body
|
|
10
|
+
{
|
|
11
|
+
"from" => address_list(@options.from)&.first,
|
|
12
|
+
"to" => address_list(@options.to),
|
|
13
|
+
"cc" => address_list(@options.cc),
|
|
14
|
+
"bcc" => address_list(@options.bcc),
|
|
15
|
+
"reply_to" => address_list(@options.reply_to)&.first,
|
|
16
|
+
"subject" => @options.subject,
|
|
17
|
+
"text" => @options.text,
|
|
18
|
+
"html" => @options.html
|
|
19
|
+
}.compact
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def default_headers = {"Api-Token" => @api_key}
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -13,12 +13,10 @@ module Courrier
|
|
|
13
13
|
},
|
|
14
14
|
"personalizations" => [
|
|
15
15
|
{
|
|
16
|
-
"to" =>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
]
|
|
21
|
-
}
|
|
16
|
+
"to" => address_list(@options.to),
|
|
17
|
+
"cc" => address_list(@options.cc),
|
|
18
|
+
"bcc" => address_list(@options.bcc)
|
|
19
|
+
}.compact
|
|
22
20
|
],
|
|
23
21
|
"reply_to" => reply_to_object,
|
|
24
22
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Courrier
|
|
4
|
+
class Email
|
|
5
|
+
module Providers
|
|
6
|
+
class Smtpcom < Base
|
|
7
|
+
def self.config_options = %w[channel]
|
|
8
|
+
|
|
9
|
+
ENDPOINT_URL = "https://api.smtp.com/v4/messages"
|
|
10
|
+
|
|
11
|
+
def body
|
|
12
|
+
{
|
|
13
|
+
"channel" => channel,
|
|
14
|
+
"recipients" => {
|
|
15
|
+
"to" => address_list(@options.to, as: :address),
|
|
16
|
+
"cc" => address_list(@options.cc, as: :address),
|
|
17
|
+
"bcc" => address_list(@options.bcc, as: :address)
|
|
18
|
+
}.compact,
|
|
19
|
+
"originator" => {
|
|
20
|
+
"from" => address_list(@options.from, as: :address)&.first,
|
|
21
|
+
"reply_to" => address_list(@options.reply_to, as: :address)&.first
|
|
22
|
+
}.compact,
|
|
23
|
+
"subject" => @options.subject,
|
|
24
|
+
"body" => {"parts" => parts}
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def default_headers
|
|
31
|
+
{"Authorization" => "Basic #{base64("api:#{@api_key}")}"}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def channel
|
|
35
|
+
@provider_options.channel || raise(Courrier::ArgumentError, "SMTP.com requires a `channel`")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def parts
|
|
39
|
+
[part("text/plain", @options.text), part("text/html", @options.html)].compact
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def part(type, content)
|
|
43
|
+
return unless content
|
|
44
|
+
|
|
45
|
+
{
|
|
46
|
+
"type" => type,
|
|
47
|
+
"charset" => "UTF-8",
|
|
48
|
+
"encoding" => "base64",
|
|
49
|
+
"content" => base64(content)
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def base64(value) = [value].pack("m0")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -13,15 +13,10 @@ module Courrier
|
|
|
13
13
|
"from" => @options.from,
|
|
14
14
|
"subject" => @options.subject,
|
|
15
15
|
"text" => @options.text,
|
|
16
|
-
"html" => @options.html
|
|
16
|
+
"html" => @options.html,
|
|
17
|
+
"headers" => cc_header
|
|
17
18
|
}.compact,
|
|
18
|
-
"recipients" =>
|
|
19
|
-
{
|
|
20
|
-
"address" => {
|
|
21
|
-
"email" => @options.to
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
]
|
|
19
|
+
"recipients" => recipients
|
|
25
20
|
}
|
|
26
21
|
end
|
|
27
22
|
|
|
@@ -32,6 +27,26 @@ module Courrier
|
|
|
32
27
|
"Authorization" => @api_key
|
|
33
28
|
}
|
|
34
29
|
end
|
|
30
|
+
|
|
31
|
+
# SparkPost has no cc/bcc field: every copy is an entry in `recipients`, and only
|
|
32
|
+
# the addresses repeated in the CC header are shown as copied. `header_to` keeps the
|
|
33
|
+
# visible To line the same for all of them, so a bcc stays hidden and a cc is not
|
|
34
|
+
# mistaken for a to.
|
|
35
|
+
def recipients
|
|
36
|
+
[@options.to, @options.cc, @options.bcc].flat_map { |value| recipients_for(value) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def recipients_for(value)
|
|
40
|
+
Array(address_list(value, as: :plain)).map do |address|
|
|
41
|
+
{"address" => {"email" => address, "header_to" => address_line(@options.to)}}
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def cc_header
|
|
46
|
+
cc = address_line(@options.cc)
|
|
47
|
+
|
|
48
|
+
{"CC" => cc} if cc
|
|
49
|
+
end
|
|
35
50
|
end
|
|
36
51
|
end
|
|
37
52
|
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "erb"
|
|
4
|
+
|
|
5
|
+
require "courrier/context_value"
|
|
6
|
+
require "courrier/safe_string"
|
|
7
|
+
require "courrier/markdown"
|
|
8
|
+
|
|
9
|
+
module Courrier
|
|
10
|
+
class Email
|
|
11
|
+
module Rendering
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def method_missing(name, *)
|
|
15
|
+
return in_html_context { render_template("html") || markdown_rendered } if name == :html
|
|
16
|
+
return render_template("text") if name == :text
|
|
17
|
+
|
|
18
|
+
value = @context_options[name]
|
|
19
|
+
return value if value.is_a?(SafeString)
|
|
20
|
+
|
|
21
|
+
value.is_a?(String) ? ContextValue.new(value, escape: @html_context) : value
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def in_html_context
|
|
25
|
+
previous = @html_context
|
|
26
|
+
@html_context = true
|
|
27
|
+
|
|
28
|
+
yield
|
|
29
|
+
ensure
|
|
30
|
+
@html_context = previous
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def in_safe_context
|
|
34
|
+
previous = @html_context
|
|
35
|
+
@html_context = false
|
|
36
|
+
|
|
37
|
+
yield
|
|
38
|
+
ensure
|
|
39
|
+
@html_context = previous
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def render_template(format)
|
|
43
|
+
template_path = template_file_path(format)
|
|
44
|
+
|
|
45
|
+
File.exist?(template_path) ? ERB.new(File.read(template_path)).result(binding) : nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def render_markdown_template
|
|
49
|
+
%w[md markdown].each do |ext|
|
|
50
|
+
template_path = template_file_path(ext)
|
|
51
|
+
|
|
52
|
+
return ERB.new(File.read(template_path)).result(binding) if File.exist?(template_path)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def markdown_rendered
|
|
59
|
+
return unless Courrier::Markdown.available?
|
|
60
|
+
|
|
61
|
+
markdown_content = render_markdown_template || (respond_to?(:markdown, true) ? markdown : nil)
|
|
62
|
+
|
|
63
|
+
Courrier::Markdown.render(markdown_content) if markdown_content
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def template_file_path(format)
|
|
67
|
+
class_path = self.class.name.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
|
|
68
|
+
|
|
69
|
+
File.join(Courrier.configuration&.email_path, "#{class_path}.#{format}.erb")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def html_safe(value = nil, &block)
|
|
73
|
+
return in_safe_context { SafeString.new(block.call.to_s) } if block
|
|
74
|
+
return value.html_safe if value.is_a?(ContextValue)
|
|
75
|
+
|
|
76
|
+
SafeString.new(value.to_s)
|
|
77
|
+
end
|
|
78
|
+
alias_method :h, :html_safe
|
|
79
|
+
|
|
80
|
+
def respond_to_missing?(name, include_private = false) = true
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
data/lib/courrier/email.rb
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
4
|
-
|
|
3
|
+
require "courrier/email/rendering"
|
|
5
4
|
require "courrier/email/address"
|
|
6
5
|
require "courrier/email/layouts"
|
|
7
|
-
require "courrier/markdown"
|
|
8
6
|
require "courrier/email/options"
|
|
9
7
|
require "courrier/email/provider"
|
|
10
8
|
|
|
11
9
|
module Courrier
|
|
12
10
|
class Email
|
|
11
|
+
include Rendering
|
|
12
|
+
|
|
13
13
|
attr_accessor :provider, :api_key, :default_url_options, :options, :queue_options
|
|
14
14
|
|
|
15
15
|
@queue_options = {}
|
|
@@ -97,7 +97,7 @@ module Courrier
|
|
|
97
97
|
bcc: options[:bcc] || self.class.bcc || Courrier.configuration&.bcc,
|
|
98
98
|
subject: subject,
|
|
99
99
|
text: text,
|
|
100
|
-
html: html,
|
|
100
|
+
html: in_html_context { html },
|
|
101
101
|
auto_generate_text: Courrier.configuration&.auto_generate_text,
|
|
102
102
|
layouts: Courrier::Email::Layouts.new(self).build
|
|
103
103
|
)
|
|
@@ -133,46 +133,5 @@ module Courrier
|
|
|
133
133
|
def delivery_disabled?
|
|
134
134
|
ENV["COURRIER_EMAIL_DISABLED"] == "true" || ENV["COURRIER_EMAIL_ENABLED"] == "false"
|
|
135
135
|
end
|
|
136
|
-
|
|
137
|
-
def method_missing(name, *)
|
|
138
|
-
if name == :text || name == :html
|
|
139
|
-
render_template(name.to_s).tap do |result|
|
|
140
|
-
return result || markdown_rendered if name == :html
|
|
141
|
-
end
|
|
142
|
-
else
|
|
143
|
-
@context_options[name]
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
def render_template(format)
|
|
148
|
-
template_path = template_file_path(format)
|
|
149
|
-
|
|
150
|
-
File.exist?(template_path) ? ERB.new(File.read(template_path)).result(binding) : nil
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def render_markdown_template
|
|
154
|
-
%w[md markdown].each do |ext|
|
|
155
|
-
template_path = template_file_path(ext)
|
|
156
|
-
|
|
157
|
-
return ERB.new(File.read(template_path)).result(binding) if File.exist?(template_path)
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
nil
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def markdown_rendered
|
|
164
|
-
return unless Courrier::Markdown.available?
|
|
165
|
-
|
|
166
|
-
markdown_content = render_markdown_template || (respond_to?(:markdown, true) ? markdown : nil)
|
|
167
|
-
Courrier::Markdown.render(markdown_content) if markdown_content
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
def template_file_path(format)
|
|
171
|
-
class_path = self.class.name.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
|
|
172
|
-
|
|
173
|
-
File.join(Courrier.configuration&.email_path, "#{class_path}.#{format}.erb")
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def respond_to_missing?(name, include_private = false) = true
|
|
177
136
|
end
|
|
178
137
|
end
|
data/lib/courrier/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: courrier
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rails Designer
|
|
@@ -68,6 +68,7 @@ files:
|
|
|
68
68
|
- lib/courrier.rb
|
|
69
69
|
- lib/courrier/configuration.rb
|
|
70
70
|
- lib/courrier/configuration/providers.rb
|
|
71
|
+
- lib/courrier/context_value.rb
|
|
71
72
|
- lib/courrier/email.rb
|
|
72
73
|
- lib/courrier/email/address.rb
|
|
73
74
|
- lib/courrier/email/layouts.rb
|
|
@@ -79,21 +80,27 @@ files:
|
|
|
79
80
|
- lib/courrier/email/providers/lettermint.rb
|
|
80
81
|
- lib/courrier/email/providers/logger.rb
|
|
81
82
|
- lib/courrier/email/providers/loops.rb
|
|
83
|
+
- lib/courrier/email/providers/mailersend.rb
|
|
82
84
|
- lib/courrier/email/providers/mailgun.rb
|
|
83
85
|
- lib/courrier/email/providers/mailjet.rb
|
|
86
|
+
- lib/courrier/email/providers/mailkite.rb
|
|
84
87
|
- lib/courrier/email/providers/mailpace.rb
|
|
88
|
+
- lib/courrier/email/providers/mailtrap.rb
|
|
85
89
|
- lib/courrier/email/providers/postmark.rb
|
|
86
90
|
- lib/courrier/email/providers/resend.rb
|
|
87
91
|
- lib/courrier/email/providers/sendgrid.rb
|
|
88
92
|
- lib/courrier/email/providers/ses.rb
|
|
89
93
|
- lib/courrier/email/providers/smtp2go.rb
|
|
94
|
+
- lib/courrier/email/providers/smtpcom.rb
|
|
90
95
|
- lib/courrier/email/providers/sparkpost.rb
|
|
91
96
|
- lib/courrier/email/providers/userlist.rb
|
|
97
|
+
- lib/courrier/email/rendering.rb
|
|
92
98
|
- lib/courrier/email/request.rb
|
|
93
99
|
- lib/courrier/email/result.rb
|
|
94
100
|
- lib/courrier/email/transformer.rb
|
|
95
101
|
- lib/courrier/errors.rb
|
|
96
102
|
- lib/courrier/markdown.rb
|
|
103
|
+
- lib/courrier/safe_string.rb
|
|
97
104
|
- lib/courrier/subscriber.rb
|
|
98
105
|
- lib/courrier/subscriber/base.rb
|
|
99
106
|
- lib/courrier/subscriber/beehiiv.rb
|