Skip to content

Commit 91e1f73

Browse files
committed
Add support for plain and html email
1 parent ed61dbb commit 91e1f73

8 files changed

Lines changed: 618 additions & 123 deletions

File tree

README.md

Lines changed: 104 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,53 @@
11
# Supermail
22

3-
Organize emails with plain 'ol Ruby objects in a Rails application, like this:
3+
Organize emails with plain 'ol Ruby objects in a Rails application:
44

55
```ruby
6-
# ./app/email/user/welcome.rb
76
class User::WelcomeEmail < ApplicationEmail
87
def initialize(user:)
98
@user = user
109
end
1110

1211
def to = @user.email
1312
def subject = "Welcome to Beautiful Ruby"
14-
def body
15-
super do
16-
<<~_
17-
Hi #{@user.name},
18-
19-
You're going to learn a ton at https://beautifulruby.com.
20-
_
21-
end
13+
14+
def plain = <<~TEXT
15+
Hi #{@user.name},
16+
17+
You're going to learn a ton at https://beautifulruby.com.
18+
TEXT
19+
20+
def html = safe <<~HTML
21+
<h1>Hi #{@user.name},</h1>
22+
<p>You're going to learn a ton at <a href="https://beautifulruby.com">Beautiful Ruby</a>.</p>
23+
HTML
24+
end
25+
```
26+
27+
Or use ERB templates like Rails:
28+
29+
```ruby
30+
# app/emails/welcome_email.rb
31+
class WelcomeEmail < ApplicationEmail
32+
include Supermail::Rails::Rendering
33+
34+
def initialize(user:)
35+
@user = user
2236
end
37+
38+
def to = @user.email
39+
def subject = "Welcome!"
40+
# Templates automatically resolved from app/emails/welcome_email.html.erb and .text.erb
2341
end
2442
```
2543

26-
Contrast that with rails ActionMailer, where you will spend 20 minutes trying to figure out how to send an email. I created this gem because I got tired of digging through Rails docs to understand how to intialize an email and send it. PORO's FTW!
44+
```erb
45+
<!-- app/emails/welcome_email.html.erb -->
46+
<h1>Hi <%= @user.name %>,</h1>
47+
<p>Welcome to the app!</p>
48+
```
49+
50+
Contrast that with Rails ActionMailer, where you will spend 20 minutes trying to figure out how to send an email. I created this gem because I got tired of digging through Rails docs to understand how to initialize an email and send it. PORO's FTW!
2751

2852
## Support this project
2953

@@ -46,117 +70,112 @@ Then install it in Rails.
4670
rails generate supermail:install
4771
```
4872

49-
This creates the `ApplicationEmail` class at `app/emails/application_email.rb` where you can customize the base for all emails, including setting defaults like the `from` address.
73+
This creates the `ApplicationEmail` class at `app/emails/application_email.rb`.
5074

51-
```ruby
52-
class ApplicationEmail < Supermail::Rails::Base
53-
def from = "website@example.com"
54-
def to = nil
55-
def subject = nil
56-
def body
57-
<<~_
58-
#{yield if block_given?}
75+
## Usage
76+
77+
### Basic emails
5978

60-
Best,
79+
Define emails by overriding `to`, `from`, `subject`, `plain`, and `html` methods:
6180

62-
The Example.com Team
63-
_
81+
```ruby
82+
class WelcomeEmail < ApplicationEmail
83+
def initialize(user:)
84+
@user = user
6485
end
86+
87+
def to = @user.email
88+
def subject = "Welcome!"
89+
def plain = "Welcome to the app, #{@user.name}!"
90+
def html = safe "<h1>Welcome to the app, #{@user.name}!</h1>"
6591
end
6692
```
6793

68-
## Usage
94+
- If both `plain` and `html` are defined, a multipart email is sent
95+
- If only `plain` is defined, a text-only email is sent
96+
- If only `html` is defined, an HTML-only email is sent
6997

70-
To generate a new email, run the following command:
98+
### Wrapping content with hooks
7199

72-
```bash
73-
rails generate supermail:email User::Welcome
100+
Use `before_*` and `after_*` hooks in your base class to wrap all emails:
101+
102+
```ruby
103+
class ApplicationEmail < Supermail::Rails::Base
104+
def from = "website@example.com"
105+
106+
def before_html = "<html><body>"
107+
def after_html = <<~HTML
108+
<p>Best,<br>The Example.com Team</p>
109+
</body></html>
110+
HTML
111+
112+
def after_plain = <<~TEXT
113+
114+
Best,
115+
The Example.com Team
116+
TEXT
117+
end
74118
```
75119

76-
This will create a new email class in `app/mailers/user/welcome_email.rb`.
120+
Child emails just define their content - the hooks handle the wrapping:
77121

78122
```ruby
79-
# ./app/email/user/welcome.rb
80-
class User::WelcomeEmail < ApplicationEmail
81-
def body = <<~PLAIN
82-
Hello there!
83-
PLAIN
123+
class WelcomeEmail < ApplicationEmail
124+
def subject = "Welcome!"
125+
def plain = "Welcome to the app!"
126+
def html = safe "<h1>Welcome!</h1>"
84127
end
85128
```
86-
You can customize the email by overriding the `to`, `from`, `subject`, and `body` methods.s
87129

88-
```ruby
89-
# ./app/email/user/welcome.rb
90-
class User::WelcomeEmail < ApplicationEmail
91-
def initialize(user:)
92-
@user = user
93-
end
130+
### Using Phlex components
94131

95-
def to = @user.email
96-
def subject = "Welcome to the website"
97-
def body
98-
super do
99-
<<~_
100-
Hi #{@user.name},
101-
102-
Welcome to the website We're excited to have you on board.
103-
_
104-
end
105-
end
132+
Return any object that responds to `#call` from `html`:
133+
134+
```ruby
135+
class WelcomeEmail < ApplicationEmail
136+
def html = WelcomeEmailView.new(user: @user)
106137
end
107138
```
108139

109-
### Send emails from the server
140+
### CSS inlining
110141

111-
Then, to send the email.
142+
Override `html_body` for post-processing like CSS inlining:
112143

113144
```ruby
114-
User::Welcome.new(user: User.first).deliver_now
145+
class ApplicationEmail < Supermail::Rails::Base
146+
protected
147+
148+
def html_body
149+
content = super
150+
return nil unless content
151+
Premailer.new(content, with_html_string: true).to_inline_css
152+
end
153+
end
115154
```
116155

117-
If you want to tweak the message on the fly, you can modify the message, then deliver it.
156+
### Send emails
118157

119158
```ruby
120-
User::Welcome.new(user: User.first).message.tap do
121-
it.to << "another@example.com"
122-
end.deliver_now
159+
WelcomeEmail.new(user: User.first).deliver_now
160+
WelcomeEmail.new(user: User.first).deliver_later
123161
```
124162

125-
### Launch the user's email client
126-
127-
Supermail clases can be used to generate `mailto:` links with Rails helpers.
163+
### Generate mailto: links
128164

129165
```erb
130-
<%= link_to Support::OrderEmail.new(
131-
user: current_user,
132-
order: @order
133-
).mail_to s%>
166+
<%= link_to "Contact Support", SupportEmail.new(user: current_user).mailto %>
134167
```
135168

136-
This opens your users email client with prefilled information. A support email about an order might look like this:
169+
## Generators
137170

138-
```ruby
139-
class Support::OrderEmail < ApplicationEmail
140-
def initialize(user:, order:)
141-
@user = user
142-
@order = order
143-
end
171+
Generate a new email:
144172

145-
def to = "support@example.com"
146-
def from = @user.email
147-
def subject = "Question about order #{@order.id}"
148-
def body = <<~BODY
149-
Hi Support,
150-
151-
I need help with my order #{@order.id}.
152-
153-
Thanks,
154-
155-
#{@user.name}
156-
BODY
157-
end
173+
```bash
174+
rails generate supermail:email User::Welcome
158175
```
159176

177+
Creates `app/emails/user/welcome_email.rb`.
178+
160179
## Development
161180

162181
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
# frozen_string_literal: true
22

33
class <%= class_name %> < ApplicationEmail
4-
def body = <<~PLAIN
4+
def subject = "TODO"
55

6-
PLAIN
6+
def plain = <<~TEXT
7+
8+
TEXT
9+
10+
def html = <<~HTML
11+
12+
HTML
713
end
Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
class ApplicationEmail < Supermail::Rails::Base
22
def from = "website@example.com"
33

4-
def body
5-
<<~_
6-
#{yield if block_given?}
4+
def before_html = "<html><body>"
75

8-
Best,
6+
def after_html = <<~HTML
7+
<p>Best,<br>The Example.com Team</p>
8+
</body></html>
9+
HTML
910

10-
The Example.com Team
11-
_
12-
end
11+
def after_plain = <<~TEXT
12+
13+
Best,
14+
The Example.com Team
15+
TEXT
1316
end

0 commit comments

Comments
 (0)