observerを利用して嬉しくなること

以前の日記で、restful_authenticaionのobserverを利用しないコードに変更してしまったが、その後、修正を繰り返すうちに、observerにはobserverの良さがあることに気付き始めた。

以前の発想

  • observerを利用した場合
# コントローラー
class UsersController < ApplicationController
  def activate
    self.current_user = params[:activation_code].blank? ? false : User.find_by_activation_code(params[:activation_code])
    if logged_in? && !current_user.active?
      current_user.activate
      flash[:notice] = "Signup complete!"
    end
    redirect_back_or_default('/')
  end


# モデル
class User < ActiveRecord::Base
  def activate
    @activated = true
    self.activated_at = Time.now.utc
    self.activation_code = nil
    save(false)
  end

  def recently_activated?
    @activated
  end


# オブザーバー
class UserObserver < ActiveRecord::Observer
  def after_save(user)
    UserMailer.deliver_activation(user) if user.recently_activated?
  end
  • observerやrecently_activated?をわざわざ用意するなら、手っ取り早くUserモデルでメールを送信してしまえば良いんじゃないかと...。
  • observerを利用せず、Userモデルの中でメールを送信する場合
# コントローラー
class UsersController < ApplicationController
  def activate
    self.current_user = params[:activation_code].blank? ? false : User.find_by_activation_code(params[:activation_code])
    if logged_in? && !current_user.active?
      current_user.activate
      flash[:notice] = "Signup complete!"
    end
    redirect_back_or_default('/')
  end


# モデル
class User < ActiveRecord::Base
  def activate
    self.activated_at = Time.now.utc
    self.activation_code = nil
    save(false)
    # @activated = trueを以下に変更
    UserMailer.deliver_activation(user)
  end

混乱の始まり

  • 上記のようなアクティベーション完了のメール送信しかないシンプルな例ではあまり感じないが、パスワードの変更手続きやその完了、その他諸々の手続きを通知するメールが増えてくると...
    • 何らかの修正をする時に、モデルの中に散在するメールを送信するコードを見つけるのが面倒臭くなってくる。
  • 現状はActionMailerを利用してメールで通知するだけだが、今後は別の通知手段を追加したくなるかもしれないし、ActionMailer以外の手段でメールを送信したくなるかもしれない...
    • observerなら、修正するコードは1カ所にまとまっているので、修正するのがとても簡単そう。
  • 自分以外の人にコードを読んでもらう時にも、observerにコールバックの処理がまとまっていれば、とても分かり易そう。
  • そもそも、Userモデルは、usersテーブルとのやり取りに責任を持ったオブジェクトのはず。
    • そこでメールの送信処理を実行してしまっては、オブジェクトの役割が曖昧になってしまう。こんなところから、スパゲッティーオブジェクト指向コードへと発展していくのかもしれない。

所感

  • observerを利用すると、Userモデルの状態を保存するインスタンス変数、その状態を外部から知るためのメソッドが必要になり、一見すると何だか無意味に冗長な気がする。
  • ところが、observerによって処理が明確に区別されるので、Userモデルにはほとんど影響を与えることなく、処理を追加したり、変更したりすることが簡単にできる。
  • Userモデルは状態を教えるだけ、その状態をobserverが読み取って、必要なオブジェクトで処理をする、なんだかここにもMVCの構造が見えてきた。


observerを、やっぱり利用することにした。

参考ページ

以下のページがたいへん参考になりました。感謝です!