開発のわだち(9):リマインダメールを実装
「リマインダメールを実装」
目標:heroku上のrailsアプリにリマインダメール機能を実装
目次:
1.ActionMailerでリマインダメールを作成
2.heroku Schedulerでメールの発送設定
久々のブログです。
“あるある”ですが、納期に追われて全然書けてませんでした。
(実は納期もこのリマインダメール機能だけは一週間遅れてしまいました。。。)
しかし、HTTPリクエストに反応する機能しか作ってなかった自分にとっても、この機能は勉強になったので忘れる前に記載します。
そもそもHTTPリクエストが発生しないのにどうやって処理すんねん。。。から始まりましたが、いろいろな友人に助けて頂き、簡単なHeroku Schrdulerに辿りつきました。
1.ActionMailerでリマインダメールを作成
まずは、リマインダメールの仕様を決めます。
リマインダメールの仕様
送信時間:毎朝 午前8:30
送信内容:ToDoリストの送付
送信内容詳細:送信対象ToDoリスト
:送信時間以降24時間以内に設定されているToDoリスト
:リマインダーメールのフラグがセットされているリスト
(開始時刻が設定されているもの)
本当はここまでの過程も全てブログにしたかったんですが、なかなか。。。 もし続けて読んでくださってる方は「あ、ToDoリストの機能を付けたんだな!」と思ってくださいw 仕様の詳細はgithubアカウントをご覧くださいm( )m
本当はセットされた時間に(分刻みで)発送するリマインダメールを作りたかったんですが、cronの設定やバックグラウンド処理、メール処理専用サーバーの作成を考えるとハードルが高かったので断念(今後実装したい)。
まずはアクションメーラーをジェネレートします。
$ rails generate mailer "クラス名" "メソッド名"
今回は"クラス名"を"RemindMailer"、"メソッド名"を"remind"として生成しました。
まずは、生成したクラスとメソッドを定義します。
app/mailers/remind_mailer.rb
class RemindMailer < ApplicationMailer def remind(user) @user = User.find_by(id: user) @to_do_lists = @user.to_do_lists @todays_to_do_lists = @to_do_lists.select{|tdl| tdl.todays_todo? && tdl.reminder_mail } mail(to: @user.email, subject: "[#{Date.today}]のリマインド") end end
remind
メソッドは引数user
をとります。Userモデルから引数に一致するIDのuser
をインスタンス変数の@user
に入れる。
@user
のToDoリストをToDoListモデルから全部取ってきてインスタンス変数の@to_do_lists
に入れます。
@to_do_lists
の中でreminder_mail
がtrue
になってて且つ、処理時間以降24時間以内に開始時刻が設定されているものをインスタンス変数@todays_to_do_lists
に入れます。
@user
のメールアドレスにその日の日付をタイトルに載せて、発送するという内容です。
ちなみに、todays_todo?
はToDoListモデルに以下のように設定してあります。
app/models/to_do_list.rb
...略 def todays_todo? schedule_sta < Time.now.since(24.hours) && schedule_sta >= Time.zone.now end ...略
開始時間schedule_sta
が処理時間と比べて24時間以降より小さい値で、且つ、現在時刻Time.zone.now
よりも未来のもの、つまり処理時間以降24時間以内に開始時刻が設定されているもの、ということです。
メールの内容は以下に作りました。
app/views/remind_mailer
メールは(次で設定するschedulerでつまづいた時の原因切り分けのためにも)テスト環境でエラーなく動くか確認しておいた方がいいです。
メールのプレビューについては、rails tutorialに非常にわかりやすく解説されてます。
ここまでで「リマインダメール」自体の設定は終わりです。
次はこの処理を着火させる設定です。
2.heroku Schedulerでメールの発送設定
リマインダメールを動かすにはHTTPリクエストがなくとも意図したタイミングで、処理をスタートさせる必要があります。
その機能を担ってくれるのが、Heroku Schedulerです。設定した時間に指定したrakeタスクを実行してくれる賢いやつです。
但し、ベストエフォートです。許してやってください。あとdynoが24時間動くような立派なサービスだと確実に有料になります。
詳しくはオフィシャルを参照。
まずはheroku schedulerに動かしてもらうrakeタスクを作ります。 以下の感じです。
lib/tasks/"動かしたい処理".rake
今回は"動かしたい処理"をscheduler.rake
にしました。
desc "This task is called by the Heroku scheduler add-on" task :reminder_mail => :environment do all_tdls = ToDoList.all rmd_tdls = all_tdls.select{|tdl| tdl.todays_todo? && tdl.reminder_mail } rmd_usrs = rmd_tdls.map{|rmd_tdl| rmd_tdl.user_id} users = rmd_usrs.uniq users.each do |user| RemindMailer.remind(user).deliver end end
heorokuのオフィシャルに従った書き出しで始めてます。タスクの内容は全てのToDoListを変数all_tdls
に入れて、reminder_mail
がtrueで処理時間から24時間以内に開始時間が設定されているものを引っ張り出してrmd_tdls
に入れます。
rmd_tdls
に紐づくuser.id
を配列にしてusers
に入れます。users
の重複を取り除いてそれぞれにremindメソッドを実行、メールを発送します。
このrakeタスクが実行されれば、リマインダメールが飛びますね。 (メールはHeroku addonのSendGridで発送しています。ここでは触れません。悪しからず。。)
そもそも「rakeってなんやねん」って方は、自分がrakeを学ばせてもらったこちらを見てみてください。
簡単に言うとrubyのプロビジョニングファイルみたいなものでしょうか(語弊があったらすみません)。
では、次にheroku schedulerが毎日午前8:30にこれを動かすように設定します。
heroku schedulerを該当のdynoに設定しましょう。
$ heroku addons:create scheduler:standard
rakeタスクがちゃんと動くか、確認します。
$ heroku run rake reminder_mail
実行するrakeタスクはscheduler.rakeのreminder_mail
というタスクです。
rakeを実行するには$ rake "実行したいrakeタスク"
という書き方になるので、上のように書きます。
ここまできたら、時間を設定するだけですね。
$ heroku addons:open scheduler
このコマンドで以下の画面がブラウザで立ち上がります。 実行したいrakeタスクを入力しましょう。
"FREQUENCY"をDailyに"NEXT DUE"を8:30にセットしてます。
日本は UTC「協定世界時」よりも9時間早く進んでいるので、設定したい日本時間から9時間引いた時間をセットする必要があります。
こうなると、当たり前ですがHeroku dynoの時間も日本時間にしなくてはなりません。
$ heroku config:add TZ=Asia/Tokyo $ heroku config # 設定されたタイムゾーンを確認できます。 …略 TZ: Asia/Tokyo
メールがちゃんと送れるか確認するには、"FREQUENCY"を10分おきとにしてやってみる手があります。
以上です。 heroku schedulerは便利ですが、rakeタスクを学ぶのが少し大変でしたー。
今回もっとも参考になった記事:How to send a daily report with Heroku's scheduler
環境
ホストOS: MacOSX 10.11.3
VirtualBox: 5.0.14
Vagrant: 1.7.4
ゲストOS: CentOS 7ゲストOSの環境