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