Use case #11: Sending emails
I got to the point where I needed to send transactional emails with my Hanami app, I wanted to render them with templates and that they would be easy to send, and ideally integrated into the framework sot that I could come from an action, a job, a rake task…
After a few tries I found that the hanami-mailer was a good choice to start, despite its rough edges that I hope I will be have the level to polish some day. Thanks to this episode from Hanami Mastery I had the instructions on how to integrate it so I adapted it into my app.
The instructions below are heavily inspired from what I learnt from the above article, all credit is due to @swilgosz , thanks a lot to him!
First you’ll need to add the gem to your gemfile and call bundle install
# Gemfile
gem "hanami-mailer", github: "hanami/mailer", branch: "main"
Then you need to load what you need in a provider. I decided to use one provider per slice to load my templates aka mailers. I felt that it suits well with the fact that each slice will have its own way to create and send emails.
# slices/my_slice/config/providers/mailer.rb
MySlice::Slice.register_provider(:mailer, namespace: true) do
prepare do
require "hanami/mailer"
configuration = Hanami::Mailer::Configuration.new do |config|
config.root = target.root.join("mailers")
config.default_charset = "UTF-8"
if ENV['HANAMI_ENV'] != "production"
config.delivery_method = :logger, {logger: Logger.new(STDOUT)}
# FYI config.delivery_method = :test is another possible option
else
# here you can specify more complex options for your SMTP params
config.delivery_method = :smtp, {:enable_starttls_auto => false}
end
end
register "configuration", configuration
end
start do
configuration = target['mailer.configuration']
# Hanami::Mailer requires to initialize all mailers before finalizing
# config, and finalizing config before using them.
mailers = Dir[configuration.root.join('*.rb')]
mailers.each do |path|
mailer_name = File.basename(path, '.*')
target["mailers.#{mailer_name}"]
end
Hanami::Mailer.finalize(configuration)
register "mailer", true
end
end
=> With that config, all “mailers” found in slices/my_slice/mailers directory will be loaded and ready to be used from dependency container.
A mailer is a ruby class inheriting from Hanami::Mailer class
Here is a sample yet realistic mailer:
# slices/my_slice/mailers/notify_print_is_ready.rb
require "hanami/mailer"
module MySlice
module Mailers
class NotifyPrintIsReady < Hanami::Mailer
include Deps['mailer.configuration']
from "Support team <support@acme.com>"
to ->(locals) { locals.fetch(:to_email) }
subject ->(locals) { "Your print '#{locals.fetch(:project_name)}' is ready ✅" }
template 'notify_print_is_ready'
end
end
end
This mailer will need an associated template, that will be rendered before sending the email.
# slices/my_slice/mailers/notify_print_is_ready.html.erb
<html>
<body>
<p>Your print <%= locals.fetch(:project_name) %> is ready</p>
<body>
</html>
The email can then be sent from anywhere in your code:
MySlice::Slice["mailers.notify_print_is_ready"].deliver({
to_email: "jim.halpert@gmail.com",
project_name: chapter_print.name,
})
And voilà ![]()
Unfortunately it does not seem possible to inherit from a base layout, I’d love to help on that some day, let me know if you have any pointer or any ongoing work about the topic.
Thanks for reading, see you next time