Skip to main content
Version: 0.4

Introduction to emails

Marten lets you define emails in a very declarative way and gives you the ability to fully customize the content of these emails, their properties and associated header values, and obviously how they should be delivered.

Email definition

Emails must be defined as subclasses of the Emailing::Email abstract class and they usually live in an emails folder at the root of an application. These classes can define to which email addresses the email is sent (including CC or BCC addresses) and with which templates the body of the email (HTML or plain text) is rendered.

For example, the following snippet defines a simple email that is sent to a specific user's email address:

class WelcomeEmail < Marten::Email
from "[email protected]"
to @user.email
subject "Hello!"
template_name "emails/welcome_email.html"

def initialize(@user : User)
end
end
info

It is not necessary to systematically specify the from email address with the #from macro. Indeed, unless specified, the default "from" email address that is defined in the emailing.from_address setting is automatically used.

In the above snippet, a WelcomeEmail email class is defined by inheriting from the Emailing::Email abstract class. This email is initialized with a hypothetical User record, and this user's email address is used as the recipient email (through the use of the #to macro). Other email properties are also defined in the above snippet, such as the "from" email address (#from macro) and the subject of the email (#subject macro).

Specifying email properties

Most email properties (eg. from address, recipient addresses, etc) can be specified in two ways:

  • through the use of a dedicated macro
  • by overriding a corresponding method in the email class

Indeed, it is convenient to define email properties through the use of the dedicated macros: #from for the sender email, #to for the recipient addresses, #cc for the CC addresses, #bcc for the BCC addresses, #reply_to for the Reply-To address, and #subject for the email subject.

That being said, if more complicated logics need to be implemented to generate these email properties, it is perfectly possible to simply override the corresponding method in the considered email class. For example:

class WelcomeEmail < Marten::Email
from "[email protected]"
to @user.email
template_name "emails/welcome_email.html"

def initialize(@user : User)
end

def subject
if @user.referred?
"Glad to see you here!"
else
"Welcome to the app!"
end
end
end

Defining HTML and text bodies

The HTML body (and optionally text body) of the email is rendered using a template whose name can be specified by using the #template_name class method. By default, unless explicitly specified, it is assumed that the template specified to this method is used for rendering the HTML body of the email. That being said, it is possible to explicitly specify for which content type the template should be used by specifying an optional content_type argument as follows:

class WelcomeEmail < Marten::Email
to @user.email
subject "Hello!"
template_name "emails/welcome_email.html", content_type: :html
template_name "emails/welcome_email.txt", content_type: :text

def initialize(@user : User)
end
end

Note that it is perfectly valid to specify one template for rendering the HTML body AND another one for rendering the text body (like in the above example).

info

Note that you can define #html_body and #text_body methods if you need to override the logic that allows generating the HTML or text body of your email.

Modifying the template context

All emails have access to a #context method that returns a template context object. This "global" context object is available for the lifetime of the considered email and can be mutated in order to define which variables are made available to the template runtime when rendering templates in order to generate the HTML/text bodies of your email (which happens when sending the email).

To modify this context object effectively, it's recommended to utilize before_render callbacks, which are invoked just before rendering a template within your email. For example, this can be achieved as follows:

class WelcomeEmail < Marten::Email
from "[email protected]"
to @user.email
subject "Hello!"
template_name "emails/welcome_email.html"

before_render :prepare_context

def initialize(@user : User)
end

private def prepare_context
context[:user] = @user
end
end

In the above example, the before_render callback simply assigns a new user variable to the email template context. Consequently, a corresponding {{ user }} variable will be available in the template configured for this email (emails/welcome_email.html in this case).

Defining custom headers

If you need to insert custom headers into your emails, then you can easily do so by defining a #headers method in your email class. This method must return a hash of string keys and values.

For example:

class WelcomeEmail < Marten::Email
to @user.email
template_name "emails/welcome_email.html"

def initialize(@user : User)
end

def headers
{"X-Foo" => "bar"}
end
end

Sending emails

Emails are sent synchronously through the use of the #deliver. For example, the WelcomeEmail email defined in the previous sections could be initialized and delivered by doing:

email = WelcomeEmail.new(user)
email.deliver

When calling #deliver, the considered email will be delivered by using the currently configured emailing backend.

Emailing backends

Emailing backends define how emails are actually sent when #deliver gets called. For example, a development backend might simply "collect" the sent emails and print their information to the standard output. Other backends might also integrate with existing email services or interact with an SMTP server to ensure email delivery.

Which backend is used when sending emails is something that is controlled by the emailing.backend setting. All the available emailing backends are listed in the emailing backend reference.

tip

If necessary, it is also possible to override which emailing backend is used on a per-email basis by leveraging the #backend class method. For example:

class WelcomeEmail < Marten::Email
from "[email protected]"
to @user.email
subject "Hello!"
template_name "emails/welcome_email.html"

backend Marten::Emailing::Backend::Development.new(print_emails: true)

def initialize(@user : User)
end
end

Callbacks

It is possible to define callbacks in order to bind methods and logics to specific events in the lifecycle of your emails. For example, it is possible to define callbacks that run before or after an email gets delivered.

Please head over to the Email callbacks guide in order to learn more about email callbacks.