初めての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を継承する。

f:id:naotooncajon:20130814230208p:image

準備

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

参考サイト

Railsで変更前の値、型変換前の値、などなど