关于Action Mailer的学习教程

94 阅读3分钟

前提条件

以下是我们在本教程中要使用的工具。

$> ruby -v  
ruby 3.1.0p0 // you need at least version 3 here  
$> bundle -v  
Bundler version 2.2.11  
$> npm -v  
8.3.0 // you need at least version 7.1 here  
$> yarn -v  
1.22.10
$> psql --version  
psql (PostgreSQL) 13.1 // let's use a production-ready database locally  
$> redis-cli ping // redis is a dependency of Sidekiq
PONG
$> foreman -v
0.87.2

创建一个简约的、空的Rails应用程序

默认情况下,Rails应用程序已经嵌入了Action Mailer,但在本教程中,我们正是想在没有帮助的情况下建立这个应用程序。Rails提供了一个--minimal 选项,允许开发者获得Rails应用程序的最低限度的文件。

另外,在默认情况下,Rails不提供路由、视图和控制器,所以下面是创建一个--minimal Rails应用程序的命令,其中至少有一个欢迎页面可以使用。

mkdir railsmails && cd railsmails  
echo "source 'https://rubygems.org'" > Gemfile  
echo "gem 'rails', '~> 7.0.0'" >> Gemfile  
bundle install  
bundle exec rails new . --force -d=postgresql --minimal

# Create a default controller
echo "class WelcomeController < ApplicationController" > app/controllers/welcome_controller.rb
echo "end" >> app/controllers/welcome_controller.rb

# Create a default route
echo "Rails.application.routes.draw do" > config/routes.rb
echo '  get "welcome/index"' >> config/routes.rb
echo '  root to: "welcome#index"' >> config/routes.rb
echo 'end' >> config/routes.rb

# Create a default view
mkdir app/views/welcome
echo '<h1>This is h1 title</h1>' > app/views/welcome/index.html.erb


# Create database and schema.rb
bin/rails db:create
bin/rails db:migrate

第一步:在应用程序中需要ActionMailer

打开application.rb,取消对action_mailer 一行的注释,如下所示。

# inside config/application.rb
require_relative "boot"

require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
# require "active_job/railtie"
require "active_record/railtie"
# require "active_storage/engine"
require "action_controller/railtie"
require "action_mailer/railtie" # ⇐ Uncomment

# ... everything else remains the same

创建ApplicationMailer,我们的基类

# inside app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  default from: "from@example.com"
  layout "mailer"
end

好了!这里非常有趣。

  • 我们猜测发件人默认是 "from@example",1)这不是我们想要的地址,2)我们可能不想让它公开。所以一个好的地方是把它放在一个.env 文件里面。
  • 需要一个布局,就像普通的Rails视图一样。
  • 最后但并非最不重要的是,通过整合我们自己的这个基类,我们现在知道了一些配置,如果应用程序是 "照常 "构建的(即没有--minimal 标志),我们就会跳过这些配置。

创建经典布局

创建app/views/layouts/mailer.html.erb

<!-- inside app/views/layouts/mailer.html.erb -->
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <style>
      /* Email styles need to be inline */
    </style>
  </head>

  <body>
    <%= yield %>
  </body>
</html>

开发模式

在开发中,你不希望任何真正的电子邮件从应用程序中流出,这可能太危险了。

为了避免这种情况,在你的Gemfile中添加letter_opener gem。这样一来,任何发送的电子邮件将只是一个写在你本地磁盘上的文件,一旦电子邮件被发送,就会立即显示在浏览器上。

# inside Gemfile
gem 'letter_opener', group: :development

然后运行

bundle install

然后通过添加以下几行配置config/environments/development.rb

# inside config/environments/development.rb
# ...
  config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
  config.action_mailer.delivery_method = :letter_opener
  config.action_mailer.perform_deliveries = true 
# ...

现在可以让应用程序发送电子邮件了。

# inside app/mailers/hello_mailer.rb
class HelloMailer < ApplicationMailer

  default from: 'me@example.com'

  def welcome_email
    @user = params[:user]
    mail(to: @user[:email], subject: 'Welcome to My Awesome Site')
  end

end

侧面说明

  • mail 将实际发送电子邮件,它来自于继承的ApplicationMailer。
  • 如第二行所述,该邮件将从 "me@example.com "发送。

现在在app/views/hello_mailer 内创建一个文件welcome_email.html.erb

<!-- inside app/views/hello_mailer/welcome_email.html.erb -->
<!DOCTYPE html>
<html>
  <head>
    <meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
  </head>
  <body>
    <h1>Welcome to example.com, <%= @user[:name] %></h1>
    <p>Thanks for joining and have a great day!</p>
  </body>
</html>

现在你可以从你的应用程序的任何地方发送电子邮件。让我们来看看如何。

# inside config/routes.rb
Rails.application.routes.draw do
  get "welcome/index"

  # route where you will send an email
  post "welcome/please_send_email"

  # where visitor are redirected once email has been sent
  get "welcome/email_sent"

  root to: "welcome#index"
end
# inside app/controllers/welcome_controller.rb
class WelcomeController < ApplicationController

  def index
  end

  def please_send_email
    HelloMailer.with(user: {name: 'jane', email: 'jane@example.com'}).welcome_email.deliver_later
  end
  
  def email_sent
  end
  
end

题外话:

  • 这里我们使用OpenStruct来模仿Ruby对象
  • deliver_later 允许我们在后台发送电子邮件,在开发或测试模式下,电子邮件将被立即发送,但我们需要一个第三方工具,如Sidekiq来处理生产中的后台工作,见这篇文章:www.bootrails.com/blog/rails-…

现在构建视图

<%# inside app/views/welcome/index.html.erb %>
<h1>This is h1 title</h1>

<%= form_with url: welcome_please_send_email_path do |f| %>
 <%= f.submit 'Send e-mail' %>
<% end %>
<%# inside app/views/welcome/email_sent.html.erb %>
<h1>We tried to send an email</h1>
<p>Please check the logs</p>

通过运行.NET,启动你的本地Web服务器。

$> bin/rails s

应该会显示以下界面。

localhost

本地主机

现在点击按钮,一封邮件应该显示在一个新的标签中。

new tab

新标签

检查控制台中的日志。

Processing by WelcomeController#email_sent as HTML
  Rendering layout layouts/application.html.erb
  Rendering welcome/email_sent.html.erb within layouts/application
[ActiveJob] [ActionMailer::MailDeliveryJob] [bf93f412-257e-4e17-8b0d-67316f95362a]   Rendered layout layouts/mailer.html.erb (Duration: 3.4ms | Allocations: 1274)
  Rendered welcome/email_sent.html.erb within layouts/application (Duration: 2.1ms | Allocations: 407)
  Rendered layout layouts/application.html.erb (Duration: 11.3ms | Allocations: 4630)
Completed 200 OK in 14ms (Views: 13.4ms | ActiveRecord: 0.0ms | Allocations: 5658)


[ActiveJob] [ActionMailer::MailDeliveryJob] [bf93f412-257e-4e17-8b0d-67316f95362a] HelloMailer#welcome_email: processed outbound mail in 289.8ms
[ActiveJob] [ActionMailer::MailDeliveryJob] [bf93f412-257e-4e17-8b0d-67316f95362a] Delivered mail 6208e3564925e_102972468-476@macbook-pro-de-david.home.mail (61.7ms)
[ActiveJob] [ActionMailer::MailDeliveryJob] [bf93f412-257e-4e17-8b0d-67316f95362a] Date: Sun, 13 Feb 2022 11:54:14 +0100
From: me@example.com
To: jane@example.com
Message-ID: <6208e3564925e_102972468-476@macbook-pro-de-david.home.mail>
Subject: Welcome to My Awesome Site
Mime-Version: 1.0
Content-Type: text/html;
 charset=UTF-8
Content-Transfer-Encoding: 7bit

很好!我们现在可以在开发模式下发送电子邮件了。

测试模式

现在是棘手的部分。在测试模式下。

  • 你不希望发送真正的电子邮件。
  • 你也不希望电子邮件像在开发模式中那样在浏览器中显示。
  • 你不希望电子邮件永远停留在工作队列中。

为了避免这一切,打开config/environments/test.rb :在文件的底部添加这3行内容

  config.action_mailer.default_url_options = { host: 'localhost', port: 5100 }
  config.action_mailer.delivery_method = :test
  config.active_job.queue_adapter = :test

default_url_options 实际数值在测试模式下并不重要,但要注意在生产模式下,你需要在 关键中添加实际的域名。host

现在谷歌一下,看看在你的测试中把perform_enqueued_jobs 放在哪里--你可以把你的测试包在这个块上,或者干脆在你的测试套件之前调用ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true

然后,在你的测试中,你所要做的就是读取最后收到的电子邮件的正文,就像这样。

ActionMailer::Base.deliveries.try(:last).try(:body).try(:decoded)

生产

在生产中,为了向真实的人发送真实的电子邮件,你需要一个第三方工具,如Mandrill、MailChimp或PostMark。

其步骤如下。

  • 订阅这些第三方供应商中的任何一个,他们通常有一个很大的慷慨的免费层级,无论如何,发送电子邮件不会在像Gmail这样的免费电子邮件供应商中大规模运作(不建议尝试)。

  • 安装相应的Ruby gem

  • 按照文档的要求获取API密钥,并按照文档的要求配置你的Rails应用程序。

警告!PostMark教程希望你修改config/application.rb ,这不是一个好主意:该配置将适用于开发和测试模式。相反,把配置放在config/environments/production.rb

就这样了

用Rails发送电子邮件并不复杂,但你必须分别处理3种环境:开发、测试和生产。3个环境,3种不同的方式来处理电子邮件。考虑到这一点,再加上缓慢但很好理解的首次安装,你在处理电子邮件时应该不会遇到任何问题。

祝您愉快!