courrier 0.5.0 → 0.6.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 +64 -8
- data/lib/courrier/configuration/preview.rb +1 -3
- data/lib/courrier/configuration.rb +1 -3
- data/lib/courrier/email/provider.rb +3 -1
- data/lib/courrier/email/providers/mailgun.rb +5 -1
- data/lib/courrier/email/providers/userlist.rb +66 -0
- data/lib/courrier/email/request.rb +3 -5
- data/lib/courrier/email.rb +2 -2
- data/lib/courrier/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2da5fe5fc7bb657449ed12d90c8443d6fa692339f9974fcbda7c4c726964ad8
|
4
|
+
data.tar.gz: ad4fc570a0bdb7cb1bce3c214a21e550a725212e0f900e5d31fe1345d5c7b221
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7170f0e34c59ceee7b88c41865da67f558357058651a9508578adb5b0fec0d5da78bea1672aed8b24335128822784c1e5b23478804fb1a08273efece5129ae3f
|
7
|
+
data.tar.gz: 89de9523761715879b487d62ca63576eb9783647a0e2d8baba5ef268b36c91d359d96c33386b06a3cd686af8e747cf548eaf74ea9654efde22030a52eaac5fe4
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
Modern, API-powered email delivery for Ruby apps.
|
4
4
|
|
5
|
+

|
6
|
+
|
5
7
|
```ruby
|
6
8
|
# Quick example
|
7
9
|
OrderEmail.deliver to: "recipient@railsdesigner.com"
|
@@ -39,6 +41,7 @@ Generate a new email:
|
|
39
41
|
```bash
|
40
42
|
bin/rails generate courrier:email Order
|
41
43
|
```
|
44
|
+
|
42
45
|
```ruby
|
43
46
|
class OrderEmail < Courrier::Email
|
44
47
|
def subject = "Here is your order!"
|
@@ -55,7 +58,10 @@ class OrderEmail < Courrier::Email
|
|
55
58
|
HTML
|
56
59
|
end
|
57
60
|
end
|
61
|
+
|
62
|
+
# OrderEmail.deliver to: "recipient@railsdesigner.com"
|
58
63
|
```
|
64
|
+
|
59
65
|
💡 Write your email content using the [Minimal Email Editor](https://railsdesigner.com/minimal-email-editor/).
|
60
66
|
|
61
67
|
|
@@ -80,7 +86,7 @@ end
|
|
80
86
|
2. **Email class defaults**
|
81
87
|
```ruby
|
82
88
|
class OrderEmail < Courrier::Email
|
83
|
-
configure from: "
|
89
|
+
configure from: "orders@railsdesigner.com",
|
84
90
|
cc: "records@railsdesigner.com",
|
85
91
|
provider: "mailgun",
|
86
92
|
end
|
@@ -89,12 +95,61 @@ end
|
|
89
95
|
3. **Instance options**
|
90
96
|
```ruby
|
91
97
|
OrderEmail.deliver to: "recipient@railsdesigner.com",\
|
92
|
-
from: "
|
98
|
+
from: "shop@railsdesigner.com",\
|
93
99
|
provider: "sendgrid",\
|
94
100
|
api_key: "sk_a1b1c3"
|
95
101
|
```
|
96
102
|
|
97
103
|
|
104
|
+
Provider and API key settings can be overridden using environment variables (`COURRIER_PROVIDER` and `COURRIER_API_KEY`) for both global configuration and email class defaults.
|
105
|
+
|
106
|
+
|
107
|
+
## Custom Attributes
|
108
|
+
|
109
|
+
Besides the standard email attributes (`from`, `to`, `reply_to`, etc.), you can pass any additional attributes that will be available in your email templates:
|
110
|
+
```ruby
|
111
|
+
OrderEmail.deliver to: "recipient@railsdesigner.com", download_url: downloads_path(token: "token")
|
112
|
+
```
|
113
|
+
|
114
|
+
These custom attributes are accessible directly in your email class:
|
115
|
+
```ruby
|
116
|
+
def text
|
117
|
+
<<~TEXT
|
118
|
+
#{download_url}
|
119
|
+
TEXT
|
120
|
+
end
|
121
|
+
```
|
122
|
+
|
123
|
+
|
124
|
+
## Result Object
|
125
|
+
|
126
|
+
When sending an email through Courrier, a `Result` object is returned that provides information about the delivery attempt. This object offers a simple interface to check the status and access response data.
|
127
|
+
|
128
|
+
|
129
|
+
### Available Methods
|
130
|
+
|
131
|
+
| Method | Return Type | Description |
|
132
|
+
|:-------|:-----------|:------------|
|
133
|
+
| `success?` | Boolean | Returns `true` if the API request was successful |
|
134
|
+
| `response` | Net::HTTP::Response | The raw HTTP response from the email provider |
|
135
|
+
| `data` | Hash | Parsed JSON response body from the provider |
|
136
|
+
| `error` | Exception | Contains any error that occurred during delivery |
|
137
|
+
|
138
|
+
|
139
|
+
### Example
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
delivery = OrderEmail.deliver(to: "recipient@example.com")
|
143
|
+
|
144
|
+
if delivery.success?
|
145
|
+
puts "Email sent successfully!"
|
146
|
+
puts "Provider response: #{delivery.data}"
|
147
|
+
else
|
148
|
+
puts "Failed to send email: #{delivery.error}"
|
149
|
+
end
|
150
|
+
```
|
151
|
+
|
152
|
+
|
98
153
|
## Providers
|
99
154
|
|
100
155
|
Courrier supports these transactional email providers:
|
@@ -106,6 +161,7 @@ Courrier supports these transactional email providers:
|
|
106
161
|
- [Postmark](https://postmarkapp.com)
|
107
162
|
- [SendGrid](https://sendgrid.com)
|
108
163
|
- [SparkPost](https://sparkpost.com)
|
164
|
+
- [Userlist](https://userlist.com)
|
109
165
|
|
110
166
|
⚠️ Some providers still need manual verification of their implementation. If you're using one of these providers, please help verify the implementation by sharing your experience in [this GitHub issue](https://github.com/Rails-Designer/courrier/issues/4). 🙏
|
111
167
|
|
@@ -119,10 +175,10 @@ Additional functionality to help with development and email handling:
|
|
119
175
|
|
120
176
|
Preview emails in your browser during development:
|
121
177
|
```ruby
|
122
|
-
|
178
|
+
|
123
179
|
```
|
124
180
|
|
125
|
-
|
181
|
+
|
126
182
|
|
127
183
|
|
128
184
|
### Layout Support
|
@@ -172,13 +228,13 @@ end
|
|
172
228
|
|
173
229
|
Automatically generate plain text versions from your HTML emails:
|
174
230
|
```ruby
|
175
|
-
|
231
|
+
|
176
232
|
```
|
177
233
|
|
178
234
|
|
179
235
|
### Email Address Helper
|
180
236
|
|
181
|
-
Compose
|
237
|
+
Compose email addresses with display names:
|
182
238
|
```ruby
|
183
239
|
class SignupsController < ApplicationController
|
184
240
|
def create
|
@@ -214,9 +270,9 @@ config.logger = custom_logger # Optional: defaults to ::Logger.new($stdout)
|
|
214
270
|
|
215
271
|
### Custom Providers
|
216
272
|
|
217
|
-
Create your own provider by inheriting from `Courrier::Email::Base`:
|
273
|
+
Create your own provider by inheriting from `Courrier::Email::Providers::Base`:
|
218
274
|
```ruby
|
219
|
-
class CustomProvider < Courrier::Email::Base
|
275
|
+
class CustomProvider < Courrier::Email::Providers::Base
|
220
276
|
ENDPOINT_URL = ""
|
221
277
|
|
222
278
|
def body = ""
|
@@ -14,9 +14,7 @@ module Courrier
|
|
14
14
|
private
|
15
15
|
|
16
16
|
def default_destination
|
17
|
-
|
18
|
-
|
19
|
-
File.join(Dir.tmpdir, "courrier", "emails")
|
17
|
+
defined?(Rails) ? Rails.root.join("tmp", "courrier", "emails").to_s : File.join(Dir.tmpdir, "courrier", "emails")
|
20
18
|
end
|
21
19
|
end
|
22
20
|
end
|
@@ -45,9 +45,7 @@ module Courrier
|
|
45
45
|
private
|
46
46
|
|
47
47
|
def default_email_path
|
48
|
-
|
49
|
-
|
50
|
-
File.join("courrier", "emails")
|
48
|
+
defined?(Rails) ? Rails.root.join("app", "emails").to_s : File.join("courrier", "emails")
|
51
49
|
end
|
52
50
|
end
|
53
51
|
end
|
@@ -10,6 +10,7 @@ require "courrier/email/providers/postmark"
|
|
10
10
|
require "courrier/email/providers/preview"
|
11
11
|
require "courrier/email/providers/sendgrid"
|
12
12
|
require "courrier/email/providers/sparkpost"
|
13
|
+
require "courrier/email/providers/userlist"
|
13
14
|
|
14
15
|
module Courrier
|
15
16
|
class Email
|
@@ -43,7 +44,8 @@ module Courrier
|
|
43
44
|
postmark: Courrier::Email::Providers::Postmark,
|
44
45
|
preview: Courrier::Email::Providers::Preview,
|
45
46
|
sendgrid: Courrier::Email::Providers::Sendgrid,
|
46
|
-
sparkpost: Courrier::Email::Providers::Sparkpost
|
47
|
+
sparkpost: Courrier::Email::Providers::Sparkpost,
|
48
|
+
userlist: Courrier::Email::Providers::Userlist
|
47
49
|
}.freeze
|
48
50
|
|
49
51
|
def configuration_missing_in_production?
|
@@ -29,7 +29,11 @@ module Courrier
|
|
29
29
|
|
30
30
|
def content_type = "multipart/form-data"
|
31
31
|
|
32
|
-
def
|
32
|
+
def headers
|
33
|
+
{
|
34
|
+
"Authorization" => "Basic #{Base64.strict_encode64("api:#{@api_key}")}"
|
35
|
+
}
|
36
|
+
end
|
33
37
|
end
|
34
38
|
end
|
35
39
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Courrier
|
4
|
+
class Email
|
5
|
+
module Providers
|
6
|
+
class Userlist < Base
|
7
|
+
ENDPOINT_URL = "https://push.userlist.com/messages"
|
8
|
+
|
9
|
+
def body
|
10
|
+
{
|
11
|
+
"from" => @options.from,
|
12
|
+
"to" => @options.to,
|
13
|
+
"subject" => @options.subject,
|
14
|
+
"body" => body_document
|
15
|
+
}.compact.merge(provider_options)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def headers
|
21
|
+
{
|
22
|
+
"Authorization" => "Push #{@api_key}"
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def body_document
|
27
|
+
if @options.html && @options.text
|
28
|
+
multipart_document
|
29
|
+
elsif @options.html
|
30
|
+
html_document
|
31
|
+
elsif @options.text
|
32
|
+
text_document
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def text_document
|
37
|
+
{
|
38
|
+
"type" => "text",
|
39
|
+
"content" => @options.text
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def html_document
|
44
|
+
{
|
45
|
+
"type" => "html",
|
46
|
+
"content" => @options.html
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def multipart_document
|
51
|
+
{
|
52
|
+
"type" => "multipart",
|
53
|
+
"content" => [
|
54
|
+
html_document,
|
55
|
+
text_document
|
56
|
+
]
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def provider_options
|
61
|
+
{"theme" => nil}.merge(@provider_options)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -42,11 +42,9 @@ module Courrier
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def body_for(request)
|
45
|
-
if requires_multipart_form?
|
46
|
-
|
47
|
-
|
48
|
-
set_json_body(request)
|
49
|
-
end
|
45
|
+
return set_multipart_form(request) if requires_multipart_form?
|
46
|
+
|
47
|
+
set_json_body(request)
|
50
48
|
end
|
51
49
|
|
52
50
|
def default_headers
|
data/lib/courrier/email.rb
CHANGED
@@ -49,7 +49,7 @@ module Courrier
|
|
49
49
|
|
50
50
|
def initialize(options = {})
|
51
51
|
@provider = options[:provider] || ENV["COURRIER_PROVIDER"] || self.class.provider || Courrier.configuration&.provider
|
52
|
-
@api_key = options[:api_key] || ENV["
|
52
|
+
@api_key = options[:api_key] || ENV["COURRIER_API_KEY"] || self.class.api_key || Courrier.configuration&.api_key
|
53
53
|
|
54
54
|
@default_url_options = self.class.default_url_options.merge(options[:default_url_options] || {})
|
55
55
|
@context_options = options.except(:provider, :api_key, :from, :to, :reply_to, :cc, :bcc, :subject, :text, :html)
|
@@ -79,7 +79,7 @@ module Courrier
|
|
79
79
|
provider: @provider,
|
80
80
|
api_key: @api_key,
|
81
81
|
options: @options,
|
82
|
-
provider_options: Courrier.configuration&.providers&.[](@provider.to_s.downcase.to_sym)
|
82
|
+
provider_options: Courrier.configuration&.providers&.[](@provider.to_s.downcase.to_sym)
|
83
83
|
).deliver
|
84
84
|
end
|
85
85
|
alias_method :deliver_now, :deliver
|
data/lib/courrier/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: courrier
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rails Designer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-05-
|
11
|
+
date: 2025-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: launchy
|
@@ -86,6 +86,7 @@ files:
|
|
86
86
|
- lib/courrier/email/providers/preview/default.html.erb
|
87
87
|
- lib/courrier/email/providers/sendgrid.rb
|
88
88
|
- lib/courrier/email/providers/sparkpost.rb
|
89
|
+
- lib/courrier/email/providers/userlist.rb
|
89
90
|
- lib/courrier/email/request.rb
|
90
91
|
- lib/courrier/email/result.rb
|
91
92
|
- lib/courrier/email/transformer.rb
|