我对 Rails 3 Observers 的替代方案是手动实现,它利用模型中定义的回调但设法(如 agmin 在上面的回答中所述)“翻转依赖关系......耦合”。
我的对象继承自提供注册观察者的基类:
class Party411BaseModel
self.abstract_class = true
class_attribute :observers
def self.add_observer(observer)
observers << observer
logger.debug("Observer #{observer.name} added to #{self.name}")
end
def notify_observers(obj, event_name, *args)
observers && observers.each do |observer|
if observer.respond_to?(event_name)
begin
observer.public_send(event_name, obj, *args)
rescue Exception => e
logger.error("Error notifying observer #{observer.name}")
logger.error e.message
logger.error e.backtrace.join("\n")
end
end
end
end
(当然,本着组合优于继承的精神,上述代码可以放在一个模块中并混合在每个模型中。)
初始化器注册观察者:
User.add_observer(NotificationSender)
User.add_observer(ProfilePictureCreator)
除了基本的 ActiveRecord 回调之外,每个模型都可以定义自己的可观察事件。例如,我的 User 模型公开了 2 个事件:
class User < Party411BaseModel
self.observers ||= []
after_commit :notify_observers, :on => :create
def signed_up_via_lunchwalla
self.account_source == ACCOUNT_SOURCES['LunchWalla']
end
def notify_observers
notify_observers(self, :new_user_created)
notify_observers(self, :new_lunchwalla_user_created) if self.signed_up_via_lunchwalla
end
end
任何希望接收这些事件通知的观察者只需要 (1) 向公开事件的模型注册和 (2) 拥有一个名称与事件匹配的方法。正如人们所预料的那样,多个观察者可以注册同一个事件,并且(参考原始问题的第二段)观察者可以跨多个模型观察事件。
下面的 NotificationSender 和 ProfilePictureCreator 观察者类为各种模型公开的事件定义了方法:
NotificationSender
def new_user_created(user_id)
...
end
def new_invitation_created(invitation_id)
...
end
def new_event_created(event_id)
...
end
end
class ProfilePictureCreator
def new_lunchwalla_user_created(user_id)
...
end
def new_twitter_user_created(user_id)
...
end
end
一个警告是,所有模型中公开的所有事件的名称必须是唯一的。