wheneverを使用して記事数一覧のメール送信

2021.6.10 応用編10

 

<目標>

  • wheneverを導入して毎日am9:00に下記の内容を管理者にメールで送信させるメールの件名には「公開済記事の集計結果」と設定

 

流れ

参照

ActionMailerとWheneverの使い方とメール送信の定期実行(letter_opener)|moeno|note

Mailer作成

$ rails g mailer ArticleMailer

ActiveMailer::Baseを継承したApplicationMailerArticleMailer(自分で命名したもの)が生成される。

・Mailerを作成するとviewのディレクトリとテストも同時に作成される。

 

app/mailer/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
default from: 'from@example.com'
layout 'mailer'
end
 
app/mailer/article_mailer.rb
 
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: '公開済記事の集計結果')
mail(to: '宛先のアドレス' , subject: '件名' )
end
end
 

 

Milerビューを作成する

"app/views/layouts/mailer.html.erb" "app/views/layouts/mailer.text.erb"

この2つのファイルがmailerを作成したタイミンで自動的に作成されている。

 

メール送信の定期実行

lib/tasks以下にcronを使用して、定期的に実行する処理のかたまり(rakeタスク)を作成していく

(lib/tasks/article_summary.rake)

namespace :article_summary do
desc '管理者に対して総記事数、昨日公開された記事数とタイトル
をメールで送信'
task mail_article_summary: :environment do
ArticleMailer.report_summary.deliver_now
end
end
ポイント

namespaceはfile名と揃える(自分で作成)

・"desc"はこのタスクの説明(自分で作成)

・"task" の "mail_article_summary:" は自分で命名していい。config/suchedule.rbにも同じ名前を記述することになる。

 

whenever

$ wheneverize . 

↑を作成中のアプリディレクトリ内で実行。config/suchedule.rbというファイルが作成され、ここに定期実行したいタスク設定(実行の間隔、時間)を書き込んで反映させる。

config/suchedule.rb

# Rails.rootを使用するために必要
require File.expand_path(File.dirname(__FILE__) + '/environment')
# cronを実行する環境変数
rails_env = ENV['RAILS_ENV'] || :development
# cronを実行する環境変数をセット
set :environment, rails_env
# cronのログの吐き出し場所
set :output, "#{Rails.root}/log/cron.log"
# rakeタスクを1時間ごとに実行
 
every 1.day, at: '9am' do
rake 'article_summary:mail_article_summary'
end

 

設定の反映

cronにデータを反映させる

 $ bundle exec whenever --update-crontab

 

letter_opener gem の使い方

開発環境で送信したメールをブラウザで確認することができる。

 

設定ファイル編集

(config/environment/development.rb)

config.action_mailer.delivery_method = :letter_opener_web
#↑メール送信方法を記述する (test.rbだけ :test にする。テストはテスト配信)
config.action_mailer.default_url_options = { host: 'localhost:3000' }

参照

Railsのconfig/enviroments配下を読んでみる - Qiita

(spec/mailers/previews/article_mailer_preview.rb)に記載されているURLでプレビューできる。

class ArticleMailerPreview < ActionMailer::Preview

le_mailer/report_summary #これ
def report_summary
ArticleMailer.report_summary
end
end

 

ルーティング編集

(route.rb)

if Rails.env.development? #開発時用の処理
get '/login_as/:user_id', to: 'development/sessions#login_as' #開発環境でログインする為
mount LetterOpenerWeb::Engine, at: '/letter_opener' #今回ここ追加
end

 

locaohost:3000/letter_opener 

送信されたメールをブラウザで確認できる。

 

ポイント

deliver deliver_now deliver_later 違い

ex) 今回の場合

(app/mailers/article_mailer.rb)

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

このreport_summaryメソッドはActionMailer::MessageDeliveryオブジェクトを返す

pry(main)> ArticleMailer.report_summary.class
=> ActionMailer::MessageDelivery

ActionMailer::MessageDeliveryオブジェクト:

そのメール自身が送信対象であることをdeliver_nowやdeliver_laterに伝える。

Mail::Message(メールのメッセージに関連することを提供する)をラップしている。

Mail::Messageのに関して参照

Class: Mail::Message — Documentation for mikel/mail (master)

deliver

ex)

ArticleMailer.report_summary.deliver

Mail::Message#deliver(=オブジェクト自体)が呼ばれる。

Mail::Message#deliverを直接呼ぶのではなく、ActionMailer::MessageDeliveryのメソッドを呼ぶべき。

 

deliver_now(同期処理=メールを送信する)

→メールを送信し終えるまで、次の行は実行はされない。

railsコンソールやrakeタスクなど、すぐに処理を実行し終了まで待ちたい時に使う。

 

deliverとの違い

ソースコード

 

ActionMailer::MessageDelivery#deliver_now```rb def deliver_now processed_mailer.handle_exceptions do message.deliver end end ``` [View on GitHub](https://github.com/rails/rails/blob/master/actionmailer/lib/action_mailer/message_delivery.rb#L113-L117)

 

例外処理をしているなかで、Mail::Message#deliverを実行している。

→同じ同期処理のdeliverではなく、deliver_nowを使用すべき。

 

deliver_later(Active Jobを使用して非同期でメールを送信する)

→メールの送信処理を待たなくても次の行を実行できる。

ユーザーはメール送信という時間のかかる処理を待たずに次の表示をみることができる。

 

ActionMailer::MessageDelivery#deliver_later```rb def deliver_later(options = {}) enqueue_delivery :deliver_now, options end ``` [View on GitHub](https://github.com/rails/rails/blob/master/actionmailer/lib/action_mailer/message_delivery.rb#L94-L96)

 

MailerのviewでHTMLタグなどを書かないこと

他のViewファイル同様レイアウトファイルが指定されているから、書いてしまうと重複になってしまう。

 

class ApplicationMailer < ActionMailer::Base
default from: 'from@example.com'
layout 'mailer' #ここで指定
end

 

(views/layouts/mailer.html.slim)

html
body
= yield

 

 

empty? blank? present? 違い

empty?

空かどうかを確認する。レシーバーがnilだとUndefineMethodになってしまう。

[1] pry(main)> .empty?
=> true
[2] pry(main)> ''.empty?
=> true
[3] pry(main)> nil.empty?
NoMethodError: undefined method `empty?' for nil:NilClass
from (pry):3:in `<main>'
[4] pry(main)> nil.respond_to? :empty?
=> false

 

blank?

空かどうかを確認する。nilに対しても使用できる。
[5] pry(main)> .blank?
=> true
[6] pry(main)> ''.blank?
=> true
[7] pry(main)> nil.blank?
=> true
[8] pry(main)> nil.respond_to? :blank?
=> true

 

present?

存在するか確認する。!blank?と同じ結果になる。nil対しても使用できる。
[9] pry(main)> [].present?
=> false
[10] pry(main)> ''.present?
=> false
[11] pry(main)> nil.present?
=> false
[12] pry(main)> nil.respond_to? :present?
=> true