Three mailers, each with an HTML and a text template

Free preview 3 min to read

Every mailer this app has, in full

app/mailers/auth/password_mailer.rb:

module Auth
  class PasswordMailer < ApplicationMailer
    def reset
      @user = params[:user]
      @reset_url = edit_auth_password_reset_url(params[:token])
      mail(to: @user.email, subject: I18n.t("auth.password_resets.mail.subject"))
    end
  end
end

app/mailers/partners/inquiry_mailer.rb:

module Partners
  class InquiryMailer < ApplicationMailer
    def new_inquiry
      @inquiry = params[:inquiry]
      mail(to: Rails.configuration.x.email.address, reply_to: @inquiry.email,
           subject: I18n.t("partners.inquiry.mail.subject", company: @inquiry.company, tier: I18n.t("partners.tiers.#{@inquiry.tier}.name")))
    end
  end
end

app/mailers/notifications/notification_mailer.rb:

module Notifications
  class NotificationMailer < ApplicationMailer
    layout "notifications_mailer"

    # One email for one or several notifications of the same kind, written
    # in the recipient's language.
    def notify
      @recipient = params[:recipient]
      notifications = params[:notifications]
      kind_class = notifications.first.kind_class

      ::Notifications::RecipientLocale.with(@recipient) do
        @content = kind_class.email_content(notifications, recipient: @recipient)
        @reason = I18n.t("notifications.kinds.#{kind_class.kind}.email.reason", default: "")
        @unsubscribe_url = notification_unsubscribe_url(::Notifications::UnsubscribeToken.generate(user: @recipient, key: kind_class.preference_key))
        @settings_url = identity_settings_url(anchor: "notifications")

        headers["List-Unsubscribe"] = "<#{@unsubscribe_url}>"
        headers["List-Unsubscribe-Post"] = "List-Unsubscribe=One-Click"
        mail(to: @recipient.email, subject: @content.subject)
      end
    end
  end
end

That's three mailers, this app's whole set. All three inherit from ApplicationMailer, set instance variables a view template reads, and end with a single call to mail(...). The third one does more around that call: it writes the email in the learner's language and adds the headers a mail client needs to offer a one-click unsubscribe. The shape underneath is the same.

ApplicationMailer, the shared base

# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  default from: email_address_with_name(Rails.configuration.x.email.address, "Newbie on Rails")
  layout "mailer"
end

default from: sets the sender once, and every mailer inherits it without repeating it. The address comes from one setting, config.x.email.address in config/application.rb, which reads ENV.fetch("APP_EMAIL", "[email protected]"). InquiryMailer sends to that same setting, so the sender and the inbox that receives partner inquiries can't drift apart. email_address_with_name is an Action Mailer helper that builds Newbie on Rails <[email protected]> and quotes the name when it needs quoting.

layout "mailer" is the same class-level mechanism the Layout namespacing course covered for controllers, declared on a mailer instead. NotificationMailer overrides it with layout "notifications_mailer", the way a controller can swap its parent's layout.

One HTML and one plain-text template per mailer

find app/views -path "*mailer*" -type f lists ten files. Six are per-action templates: password_mailer/reset.html.erb, password_mailer/reset.text.erb, inquiry_mailer/new_inquiry.html.erb, inquiry_mailer/new_inquiry.text.erb, notification_mailer/notify.html.erb and notification_mailer/notify.text.erb. The other four are layouts in pairs: layouts/mailer.html.erb and layouts/mailer.text.erb, shared by the first two mailers, and layouts/notifications_mailer.html.erb and layouts/notifications_mailer.text.erb for the third.

Each mailer action calls mail(...) with no format: argument. Action Mailer's convention finds both files by name and sends a multipart email: HTML for a client that renders it, and plain text as the fallback for one that doesn't. None of the mailer files asks for that explicitly.

What this course covers

Seven lessons, each checked against this app's three mailers and its installed job and queue configuration. This lesson covers the multipart setup. Lesson 2 covers the .with(...) parameterized pattern, with params[:user] and params[:token] reading the values back inside the action. Lesson 3 covers default_url_options, set differently per environment, which lets a mailer build an absolute URL with no request behind it. Lesson 4 covers deliver_later versus deliver_now, and where this app uses each, found with a grep. Lesson 5 covers Solid Queue, a durable, database-backed job backend with its own database in development and test. Lesson 6 covers config/recurring.yml, Solid Queue's scheduled-task feature, which already runs three tasks in production.

Lesson 1 of 7 · free

Keep going with lesson 2

with(...): how this app passes data into a mailer

How .with(...) turns keyword arguments into a params hash, and why positional calls fail against zero-parameter actions.

A free account also gives you the quizzes, your progress and notes, and the discussion.

Type to start searching...