前提条件
以下是我们在本教程中要使用的工具。
$> 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
应该会显示以下界面。

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

新标签
检查控制台中的日志。
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种不同的方式来处理电子邮件。考虑到这一点,再加上缓慢但很好理解的首次安装,你在处理电子邮件时应该不会遇到任何问题。
祝您愉快!