wheneverとは
○時になったら○○のコマンドを実行
してくれるcron
というものがある。このcron
の設定を、ruby
の簡単な文法で扱えるようにしたライブラリがwhenever
。
▷【Rails】wheneverでcronを設定
Mailerとは
Rails
には、デフォルトでActionMailer
と呼ばれるメール送信機能が備わっている。
通常のテキストメール送信に加え、HTMLメールが送信できたり、添付ファイルを付けるなど様々なオプションがある。
実装内容
毎日am9:00に下記の内容を管理者にメールで送信。
・公開済の記事の総記事数
・昨日公開された記事の件数とタイトル
・メールの件名には「公開済記事の集計結果」と設定
・管理者のメールアドレスにはadmin@example.com
を設定
・users
テーブルにemail
カラムは追加しなくてOK
実装の流れ
1.モデルにスコープを追加
2.メールを作成
3.毎日am9:00にメールを送信できるよう設定
1.モデルにスコープを追加
実装内容の条件の1つに、「昨日公開された記事の件数とタイトル」とあるので、昨日公開された記事をスコープで取得できるようにしておく。
scope :published_at_yesterday, -> { published.where(published_at: 1.day.ago.all_day) }
2.メールを作成
Mailer
は導入済みで進める。
ターミナルにて rails g mailer ArticleMailer
を入力。
上記のコマンドで
・ActiveMailer::Base
を継承したApplicationMailer
とArticleMailer
(自分で命名したもの)が生成される。
・Mailer
を作成するとview
のディレクトリとテストも同時に作成される。
app/mailers/application_mailer.rb
とapp/mailers/article_mailer.rb
の2つに内容を記述していく。
class ApplicationMailer < ActionMailer::Base
default from: 'from@example.com'
layout 'mailer'
end
class ArticleMailer < ApplicationMailer
def report_summary
@published_article_count = Article.published.count
@articles_published_at_yesterday = Article.published_at_yesterday
#↑先程モデルで定義したスコープ
mail(to: "admin@example.com",subject: "公開済記事の集計結果")
end
end
次に、メール本文を記述する。
公開済の記事数: <%= @published_article_count %>件
<% if @articles_published_at_yesterday.present? %>
昨日公開された記事数: <%= @articles_published_at_yesterday.count %>件
<% @articles_published_at_yesterday.each do |article| %>
タイトル: <%= article.title %>
<% end %>
<% else %>
昨日公開された記事はありません
<% end %>
config
にてメールが直接確認できるよう設定。
config.action_mailer.delivery_method = :letter_opener_web
#↑配信方法を指定している
config.action_mailer.default_url_options = { host: 'localhost:3000' }
#↑アプリケーションのホスト情報をメイラー内で使うためのオプション
2.毎日am9:00にメールを送信できるよう設定
冒頭でwhenever
とは○時になったら○○のコマンドを実行してくれる〜と記載したが、まずは実行する 中身 をrakeタスク
にて作成していく。
今回はタスク名をarticle_summary
として命名。
$ rails g task article_summary
lib/tasks/article_summary.rake
が作成されるので、この中に定義する。
namespace :article_summary do
desc "am9時に公開済記事の集計結果を管理者にメールで送る"
task mail_article_summary: :environment do
ArticleMailer.report_summary.deliver_now
end
end
・ArticleMailer.report_summary
先程メール作成で定義したもの
・deliver_now
非同期ではなく、すぐにメールを送信する。つまりメールを送信し終えるまで、次の行は実行されない。
用途としてはrailsコンソール
やrakeタスク
など、すぐに処理を実行して終了まで待ちたいときに使う。
似たようなものでdeliver_later
がある。こちらはActive Job
を使用して非同期でメールを送信する。つまりメールの送信処理を待たなくても、次の行を実行できる。
これによりユーザーはメール送信という時間のかかる処理を待たずに、次の画面を見ることができる。
実行する中身を定義できたので、後は「毎日am9:00にメールを送信」を設定すればOK
require File.expand_path(File.dirname(__FILE__) + '/environment')
# Rails.rootを使用するために必要
rails_env = ENV['RAILS_ENV'] || :development
# cronを実行する環境変数
set :environment, rails_env
# cronを実行する環境変数をセット
set :output, "#{Rails.root}/log/cron.log"
# cronのログの吐き出し場所
every 1.day, at: '9am' do
rake 'article_summary:mail_article_summary'
end
rspec
FactoryBot.define do
factory :article do
sequence(:title) { |n| "title-#{n}" }
sequence(:slug) { |n| "slug-#{n}" }
category
end
trait :published_tomorrow do
published_at { Time.current.tomorrow }
state { :publish_wait }
category
end
trait :published_yesterday do
published_at { Time.current.yesterday }
state { :published }
category
end
trait :published_two_days_ago do
published_at { Time.current.ago(2.days) }
state { :published }
category
end
end
require "rails_helper"
RSpec.describe ArticleMailer, type: :mailer do
let(:article_tomorrow) { create :article, :published_tomorrow}
let(:article_yesterday) { create :article, :published_yesterday}
let(:article_two_days_ago) { create :article, :published_two_days_ago}
let(:mail) { ArticleMailer.report_summary.deliver_now }
let(:check_sent_mail) {
expect(mail.present?).to be_truthy, 'メールが送信されていません'
expect(mail.to).to eq(['admin@example.com']), 'メールの送信先が正しくありません'
expect(mail.subject).to eq('公開済記事の集計結果'), 'メールのタイトルが正しくありません'
}
describe '公開済記事の集計結果通知メールの送信' do
context '公開日が2日前の記事が存在する場合' do
it '公開日が2日前分の記事の結果が送られること' do
article_tomorrow
article_yesterday
article_two_days_ago
check_sent_mail
expect(mail.body).to match '2'
expect(mail.body).to match '公開済の記事数: 2件'
expect(mail.body).to match 'タイトル: ' + article_yesterday.title
expect(mail.body).not_to match '昨日公開された記事はありません'
expect(mail.body).not_to match 'タイトル: ' + article_tomorrow.title
expect(mail.body).not_to match 'タイトル: ' + article_two_days_ago.title
end
end
end
end
・be_truthy
be_truthy / be_falsey
を使うと、その仕様にあわせて戻り値の真偽を検証してくれる。
つまり、「trueっぽい値」または「falseっぽい値」かどうかを検証してくれる。
・match
正規表現にマッチするかどうかを判断している。
正規表現とは
メタ文字と呼ばれる記号と文字列を組み合わせた構文のことで、検索対象となる文章中から任意の文字列パターンを検索することができます。