Rails 學習筆記簿: 實作開發模式 Action Mailer 寄信功能 with Sidekiq

王建豪
10 min readApr 12, 2020

--

Sidekiq 是ㄧ個功能齊全並且能夠快速處理背景任務的Ruby架構。它搭配Redis(key-value 的記憶體資料庫) ,提升性能及整合 Rails 的應用程式,例如處理大量耗時的計算、寄送電子信件、或是定期的報表製作。

此篇文章主要是記錄在撰寫專案中所遇到的坑,希望提供別人及自己往後的學習之用,如果內容有錯誤的地方,歡迎指正。

接下來需要先了解以下主題:

- Active Job 基礎
- Action Mailer Basics
- Redis 的基本認識

實作之前準備:

  • 一個部署在 Heroku 的 rails 專案
  • 註冊 Gandi 的域名 或是其他提供相同服務的 service provider (Mailgun)
  • 實作版本:ruby 2.6.3 以及 Rails 6.0.2

第一步:新增 Mailer

Rails 有提供內建的 Action Mailer 功能,所以只要在終端機輸入以下指令:

rails generate mailer UserMailer

之後修改預設的 from@example.com 為寄件人的 E-mail。

# app/mailers/application_mailer.rbclass ApplicationMailer < ActionMailer::Base
default from: 'from@example.com'
layout 'mailer'
end
# app/mailers/user_mailer.rbclass UserMailer < ApplicationMailer
end

第二步:修改 Mailer

Mailer 也有類似像 Controller 的 action, 也會依照慣例對應到 view。在 UserMailer 中我們新增 “ welcome_email “ action, 從 params 中取得該使用者的 E-mail後,再寄出信件。

# app/mailers/user_mailer.rbclass UserMailer < ApplicationMailer
default from: 'postmaster@chienhao.tw'

def welcome_email
@user = params[:user]
@url = 'https://taskmanager0117.herokuapp.com/login'
mail(to: @user.email, subject: "Welcome to Task Manager")
end
end

第三步:建立 Mailer View

新增歡迎信件的樣板

# app/views/user_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 Task Manager, <%= @user.username %></h1>
<p>
You have signed up to taskmanager0117.herokuapp.com,
your username is: <%= @user.email%><br>
</p>
<p>
To login to the site, just follow this link: <%= @url %>.
</p>
<p>
Thanks for joining and have a great day!
</p>
</body>
</html>

第四步:呼叫 Mailer

當使用者註冊成為會員時,就會收到來自系統的歡迎信件。因此在 UserController 的 create action時就要呼叫 UserMailer 執行寄信動作。

# app/controllers/users_controller.rbdef create
@user = User.new(user_params)
if @user.save
UserMailer.with(user: @user).welcome_email.deliver_later
redirect_to root_path, notice: "#{@user.username} 已註冊成功!"
else
@error_message = @user.errors.full_messages.to_sentence
render :new
end
end

第五步:修改開發環境設定檔

從 gandi 取得 SMTP 以及帳號、密碼資訊之後,再修改 development.rb 的設定檔,便可以利用 gandi 的 SMTP 伺服器寄信。 當然帳號及密碼都是需要利用環境變數存放,可以試試 figaro 來設定你自己的環境變數。

gandi.net 的 SMTP

順利的話,以上設定完成後就可以寄信件出去了!!

第六步:安裝 sidekiq gem and Redis

首先在 Gemfile 裡新增 Sidekiq,然後記得要 bundle

gem 'sidekiq',‘~> 6.0’, ‘>= 6.0.6’

接著你可以在不同環境(development.rb / production.rb) 做以下設定,或是直接設定在 application.rb 都可以

config.active_job.queue_adapter = :sidekiq

如同先前所說 Sidekiq 是搭配 Redis 來做存取背景任務,所以要在終端機內輸入以下指令來安裝 Redis

brew install redis

第七步:建立 Worker

成功完成安裝 Sidekiq 後,便可以開始新增 一個 worker 來處理信件的寄送。

# app/workers/mail_worker.rbclass MailWorker
include Sidekiq::Worker
sidekiq_options queue: :default, retry: 3
def perform(user_id)
user = User.find(user_id)
if user
UserMailer.welcome_email(user).deliver_later
else
errors.add(:base, '找不到使用者')
throw(:abort)
end
end
end

注意:你的 perform 方法內的引數必須是簡單的屬性且能夠被 JSON 所支援,例如:字串、整數或是布林值。複雜的 Ruby 物件是不被允許的,比較好的作法就是將 user_id 傳進去方法裡,再取得所需要的 User 物件

第八步:重構程式碼

修改 Users Controller 內的 create action

# app/controllers/users_controller.rbdef create
@user = User.new(user_params)
if @user.save
MailWorker.perform_async(@user.id)
redirect_to root_path, notice: "#{@user.username} 已註冊成功!"
else
@error_message = @user.errors.full_messages.to_sentence
render :new
end
end
### 注意:perform 是實體方法,而 perform_async 類別方法

在 UserMailer 的 welcome_email 方法將取得 User 物件, 並從參數中取得該使用者的 E-mail後,便可以寄出信件。

# app/mailers/user_mailer.rbclass UserMailer < ApplicationMailer
default from: 'postmaster@chienhao.tw'
def welcome_email(user_object)
@user = user_object
@url = 'https://taskmanager0117.herokuapp.com/login'
mail(to: @user.email, subject: "Welcome to Task Manager")
end
end

第九步:建立 Web UI

Sidekiq 提供Web UI 介面可以看出目前有哪些任務在執行,在routes.rb加入

# config/routes.rbrequire 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'

這邊要注意的是 mount 並沒有提供權限控管,所以任何人都可以檢視 Web UI 介面。如果你是使用 Devise 會員系統,可以加上

authenticate :user, ->(user) { user.admin? } do
mount Sidekiq::Web => '/sidekiq'
end

更多的詳細資料可以閱讀在 Sidekiq 的 Wiki-Monitoring

第十步:本地端測試

此步驟需要在終端機開啟

  1. redis: 輸入 redis-server
  2. sidekiq: 輸入 bundle exec sidekiq -q default -q mailers
  3. rails: 輸入 rails shttp://localhost:3000/sidekiq

測試註冊一個會員後,是否有收到信件?以及在 http://localhost:3000/sidekiq是否有看到 Web UI?

參考資料:

--

--

No responses yet