Skip to content


Repository files navigation


Mail for Ruby applications.


This branch contains the code for hanami-mailer 2.x.


Gem Version CI Test Coverage Depfu Inline Docs



Hanami::Mailer supports Ruby (MRI) 3.0+


Add this line to your application's Gemfile:

gem 'hanami-mailer'

And then execute:

$ bundle

Or install it yourself as:

$ gem install hanami-mailer



  • Templates are searched under Hanami::Mailer::Configuration#root, set this value according to your app structure (eg. "app/templates").
  • A mailer will look for a template with a file name that is composed by its full class name (eg. "articles/index").
  • A template must have two concatenated extensions: one for the format and one for the engine (eg. ".html.erb").
  • The framework must be loaded before rendering the first time: Hanami::Mailer.finalize(configuration).


A simple mailer looks like this:

require 'hanami/mailer'
require 'ostruct'

# Create two files: `invoice.html.erb` and `invoice.txt.erb`

configuration = do |config|
  config.delivery_method = :test

class InvoiceMailer < Hanami::Mailer
  from ""
  to ->(locals) { locals.fetch(:user).email }

configuration = Hanami::Mailer.finalize(configuration)

invoice = 23)
mailer  = configuration)
mail    = mailer.deliver(invoice: invoice)

  # => #<Mail::Message:70303354246540, Multipart: true, Headers: <Date: Wed, 22 Mar 2017 11:48:57 +0100>, <From:>, <To:>, <Cc: >, <Bcc: >, <Message-ID: <58d25699e47f9_b4e13ff0c503e4f4632e6@escher.mail>>, <Subject: >, <Mime-Version: 1.0>, <Content-Type: multipart/alternative; boundary=--==_mimepart_58d25699e42d2_b4e13ff0c503e4f463186>, <Content-Transfer-Encoding: 7bit>>

  # =>
  # From:
  # To:
  # Message-ID: <58d25699e47f9_b4e13ff0c503e4f4632e6@escher.mail>
  # Subject:
  # Mime-Version: 1.0
  # Content-Type: multipart/alternative;
  #  boundary="--==_mimepart_58d25699e42d2_b4e13ff0c503e4f463186";
  #  charset=UTF-8
  # Content-Transfer-Encoding: 7bit
  # ----==_mimepart_58d25699e42d2_b4e13ff0c503e4f463186
  # Content-Type: text/plain;
  #  charset=UTF-8
  # Content-Transfer-Encoding: 7bit
  # Invoice #23
  # ----==_mimepart_58d25699e42d2_b4e13ff0c503e4f463186
  # Content-Type: text/html;
  #  charset=UTF-8
  # Content-Transfer-Encoding: 7bit
  # <html>
  #   <body>
  #     <h1>Invoice template</h1>
  #   </body>
  # </html>
  # ----==_mimepart_58d25699e42d2_b4e13ff0c503e4f463186--

A mailer with .to and .from addresses and mailer delivery:

require 'hanami/mailer'

configuration = do |config|
  config.delivery_method = :smtp,
                           address:              "",
                           port:                 587,
                           domain:               "",
                           user_name:            ENV['SMTP_USERNAME'],
                           password:             ENV['SMTP_PASSWORD'],
                           authentication:       "plain",
                           enable_starttls_auto: true

class WelcomeMailer < Hanami::Mailer
  return_path ''
  from ''
  to   ''
  cc   ''
  bcc  ''

  subject 'Welcome'
end configuration).call(locals)


The set of objects passed in the deliver call are called locals and are available inside the mailer and the template.

require 'hanami/mailer'
require 'ostruct'

user = Luca', email: '')

class WelcomeMailer < Hanami::Mailer
  from    ''
  subject 'Welcome'
  to      ->(locals) { locals.fetch(:user).email }
end configuration).deliver(user: luca)

The corresponding erb file:

Hello <%= %>!


All public methods defined in the mailer are accessible from the template:

require 'hanami/mailer'

class WelcomeMailer < Hanami::Mailer
  from    ''
  to      ''
  subject 'Welcome'

  def greeting
<h2><%= greeting %></h2>


The template file must be located under the relevant root and must match the inflected snake case of the mailer class name.

# Given this root
configuration.root      # => #<Pathname:app/templates>

# For InvoiceMailer, it looks for:
#  * app/templates/invoice_mailer.html.erb
#  * app/templates/invoice_mailer.txt.erb

If we want to specify a different template, we can do:

class InvoiceMailer < Hanami::Mailer
  template 'invoice'

# It will look for:
#  * app/templates/invoice.html.erb
#  * app/templates/invoice.txt.erb


The builtin rendering engine is ERb.

This is the list of the supported engines. They are listed in order of higher precedence, for a given extension. For instance, if ERubis is loaded, it will be preferred over ERb to render .erb templates.

Engine Extensions
Erubis erb, rhtml, erubis
ERb erb, rhtml
Redcarpet markdown, mkd, md
RDiscount markdown, mkd, md
Kramdown markdown, mkd, md
Maruku markdown, mkd, md
BlueCloth markdown, mkd, md
Asciidoctor ad, adoc, asciidoc
Builder builder
CSV rcsv
CoffeeScript coffee
WikiCloth wiki, mediawiki, mw
Creole wiki, creole
Etanni etn, etanni
Haml haml
Less less
Liquid liquid
Markaby mab
Nokogiri nokogiri
Plain html
RDoc rdoc
Radius radius
RedCloth textile
Sass sass
Scss scss
Slim slim
String str
Yajl yajl


Hanami::Mailer can be configured with a DSL that determines its behavior. It supports a few options:

require "hanami/mailer"

configuration = do |config|
  # Set the root path where to search for templates
  # Argument: String, Pathname, #to_pathname, defaults to the current directory
  config.root = "path/to/root"

  # Set the default charset for emails
  # Argument: String, defaults to "UTF-8"
  config.default_charset = "iso-8859"

  # Set the delivery method
  # Argument: Symbol
  # Argument: Hash, optional configurations
  config.delivery_method = :smtp


Attachments can be added with the following API:

class InvoiceMailer < Hanami::Mailer
  # ...
  before do |mail, locals|
    mail.attachments["invoice-#{locals.fetch(:invoice).number}.pdf"] = 'path/to/invoice.pdf'

Delivery Method

The global delivery method is defined through the Hanami::Mailer configuration, as:

configuration = do |config|
  config.delivery_method = :smtp
configuration = do |config|
  config.delivery_method = :smtp, { address: "localhost", port: 1025 }

Builtin options are:

  • Exim (:exim)
  • Sendmail (:sendmail)
  • SMTP (:smtp, for local installations)
  • SMTP Connection (:smtp_connection, via Net::SMTP - for remote installations)
  • Test (:test, for testing purposes)

Custom Delivery Method

Developers can specify their own custom delivery policy:

require 'hanami/mailer'

class MandrillDeliveryMethod
  def initialize(options)
    @options = options

  def deliver!(mail)
    # ...

configuration = do |config|
  config.delivery_method = MandrillDeliveryMethod,
                           username: ENV['MANDRILL_USERNAME'],
                           password: ENV['MANDRILL_API_KEY']

The class passed to .delivery_method= must accept an optional set of options with the constructor (#initialize) and respond to #deliver!.

Multipart Delivery

All the email are sent as multipart messages by default. For a given mailer, the framework looks up for associated text (.txt) and HTML (.html) templates and render them. configuration).deliver({})           # delivers both text and html templates configuration).deliver(format: :txt) # delivers only text template

Please note that they aren't both mandatory, but at least one of them MUST be present.


Hanami::Mailer uses Semantic Versioning 2.0.0


Copyright © 2015-2021 Luca Guidi – Released under MIT License

This project was formerly known as Lotus (lotus-mailer).