習作: Rails: イベントの案内メール
イベントの開催日時の1日前に案内メールを送る。
イベントの開催日時を変更できる。
外観
環境
ruby 3.2.2
Rails 7.1.3.2
sqlite3 (1.7.3)
solid_queue (0.3.0)
リンク
https://note.com/usutani/n/n0cd86869ff3c
https://github.com/usutani/event_information_email
手順
準備
rails new event_information_email
cd event_information_email
bundle add solid_queue
bin/rails generate solid_queue:install
bin/rails db:migrate
config/environments/development.rb
config/environments/test.rb
config/environments/production.rb
config.active_job.queue_adapter = :solid_queue
config.solid_queue.use_skip_locked = false
config/puma.rb
plugin :solid_queue
bin/rails generate scaffold member email
bin/rails generate scaffold event name started_at:datetime
bin/rails generate scaffold notification job_id event:belongs_to member:belongs_to
bin/rails db:migrate
bin/rails generate job notification
bin/rails generate mailer member notify
モデル
app/models/notification.rb
class Notification < ApplicationRecord
belongs_to :event
belongs_to :member
def self.create_or_update_and_notify(event:, member:)
job_id = nil
Notification.transaction do
notification = Notification.transaction do
Notification.find_or_create_by(event: event, member: member)
end
discard_job_by(notification.job_id)
job_id = job_one_day_before(event.started_at).perform_later(notification).job_id
notification.update!(job_id: job_id)
notification
end
rescue
discard_job_by(job_id)
nil
end
def job
Notification.find_job_by(job_id)
end
private
def self.find_job_by(job_id)
SolidQueue::Job.find_by(active_job_id: job_id)
end
def self.discard_job_by(job_id)
find_job_by(job_id)&.discard
end
def self.job_one_day_before(started_at)
NotificationJob.set(wait_until: started_at - 1.day)
end
end
test/models/notification_test.rb
require "test_helper"
class NotificationTest < ActiveSupport::TestCase
test "Create - create_or_update_and_notify" do
started_at = Time.current.since(1.week)
event = Event.create! name: "イベント1", started_at: started_at
member = Member.create! email: "member1@example.com"
assert_difference "Notification.count" do "Notificationは1件だけ生成されること"
assert_difference "SolidQueue::Job.count" do "SolidQueue::Jobは1件だけ生成されること"
notification = Notification.create_or_update_and_notify(event: event, member: member)
assert notification, "生成に成功すること"
notifications = Notification.where(event: event, member: member)
assert_equal notification.event, event, "イベントは一致すること"
assert_equal notification.member, member, "メンバーは一致すること"
assert_in_delta started_at - 1.day, notification.job.scheduled_at, 1, "ジョブの開始は開始日時の1日前(誤差1秒)"
end
end
end
test "Update - create_or_update_and_notify" do
started_at_old = Time.current.since(1.week)
event = Event.create! name: "イベント1", started_at: started_at_old
member = Member.create! email: "member1@example.com"
Notification.create_or_update_and_notify(event: event, member: member)
# イベントの開始日時を変更する
started_at_new = Time.current
event.update! started_at: started_at_new
assert_no_difference "Notification.count" do "Notificationの件数は変わらないこと"
assert_no_difference "SolidQueue::Job.count" do "SolidQueue::Jobの件数は変わらないこと"
notification = Notification.create_or_update_and_notify(event: event, member: member)
assert notification, "更新に成功すること"
notifications = Notification.where(event: event, member: member)
assert_equal notification.event, event, "イベントは一致すること"
assert_equal notification.member, member, "メンバーは一致すること"
assert_in_delta started_at_new - 1.day, notification.job.scheduled_at, 1, "ジョブの開始は変更した開始日時の1日前(誤差1秒)"
end
end
end
test "job" do
started_at = Time.current.since(1.week)
event = Event.create! name: "イベント1", started_at: started_at
member = Member.create! email: "member1@example.com"
assert_difference "Notification.count" do "Notificationは1件だけ生成されること"
assert_difference "SolidQueue::Job.count" do "SolidQueue::Jobは1件だけ生成されること"
Notification.create_or_update_and_notify(event: event, member: member)
assert job_id = Notification.find_by(event: event, member: member).job_id, "ジョブIDが設定されていること"
assert job = SolidQueue::Job.find_by(active_job_id: job_id), "ジョブが生成されていること"
assert_in_delta started_at - 1.day, job.scheduled_at, 1, "ジョブの開始は開始日時の1日前(誤差1秒)"
end
end
end
end
以上です。
この記事が気に入ったらサポートをしてみませんか?