Ruby 将方法包含到块中

Ruby 将方法包含到块中,ruby,Ruby,如果可能的话,有人知道如何让它工作吗 class Foo def self.go(&block) class << block include Bar end puts "Within Foo#go: #{block.methods.include? 'baz'}" block.call end end module Bar def baz puts "I'm happily in my place!"

如果可能的话,有人知道如何让它工作吗

class Foo
  def self.go(&block)
    class << block
      include Bar
    end    
    puts "Within Foo#go: #{block.methods.include? 'baz'}"
    block.call
  end
end

module Bar
  def baz
    puts "I'm happily in my place!"
  end
end

Foo.go { 
  puts "Within actual block: #{methods.include? 'baz'}"
  baz
}

编辑:当我在Foo#go中打印出块的类时,它是Proc,但当我在Proc中打印出它时,它是Object。这有关系吗?

你不能这样做。你看到的原因是这里有两种不同的背景。一个是块的上下文,它是定义块的上下文。另一个是Proc对象包装器的上下文,它与任何其他对象上下文一样,与块本身的上下文完全无关

我认为最接近的方法是使用具有所需方法的上下文对象访问
instance\u eval
块,但是该块将无法访问定义它的地方存在的
self
。这取决于你想写的方法是否有意义


另一个选项是将块传递给
baz
方法的实际接收器。

您可以使用
eval
Proc#binding

module Bar
  def baz
    puts "hi from baz!"
  end
end
def go(&block)
  eval('include Bar', block.binding)
  block[]
end

baz #=> NameError
go { baz } #=> "hi from baz!"
baz #=> "hi from baz!"
但是,除非您使用mixin/mixout框架(如mixico或mixology),否则您将把包含模块中的方法放在词法作用域中,因此一旦块返回,它们仍然可以访问

require 'rubygems'
require 'mixico'

module Bar
  def baz
    puts "hi from baz!"
  end
end
def go(&block)
  Module.mix_eval(Bar, &block)
end

baz #=> NameError
go { baz } #=> "hi from baz!"
baz #=> NameError

这是一篇关于在块中使用DSL的不同方法的好文章。

另一种替代方法,继rampion之后,是在混合到块中之前复制块的上下文,这样你就不会在完成后弄乱上下文

module Bar
  def baz
    puts "hi from baz!"
  end
end
def go(&block)
  dup_context = eval('self', block.binding).dup
  dup_context.send(:include, Bar)
  dup_context.instance_eval &block
end

注意:只有在块中没有运行任何mutator方法时,这才对您有用。

您是否只希望模块
Bar
Foo.go
中可用?谢谢,您是否有机会从类型的角度准确解释块在传递到函数时会发生什么情况?@Eli:我不确定您的意思。块在Ruby中没有类型——它只是一种语言构造。Proc对象的类型始终是Proc。谢谢,当我打印出块中的类时,它是“对象”,但后来它是“Proc”。@Eli:当您在块中打印
self.class
时,您不会打印块的类,因为正如我所说,块不是对象<块内的代码>自我与定义块的上下文中的代码>自我相同。看起来您是在脚本的顶层创建块,因此当时的
self
是一个对象(称为
main
的特殊对象用作Ruby脚本顶层的上下文)。另一方面,传递给您的方法的Proc对象显然是一个Proc.Article已消失…:-/
module Bar
  def baz
    puts "hi from baz!"
  end
end
def go(&block)
  dup_context = eval('self', block.binding).dup
  dup_context.send(:include, Bar)
  dup_context.instance_eval &block
end