初めてのRailsアプリ(その8−2)
railsubject_08_02
初めてのRailsアプリ(その8−2)
「観測者」って世界5分前仮説的な妄想が膨らむ。
8. Observerパターン
前回は一般的なObserverパターンに関して学習した。今回はRuby on RailsにおけるObserverの使い方に関して勉強する。
RoRにおけるObserver
モデルであるActiveRecordの状態を検知する。
モデル図
以下のようなモデルを考える。ポイントは以下。
- SubjectObserverはモデルSubjectの状態変化観測する。
- モデルの作成の直後(after_create)
- モデルの更新の直後(after_update)
- モデルの削除の直後(after_destroy)
- SubjectはActiverRecord::Baseを継承する
- SubjectObserverはActiverRecord::Observerを継承する。
準備
Observerの作成
以下のRailsコマンドでObserverを作成する。
mba:railsubject 7010oncajon$ rails generate observer Subject
invoke active_record
create app/models/subject_observer.rb
invoke test_unit
create test/unit/subject_observer_test.rb
関連付け
設定ファイルによりモデルとオブザーバを関連付ける。(./config/application.rb)
# SubjectObserverを関連付ける
config.active_record.observers = :subject_observer
実装
./app/models/subject_observer.rb
class SubjectObserver < ActiveRecord::Observer
def after_create(subject)
CustomLogger::debug("SubjectObserver", "MSG_DBG_999", "Subject(id:#{subject.id})が作成されました")
end
def after_update(subject)
CustomLogger::debug("SubjectObserver", "MSG_DBG_999", "Subject(id:#{subject.id})が更新されました")
end
def after_destroy(subject)
CustomLogger::debug("SubjectObserver", "MSG_DBG_999", "Subject(id:#{subject.id})が削除されました")
end
end
実行結果
画面よりSubjectの作成/更新/削除を行う。その時のログ
...
2013/08/14 21:27:54.214585 #22833 DEBUG (SubjectObserver) (subject_observer.rb:3:in `after_create') Subject(id:10)が作成されました
...
2013/08/14 21:28:00.723749 #22833 DEBUG (SubjectObserver) (subject_observer.rb:7:in `after_update') Subject(id:10)が更新されました
...
2013/08/14 21:28:05.762217 #22833 DEBUG (SubjectObserver) (subject_observer.rb:11:in `after_destroy') Subject(id:10)が削除されました
...
条件をつける
このままだと状態変化が行われた際に、必ず処理が実行さる。更新の時になど、内容によって処理を分離させたいことを考える。例えば以下。
- Subject.statusが変更された時の処理
- Subject.statusが「実施中」に変更された時の処理
- Subject.statusが「実施中」から「実施済」に変更された時の処理
解決策
ActiveRecordは属性の変更内容をトレースできる(すげー)のでそれを利用する。
具体的に
./app/models/subject_observer.rb
def after_update(subject)
CustomLogger::debug("SubjectObserver", "MSG_DBG_999", "Subject(id:#{subject.id})が更新されました")
# Subject.statusが変更された時の処理
if (subject.status_changed?) then
CustomLogger::debug("SubjectObserver", "MSG_DBG_999", "statusが変更されたました。")
end
# Subject.statusが「実施中」に変更された時の処理
if (subject.status_changed? && subject.status == "実施中") then
CustomLogger::debug("SubjectObserver", "MSG_DBG_999", "statusが「実施中」に変更されたました。")
end
# Subject.statusが「実施中」から「実施済」に変更された時の処理
if (subject.status_changed? && subject.status_change.first == "実施中" && subject.status_change.last == "実施済") then
CustomLogger::debug("SubjectObserver", "MSG_DBG_999", "statusが「実施中」から「実施済」に変更されたました。")
end
end