Ruby:在事件触发器中自动包装方法 [英] Ruby: automatically wrapping methods in event triggers
问题描述
Heres我有/想要的:
module Observable
pre>
def观察者; @observers; end
def trigger(event,* args)
good = true
return good unless(@observers || = {})[event]
@obersvers [事件] .each {| e | good = false并且break除非是e.call(self,args)}
good
end
def on(event,& block)
@obersvers || = {}
@obersvers [event] || = []
@observers [event]<<块
end
end
类项目< Thing
包括Observable
def pickup(pickuper)
返回除非触发器(:before_pick_up,pickuper)
pickuper.add_to_pocket self
触发器(:after_pick_up,pickuper)
end
def drop(droper)
返回除非触发器(:before_drop,droper)
droper.remove_from_pocket self
触发器(:after_drop,droper)
end
#其他方法很多
end
# work
Item.new.on(:before_pickup)do | item,pickuper |
放置嘿#{picker} thats我的#{item}
return false#拾音器从不拿起对象
end
在开始尝试在Ruby中创建游戏时,我认为如果可以围绕观察者和事件,这将是巨大的。问题是必须写所有这些触发器似乎是一种浪费,因为它似乎是很多重复的代码。我觉得必须有一些元编程方法来包装功能的方法。
理想的Sceanrio:
class CustomBaseObject
class<<< self
###替换正确的元魔法
def public_method_called(name,* args,& block)
返回除非触发器(:before_ + name.to_sym,args)
yield block
trigger(:after_ + name.to_sym,args)
end
###
end
end
然后我将所有的对象从这个
类继承
。
我还是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)
放置Foo
result =#{m} _original(* args,& block)
放置Bar
result
end
}
end
end
class CustomBaseObject
def test(a,& block)
putTest:#{a}
yield
end
end
override_public_methods(CustomBaseObject)
foo = CustomBaseObject.new
foo.test(2){puts'Block! '}
#=> Foo
测试:2
阻止!
Bar
在这种情况下,您可以找出类中定义的所有必需的方法使用
instance_methods
然后覆盖它们。
另一种方法是使用所谓的钩子方法: p>
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)
放置Foo
result =#{m} _original(* args,& block)
放置Bar
result
end
}
end
end
end
class CustomBaseObject
def test(a,& block)
putsTest:#{a}
yield
end
包含可覆盖
end
包含
钩子,在此模块中定义,当您包含
该模块时,将调用该钩子。这要求您在类定义的末尾包含
,因为包含
应该知道所有已经定义的方法。我认为这很丑陋:)Heres what I have/want:
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
While starting on trying to create a game in Ruby, I thought it would be great if it could be based all around Observers and Events. The problem is have to write all of these triggers seems to be a waste, as it seems like a lot of duplicated code. I feel there must be some meta programming method out there to wrap methods with functionality.
Ideal 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
And then I have all of my object inherit from this
Class
.I'm still new to Ruby's more advanced meta programming subjects, so any knowledge about this type of thing would be awesome.
解决方案There are a several ways to do it with the help of metaprogramming magic. For example, you can define a method like this:
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
In this case, you figure out all the required methods defined in the class by using
instance_methods
and then override them.Another way is to use so-called 'hook' methods:
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
The
included
hook, defined in this module, is called when youinclude
that module. This requires that youinclude
the module at the end of the class definition, becauseincluded
should know about all the already defined methods. I think it's rather ugly :)这篇关于Ruby:在事件触发器中自动包装方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!