如何制作一个持续一段时间的Ruby方法?

时间:2014-04-25 14:13:16

标签: ruby metaprogramming

在一个类的主体内部,我想将一个块传递给一个名为with的方法。对于块的生命周期,我希望with_value方法可用。

否则,块内的所有内容都应该像在块外面一样。

以下是一个例子:

class C
  extend M

  with "some value" do
    do_something_complicated
    do_something_complicated
    do_something_complicated
  end
end

我们几乎得到这个:

module M
  def with(str, &block)
    Object.new.tap do |wrapper|
      wrapper.define_singleton_method :with_value do  # Here's our with_value
        str                                           # method.
      end
    end.instance_eval &block
  end

  def do_something_complicated                        # Push a value onto an
    (@foo ||= []).push with_value                     # array.
  end
end

但存在问题:由于我们正在评估在不同对象的上下文中传递给with的块,do_something_complicated不可用。

什么是解决这个问题的正确方法?

3 个答案:

答案 0 :(得分:3)

这将使with_value仅在块内可用。但是,_with_value将在块内部或外部定义。

module M
  def _with_value
    ...
  end
  def with(str, &block)
    alias with_value _with_value
    block.call
    undef with_value
  end
  ...
end

我无法从问题中判断这是否是一个问题。如果这是一个问题,你需要进一步描述你想要做的事情。

答案 1 :(得分:1)

基本上,我们的想法是使用method_missing将方法调用从伪类转发到调用类。如果您还需要访问实例变量,可以将它们从调用类复制到虚拟类,然后在块返回后再将它们复制。

Ruby gem docile是这种系统的一个非常简单的实现。我建议你阅读该存储库中的源代码(不用担心,这是一个非常小的代码库),以获得一个很好的例子,说明像你的示例中的方法一样有效。

答案 2 :(得分:0)

这是一种更接近你的尝试的方式:

module M
  def with(str, &block)
    dup.tap do |wrapper|
      wrapper.define_singleton_method :with_value do
        ...
      end
    end.instance_eval &block
  end
  ...
end

dup将复制从with作为类方法调用的类。