Đã khám phá chủ đề nhiều hơn một chút và cập nhật mã.
Phiên bản sau là một nỗ lực để tổng quát hóa kỹ thuật, mặc dù vẫn được đơn giản hóa và chưa hoàn thiện.
Phần lớn tôi đã đánh cắp - hem, đã tìm thấy cảm hứng - việc triển khai các lệnh gọi lại của DataMapper, đối với tôi, nó có vẻ khá hoàn chỉnh và đẹp mắt.
Tôi thực sự khuyên bạn nên xem mã @ http://github.com/datamapper/dm-core/blob/master/lib/dm-core/support/hook.rb
Dù sao, cố gắng tái tạo chức năng bằng mô-đun Observable khá hấp dẫn và mang tính hướng dẫn. Một số lưu ý:
- Phương thức được thêm vào dường như được yêu cầu vì các phương thức phiên bản gốc không khả dụng tại thời điểm đăng ký các lệnh gọi lại
- lớp bao gồm cả được quan sát và tự quan sát
- ví dụ được giới hạn cho các phương thức cá thể, không hỗ trợ khối, args, v.v.
mã:
require 'observer'
module SuperSimpleCallbacks
include Observable
def self.included(klass)
klass.extend ClassMethods
klass.initialize_included_features
end
def initialize
add_observer(self)
end
def update(method_name, callback_type)
case callback_type
when :before then self.class.callbacks[:before][method_name.to_sym].each{|callback| send callback}
when :after then self.class.callbacks[:after][method_name.to_sym].each{|callback| send callback}
end
end
module ClassMethods
def initialize_included_features
@callbacks = Hash.new
@callbacks[:before] = Hash.new{|h,k| h[k] = []}
@callbacks[:after] = @callbacks[:before].clone
class << self
attr_accessor :callbacks
end
end
def method_added(method)
redefine_method(method) if is_a_callback?(method)
end
def is_a_callback?(method)
registered_methods.include?(method)
end
def registered_methods
callbacks.values.map(&:keys).flatten.uniq
end
def store_callbacks(type, method_name, *callback_methods)
callbacks[type.to_sym][method_name.to_sym] += callback_methods.flatten.map(&:to_sym)
end
def before(original_method, *callbacks)
store_callbacks(:before, original_method, *callbacks)
end
def after(original_method, *callbacks)
store_callbacks(:after, original_method, *callbacks)
end
def objectify_and_remove_method(method)
if method_defined?(method.to_sym)
original = instance_method(method.to_sym)
remove_method(method.to_sym)
original
else
nil
end
end
def redefine_method(original_method)
original = objectify_and_remove_method(original_method)
mod = Module.new
mod.class_eval do
define_method(original_method.to_sym) do
changed; notify_observers(original_method, :before)
original.bind(self).call if original
changed; notify_observers(original_method, :after)
end
end
include mod
end
end
end
class MyObservedHouse
include SuperSimpleCallbacks
before :party, [:walk_dinosaure, :prepare, :just_idle]
after :party, [:just_idle, :keep_house, :walk_dinosaure]
before :home_office, [:just_idle, :prepare, :just_idle]
after :home_office, [:just_idle, :walk_dinosaure, :just_idle]
before :second_level, [:party]
def home_office
puts "learning and working with ruby...".upcase
end
def party
puts "having party...".upcase
end
def just_idle
puts "...."
end
def prepare
puts "preparing snacks..."
end
def keep_house
puts "house keeping..."
end
def walk_dinosaure
puts "walking the dinosaure..."
end
def second_level
puts "second level..."
end
end
MyObservedHouse.new.tap do |house|
puts "-------------------------"
puts "-- about calling party --"
puts "-------------------------"
house.party
puts "-------------------------------"
puts "-- about calling home_office --"
puts "-------------------------------"
house.home_office
puts "--------------------------------"
puts "-- about calling second_level --"
puts "--------------------------------"
house.second_level
end
Bản trình bày đơn giản này về việc sử dụng Observable có thể hữu ích: http://www.oreillynet.com/ruby/blog/2006/01/ruby_design_patterns_observer.html