courrier 0.10.0 → 0.11.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 +1 -1
- data/Gemfile.lock +16 -24
- data/README.md +120 -75
- data/courrier.gemspec +4 -4
- data/lib/courrier/configuration.rb +2 -4
- data/lib/courrier/email/provider.rb +10 -4
- data/lib/courrier/email/providers/cloudflare.rb +35 -0
- data/lib/courrier/email/providers/lettermint.rb +31 -0
- data/lib/courrier/email/providers/ses.rb +75 -0
- data/lib/courrier/email/providers/smtp2go.rb +29 -0
- data/lib/courrier/email/request.rb +1 -1
- data/lib/courrier/email/transformer.rb +9 -9
- data/lib/courrier/email.rb +22 -35
- data/lib/courrier/errors.rb +0 -2
- data/lib/courrier/test.rb +38 -0
- data/lib/courrier/test_helper.rb +65 -0
- data/lib/courrier/version.rb +1 -1
- data/lib/courrier.rb +2 -2
- metadata +17 -31
- data/app/controllers/courrier/previews/cleanups_controller.rb +0 -13
- data/app/controllers/courrier/previews_controller.rb +0 -23
- data/app/views/courrier/previews/index.html.erb +0 -171
- data/config/routes.rb +0 -8
- data/lib/courrier/configuration/inbox.rb +0 -21
- data/lib/courrier/email/providers/inbox/default.html.erb +0 -126
- data/lib/courrier/email/providers/inbox.rb +0 -83
- data/lib/courrier/engine.rb +0 -7
- data/lib/courrier/jobs/email_delivery_job.rb +0 -23
- data/lib/courrier/railtie.rb +0 -23
- data/lib/courrier/tasks/courrier.rake +0 -13
- data/lib/generators/courrier/email_generator.rb +0 -42
- data/lib/generators/courrier/install_generator.rb +0 -11
- data/lib/generators/courrier/templates/email/password_reset.rb.tt +0 -29
- data/lib/generators/courrier/templates/email/welcome.rb.tt +0 -43
- data/lib/generators/courrier/templates/email.rb.tt +0 -13
- data/lib/generators/courrier/templates/initializer.rb.tt +0 -43
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d793cbc73ad9612cbb5e010e6aed85f9885e41fc8b672d16f82a6d155f63b726
|
|
4
|
+
data.tar.gz: 1143fc17abc330c3f94192ec95aea7618583266491f109b62aee3178826ede97
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 64991e00fdb46c81164c29ffc7dc9de6078bcbc6cad12407355424f19f54e3a767e8732abf1438105d1f14b3ebe8ecf8367aa88bd7e53fadd7787fa8dd163609
|
|
7
|
+
data.tar.gz: eed7664f6a7211fc98e8cd53e0dc24ad1784e5373575c006feb04fb7d3a2706da3127c0db801b3c960697f774cd3df68df3622b975f365ed5fdfa211327aa3d3
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
courrier (0.
|
|
5
|
-
|
|
4
|
+
courrier (0.11.0)
|
|
5
|
+
logger (>= 1.5, < 3)
|
|
6
6
|
nokogiri (>= 1.18, < 2)
|
|
7
7
|
|
|
8
8
|
GEM
|
|
9
9
|
remote: https://rubygems.org/
|
|
10
10
|
specs:
|
|
11
|
-
addressable (2.8.7)
|
|
12
|
-
public_suffix (>= 2.0.2, < 7.0)
|
|
13
11
|
ast (2.4.3)
|
|
14
|
-
childprocess (5.1.0)
|
|
15
|
-
logger (~> 1.5)
|
|
16
12
|
date (3.4.1)
|
|
17
13
|
debug (1.10.0)
|
|
18
14
|
irb (~> 1.10)
|
|
@@ -24,18 +20,14 @@ GEM
|
|
|
24
20
|
reline (>= 0.4.2)
|
|
25
21
|
json (2.11.1)
|
|
26
22
|
language_server-protocol (3.17.0.4)
|
|
27
|
-
launchy (3.1.1)
|
|
28
|
-
addressable (~> 2.8)
|
|
29
|
-
childprocess (~> 5.0)
|
|
30
|
-
logger (~> 1.6)
|
|
31
23
|
lint_roller (1.1.0)
|
|
32
24
|
logger (1.7.0)
|
|
33
|
-
minitest (5.
|
|
34
|
-
nokogiri (1.
|
|
25
|
+
minitest (5.27.0)
|
|
26
|
+
nokogiri (1.19.3-arm64-darwin)
|
|
35
27
|
racc (~> 1.4)
|
|
36
|
-
nokogiri (1.
|
|
28
|
+
nokogiri (1.19.3-x86_64-darwin)
|
|
37
29
|
racc (~> 1.4)
|
|
38
|
-
nokogiri (1.
|
|
30
|
+
nokogiri (1.19.3-x86_64-linux-gnu)
|
|
39
31
|
racc (~> 1.4)
|
|
40
32
|
parallel (1.27.0)
|
|
41
33
|
parser (3.3.8.0)
|
|
@@ -44,11 +36,10 @@ GEM
|
|
|
44
36
|
pp (0.6.2)
|
|
45
37
|
prettyprint
|
|
46
38
|
prettyprint (0.2.0)
|
|
47
|
-
prism (1.
|
|
39
|
+
prism (1.9.0)
|
|
48
40
|
psych (5.2.3)
|
|
49
41
|
date
|
|
50
42
|
stringio
|
|
51
|
-
public_suffix (6.0.2)
|
|
52
43
|
racc (1.8.1)
|
|
53
44
|
rainbow (3.1.1)
|
|
54
45
|
rake (13.2.1)
|
|
@@ -57,7 +48,7 @@ GEM
|
|
|
57
48
|
regexp_parser (2.10.0)
|
|
58
49
|
reline (0.6.1)
|
|
59
50
|
io-console (~> 0.5)
|
|
60
|
-
rubocop (1.
|
|
51
|
+
rubocop (1.84.2)
|
|
61
52
|
json (~> 2.3)
|
|
62
53
|
language_server-protocol (~> 3.17.0.2)
|
|
63
54
|
lint_roller (~> 1.1.0)
|
|
@@ -65,21 +56,21 @@ GEM
|
|
|
65
56
|
parser (>= 3.3.0.2)
|
|
66
57
|
rainbow (>= 2.2.2, < 4.0)
|
|
67
58
|
regexp_parser (>= 2.9.3, < 3.0)
|
|
68
|
-
rubocop-ast (>= 1.
|
|
59
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
69
60
|
ruby-progressbar (~> 1.7)
|
|
70
61
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
71
|
-
rubocop-ast (1.
|
|
62
|
+
rubocop-ast (1.49.1)
|
|
72
63
|
parser (>= 3.3.7.2)
|
|
73
|
-
prism (~> 1.
|
|
64
|
+
prism (~> 1.7)
|
|
74
65
|
rubocop-performance (1.25.0)
|
|
75
66
|
lint_roller (~> 1.1)
|
|
76
67
|
rubocop (>= 1.75.0, < 2.0)
|
|
77
68
|
rubocop-ast (>= 1.38.0, < 2.0)
|
|
78
69
|
ruby-progressbar (1.13.0)
|
|
79
|
-
standard (1.
|
|
70
|
+
standard (1.54.0)
|
|
80
71
|
language_server-protocol (~> 3.17.0.2)
|
|
81
72
|
lint_roller (~> 1.0)
|
|
82
|
-
rubocop (~> 1.
|
|
73
|
+
rubocop (~> 1.84.0)
|
|
83
74
|
standard-custom (~> 1.0.0)
|
|
84
75
|
standard-performance (~> 1.8)
|
|
85
76
|
standard-custom (1.0.2)
|
|
@@ -91,10 +82,11 @@ GEM
|
|
|
91
82
|
stringio (3.1.7)
|
|
92
83
|
unicode-display_width (3.1.4)
|
|
93
84
|
unicode-emoji (~> 4.0, >= 4.0.4)
|
|
94
|
-
unicode-emoji (4.0
|
|
85
|
+
unicode-emoji (4.2.0)
|
|
95
86
|
|
|
96
87
|
PLATFORMS
|
|
97
88
|
arm64-darwin-23
|
|
89
|
+
arm64-darwin-24
|
|
98
90
|
x86_64-darwin-23
|
|
99
91
|
x86_64-linux
|
|
100
92
|
|
|
@@ -103,7 +95,7 @@ DEPENDENCIES
|
|
|
103
95
|
debug (~> 1.9, >= 1.9.2)
|
|
104
96
|
minitest (~> 5.25, >= 5.25.5)
|
|
105
97
|
rake (~> 13.2, >= 13.2.1)
|
|
106
|
-
standard (~> 1.
|
|
98
|
+
standard (~> 1.54.0)
|
|
107
99
|
|
|
108
100
|
BUNDLED WITH
|
|
109
101
|
2.6.8
|
data/README.md
CHANGED
|
@@ -38,22 +38,24 @@ Add the gem:
|
|
|
38
38
|
```bash
|
|
39
39
|
bundle add courrier
|
|
40
40
|
```
|
|
41
|
+
> [!tip]
|
|
42
|
+
> For **Rails** apps, use [`rails_courrier`](https://github.com/Rails-Designer/rails_courrier) instead. It includes generators, ActiveJob support (`deliver_later`), inbox previews and more.
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
Configure Courrier in your app:
|
|
45
|
+
```ruby
|
|
46
|
+
Courrier.configure do |config|
|
|
47
|
+
config.email = {
|
|
48
|
+
provider: "postmark",
|
|
49
|
+
api_key: "your-api-key"
|
|
50
|
+
}
|
|
46
51
|
|
|
47
|
-
|
|
52
|
+
config.from = "devs@example.com"
|
|
53
|
+
end
|
|
54
|
+
```
|
|
48
55
|
|
|
49
56
|
|
|
50
57
|
## Usage
|
|
51
58
|
|
|
52
|
-
Generate a new email:
|
|
53
|
-
```bash
|
|
54
|
-
bin/rails generate courrier:email Order
|
|
55
|
-
```
|
|
56
|
-
|
|
57
59
|
```ruby
|
|
58
60
|
class OrderEmail < Courrier::Email
|
|
59
61
|
def subject = "Here is your order!"
|
|
@@ -91,8 +93,13 @@ Courrier.configure do |config|
|
|
|
91
93
|
config.default_url_options = { host: "railsdesigner.com" }
|
|
92
94
|
|
|
93
95
|
# Provider-specific configuration
|
|
96
|
+
config.providers.cloudflare.account_id = "your-account-id"
|
|
94
97
|
config.providers.loops.transactional_id = "default-template"
|
|
95
98
|
config.providers.mailgun.domain = "notifications.railsdesigner.com"
|
|
99
|
+
|
|
100
|
+
config.providers.ses.region = "us-east-1"
|
|
101
|
+
config.providers.ses.access_key_id = "your-access-key-id"
|
|
102
|
+
config.providers.ses.secret_access_key = "your-secret-access-key"
|
|
96
103
|
end
|
|
97
104
|
```
|
|
98
105
|
|
|
@@ -127,11 +134,12 @@ class OrderEmail < Courrier::Email
|
|
|
127
134
|
def subject = "Rails Icons now supports SVG sprites!"
|
|
128
135
|
|
|
129
136
|
def text = # …
|
|
130
|
-
|
|
137
|
+
|
|
138
|
+
def html = # …
|
|
131
139
|
end
|
|
132
140
|
```
|
|
133
141
|
|
|
134
|
-
Useful for adding provider-specific headers like List-Unsubscribe for Postmark, X-Mailer identifiers
|
|
142
|
+
Useful for adding provider-specific headers like List-Unsubscribe for Postmark, X-Mailer identifiers or custom metadata headers required.
|
|
135
143
|
|
|
136
144
|
|
|
137
145
|
## Custom attributes
|
|
@@ -139,7 +147,7 @@ Useful for adding provider-specific headers like List-Unsubscribe for Postmark,
|
|
|
139
147
|
Besides the standard email attributes (`from`, `to`, `reply_to`, etc.), you can pass any additional attributes that will be available in your email templates:
|
|
140
148
|
```ruby
|
|
141
149
|
OrderEmail.deliver to: "recipient@railsdesigner.com",\
|
|
142
|
-
download_url:
|
|
150
|
+
download_url: "https://example.com/download?token=abc123"
|
|
143
151
|
```
|
|
144
152
|
|
|
145
153
|
These custom attributes are accessible directly in your email class:
|
|
@@ -170,7 +178,7 @@ When sending an email through Courrier, a `Result` object is returned that provi
|
|
|
170
178
|
### Example
|
|
171
179
|
|
|
172
180
|
```ruby
|
|
173
|
-
delivery = OrderEmail.deliver
|
|
181
|
+
delivery = OrderEmail.deliver to: "recipient@example.com"
|
|
174
182
|
|
|
175
183
|
if delivery.success?
|
|
176
184
|
puts "Email sent successfully!"
|
|
@@ -185,11 +193,17 @@ end
|
|
|
185
193
|
|
|
186
194
|
Courrier supports these transactional email providers:
|
|
187
195
|
|
|
196
|
+
- [AWS SES](https://aws.amazon.com/ses/) — requires `aws-sigv4` gem
|
|
197
|
+
- [Cloudflare Email Service](https://developers.cloudflare.com/email-service/)
|
|
198
|
+
- [Lettermint](https://lettermint.co)
|
|
188
199
|
- [Loops](https://loops.so)
|
|
189
200
|
- [Mailgun](https://mailgun.com)
|
|
190
201
|
- [MailPace](https://mailpace.com)
|
|
191
202
|
- [Postmark](https://postmarkapp.com)
|
|
192
203
|
- [Resend](https://resend.com)
|
|
204
|
+
- [SendGrid](https://sendgrid.com)
|
|
205
|
+
- [SMTP2GO](https://www.smtp2go.com/)
|
|
206
|
+
- [SparkPost](https://www.sparkpost.com/)
|
|
193
207
|
- [Userlist](https://userlist.com)
|
|
194
208
|
|
|
195
209
|
|
|
@@ -198,36 +212,6 @@ Courrier supports these transactional email providers:
|
|
|
198
212
|
Additional functionality to help with development and testing:
|
|
199
213
|
|
|
200
214
|
|
|
201
|
-
### Background jobs (Rails only)
|
|
202
|
-
|
|
203
|
-
Use `deliver_later` to enqueue delivering using Rails' ActiveJob. You can set
|
|
204
|
-
various ActiveJob-supported options in the email class, like so: `enqueue queue: "emails", wait: 5.minutes`.
|
|
205
|
-
|
|
206
|
-
- `queue`, enqueue the email on the specified queue;
|
|
207
|
-
- `wait`, enqueue the email to be delivered with a delay;
|
|
208
|
-
- `wait_until`, enqueue the email to be delivered at (after) a specific date/time;
|
|
209
|
-
- `priority`, enqueues the email with the specified priority.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
### Inbox (Rails only)
|
|
213
|
-
|
|
214
|
-
You can preview your emails in the inbox:
|
|
215
|
-
```ruby
|
|
216
|
-
config.provider = "inbox"
|
|
217
|
-
|
|
218
|
-
# And add to your routes:
|
|
219
|
-
mount Courrier::Engine => "/courrier"
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
If you want to automatically open every email in your default browser:
|
|
223
|
-
```ruby
|
|
224
|
-
config.provider = "inbox"
|
|
225
|
-
config.inbox.auto_open = true
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
Emails are automatically cleared with `bin/rails tmp:clear`, or manually with `bin/rails courrier:clear`.
|
|
229
|
-
|
|
230
|
-
|
|
231
215
|
### Layout support
|
|
232
216
|
|
|
233
217
|
Wrap your email content using layouts:
|
|
@@ -277,17 +261,18 @@ Instead of defining `text` and `html` methods, you can create ERB template files
|
|
|
277
261
|
```ruby
|
|
278
262
|
class OrderEmail < Courrier::Email
|
|
279
263
|
def subject = "Your order is ready!"
|
|
264
|
+
|
|
280
265
|
# text and html content will be loaded from template files
|
|
281
266
|
end
|
|
282
267
|
```
|
|
283
268
|
|
|
284
|
-
Create template files alongside your email class:
|
|
285
|
-
- `
|
|
286
|
-
- `
|
|
269
|
+
Create template files alongside your email class (default path is `courrier/emails`):
|
|
270
|
+
- `courrier/emails/order_email.text.erb`
|
|
271
|
+
- `courrier/emails/order_email.html.erb`
|
|
287
272
|
|
|
288
273
|
Templates have access to all context options and instance variables:
|
|
289
274
|
```erb
|
|
290
|
-
<!--
|
|
275
|
+
<!-- courrier/emails/order_email.html.erb -->
|
|
291
276
|
<h1>Hello <%= name %>!</h1>
|
|
292
277
|
<p>Your order #<%= order_id %> is ready for pickup.</p>
|
|
293
278
|
```
|
|
@@ -299,7 +284,7 @@ class OrderEmail < Courrier::Email
|
|
|
299
284
|
|
|
300
285
|
def text = "Hello #{name}! Your order ##{order_id} is ready."
|
|
301
286
|
|
|
302
|
-
# html will be loaded from
|
|
287
|
+
# html will be loaded from courrier/emails/order_email.html.erb
|
|
303
288
|
end
|
|
304
289
|
```
|
|
305
290
|
|
|
@@ -333,12 +318,12 @@ end
|
|
|
333
318
|
|
|
334
319
|
### Markdown templates
|
|
335
320
|
|
|
336
|
-
Create markdown template files alongside your email class:
|
|
337
|
-
- `
|
|
338
|
-
- `
|
|
321
|
+
Create markdown template files alongside your email class (default path is `courrier/emails`):
|
|
322
|
+
- `courrier/emails/order_email.md.erb`
|
|
323
|
+
- `courrier/emails/order_email.markdown.erb`
|
|
339
324
|
|
|
340
325
|
```erb
|
|
341
|
-
<!--
|
|
326
|
+
<!-- courrier/emails/order_email.md.erb -->
|
|
342
327
|
# Hello <%= name %>!
|
|
343
328
|
|
|
344
329
|
Your order **#<%= order_id %>** is ready for pickup.
|
|
@@ -355,7 +340,7 @@ Method definitions take precedence over template files. You can mix approaches.
|
|
|
355
340
|
|
|
356
341
|
Automatically generate plain text versions from your HTML emails:
|
|
357
342
|
```ruby
|
|
358
|
-
config.auto_generate_text = true
|
|
343
|
+
config.auto_generate_text = true # defaults to false
|
|
359
344
|
```
|
|
360
345
|
|
|
361
346
|
|
|
@@ -363,17 +348,6 @@ config.auto_generate_text = true # Defaults to false
|
|
|
363
348
|
|
|
364
349
|
Compose email addresses with display names:
|
|
365
350
|
```ruby
|
|
366
|
-
class SignupsController < ApplicationController
|
|
367
|
-
def create
|
|
368
|
-
recipient = email_with_name("devs@railsdesigner.com", "Rails Designer Devs")
|
|
369
|
-
|
|
370
|
-
WelcomeEmail.deliver to: recipient
|
|
371
|
-
end
|
|
372
|
-
end
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
In Plain Ruby Objects:
|
|
376
|
-
```ruby
|
|
377
351
|
class Signup
|
|
378
352
|
include Courrier::Email::Address
|
|
379
353
|
|
|
@@ -391,8 +365,8 @@ end
|
|
|
391
365
|
Use Ruby's built-in Logger for development and testing:
|
|
392
366
|
|
|
393
367
|
```ruby
|
|
394
|
-
config.provider = "logger"
|
|
395
|
-
config.logger = custom_logger
|
|
368
|
+
config.provider = "logger" # outputs emails to STDOUT
|
|
369
|
+
config.logger = custom_logger # optional: defaults to ::Logger.new($stdout)
|
|
396
370
|
```
|
|
397
371
|
|
|
398
372
|
|
|
@@ -417,6 +391,82 @@ config.provider = "CustomProvider"
|
|
|
417
391
|
Check the [existing providers](https://github.com/Rails-Designer/courrier/tree/main/lib/courrier/email/providers) for implementation examples.
|
|
418
392
|
|
|
419
393
|
|
|
394
|
+
### Testing
|
|
395
|
+
|
|
396
|
+
Courrier provides `Test` and `TestHelper` for testing email delivery, similar to Action Mailer's testing API.
|
|
397
|
+
|
|
398
|
+
Access all delivered emails:
|
|
399
|
+
```ruby
|
|
400
|
+
# Clear deliveries between tests
|
|
401
|
+
Courrier::Test.clear!
|
|
402
|
+
|
|
403
|
+
# Access all deliveries
|
|
404
|
+
Courrier::Test.deliveries
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
Each delivery record contains:
|
|
408
|
+
|
|
409
|
+
- `email_class`; the email class name
|
|
410
|
+
- `to`, `from`, `reply_to`, `cc`, `bcc`; email addresses
|
|
411
|
+
- `subject`; email subject
|
|
412
|
+
- `body` - Hash with `:text` and `:html` keys
|
|
413
|
+
- `headers`; custom headers
|
|
414
|
+
- `provider`; provider used
|
|
415
|
+
- `result`; result object with `success?` method
|
|
416
|
+
- `timestamp`; delivery time
|
|
417
|
+
|
|
418
|
+
Include the helper in your test class for assertions:
|
|
419
|
+
```ruby
|
|
420
|
+
class OrderTest < Minitest::Test
|
|
421
|
+
include Courrier::TestHelper
|
|
422
|
+
|
|
423
|
+
def setup
|
|
424
|
+
Courrier::Test.clear!
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def test_sends_confirmation_email
|
|
428
|
+
order = Order.create! product: "Widget", customer_email: "customer@example.com"
|
|
429
|
+
|
|
430
|
+
OrderEmail.deliver to: order.customer_email, order: order
|
|
431
|
+
|
|
432
|
+
assert_emails_delivered 1
|
|
433
|
+
assert_email_delivered to: "customer@example.com"
|
|
434
|
+
assert_email_delivered OrderEmail, subject: "Order"
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def test_no_emails_when_order_cancelled
|
|
438
|
+
order = Order.create! product: "Widget", status: :cancelled
|
|
439
|
+
|
|
440
|
+
assert_no_emails_delivered
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Available assertions:
|
|
446
|
+
|
|
447
|
+
- `assert_emails_delivered(count)`; assert the number of emails delivered
|
|
448
|
+
- `assert_no_emails_delivered`; assert no emails were delivered
|
|
449
|
+
- `assert_email_delivered(email_class, to:, from:, subject:, provider:)`; assert an email matching criteria was delivered
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
### Delivery callbacks
|
|
453
|
+
|
|
454
|
+
Hook into the email delivery lifecycle with `before_deliver` and `after_deliver` callbacks:
|
|
455
|
+
```ruby
|
|
456
|
+
class OrderEmail < Courrier::Email
|
|
457
|
+
before_deliver do |email|
|
|
458
|
+
puts "Sending to #{email.options.to}" # access email options, abort delivery by returning false
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
after_deliver do |email, result|
|
|
462
|
+
puts "Delivered: #{result.success?}" # access email and delivery result
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Callbacks are isolated per class (subclasses don't inherit parent callbacks).
|
|
468
|
+
|
|
469
|
+
|
|
420
470
|
## Newsletter subscriptions
|
|
421
471
|
|
|
422
472
|
Manage subscribers across popular email marketing platforms:
|
|
@@ -463,6 +513,7 @@ config.subscriber = {
|
|
|
463
513
|
}
|
|
464
514
|
```
|
|
465
515
|
|
|
516
|
+
|
|
466
517
|
### Custom providers
|
|
467
518
|
|
|
468
519
|
Create custom providers by inheriting from `Courrier::Subscriber::Base`:
|
|
@@ -502,17 +553,11 @@ See [existing providers](https://github.com/Rails-Designer/courrier/tree/main/li
|
|
|
502
553
|
|
|
503
554
|
## FAQ
|
|
504
555
|
|
|
505
|
-
### Is this a replacement for ActionMailer?
|
|
506
|
-
Yes! While different in approach, Courrier can fully replace ActionMailer. It's a modern alternative that focuses on API-based delivery. The main difference is in how emails are structured - Courrier uses a more straightforward, class-based approach.
|
|
507
|
-
|
|
508
556
|
### Is this for Rails only?
|
|
509
|
-
Not at all!
|
|
557
|
+
Not at all! Courrier works with any Ruby application. For Rails apps, use [`rails_courrier`](https://github.com/Rails-Designer/rails_courrier).
|
|
510
558
|
|
|
511
559
|
### Can it send using SMTP?
|
|
512
|
-
No. Courrier is specifically built for API-based email delivery.
|
|
513
|
-
|
|
514
|
-
### What's the main benefit over ActionMailer?
|
|
515
|
-
Courrier offers a simpler, more modern approach to sending emails. Each email is a standalone class, configuration is straightforward (typically just only an API key is needed) and it packs few quality-of-life features (like the inbox feature and auto-generate text version).
|
|
560
|
+
No. Courrier is specifically built for API-based email delivery.
|
|
516
561
|
|
|
517
562
|
|
|
518
563
|
## Contributing
|
data/courrier.gemspec
CHANGED
|
@@ -10,16 +10,16 @@ Gem::Specification.new do |spec|
|
|
|
10
10
|
|
|
11
11
|
spec.summary = "API-powered email delivery for Ruby apps"
|
|
12
12
|
spec.description = "API-powered email delivery for Ruby apps with support for Postmark, SendGrid, Mailgun and more."
|
|
13
|
-
spec.homepage = "https://railsdesigner.com/courrier/"
|
|
13
|
+
spec.homepage = "https://railsdesigner.com/open-source/courrier/"
|
|
14
14
|
spec.license = "MIT"
|
|
15
15
|
|
|
16
16
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
17
17
|
spec.metadata["source_code_uri"] = "https://github.com/Rails-Designer/courrier/"
|
|
18
18
|
|
|
19
|
-
spec.files = Dir["{bin,
|
|
19
|
+
spec.files = Dir["{bin,lib}/**/*", "Rakefile", "README.md", "courrier.gemspec", "Gemfile", "Gemfile.lock"]
|
|
20
20
|
|
|
21
|
-
spec.required_ruby_version = ">=
|
|
21
|
+
spec.required_ruby_version = ">= 4.0.0"
|
|
22
22
|
|
|
23
|
-
spec.add_dependency "
|
|
23
|
+
spec.add_dependency "logger", ">= 1.5", "< 3"
|
|
24
24
|
spec.add_dependency "nokogiri", ">= 1.18", "< 2"
|
|
25
25
|
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "courrier/configuration/inbox"
|
|
4
3
|
require "courrier/configuration/providers"
|
|
5
4
|
|
|
6
5
|
module Courrier
|
|
@@ -22,7 +21,7 @@ module Courrier
|
|
|
22
21
|
attr_accessor :email, :subscriber, :logger, :email_path, :layouts, :default_url_options, :auto_generate_text,
|
|
23
22
|
:from, :reply_to, :cc, :bcc
|
|
24
23
|
|
|
25
|
-
attr_reader :providers
|
|
24
|
+
attr_reader :providers
|
|
26
25
|
|
|
27
26
|
def initialize
|
|
28
27
|
@email = {provider: "logger"}
|
|
@@ -41,7 +40,6 @@ module Courrier
|
|
|
41
40
|
@bcc = nil
|
|
42
41
|
|
|
43
42
|
@providers = Courrier::Configuration::Providers.new
|
|
44
|
-
@inbox = Courrier::Configuration::Inbox.new
|
|
45
43
|
end
|
|
46
44
|
|
|
47
45
|
def provider
|
|
@@ -71,7 +69,7 @@ module Courrier
|
|
|
71
69
|
private
|
|
72
70
|
|
|
73
71
|
def default_email_path
|
|
74
|
-
|
|
72
|
+
File.join("courrier", "emails")
|
|
75
73
|
end
|
|
76
74
|
end
|
|
77
75
|
end
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "courrier/email/providers/base"
|
|
4
|
-
require "courrier/email/providers/
|
|
4
|
+
require "courrier/email/providers/cloudflare"
|
|
5
|
+
require "courrier/email/providers/lettermint"
|
|
5
6
|
require "courrier/email/providers/logger"
|
|
6
7
|
require "courrier/email/providers/loops"
|
|
7
8
|
require "courrier/email/providers/mailgun"
|
|
@@ -9,7 +10,9 @@ require "courrier/email/providers/mailjet"
|
|
|
9
10
|
require "courrier/email/providers/mailpace"
|
|
10
11
|
require "courrier/email/providers/postmark"
|
|
11
12
|
require "courrier/email/providers/resend"
|
|
13
|
+
require "courrier/email/providers/ses"
|
|
12
14
|
require "courrier/email/providers/sendgrid"
|
|
15
|
+
require "courrier/email/providers/smtp2go"
|
|
13
16
|
require "courrier/email/providers/sparkpost"
|
|
14
17
|
require "courrier/email/providers/userlist"
|
|
15
18
|
|
|
@@ -17,15 +20,18 @@ module Courrier
|
|
|
17
20
|
class Email
|
|
18
21
|
class Provider
|
|
19
22
|
PROVIDERS = {
|
|
20
|
-
|
|
23
|
+
cloudflare: Courrier::Email::Providers::Cloudflare,
|
|
21
24
|
logger: Courrier::Email::Providers::Logger,
|
|
25
|
+
lettermint: Courrier::Email::Providers::Lettermint,
|
|
22
26
|
loops: Courrier::Email::Providers::Loops,
|
|
23
27
|
mailgun: Courrier::Email::Providers::Mailgun,
|
|
24
28
|
mailjet: Courrier::Email::Providers::Mailjet,
|
|
25
29
|
mailpace: Courrier::Email::Providers::Mailpace,
|
|
26
30
|
postmark: Courrier::Email::Providers::Postmark,
|
|
27
31
|
resend: Courrier::Email::Providers::Resend,
|
|
32
|
+
ses: Courrier::Email::Providers::Ses,
|
|
28
33
|
sendgrid: Courrier::Email::Providers::Sendgrid,
|
|
34
|
+
smtp2go: Courrier::Email::Providers::Smtp2go,
|
|
29
35
|
sparkpost: Courrier::Email::Providers::Sparkpost,
|
|
30
36
|
userlist: Courrier::Email::Providers::Userlist
|
|
31
37
|
}
|
|
@@ -72,7 +78,7 @@ module Courrier
|
|
|
72
78
|
end
|
|
73
79
|
|
|
74
80
|
def api_key_required_providers?
|
|
75
|
-
!%w[logger
|
|
81
|
+
!%w[logger].include?(@provider.to_s)
|
|
76
82
|
end
|
|
77
83
|
|
|
78
84
|
def api_key_blank?
|
|
@@ -80,7 +86,7 @@ module Courrier
|
|
|
80
86
|
end
|
|
81
87
|
|
|
82
88
|
def production?
|
|
83
|
-
|
|
89
|
+
ENV["RACK_ENV"] == "production"
|
|
84
90
|
end
|
|
85
91
|
end
|
|
86
92
|
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Courrier
|
|
4
|
+
class Email
|
|
5
|
+
module Providers
|
|
6
|
+
class Cloudflare < Base
|
|
7
|
+
def body
|
|
8
|
+
{
|
|
9
|
+
"from" => @options.from,
|
|
10
|
+
"to" => @options.to,
|
|
11
|
+
"reply_to" => @options.reply_to,
|
|
12
|
+
"cc" => @options.cc,
|
|
13
|
+
"bcc" => @options.bcc,
|
|
14
|
+
"subject" => @options.subject,
|
|
15
|
+
"text" => @options.text,
|
|
16
|
+
"html" => @options.html
|
|
17
|
+
}.compact
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def endpoint_url
|
|
23
|
+
account_id = @provider_options.account_id
|
|
24
|
+
raise Courrier::ArgumentError, "Cloudflare requires an `account_id`" unless account_id
|
|
25
|
+
|
|
26
|
+
"https://api.cloudflare.com/client/v4/accounts/#{account_id}/email/sending/send"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def default_headers
|
|
30
|
+
{"Authorization" => "Bearer #{@api_key}"}
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Courrier
|
|
4
|
+
class Email
|
|
5
|
+
module Providers
|
|
6
|
+
class Lettermint < Base
|
|
7
|
+
ENDPOINT_URL = "https://api.lettermint.co/v1/send"
|
|
8
|
+
|
|
9
|
+
def body
|
|
10
|
+
{
|
|
11
|
+
"route" => @provider_options.route,
|
|
12
|
+
"from" => @options.from,
|
|
13
|
+
"to" => @options.to.to_s.split(",").map(&:strip),
|
|
14
|
+
"cc" => @options.cc&.split(",")&.map(&:strip),
|
|
15
|
+
"bcc" => @options.bcc&.split(",")&.map(&:strip),
|
|
16
|
+
"reply_to" => @options.reply_to&.split(",")&.map(&:strip),
|
|
17
|
+
"subject" => @options.subject,
|
|
18
|
+
"html" => @options.html,
|
|
19
|
+
"text" => @options.text
|
|
20
|
+
}.compact
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def default_headers
|
|
26
|
+
{"x-lettermint-token" => @api_key}
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|