Ruby:在事件触发器中自动包装方法

inheritance人我拥有/想要的东西:

module Observable def observers; @observers; end def trigger(event, *args) good = true return good unless (@observers ||= {})[event] @obersvers[event].each { |e| good = false and break unless e.call(self, args) } good end def on(event, &block) @obersvers ||= {} @obersvers[event] ||= [] @observers[event] << block end end class Item < Thing include Observable def pickup(pickuper) return unless trigger(:before_pick_up, pickuper) pickuper.add_to_pocket self trigger(:after_pick_up, pickuper) end def drop(droper) return unless trigger(:before_drop, droper) droper.remove_from_pocket self trigger(:after_drop, droper) end # Lots of other methods end # How it all should work Item.new.on(:before_pickup) do |item, pickuper| puts "Hey #{pickuper} thats my #{item}" return false # The pickuper never picks up the object end 

在开始尝试使用Ruby创建游戏的过程中,我认为如果它可以基于观察者和事件的整个过程,那就太棒了。 问题是必须编写所有这些触发器似乎是一种浪费,因为它似乎是很多重复的代码。 我觉得必须有一些元编程方法来包装具有function的方法。

理想的Sceanrio:

 class CustomBaseObject class << self ### Replace with correct meta magic def public_method_called(name, *args, &block) return unless trigger(:before_+name.to_sym, args) yield block trigger(:after_+name.to_sym, args) end ### end end 

然后我将所有对象都inheritance自此类。

我还是Ruby的更高级元编程主题的新手,所以关于这类事物的任何知识都会很棒。

在元编程魔法的帮助下,有几种方法可以做到这一点。 例如,您可以定义如下方法:

 def override_public_methods(c) c.instance_methods(false).each do |m| m = m.to_sym c.class_eval %Q{ alias #{m}_original #{m} def #{m}(*args, &block) puts "Foo" result = #{m}_original(*args, &block) puts "Bar" result end } end end class CustomBaseObject def test(a, &block) puts "Test: #{a}" yield end end override_public_methods(CustomBaseObject) foo = CustomBaseObject.new foo.test(2) { puts 'Block!' } # => Foo Test: 2 Block! Bar 

在这种情况下,您可以使用instance_methods出类中定义的所有必需方法,然后覆盖它们。

另一种方法是使用所谓的’hook’方法:

 module Overrideable def self.included(c) c.instance_methods(false).each do |m| m = m.to_sym c.class_eval %Q{ alias #{m}_original #{m} def #{m}(*args, &block) puts "Foo" result = #{m}_original(*args, &block) puts "Bar" result end } end end end class CustomBaseObject def test(a, &block) puts "Test: #{a}" yield end include Overrideable end 

当您include该模块时,将调用此模块中定义的included钩子。 这要求您在类定义的末尾include模块,因为included应该知道所有已定义的方法。 我觉得这很难看:)