Our Hanami 3.0 release is coming soon (a release candidate in a matter of weeks), and one of the big new features is a rebuilt hanami-mailer gem.
Here I’ll outline how mailers will integrate into Hanami apps, so that you can understand how it all fits together, give it a try yourself, and share your feedback.
Hanami Mailer is completely rebuilt!
For starters, go check out the Hanami Mailer README. This is a completely rebuilt gem designed in the same spirit as every other first-party Hanami component, like Hanami Action and Hanami View.
The README outlines all the key capabilities and APIs. This is 99% of what you’ll be writing for your mailers in Hanami apps, so please have a read and make sure your own expected use cases are covered.
App and slices have base mailer classes
Following our standard approach, we’ll generate app/mailer.rb and slices/[your_slice]/mailer.rb base classes. These will be good places to put your standard headers (which can be overridden in subclasses as required).
require "hanami-mailer"
module MyApp
class Mailer < Hanami::Mailer
# A standard `from` header for every mailer
from "noreply@example.com"
end
end
Mailers will be generated into mailers/, but can go anywhere
For all our standard components, we need to pick a default location. For mailers, this will be mailers/ (and the Mailers namespace) in the app or slice.
# app/mailers/user_welcome.rb
module MyApp
module Mailers
class UserWelcome < MyApp::Mailer
to { |user:| user.email }
expose :user
end
end
end
We will not be encouraging a “Mailer” suffix on these class names. Their presence within their namespace is indication enough that it is a mailer.
We will provide a hanami generate mailer CLI command to generate these files.
Like every Hanami component, however, mailers can be relocated to wherever you want them. So if you want to organise your app differently, you’re very welcome to!
Mailer templates are loaded from templates/mailers/
Mailer templates are rendered via Hanami View (provided you keep it in your app’s Gemfile), and are configured to load their templates from templates/mailers/
In the case of the UserWelcome mailer above, it will render these two templates:
app/templates/mailers/user_welcome.html.erbapp/templates/mailers/user_welcome.text.erb
You don’t need to provide both formats. You can provide just one of :html or :text if that’s your preference for your emails.
All standard view facilities are available for your mailers
Mailers render their views via an internally-created view class. For mailers in Hanami apps, this class will inherit from your app or slice’s base view class (e.g. MyApp::View). This means that all framework-provided view facilities are available for your mailers too, such as your standard view context and helpers.
Any view functionality that depends on a current request will of course not work, since mailers are not tied to a web request/response cycle.
You’ll primarily use mailers as injectable dependencies
Mailers are designed to work as injectable dependencies, and that’s exactly how you’re likely to use them in your apps.
For example, to send the user welcome email:
module MyApp
module Account
class SignUp < MyApp::Operation
include Deps["mailers.user_welcome"]
def call(attrs)
user = step create_user(attrs)
user_welcome.deliver(user:)
user
end
private
def create_user(attrs)
user = create_the_user # (your logic here)
Success(user)
end
end
end
end
An SMTP delivery method is created based on convention env vars
Your mailers will deliver via a Hanami::Mailer::Delivery::SMTP delivery method instance, if you provide the following env vars:
SMTP_ADDRESSSMTP_PORTSMTP_USERNAMESMTP_PASSWORDSMTP_AUTHENTICATION
Example values:
SMTP_ADDRESS=smtp.example.com
SMTP_PORT=587
SMTP_USERNAME=postmaster@example.com
SMTP_PASSWORD=s3cr3t
SMTP_AUTHENTICATION=plain
Provide as many or as few as you need to connect to your SMTP server.
These env vars can be prefixed by slice name to set up a delivery method for a specific slice:
MY_SLICE__SMTP_ADDRESS=smtp.example.com
MY_SLICE__SMTP_PORT=587
This delivery method is registered in your containers as "mailers.delivery_method", so you can access it independently if required.
A test delivery method is set up as a fallback
When no SMTP ENV vars are available, a Hanami::Mailer::Delivery::Test delivery method will be used for your mailers.
This delivery method does not actually send your emails. Instead, it collects the mail objects in memory:
MyApp["mailers.delivery_method"].deliveries # => [your, delivered, emails]
If this fallback runs when Hanami is in the :production env, then it will emit a warning to make sure you haven’t misconfigured your app:
No SMTP configuration found for my_app in production. Falling back to the test delivery method. Mail will NOT be sent.
Set SMTP_ADDRESS (and related SMTP_* variables), or register a custom :mailers provider. See https://hanakai.org/learn/hanami/mailers for more.
We warn rather than fail to boot because it’s reasonable for a user to deploy their app to production before they’re ready to send emails. We don’t want to get in the way of that.
A test delivery method is standard in :test env
When running your app with HANAMI_ENV=test, then a test delivery method is used by default, even if SMTP_* env vars exist. The test delivery method is more appropriate for testing, and we don’t want you to accidentally make real deliveries of test-generated emails.
You can customise mailer setup with your own :mailers provider
If you want to set up a custom delivery method or customise any other aspect of the delivery method setup, you can create your own :mailers provider.
Hanami.app.register_provider :mailers, namespace: true do
start do
delivery_method =
if Hanami.app.env == :test
Hanami::Mailer::Delivery::Test.new
else
MyCustomDeliveryMethod.new
end
register "delivery_method", delivery_method
end
end
When you use your own provider, you’ll need to take care of the test-mode delivery method yourself, as shown above.
OK! I think that’s all the ins and outs you need to know. I’m keen for your feedback. Please let me know if there’s anything else you think we should be doing for mailers in Hanami! ![]()