[博客翻译]Phlex for Rails电子邮件:没有ERB的行动邮件


原文地址:https://camillovisini.com/coding/phlex-for-rails-emails-action-mailer-without-erb


Phlex 为 Rails 邮件服务:无需 ERB 的 Action Mailer

使用 Phlex 组件和布局渲染 Action Mailer 邮件:简洁、可组合、纯 Ruby 实现

发布日期:2025年2月27日
更新日期:2025年3月2日

整天写 Ruby 很有趣,但要在 HTML 中嵌入 Ruby 就不那么令人愉快了。在过去几个月里,我发现频繁在 Ruby 和 ERB 视图及部分代码之间切换,极大地打断了我的工作流,甚至让人越来越感到厌烦。

这时我发现了 Phlex了解更多),这是一款允许你在纯 Ruby 环境下编写 HTML(组件、视图和布局)的 Ruby 库。我已经在多个 Rails 应用中完全用 Phlex 取代了 ERB,因此我想尝试将其应用于 Action Mailer。再见,ERB!

为什么这个方法会如此吸引人呢?除了单纯为了尝试而尝试之外,还有以下几个优势:

  • 告别 ERB:无论是邮件还是普通页面,都可以全程使用 Phlex。
  • 集中管理 HTML 和文本版邮件内容:在一个地方同时管理邮件的 HTML 和纯文本版本。
  • 使用 Phlex 组件构建布局和视图:让代码更加模块化和易于维护。

对于我个人而言,在 Rails 应用中彻底摆脱 ERB 是一件值得庆祝的事情。毕竟,如果某个工具不能带来“喜悦”(参考 Marie Kondo 的整理法则),那我们就应该果断舍弃它。

完整代码可以在我的 GitHub 仓库中找到,希望你也能像我一样觉得这个想法充满吸引力!

项目源码链接: visini/rails-phlex-action-mailer

接下来,我们将从一个基础的 Rails 应用入手,逐步展示如何将所有东西迁移到 Phlex。


ERB 方式的传统方法

UserMailer 类是 Action Mailer 的入口点,负责处理 Rails 应用中的邮件发送功能。下面是一个典型的实现示例:

class UserMailer < ApplicationMailer
  def welcome
    @user = params[:user]
    mail(to: @user.email, subject: "欢迎")
  end
end

我们还需要为该邮件提供对应的视图文件或“邮件模板”。由于并非所有邮件客户端都支持 HTML,我们需要分别提供 HTML 和纯文本版本。通过 ERB 模板,我们可以嵌入 Ruby 代码以访问实例变量或调用辅助方法。

HTML 版本模板:

<div>我们很高兴您加入我们!感谢您的注册。</div>
<%= link_to '点击这里登录', 'https://example.com' %>

纯文本版本模板:

我们很高兴您加入我们!感谢您的注册。
点击这里登录:https://example.com

此外,我们还需要定义一些基本的布局。布局中可以添加诸如电子邮件顶部的 logo、底部的链接、联系信息等内容。通过这种方式,修改布局变得非常方便——只需在一处进行更改即可。

HTML 布局模板:

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
  <div><b>应用名称</b></div>
  <%= yield %>
  <div>此致</div>
  <div>公司名称</div>
</body>
</html>

纯文本布局模板:

应用名称
<%= yield %>
此致
公司名称

上述模板结合它们各自的布局后,生成的邮件内容如下:

HTML 版本:

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
  <div><b>应用名称</b></div>
  <div>我们很高兴您加入我们!感谢您的注册。</div>
  <a href="https://example.com">点击这里登录</a>
  <div>此致</div>
  <div>公司名称</div>
</body>
</html>

纯文本版本:

应用名称
我们很高兴您加入我们!感谢您的注册。
点击这里登录:https://example.com
此致
公司名称

可以看到,最终生成的邮件内容包含了布局的内容包裹着主体内容。

为了测试这些模板的正确性,我们可以编写单元测试,并通过 fixture 文件来验证 HTML 和纯文本版本的邮件内容是否符合预期。


使用 Phlex 构建邮件模板

首先,按照 Phlex 安装指南 在 Rails 应用中安装 Phlex。接下来,我们可以为邮件模板创建 Phlex 视图。以下是 HTML 和纯文本两个版本的实现。

HTML 版本:

class Views::Mailers::Users::Welcome::Html < Views::Base
  def initialize(user)
    @user = user
  end

  def view_template
    div do
      plain "我们很高兴您加入我们!感谢您的注册。"
    end
    a href: "https://example.com" do
      plain "点击这里登录"
    end
  end
end

纯文本版本:

class Views::Mailers::Users::Welcome::Text < Views::Base
  def initialize(user)
    @user = user
  end

  def view_template
    plain "我们很高兴您加入我们!感谢您的注册。"
    plain "\n\n"
    plain "点击这里登录:https://example.com"
  end
end

接着,我们在原来的 ERB 模板中渲染这些 Phlex 视图:

HTML 版本:

<%= render Views::Mailers::Users::Welcome::Html.new(user: @user) %>

纯文本版本:

<%= render Views::Mailers::Users::Welcome::Text.new(user: @user) %>

此时,虽然我们已经通过 Phlex 渲染了视图,但仍然需要保留 ERB 文件作为“入口点”。这看起来有些多余,因为文件数量从原本的两份增加到了四份,增加了复杂性。不过别急,让我们继续改进。


为邮件模板设置 Phlex 布局

类似于 ERB 布局,我们也可以为邮件模板创建 Phlex 布局。首先,我们需要告诉 Action Mailer 不要使用任何默认布局,因为我们将在 Phlex 中手动组装模板和布局。

class ApplicationMailer < ActionMailer::Base
  default from: "from@example.com"
  layout false
end

删除原有的 ERB 布局文件后,我们开始为 HTML 和纯文本版本创建 Phlex 布局。

HTML 布局:

class Components::Layouts::Mailers::Html < Phlex::HTML
  def view_template(&block)
    doctype
    html do
      head do
        meta charset: "utf-8"
      end
      body do
        div do
          b { "应用名称" }
        end
        yield block
        div { "此致" }
        div { "公司名称" }
      end
    end
  end
end

纯文本布局:

class Components::Layouts::Mailers::Text < Phlex::HTML
  def view_template(&block)
    plain "应用名称"
    plain "\n\n"
    yield block
    plain "\n\n\n"
    plain "此致"
    plain "\n"
    plain "公司名称"
  end
end

在视图中,我们可以利用 around_template 钩子指定具体的布局:

HTML 版本:

class Views::Mailers::Users::Welcome::Html < Views::Base
  include Components

  def around_template
    render Components::Layouts::Mailers::Html.new do
      super
    end
  end

  def view_template
    div do
      plain "我们很高兴您加入我们!感谢您的注册。"
    end
    a href: "https://example.com" do
      plain "点击这里登录"
    end
  end
end

纯文本版本:

class Views::Mailers::Users::Welcome::Text < Views::Base
  inclu