Ruby:在事件触发器中自动包装方法 [英] Ruby: automatically wrapping methods in event triggers

查看:188
本文介绍了Ruby:在事件触发器中自动包装方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Heres我有/想要的:

  module Observable 
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
pre>

在开始尝试在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 you include that module. This requires that you include the module at the end of the class definition, because included should know about all the already defined methods. I think it's rather ugly :)

这篇关于Ruby:在事件触发器中自动包装方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆