-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
154 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,17 +9,23 @@ class Guides::Emails::SendingEmailsWithCarbon < GuideAction | |
<<-MD | ||
## Configuring Email | ||
Lucky leverages the [Carbon](https://github.com/luckyframework/carbon) library for writing, sending, and testing emails. Carbon can be configured using the default file generated with a new Lucky application in `config/email.cr`. In that file you can add SendGrid keys and change adapters. | ||
Lucky leverages the [Carbon](https://github.com/luckyframework/carbon) library for writing, sending, and testing emails. | ||
Carbon can be configured using the default file generated with a new Lucky application in `config/email.cr`. In that file | ||
you can add SendGrid keys and change adapters. | ||
## Adapters | ||
Carbon supports a growing number of adapters thanks to contributions from the community. | ||
Carbon supports a growing number of adapters thanks to contributions from the community. [View supported adapters](https://github.com/luckyframework/carbon#adapters) | ||
> If you've built an adapter not listed, be sure to let us know! | ||
### Dev Adapter | ||
The `DevAdapter` ships with Carbon by default, and is useful for handling emails in a development or test environment. It can also be leveraged in production to effectively disable emails. | ||
The `DevAdapter` ships with Carbon by default, and is useful for handling emails in a development or test environment. | ||
It can also be leveraged in production to effectively disable emails. | ||
There are two ways to leverage the `DevAdapter`. The first is by telling the adapter to simply capture all Carbon output without printing or displaying the email content, which is the default: | ||
There are two ways to leverage the `DevAdapter`. The first is by telling the adapter to simply capture all Carbon output | ||
without printing or displaying the email content, which is the default: | ||
```crystal | ||
# config/email.cr | ||
|
@@ -50,12 +56,152 @@ class Guides::Emails::SendingEmailsWithCarbon < GuideAction | |
end | ||
``` | ||
## Sending and testing emails | ||
## Creating Emails | ||
Emails are setup and configured through Crystal classes that live in your `src/emails/` directory. In that directory, you should already have a | ||
`base_email.cr` file. This is the abstract class all of your email objects will inherit from. Use the `BaseEmail` for any defaults that | ||
should be applied to all of your emails (e.g. `default_from` address, etc...) | ||
The views (HTML) related to the emails will reside in the `src/emails/templates/{ NAME_OF_EMAIL }/` directory. For example, if your email file | ||
is named `welcome_email.cr`, the templates for this will live in `src/emails/templates/welcome_email/`. | ||
> You can also check out the `PasswordResetEmail` in the `src/emails/` directory of a newly generated auth project for a live example. | ||
### Email templates | ||
There are two basic templates for emails; HTML, and TEXT. The HTML template will be where you write the raw HTML for your email. The TEXT format | ||
is used as a plain text (no HTML) email for devices and/or email apps that don't support HTML. | ||
Place the templates inside of each specific email directory they belong to. Then name them `html.ecr`, and `text.ecr`. For example, if your email | ||
file is named `welcome_email.cr`, your templates will be in `src/emails/templates/welcome_email/html.ecr` and `src/emails/templates/welcome_email/text.ecr`. | ||
The email templates will use [ECR](https://crystal-lang.org/api/1.0.0/ECR.html) for interpolating Crystal code. All instance variables/methods defined in | ||
your email class will be available within your template. | ||
See the README at https://github.com/luckyframework/carbon | ||
```html | ||
<!-- src/emails/templates/welcome_email/html.ecr --> | ||
<h1>Welcome, <%= @user.name %>!</h1> | ||
<p>...</p> | ||
<p>Secret token <%= @token %>.</p> | ||
<p>Thanks, <%= email_signature %></p> | ||
``` | ||
### Email class | ||
In the `BaseEmail`, you can set defaults that will apply to all of your emails. This includes setting special email headers, `from` address, | ||
or maybe helper methods you need to use in your email templates. | ||
```crystal | ||
# src/emails/base_email.cr | ||
abstract class BaseEmail < Carbon::Email | ||
macro inherited | ||
from default_from | ||
header "Return-Path", "[email protected]" | ||
header "Message-ID", default_message_id | ||
end | ||
def default_from | ||
Carbon::Address.new("[email protected]") | ||
end | ||
def default_message_id | ||
digest = OpenSSL::Digest.new("SHA256") | ||
digest.update(Time.utc.to_unix.to_s) | ||
message_id = digest.final.hexstring | ||
"<\#{message_id}@myapp.io>" | ||
end | ||
def email_signature : String | ||
"The MyApp Crew" | ||
end | ||
end | ||
``` | ||
```crystal | ||
# src/emails/welcome_email.cr | ||
class WelcomeEmail < BaseEmail | ||
# Define your own initializer with the | ||
# references it needs | ||
def initializer(@user : User) | ||
encryptor = Lucky::MessageEncryptor.new(secret: Lucky::Server.settings.secret_key_base) | ||
# Instance variables defined are available in your templates | ||
@token = encryptor.encrypt_and_sign("\#{@user.id}:\#{24.hours.from_now.to_unix_ms}") | ||
end | ||
to @user | ||
subject "Welcome to MyApp.io!" | ||
templates html, text | ||
end | ||
``` | ||
## Sending Emails | ||
There's two strategies to sending emails; deliver now, or deliver later. | ||
### Deliver email now | ||
Once your email class is defined, you can call the `deliver` method to send now. | ||
```crystal | ||
WelcomeEmail.new(current_user).deliver | ||
``` | ||
### Deliver email later | ||
If you need to delay sending the email, call the `deliver_later` method to send later. | ||
```crystal | ||
WelcomeEmail.new(current_user).deliver_later | ||
``` | ||
[read more](https://github.com/luckyframework/carbon#delay-email-delivery) on the `deliver_later` strategy. | ||
## Testing Emails | ||
Carbon comes with a few methods you can use in your specs to ensure emails are being sent. [read more](https://github.com/luckyframework/carbon#testing) | ||
To configure testing your emails, you'll need to add `include Carbon::Expectations` in to `spec/spec_helper.cr`. | ||
Then to make sure that emails are cleared between specs, you need to add `spec/setup/reset_emails.cr` with | ||
```crystal | ||
Spec.before_each do | ||
Carbon::DevAdapter.reset | ||
end | ||
``` | ||
### be_delivered expectation | ||
The `be_delivered` expectation is used to assert a specific email was delivered. | ||
> This only checks that the `deliver` method was called. It does not account for API hanlding in other adapters. | ||
```crystal | ||
it "delivers the email" do | ||
user = UserFactory.create &.email("[email protected]") | ||
WelcomeEmail.new(user).deliver_now | ||
# Test that this email was sent | ||
WelcomeEmail.new(user).should be_delivered | ||
end | ||
``` | ||
### have_delivered_emails expectation | ||
The `have_delivered_emails` is a bit more generic, and asserts that Carbon sent *any* email. | ||
```crystal | ||
it "delivers the email" do | ||
user = UserFactory.create &.email("[email protected]") | ||
WelcomeEmail.new(user).deliver_now | ||
# Test that any email was sent | ||
Carbon.should have_delivered_emails | ||
end | ||
``` | ||
You can also check out the `PasswordResetEmail` in the `src/emails` directory | ||
of a newly generated project. | ||
MD | ||
end | ||
end |