如何使Ruby方法在一个块的生命周期内保持不变?

如何使Ruby方法在一个块的生命周期内保持不变?,ruby,metaprogramming,Ruby,Metaprogramming,在类的主体内部,我想将一个块传递给一个名为的方法。对于块的生命周期,我希望有一个with_值方法可用 否则,块内的所有内容的行为都应与块外的内容相同 module M def _with_value ... end def with(str, &block) alias with_value _with_value block.call undef with_value end ... end 下面是一个例子: class C e

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

否则,块内的所有内容的行为都应与块外的内容相同

module M
  def _with_value
    ...
  end
  def with(str, &block)
    alias with_value _with_value
    block.call
    undef with_value
  end
  ...
end
下面是一个例子:

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
但有一个问题:因为我们在计算传递给另一个对象的上下文中的块,所以dou\u复杂的东西不可用


实现这一点的正确方法是什么?

基本上,这个想法是用来将方法调用从伪类转发到调用类。如果还需要访问实例变量,可以将它们从调用类复制到伪类,然后在块返回后再次复制


RubyGem是这样一个系统的一个非常简单的实现。我建议您阅读该存储库中的源代码不要担心,这是一个非常小的代码库,可以作为一个很好的示例,说明DSL方法(如示例中的方法)是如何工作的。

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

RubyGem是这样一个系统的一个非常简单的实现。我建议您阅读该存储库中的源代码不要担心,这是一个非常小的代码库,可以作为一个很好的示例,说明DSL方法(如示例中的方法)是如何工作的。

这将使with_值仅在块中可用。但是,将在块内或块外定义具有_值的_

module M
  def _with_value
    ...
  end
  def with(str, &block)
    alias with_value _with_value
    block.call
    undef with_value
  end
  ...
end
我无法从这个问题判断这是否是一个问题。如果是问题,您需要进一步描述您正在尝试执行的操作。

这将使with_值仅在块内可用。但是,将在块内或块外定义具有_值的_

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

我无法从这个问题判断这是否是一个问题。如果是问题,您需要进一步描述您正在尝试做的事情。

以下是一种更接近您尝试的方法:

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作为类方法的位置复制该类。

以下是一种更接近您尝试的方法:

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作为类方法的位置复制该类。

您可以在实例求值之前对wrapper.extend M进行扩展。你能解释一下你的理由吗?它可能会帮助我们找到一个固溶体。@Kyle如果你扩展包装器,做一些复杂的事情会将包装器的@foo而不是C的@foo推到错误的数组中。我这样做是为了制作一个小型DSL,用于以可组合的形式包含ActiveRecord对象周围的一些乱七八糟的东西。你能解释一下你的理由吗?它可能会帮助我们找到一个固溶体。@Kyle如果你扩展包装器,做一些复杂的事情会将包装器的@foo而不是C的@foo推到错误的数组中。我这样做是为了制作一个小的DSL,用于以可组合的形式包含ActiveRecord对象周围的一些乱七八糟的东西。这感觉很难看,但它可以工作,所以我不能抱怨+1.@JohnFeminella对我来说,这并不难看。它有_的定义体,其中_值与with分开,块前后各只有一行。如果这对您来说很难看,那么没有什么可以做得更好。我所说的难看是指块内的任何故障都会导致with_值仍然存在,从而污染后续调用的空间。在一个假设的更优雅的解决方案中,该语言将通过不允许在块外使用_值进行查看来解决这个问题。但既然我想不出更好的了,那就是为什么我给你+1@如果你希望在块中出现一些异常,就用begin-sure-end子句来包装它。接受这个答案,因为我认为它是最接近的。谢谢,@sawa。这感觉很难看,但它很有效,所以我不能抱怨+1.@JohnFeminella对我来说,这并不难看。它有_的定义体,其中_值与with分开,块前后各只有一行。如果这对您来说很难看,那么没有什么可以做得更好。我所说的难看是指块内的任何故障都会导致with_值仍然存在,从而污染后续调用的空间。在一个假设的更优雅的解决方案中,该语言将通过不允许在块外使用_值进行查看来解决这个问题。但既然我想不出更好的了,那就是为什么我给你+1@如果你希望在块中出现一些异常,就用begin-sure-end子句来包装它。接受这个答案,因为我认为它是最接近的。谢谢,萨瓦。