courrier 0.5.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 +7 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +109 -0
- data/README.md +243 -0
- data/Rakefile +9 -0
- data/bin/console +11 -0
- data/bin/release +19 -0
- data/bin/setup +8 -0
- data/courrier.gemspec +25 -0
- data/lib/courrier/configuration/preview.rb +23 -0
- data/lib/courrier/configuration/providers.rb +41 -0
- data/lib/courrier/configuration.rb +53 -0
- data/lib/courrier/email/address.rb +37 -0
- data/lib/courrier/email/layouts.rb +40 -0
- data/lib/courrier/email/options.rb +55 -0
- data/lib/courrier/email/provider.rb +68 -0
- data/lib/courrier/email/providers/base.rb +39 -0
- data/lib/courrier/email/providers/logger.rb +41 -0
- data/lib/courrier/email/providers/loops.rb +36 -0
- data/lib/courrier/email/providers/mailgun.rb +36 -0
- data/lib/courrier/email/providers/mailjet.rb +50 -0
- data/lib/courrier/email/providers/mailpace.rb +32 -0
- data/lib/courrier/email/providers/postmark.rb +34 -0
- data/lib/courrier/email/providers/preview/default.html.erb +119 -0
- data/lib/courrier/email/providers/preview.rb +51 -0
- data/lib/courrier/email/providers/sendgrid.rb +57 -0
- data/lib/courrier/email/providers/sparkpost.rb +38 -0
- data/lib/courrier/email/request.rb +73 -0
- data/lib/courrier/email/result.rb +43 -0
- data/lib/courrier/email/transformer.rb +46 -0
- data/lib/courrier/email.rb +97 -0
- data/lib/courrier/errors.rb +11 -0
- data/lib/courrier/railtie.rb +23 -0
- data/lib/courrier/tasks/courrier.rake +13 -0
- data/lib/courrier/version.rb +3 -0
- data/lib/courrier.rb +10 -0
- data/lib/generators/courrier/email_generator.rb +19 -0
- data/lib/generators/courrier/install_generator.rb +11 -0
- data/lib/generators/courrier/templates/email.rb.tt +15 -0
- data/lib/generators/courrier/templates/initializer.rb.tt +38 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 394bb78e24569d93855f2bcf788662ec8773371779b7743c5737867b893d4b38
|
4
|
+
data.tar.gz: 4f35baf4f3ccca75ac05cd6bc7140f2eb4585c0c95e528f1aa45bc1227ec3777
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 387e9a879abac10ecbd97ac1a254fb08c40e48e8faf1a37f3a284dd2efd118dd70e0bf1d0032d3318914b2ddaa9242a56495547c6599dc3483844b3449114846
|
7
|
+
data.tar.gz: f5b458d0cc5ff6f640f0e3ffc19be8e0e6982721dcd4ff29a3dc49d42e7a3d6e9e6b0ba2d132e8f75d2a6c5edb8f6d2ae581b3076027fc9a5a48207c54ecc2ec
|
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gemspec
|
6
|
+
|
7
|
+
group :development do
|
8
|
+
gem "standard", "~> 1.49"
|
9
|
+
end
|
10
|
+
|
11
|
+
group :development, :test do
|
12
|
+
gem "rake", "~> 13.2", ">= 13.2.1"
|
13
|
+
gem "minitest", "~> 5.25", ">= 5.25.5"
|
14
|
+
gem "debug", "~> 1.9", ">= 1.9.2"
|
15
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
courrier (0.5.0)
|
5
|
+
launchy (>= 3.1, < 4)
|
6
|
+
nokogiri (>= 1.18, < 2)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
addressable (2.8.7)
|
12
|
+
public_suffix (>= 2.0.2, < 7.0)
|
13
|
+
ast (2.4.3)
|
14
|
+
childprocess (5.1.0)
|
15
|
+
logger (~> 1.5)
|
16
|
+
date (3.4.1)
|
17
|
+
debug (1.10.0)
|
18
|
+
irb (~> 1.10)
|
19
|
+
reline (>= 0.3.8)
|
20
|
+
io-console (0.8.0)
|
21
|
+
irb (1.15.2)
|
22
|
+
pp (>= 0.6.0)
|
23
|
+
rdoc (>= 4.0.0)
|
24
|
+
reline (>= 0.4.2)
|
25
|
+
json (2.11.1)
|
26
|
+
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
|
+
lint_roller (1.1.0)
|
32
|
+
logger (1.7.0)
|
33
|
+
minitest (5.25.5)
|
34
|
+
nokogiri (1.18.8-arm64-darwin)
|
35
|
+
racc (~> 1.4)
|
36
|
+
nokogiri (1.18.8-x86_64-darwin)
|
37
|
+
racc (~> 1.4)
|
38
|
+
nokogiri (1.18.8-x86_64-linux-gnu)
|
39
|
+
racc (~> 1.4)
|
40
|
+
parallel (1.27.0)
|
41
|
+
parser (3.3.8.0)
|
42
|
+
ast (~> 2.4.1)
|
43
|
+
racc
|
44
|
+
pp (0.6.2)
|
45
|
+
prettyprint
|
46
|
+
prettyprint (0.2.0)
|
47
|
+
prism (1.4.0)
|
48
|
+
psych (5.2.3)
|
49
|
+
date
|
50
|
+
stringio
|
51
|
+
public_suffix (6.0.2)
|
52
|
+
racc (1.8.1)
|
53
|
+
rainbow (3.1.1)
|
54
|
+
rake (13.2.1)
|
55
|
+
rdoc (6.13.1)
|
56
|
+
psych (>= 4.0.0)
|
57
|
+
regexp_parser (2.10.0)
|
58
|
+
reline (0.6.1)
|
59
|
+
io-console (~> 0.5)
|
60
|
+
rubocop (1.75.3)
|
61
|
+
json (~> 2.3)
|
62
|
+
language_server-protocol (~> 3.17.0.2)
|
63
|
+
lint_roller (~> 1.1.0)
|
64
|
+
parallel (~> 1.10)
|
65
|
+
parser (>= 3.3.0.2)
|
66
|
+
rainbow (>= 2.2.2, < 4.0)
|
67
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
68
|
+
rubocop-ast (>= 1.44.0, < 2.0)
|
69
|
+
ruby-progressbar (~> 1.7)
|
70
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
71
|
+
rubocop-ast (1.44.1)
|
72
|
+
parser (>= 3.3.7.2)
|
73
|
+
prism (~> 1.4)
|
74
|
+
rubocop-performance (1.25.0)
|
75
|
+
lint_roller (~> 1.1)
|
76
|
+
rubocop (>= 1.75.0, < 2.0)
|
77
|
+
rubocop-ast (>= 1.38.0, < 2.0)
|
78
|
+
ruby-progressbar (1.13.0)
|
79
|
+
standard (1.49.0)
|
80
|
+
language_server-protocol (~> 3.17.0.2)
|
81
|
+
lint_roller (~> 1.0)
|
82
|
+
rubocop (~> 1.75.2)
|
83
|
+
standard-custom (~> 1.0.0)
|
84
|
+
standard-performance (~> 1.8)
|
85
|
+
standard-custom (1.0.2)
|
86
|
+
lint_roller (~> 1.0)
|
87
|
+
rubocop (~> 1.50)
|
88
|
+
standard-performance (1.8.0)
|
89
|
+
lint_roller (~> 1.1)
|
90
|
+
rubocop-performance (~> 1.25.0)
|
91
|
+
stringio (3.1.7)
|
92
|
+
unicode-display_width (3.1.4)
|
93
|
+
unicode-emoji (~> 4.0, >= 4.0.4)
|
94
|
+
unicode-emoji (4.0.4)
|
95
|
+
|
96
|
+
PLATFORMS
|
97
|
+
arm64-darwin-23
|
98
|
+
x86_64-darwin-23
|
99
|
+
x86_64-linux
|
100
|
+
|
101
|
+
DEPENDENCIES
|
102
|
+
courrier!
|
103
|
+
debug (~> 1.9, >= 1.9.2)
|
104
|
+
minitest (~> 5.25, >= 5.25.5)
|
105
|
+
rake (~> 13.2, >= 13.2.1)
|
106
|
+
standard (~> 1.49)
|
107
|
+
|
108
|
+
BUNDLED WITH
|
109
|
+
2.6.8
|
data/README.md
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
# Courrier
|
2
|
+
|
3
|
+
Modern, API-powered email delivery for Ruby apps.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
# Quick example
|
7
|
+
OrderEmail.deliver to: "recipient@railsdesigner.com"
|
8
|
+
```
|
9
|
+
|
10
|
+
<a href="https://railsdesigner.com/" target="_blank">
|
11
|
+
<picture>
|
12
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Rails-Designer/courrier/HEAD/.github/logo-dark.svg">
|
13
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/Rails-Designer/courrier/HEAD/.github/logo-light.svg">
|
14
|
+
<img alt="Rails Designer logo" src="https://raw.githubusercontent.com/Rails-Designer/courrier/HEAD/.github/logo-light.svg" width="240" style="max-width: 100%;">
|
15
|
+
</picture>
|
16
|
+
</a>
|
17
|
+
|
18
|
+
**Sponsored By [Rails Designer](https://railsdesigner.com/)**
|
19
|
+
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
Add the gem:
|
24
|
+
```bash
|
25
|
+
bundle add courrier
|
26
|
+
```
|
27
|
+
|
28
|
+
Generate the configuration file:
|
29
|
+
```bash
|
30
|
+
bin/rails generate courrier:install
|
31
|
+
```
|
32
|
+
|
33
|
+
This creates `config/initializers/courrier.rb` for configuring email providers and default settings.
|
34
|
+
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
Generate a new email:
|
39
|
+
```bash
|
40
|
+
bin/rails generate courrier:email Order
|
41
|
+
```
|
42
|
+
```ruby
|
43
|
+
class OrderEmail < Courrier::Email
|
44
|
+
def subject = "Here is your order!"
|
45
|
+
|
46
|
+
def text
|
47
|
+
<<~TEXT
|
48
|
+
text body here
|
49
|
+
TEXT
|
50
|
+
end
|
51
|
+
|
52
|
+
def html
|
53
|
+
<<~HTML
|
54
|
+
html body here
|
55
|
+
HTML
|
56
|
+
end
|
57
|
+
end
|
58
|
+
```
|
59
|
+
💡 Write your email content using the [Minimal Email Editor](https://railsdesigner.com/minimal-email-editor/).
|
60
|
+
|
61
|
+
|
62
|
+
## Configuration
|
63
|
+
|
64
|
+
Courrier uses a configuration system with three levels (from lowest to highest priority):
|
65
|
+
|
66
|
+
1. **Global configuration**
|
67
|
+
```ruby
|
68
|
+
Courrier.configure do |config|
|
69
|
+
config.provider = "postmark"
|
70
|
+
config.api_key = "xyz"
|
71
|
+
config.from = "devs@railsdesigner.com"
|
72
|
+
config.default_url_options = { host: "railsdesigner.com" }
|
73
|
+
|
74
|
+
# Provider-specific configuration
|
75
|
+
config.providers.loops.transactional_id = "default-template"
|
76
|
+
config.providers.mailgun.domain = "notifications.railsdesigner.com"
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
2. **Email class defaults**
|
81
|
+
```ruby
|
82
|
+
class OrderEmail < Courrier::Email
|
83
|
+
configure from: "devs@railsdesigner.com",
|
84
|
+
cc: "records@railsdesigner.com",
|
85
|
+
provider: "mailgun",
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
3. **Instance options**
|
90
|
+
```ruby
|
91
|
+
OrderEmail.deliver to: "recipient@railsdesigner.com",\
|
92
|
+
from: "devs@railsdesigner.com",\
|
93
|
+
provider: "sendgrid",\
|
94
|
+
api_key: "sk_a1b1c3"
|
95
|
+
```
|
96
|
+
|
97
|
+
|
98
|
+
## Providers
|
99
|
+
|
100
|
+
Courrier supports these transactional email providers:
|
101
|
+
|
102
|
+
- [Loops](https://loops.so)
|
103
|
+
- [Mailgun](https://mailgun.com)
|
104
|
+
- [Mailjet](https://mailjet.com)
|
105
|
+
- [MailPace](https://mailpace.com)
|
106
|
+
- [Postmark](https://postmarkapp.com)
|
107
|
+
- [SendGrid](https://sendgrid.com)
|
108
|
+
- [SparkPost](https://sparkpost.com)
|
109
|
+
|
110
|
+
⚠️ 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
|
+
|
112
|
+
|
113
|
+
## More Features
|
114
|
+
|
115
|
+
Additional functionality to help with development and email handling:
|
116
|
+
|
117
|
+
|
118
|
+
### Email Preview
|
119
|
+
|
120
|
+
Preview emails in your browser during development:
|
121
|
+
```ruby
|
122
|
+
config.provider = "preview" # Opens emails in your default browser
|
123
|
+
```
|
124
|
+
|
125
|
+
Previews are automatically cleared with `bin/rails tmp:clear`, or manually with `bin/rails courrier:clear`.
|
126
|
+
|
127
|
+
|
128
|
+
### Layout Support
|
129
|
+
|
130
|
+
Wrap your email content using layouts:
|
131
|
+
```ruby
|
132
|
+
class OrderEmail < Courrier::Email
|
133
|
+
layout text: "%{content}\n\nThanks for your order!",
|
134
|
+
html: "<div>\n%{content}\n</div>"
|
135
|
+
end
|
136
|
+
```
|
137
|
+
|
138
|
+
Using a method:
|
139
|
+
```ruby
|
140
|
+
class OrderEmail < Courrier::Email
|
141
|
+
layout html: :html_layout
|
142
|
+
|
143
|
+
def html_layout
|
144
|
+
<<~HTML
|
145
|
+
<div style='font-family: ui-sans-serif, system-ui;'>
|
146
|
+
%{content}
|
147
|
+
</div>
|
148
|
+
HTML
|
149
|
+
end
|
150
|
+
end
|
151
|
+
```
|
152
|
+
|
153
|
+
Using a separate class:
|
154
|
+
```ruby
|
155
|
+
class OrderEmail < Courrier::Email
|
156
|
+
layout html: OrderLayout
|
157
|
+
end
|
158
|
+
|
159
|
+
class OrderLayout
|
160
|
+
self.call
|
161
|
+
<<~HTML
|
162
|
+
<div style='font-family: ui-sans-serif, system-ui;'>
|
163
|
+
%{content}
|
164
|
+
</div>
|
165
|
+
HTML
|
166
|
+
end
|
167
|
+
end
|
168
|
+
```
|
169
|
+
|
170
|
+
|
171
|
+
### Auto-generate Text from HTML
|
172
|
+
|
173
|
+
Automatically generate plain text versions from your HTML emails:
|
174
|
+
```ruby
|
175
|
+
config.auto_generate_text = true # Defaults to false
|
176
|
+
```
|
177
|
+
|
178
|
+
|
179
|
+
### Email Address Helper
|
180
|
+
|
181
|
+
Compose RFC 2822-compliant email addresses with display names:
|
182
|
+
```ruby
|
183
|
+
class SignupsController < ApplicationController
|
184
|
+
def create
|
185
|
+
recipient = email_with_name("devs@railsdesigner.com", "Rails Designer Devs")
|
186
|
+
|
187
|
+
WelcomeEmail.deliver to: recipient
|
188
|
+
end
|
189
|
+
end
|
190
|
+
```
|
191
|
+
|
192
|
+
In Plain Ruby Objects:
|
193
|
+
```ruby
|
194
|
+
class Signup
|
195
|
+
include Courrier::Email::Address
|
196
|
+
|
197
|
+
def send_welcome_email(user)
|
198
|
+
recipient = email_with_name(user.email_address, user.name)
|
199
|
+
|
200
|
+
WelcomeEmail.deliver to: recipient
|
201
|
+
end
|
202
|
+
end
|
203
|
+
```
|
204
|
+
|
205
|
+
|
206
|
+
### Logger Provider
|
207
|
+
|
208
|
+
Use Ruby's built-in Logger for development and testing:
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
config.provider = "logger" # Outputs emails to STDOUT
|
212
|
+
config.logger = custom_logger # Optional: defaults to ::Logger.new($stdout)
|
213
|
+
```
|
214
|
+
|
215
|
+
### Custom Providers
|
216
|
+
|
217
|
+
Create your own provider by inheriting from `Courrier::Email::Base`:
|
218
|
+
```ruby
|
219
|
+
class CustomProvider < Courrier::Email::Base
|
220
|
+
ENDPOINT_URL = ""
|
221
|
+
|
222
|
+
def body = ""
|
223
|
+
|
224
|
+
def headers = ""
|
225
|
+
end
|
226
|
+
```
|
227
|
+
|
228
|
+
Then configure it:
|
229
|
+
```ruby
|
230
|
+
config.provider = "CustomProvider"
|
231
|
+
```
|
232
|
+
|
233
|
+
Check the [existing providers](https://github.com/Rails-Designer/courrier/tree/main/lib/courrier/email/providers) for implementation examples.
|
234
|
+
|
235
|
+
|
236
|
+
## Contributing
|
237
|
+
|
238
|
+
This project uses [Standard](https://github.com/testdouble/standard) for formatting Ruby code. Please make sure to run `rake` before submitting pull requests.
|
239
|
+
|
240
|
+
|
241
|
+
## License
|
242
|
+
|
243
|
+
Courrier is released under the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "courrier"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
require "irb"
|
11
|
+
IRB.start(__FILE__)
|
data/bin/release
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
VERSION=$1
|
4
|
+
|
5
|
+
if [ -z "$VERSION" ]; then
|
6
|
+
echo "Error: The version number is required."
|
7
|
+
echo "Usage: $0 <version-number>"
|
8
|
+
exit 1
|
9
|
+
fi
|
10
|
+
|
11
|
+
printf "module Courrier\n VERSION = \"$VERSION\"\nend\n" > ./lib/courrier/version.rb
|
12
|
+
bundle
|
13
|
+
git add Gemfile.lock lib/courrier/version.rb
|
14
|
+
git commit -m "Bump version for $VERSION"
|
15
|
+
git push
|
16
|
+
git tag v$VERSION
|
17
|
+
git push --tags
|
18
|
+
gem build courrier.gemspec
|
19
|
+
gem push "courrier-$VERSION.gem"
|
data/bin/setup
ADDED
data/courrier.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/courrier/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "courrier"
|
7
|
+
spec.version = Courrier::VERSION
|
8
|
+
spec.authors = ["Rails Designer"]
|
9
|
+
spec.email = ["devs@railsdesigner.com"]
|
10
|
+
|
11
|
+
spec.summary = "Modern, API-powered email delivery for Rails apps"
|
12
|
+
spec.description = "Modern, API-powered email delivery for Ruby apps with support for Postmark, SendGrid, Mailgun and more."
|
13
|
+
spec.homepage = "https://railsdesigner.com/courrier/"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
17
|
+
spec.metadata["source_code_uri"] = "https://github.com/Rails-Designer/courrier/"
|
18
|
+
|
19
|
+
spec.files = Dir["{bin,app,config,db,lib,public}/**/*", "Rakefile", "README.md", "courrier.gemspec", "Gemfile", "Gemfile.lock"]
|
20
|
+
|
21
|
+
spec.required_ruby_version = ">= 3.2.0"
|
22
|
+
|
23
|
+
spec.add_dependency "launchy", ">= 3.1", "< 4"
|
24
|
+
spec.add_dependency "nokogiri", ">= 1.18", "< 2"
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Courrier
|
4
|
+
class Configuration
|
5
|
+
class Preview
|
6
|
+
attr_accessor :destination, :auto_open, :template_path
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@destination = default_destination
|
10
|
+
@auto_open = true
|
11
|
+
@template_path = File.expand_path("../../../courrier/email/providers/preview/default.html.erb", __FILE__)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def default_destination
|
17
|
+
return Rails.root.join("tmp", "courrier", "emails").to_s if defined?(Rails)
|
18
|
+
|
19
|
+
File.join(Dir.tmpdir, "courrier", "emails")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Courrier
|
4
|
+
class Configuration
|
5
|
+
class Providers
|
6
|
+
def initialize
|
7
|
+
@providers = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(name, *, **)
|
11
|
+
@providers[name.to_sym] ||= ProviderConfig.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def respond_to_missing?(name, include_private = false) = true
|
15
|
+
|
16
|
+
def [](provider_name)
|
17
|
+
@providers[provider_name.to_sym] ||= ProviderConfig.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_h = @providers.transform_values(&:to_h)
|
21
|
+
end
|
22
|
+
|
23
|
+
class ProviderConfig
|
24
|
+
def initialize
|
25
|
+
@options = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_missing(name, value = nil, **)
|
29
|
+
option_name = name.to_s.chomp("=").to_sym
|
30
|
+
|
31
|
+
return @options[option_name] = value if name.to_s.end_with?("=")
|
32
|
+
|
33
|
+
@options[name.to_sym]
|
34
|
+
end
|
35
|
+
|
36
|
+
def respond_to_missing?(name, include_private = false) = true
|
37
|
+
|
38
|
+
def to_h = @options
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "courrier/configuration/preview"
|
4
|
+
require "courrier/configuration/providers"
|
5
|
+
|
6
|
+
module Courrier
|
7
|
+
class << self
|
8
|
+
attr_writer :configuration
|
9
|
+
|
10
|
+
def configure
|
11
|
+
self.configuration ||= Configuration.new
|
12
|
+
|
13
|
+
yield(configuration) if block_given?
|
14
|
+
end
|
15
|
+
|
16
|
+
def configuration
|
17
|
+
@configuration ||= Configuration.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Configuration
|
22
|
+
attr_accessor :provider, :api_key, :logger, :email_path, :layouts, :default_url_options, :auto_generate_text,
|
23
|
+
:from, :reply_to, :cc, :bcc
|
24
|
+
attr_reader :providers, :preview
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@provider = "logger"
|
28
|
+
@api_key = nil
|
29
|
+
@logger = ::Logger.new($stdout)
|
30
|
+
@email_path = default_email_path
|
31
|
+
|
32
|
+
@layouts = nil
|
33
|
+
@default_url_options = {host: ""}
|
34
|
+
@auto_generate_text = false
|
35
|
+
|
36
|
+
@from = nil
|
37
|
+
@reply_to = nil
|
38
|
+
@cc = nil
|
39
|
+
@bcc = nil
|
40
|
+
|
41
|
+
@providers = Courrier::Configuration::Providers.new
|
42
|
+
@preview = Courrier::Configuration::Preview.new
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def default_email_path
|
48
|
+
return Rails.root.join("app", "emails").to_s if defined?(Rails)
|
49
|
+
|
50
|
+
File.join("courrier", "emails")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Courrier
|
4
|
+
class Email
|
5
|
+
module Address
|
6
|
+
class << self
|
7
|
+
def with_name(address, name)
|
8
|
+
raise Courrier::ArgumentError, "Both `address` and `name` are required" if address.nil? || name.nil?
|
9
|
+
raise Courrier::ArgumentError, "Both `address` and `name` must not be empty" if address.empty? || name.empty?
|
10
|
+
|
11
|
+
address = address.gsub(/[<>]/, "")
|
12
|
+
formatted_name = format_display_for(name)
|
13
|
+
|
14
|
+
"#{formatted_name} <#{address}>"
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def format_display_for(name)
|
20
|
+
return quote_escaped(name) if special_characters_in?(name)
|
21
|
+
|
22
|
+
name
|
23
|
+
end
|
24
|
+
|
25
|
+
def quote_escaped(name) = %("#{name.gsub('"', '\\"')}")
|
26
|
+
|
27
|
+
def special_characters_in?(name)
|
28
|
+
name =~ /[(),.:;<>@\[\]"]/
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def email_with_name(email, name) = Address.with_name(email, name)
|
33
|
+
alias_method :email_address_with_name, :email_with_name
|
34
|
+
module_function :email_with_name, :email_address_with_name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Courrier
|
4
|
+
class Email
|
5
|
+
class Layouts
|
6
|
+
def initialize(email)
|
7
|
+
@email = email
|
8
|
+
end
|
9
|
+
|
10
|
+
def build
|
11
|
+
return [] if no_layouts?
|
12
|
+
|
13
|
+
[layouts]
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
FORMATS = %w[html text]
|
19
|
+
|
20
|
+
def no_layouts? = @email.class.layouts.nil?
|
21
|
+
|
22
|
+
def layouts
|
23
|
+
FORMATS.map(&:to_sym).to_h do |format|
|
24
|
+
template = @email.class.layouts[format]
|
25
|
+
|
26
|
+
next if template.nil?
|
27
|
+
|
28
|
+
[format, render(template)]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def render(template)
|
33
|
+
return @email.send(template) if template.is_a?(Symbol)
|
34
|
+
return template.call if template.is_a?(Class)
|
35
|
+
|
36
|
+
template
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|