動機
Delayed_jobは処理に時間が掛かるようなタスクをバックグラウンドで非同期処理を実現するGemです。
delayed_jobをつかった簡単な使い方はいろいろなところで見かけますが、ジョブをハンドルして、特に削除を好きなタイミングで行う方法がわからず調べるのに苦労したのでまとめておきます。
delayed_jobの設定方法などはインストールなどの情報はこちら
【Rails 4】delayed_jobを使う
今回作りたかったのは以下の機能です
- connpassのようなイベントサイトの簡易版として、イベント登録、参加者募集し、日程の管理などもできるようなサービス
- イベントは下書き、公開、終了という状態を持っていてイベントがおわると自動的に終了状態になる-①
- このサービスはイベントの参加者に実施日の24時間前にリマインダーを送信する。②
- 25時間前まではイベントの開催時間、を変更可能でリマインダー開催、終了時刻を設定しなおす。
model設計
Event : イベント内容、開始時刻、終了時刻を保持
EventJob : イベントのリマインダー、終了状態遷移遷移処理、delayed_jobのモデルである__Delayed::Backend::ActiveRecord::Job__へのハンドル(idを保持する)
# イベント
create_table "events", force: :cascade do |t|
t.string "content"
t.datetime "start_datetime"
t.datetime "end_datetime"
t.string "state"
t.string "title"
end
# ジョブ
create_table "event_jobs", force: :cascade do |t|
t.integer "job_type", null: false
t.integer "event_id", null: false
t.integer "job_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
実装
- __EventJob__モデル
# イベントに関するジョブを削除できるようにハンドルを保持するため、モデルとして扱う
class EventJob < ActiveRecord::Base
belongs_to :event
# reminder リマインダージョブ
# finish 終了
enum job_type: { reminder: 0, finish: 1 }
scope :reminder, ->() {find_by_job_type(0)}
scope :finish, ->() {find_by_job_type(1)}
before_destroy :delete_job
# ジョブを設定する
def setup!
if reminder?
update!(job_id: delay(run_at: event.start_datetime - 1.day).exec_remider.id)
elsif finish?
update!(job_id: delay(run_at: event.end_datetime).exec_finish.id)
else
logger.info('unknown_jobtype')
end
end
# ジョブを再設定する
# 時刻変更のため
def reset!
delete_job
setup!
end
private
def exec_remider
# リマインダー送信セットアップ
# ここでメールを送る処理を実装
end
def exec_finish
# イベント終了
# ここはAASM というステートマシンGEMを利用して状態を変更
event.finish!
end
def delete_job
Delayed::Backend::ActiveRecord::Job.find_by_id(job_id).try(:destroy)
end
end
ここのコードのポイントは__EventJob__クラスのメソッドをdelayつきで呼ぶと、戻り値がDelayed::Backend::ActiveRecord::Jobtoとなるのでここでハンドル(id)を取得することができます!
delay(run_at: event.start_datetime - 1.day).exec_remider
=> #<Delayed::Backend::ActiveRecord::Job:0x007fa352365b58
- 呼び出す側
def setup_finish
# 終了状態にする
logger.info('終了状態にするジョブをセットします!')
EventJob.create(event_id: id, job_type: 'finish').setup!
end
def setup_reminder
# リマインダーをセットアップする
logger.info('リマインダーセットします!')
EventJob.create(event_id: id, job_type: 'reminder').setup!
end