检测方法调用

时间:2012-10-30 13:20:10

标签: ruby metaprogramming

我正在尝试在ruby中实现延迟方法执行。假设我有一个有两个方法的类,在调用

之后不应该立即执行
class Foo
  lazy_evaluate :bar, :baz

  def bar(string)
    puts string
  end

  def baz(hash)
    puts hash.inspect
  end
end

f = Foo.new
f.bar('hello world') => nil
f.baz(hello: :world) => nil

f.run_lazy_methods =>
'hello world'
'{:hello=>:world}'

我不会在我的宝石http://pastie.org/5137463

中使用它

我想知道如何实现这种行为

2 个答案:

答案 0 :(得分:3)

使用委托对象,将调用的方法记录到堆栈上,然后在委托上重放它们。

class LazyObject
  def initialize(delegate)
    @invocations = []
    @delegate    = delegate
  end

  def bar(*args, &block)
    @invocations << {
      method: :bar,
      args:   args,
      block:  block
    }
  end

  def baz(*args, &block)
    @invocations << {
      method: :baz,
      args:   args,
      block:  block
    }
  end

  def run_lazy_methods
    @invocations.each do |inv|
      @delegate.send(
        inv[:method],
        *inv[:args],
        &inv[:block]
      )
    end
  end
end

obj = LazyObject.new(RealObject.new)
obj.bar(hello: :world)
obj.baz("Hello World")
obj.run_lazy_methods

您可以使用method_missing更好地编写上述内容,但我想说清楚;)

答案 1 :(得分:0)

我发现很难在相应的方法定义之前允许lazy_evaluate。当你把它放在相应的定义之后,我的实现就可以了。

准备部分是:

class Foo
  def initialize
    @queue = []
  end
  def run_lazy_methods
    @queue.each{|proc| proc.call}
  end
  def self.lazy_evaluate *methods
    methods.each do |method|
      alias :"old_#{method}" :"#{method}"
      define_method method do |*args, &pr|
        @queue.push(->{send(:"old_#{method}", *args, &pr)})
      end
    end
  end
end

然后,当您定义方法并调用lazy_evaluate时,它们会变得懒惰。

class Foo
  def bar(string)
    puts string
  end
  def baz(hash)
    puts hash.inspect
  end

  lazy_evaluate :bar, :baz
end

您将获得预期的结果。

f = Foo.new
f.bar('hello world')
f.baz(hello: :world)
f.run_lazy_methods