LoginSignup
1
2

More than 3 years have passed since last update.

wheneverを使って定期的にメールを送信する方法

Posted at

wheneverを用いて毎朝9時に記事数一覧メールの送信をする

1. 記事数一覧メールを送信できるように「送信するためのメソッド」と「メール本体」を作成する。

console
$ rails g mailer ArticleMailer

今回の「送信するためのメソッド」はArticleMailerに記述していく。
(メソッド名はreport_summaryとする)

app/mailer/ArticleMailer
class ArticleMailer < ApplicationMailer
  def report_summary
    @publish_article_count = Article.published.count
    @article_publish_at_yesterday = Article.published_at_yesterday
    mail(to: 'admin@example.com', subject: '公開済記事の集計結果')
  end
end

@publish_article_countは全ての記事の中で公開されている記事の数を出しているもの
@article_publish_at_yesterdayは全ての記事の中で公開日が昨日のものを表示している。
admin@example.comに送る設定で、題名は「公開済記事の集計結果」
Article.published_at_yesterdaypublished_at_yesterdayの部分はscopeを使い指定してやる必要がある(そうじゃないと使えない)

article.rb
scope :published_at_yesterday, -> { where(published_at: 1.day.ago.all_day) }

@publish_article_count@article_publish_at_yesterdayのように各変数を定義することでメール内でその変数、

つまり@publish_article_count=公開記事の総数@article_publish_at_yesterday=昨日記事を公開した記事の情報をメールに表示することができるようになる。

2. メールを書く

次に変数を使ってメールの本文を記述していく(ファイルは$ rails g mailer ArticleMailerをした時に自動生成される)

app/view/article_mailer/report_summary.text.erb
公開済の記事数: <%= @publish_article_count %><% if @article_publish_at_yesterday.present? %>
昨日公開された記事数: <%= @article_publish_at_yesterday.count %><% @article_publish_at_yesterday.each do |article_yesterday| %>
    タイトル: <%= article_yesterday.title %>
  <% end %>
<% else %>
昨日公開された記事はありません
<% end %>

このメールでは「公開済の記事数」「昨日公開された記事数」公開されていれば「タイトル」公開されていない場合は「昨日公開された記事はありません」と記述している。
メソッドで定義した変数を使い上記に必要な情報を取得し表示している

3. wheneverを使って毎朝9時に「記事数一覧メールを送信する」というメソッドを実行させていく

1.2でreport_summaryメソッドを実行すればメールが送信されるようになった。
次にwheneverを使い毎朝9時にreport_summaryが実行されるように記述する

schedule.rb
every 1.day, at: '9:00 am' do
  runner 'ArticleMailer.report_summary'
end

runnerと実行することでrunner以下のメソッドを実行することができるようになる。
ArticleMailerにあるreport_summaryを実行したいので上記のような書き方になる

console
$ bundle exec whenever --update-crontab 

最後に変更をcronに反映させる

メール送信のテストを記述する

メールのテストを記述していく(mailerのspecファイルは$ rails g mailer ArticleMailerの時に自動生成される)

spec/mailers/article_mailer_spec.rb
require "rails_helper"

 RSpec.describe ArticleMailer, type: :mailer do
   let(:article_publish_wait_tomorrow) { create(:article, :article_publish_wait_tomorrow) }
   let(:article_published_yesterday) { create(:article, :article_published_yesterday) }
   let(:article_published_two_days_ago) { create(:article, :article_published_two_days_ago) }
   let(:mail) { ArticleMailer.report_summary.deliver }
   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 '昨日までに公開された記事が存在しない場合' do
       it '昨日までに公開された記事がない旨の結果が送られること' do
         article_publish_wait_tomorrow
         check_sent_mail
         expect(mail.body).to match('0'), '公開済記事数の件数取得結果が正しくありません'
         expect(mail.body).to match('公開済の記事数: 0件'), '公開済記事数の送信フォーマットが正しくありません'
         expect(mail.body).to match('昨日公開された記事はありません'), '昨日公開された記事の件数取得結果が正しくありません'
         expect(mail.body).not_to match('タイトル: ' + article_publish_wait_tomorrow.title), '公開されていない記事のタイトルを取得しています'
       end
     end

     context '公開日が昨日の記事が存在する場合' do
       it '公開日が昨日の記事を含めた集計結果が送られること' do
         article_publish_wait_tomorrow
         article_published_yesterday
         check_sent_mail
         expect(mail.body).to match('1'), '公開済記事数の件数取得結果が正しくありません'
         expect(mail.body).to match('公開済の記事数: 1件'), '公開済記事数の送信フォーマットが正しくありません'
         expect(mail.body).to match('昨日公開された記事数: 1件'), '昨日公開された記事の件数取得結果が正しくありません'
         expect(mail.body).not_to match('タイトル: ' + article_publish_wait_tomorrow.title), '公開されていない記事のタイトルを取得しています'
         expect(mail.body).to match(article_published_yesterday.title), '昨日公開された記事のタイトルが取得されていません'
         expect(mail.body).to match('タイトル: ' + article_published_yesterday.title), '昨日公開された記事のタイトルの送信フォーマットが正しくありません'
       end
     end

     context '公開日が2日前の記事が存在する場合' do
       it '公開日が2日前の記事を含めた集計結果が送られること' do
         article_publish_wait_tomorrow
         article_published_two_days_ago
         check_sent_mail
         expect(mail.body).to match('1'), '公開済記事数の件数取得結果が正しくありません'
         expect(mail.body).to match('公開済の記事数: 1件'), '公開済記事数の送信フォーマットが正しくありません'
         expect(mail.body).to match('昨日公開された記事はありません'), '昨日公開された記事の件数取得結果が正しくありません'
         expect(mail.body).not_to match('タイトル: ' + article_publish_wait_tomorrow.title), '公開されていない記事のタイトルを取得しています'
         expect(mail.body).not_to match('タイトル: ' + article_published_two_days_ago.title), '昨日以前に公開された記事のタイトルを取得しています'
       end
     end

     context '公開日が昨日と2日前の記事が存在する場合' do
       it '公開日が昨日と2日前の記事を含めた集計結果が送られること' do
         article_publish_wait_tomorrow
         article_published_yesterday
         article_published_two_days_ago
         check_sent_mail
         expect(mail.body).to match('2'), '公開済記事数の件数取得結果が正しくありません'
         expect(mail.body).to match('公開済の記事数: 2件'), '公開済記事数の送信フォーマットが正しくありません'
         expect(mail.body).to match('昨日公開された記事数: 1件'), '昨日公開された記事の件数取得結果が正しくありません'
         expect(mail.body).not_to match('タイトル: ' + article_publish_wait_tomorrow.title), '公開されていない記事のタイトルを取得しています'
         expect(mail.body).to match(article_published_yesterday.title), '昨日公開された記事のタイトルが取得されていません'
         expect(mail.body).to match('タイトル: ' + article_published_yesterday.title), '昨日公開された記事のタイトルの送信フォーマットが正しくありません'
         expect(mail.body).not_to match('タイトル: ' + article_published_two_days_ago.title), '昨日以前に公開された記事のタイトルを取得しています'
       end
     end
   end
 end
spec/factories/article.rb
#上記省略
trait :article_publish_wait_tomorrow do
     state { :publish_wait }
     published_at { Time.current.tomorrow }
     category
   end

   trait :article_published_yesterday do
     state { :published }
     published_at { Time.current.yesterday }
     category
   end

   trait :article_published_two_days_ago do
     state { :published }
     published_at { Time.current.ago(2.days) }
     category
   end
end

1. letの記述

spec/mailers/article_mailer_spec.rb
require "rails_helper"

 RSpec.describe ArticleMailer, type: :mailer do
   let(:article_publish_wait_tomorrow) { create(:article, :article_publish_wait_tomorrow) }
   let(:article_published_yesterday) { create(:article, :article_published_yesterday) }
   let(:article_published_two_days_ago) { create(:article, :article_published_two_days_ago) }
   let(:mail) { ArticleMailer.report_summary.deliver }
   let(:check_sent_mail) {
     expect(mail.present?).to be_truthy, 'メールが送信されていません'
     expect(mail.to).to eq(['admin@example.com']), 'メールの送信先が正しくありません'
     expect(mail.subject).to eq('公開済記事の集計結果'), 'メールのタイトルが正しくありません'
   }

article_publish_wait_tomorrow article_published_yesterday article_published_two_days_agoはarticleにそれぞれ独自の公開日時を指定したものになる。これで公開日時によって動作が変わるテストを行うことができるようになる

・let(:mail)は ArticleMailer.report_summary.deliverでArticleMailerクラスのreport_summaryメソッドを実行する、つまりメールを送信するということ

expect(mail.present?).to be_truthy expect(mail.to).to eq(['admin@example.com']) expect(mail.subject).to eq('公開済記事の集計結果')は何度も使うのでまとめて使えるようにしておく

・メールのテストではexpect(mail.実行結果)を記述する。

expect(mail.present?).to be_truthyではmail.present?がtrueならtrueを返す。逆にexpect(mail.present?).to be_falseyはmail.present?がfalseならtrueを返す

2. テスト記述

spec/mailers/article_mailer_spec.rb
describe '公開済記事の集計結果通知メールの送信' do
     context '昨日までに公開された記事が存在しない場合' do
       it '昨日までに公開された記事がない旨の結果が送られること' do
         article_publish_wait_tomorrow
         check_sent_mail
         expect(mail.body).to match('0'), '公開済記事数の件数取得結果が正しくありません'
         expect(mail.body).to match('公開済の記事数: 0件'), '公開済記事数の送信フォーマットが正しくありません'
         expect(mail.body).to match('昨日公開された記事はありません'), '昨日公開された記事の件数取得結果が正しくありません'
         expect(mail.body).not_to match('タイトル: ' + article_publish_wait_tomorrow.title), '公開されていない記事のタイトルを取得しています'
       end
     end

expect(mail.body).to matchを使ってそれぞれ想定している単語がマッチしているかの確認をする。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2