action-mailer-coder

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Action Mailer Coder

Action Mailer 编码指南

Mailer Design Principles

邮件器设计原则

Group Related Emails

归类相关邮件

ruby
class NotificationMailer < ApplicationMailer
  def comment_reply(user, comment)
    @user = user
    @comment = comment
    mail(to: @user.email, subject: "New reply to your comment")
  end

  def mentioned(user, mention)
    @user = user
    @mention = mention
    mail(to: @user.email, subject: "You were mentioned")
  end
end
ruby
class NotificationMailer < ApplicationMailer
  def comment_reply(user, comment)
    @user = user
    @comment = comment
    mail(to: @user.email, subject: "New reply to your comment")
  end

  def mentioned(user, mention)
    @user = user
    @mention = mention
    mail(to: @user.email, subject: "You were mentioned")
  end
end

Parameterized Mailers

参数化邮件器

ruby
class NotificationMailer < ApplicationMailer
  before_action { @user = params.fetch(:user) }
  before_action { @account = params.fetch(:account) }

  def comment_reply
    @comment = params.fetch(:comment)
    mail(to: @user.email, subject: "New reply on #{@account.name}")
  end
end
ruby
class NotificationMailer < ApplicationMailer
  before_action { @user = params.fetch(:user) }
  before_action { @account = params.fetch(:account) }

  def comment_reply
    @comment = params.fetch(:comment)
    mail(to: @user.email, subject: "New reply on #{@account.name}")
  end
end

Calling the mailer

调用邮件器

NotificationMailer.with(user: user, account: account, comment: comment).comment_reply.deliver_later
undefined
NotificationMailer.with(user: user, account: account, comment: comment).comment_reply.deliver_later
undefined

Dynamic Defaults with Inheritance

基于继承的动态默认配置

ruby
class AccountMailer < ApplicationMailer
  default from: -> { build_from_address }
  before_action { @account = params.fetch(:account) }

  private

  def build_from_address
    @account.custom_email_sender? ?
      email_address_with_name(@account.custom_email_address, @account.custom_email_name) :
      email_address_with_name("hello@example.com", @account.name)
  end
end
ruby
class AccountMailer < ApplicationMailer
  default from: -> { build_from_address }
  before_action { @account = params.fetch(:account) }

  private

  def build_from_address
    @account.custom_email_sender? ?
      email_address_with_name(@account.custom_email_address, @account.custom_email_name) :
      email_address_with_name("hello@example.com", @account.name)
  end
end

Background Delivery

后台发送

ruby
UserMailer.with(user: user).welcome.deliver_later                    # Immediate queue
UserMailer.with(user: user).welcome.deliver_later(wait: 1.hour)      # Delayed
UserMailer.with(user: user).digest.deliver_later(wait_until: Date.tomorrow.morning)  # Scheduled
ruby
UserMailer.with(user: user).welcome.deliver_later                    # 立即加入队列
UserMailer.with(user: user).welcome.deliver_later(wait: 1.hour)      # 延迟发送
UserMailer.with(user: user).digest.deliver_later(wait_until: Date.tomorrow.morning)  # 定时发送

Email Previews

邮件预览

ruby
undefined
ruby
undefined

test/mailers/previews/notification_mailer_preview.rb

test/mailers/previews/notification_mailer_preview.rb

class NotificationMailerPreview < ActionMailer::Preview def comment_reply NotificationMailer.with( user: User.first, account: Account.first, comment: Comment.first ).comment_reply end end

Access at: `http://localhost:3000/rails/mailers`
class NotificationMailerPreview < ActionMailer::Preview def comment_reply NotificationMailer.with( user: User.first, account: Account.first, comment: Comment.first ).comment_reply end end

访问地址:`http://localhost:3000/rails/mailers`

Internationalization

国际化

ruby
def welcome
  @user = params.fetch(:user)
  I18n.with_locale(@user.locale) do
    mail(to: @user.email, subject: t(".subject", name: @user.name))
  end
end
ruby
def welcome
  @user = params.fetch(:user)
  I18n.with_locale(@user.locale) do
    mail(to: @user.email, subject: t(".subject", name: @user.name))
  end
end

Attachments

附件处理

ruby
def invoice(order)
  attachments.inline["logo.png"] = File.read("app/assets/images/logo.png")
  attachments["invoice.pdf"] = generate_pdf(order)
  mail(to: order.email, subject: "Your Invoice ##{order.number}")
end
ruby
def invoice(order)
  attachments.inline["logo.png"] = File.read("app/assets/images/logo.png")
  attachments["invoice.pdf"] = generate_pdf(order)
  mail(to: order.email, subject: "Your Invoice ##{order.number}")
end

Testing (RSpec)

测试(RSpec)

ruby
RSpec.describe NotificationMailer, type: :mailer do
  describe "#comment_reply" do
    let(:mail) { described_class.with(user: user, comment: comment).comment_reply }

    it "renders the headers" do
      expect(mail.subject).to match(/New reply/)
      expect(mail.to).to eq([user.email])
    end

    it "delivers later" do
      expect { mail.deliver_later }.to have_enqueued_job(ActionMailer::MailDeliveryJob)
    end
  end
end
ruby
RSpec.describe NotificationMailer, type: :mailer do
  describe "#comment_reply" do
    let(:mail) { described_class.with(user: user, comment: comment).comment_reply }

    it "renders the headers" do
      expect(mail.subject).to match(/New reply/)
      expect(mail.to).to eq([user.email])
    end

    it "delivers later" do
      expect { mail.deliver_later }.to have_enqueued_job(ActionMailer::MailDeliveryJob)
    end
  end
end

Anti-Patterns

反模式

Anti-PatternProblemSolution
One mailer per emailHard to navigateGroup related emails
Skipping
.with()
Implicit dependenciesUse parameterized mailers
deliver_now
Blocks requestUse
deliver_later
Missing previewsCan't visually testCreate preview classes
反模式问题解决方案
每个邮件对应一个邮件器难以导航管理归类相关邮件
跳过
.with()
方法
存在隐式依赖使用参数化邮件器
使用
deliver_now
阻塞请求使用
deliver_later
缺少预览类无法进行可视化测试创建预览类

Output Format

输出格式

When creating mailers, provide:
  1. Mailer Class - The complete implementation
  2. Views - HTML and text templates
  3. Preview - Preview class for visual testing
  4. Tests - Example test cases
创建邮件器时,需提供:
  1. 邮件器类 - 完整的实现代码
  2. 视图 - HTML和文本模板
  3. 预览类 - 用于可视化测试的预览类
  4. 测试用例 - 示例测试代码