Ruby 为什么我的define_方法内容在类范围内被评估?

Ruby 为什么我的define_方法内容在类范围内被评估?,ruby,metaprogramming,Ruby,Metaprogramming,我正在做一些(也)奇特的元编程,我很难理解为什么在以下两种情况下范围不同: 案例1: class TesterA def the_method puts "I'm an instance_method!" end def self.the_method puts "I'm a class_method!" end def self.define_my_methods *method_names method_names.each do |name

我正在做一些(也)奇特的元编程,我很难理解为什么在以下两种情况下范围不同:

案例1:

class TesterA

  def the_method
    puts "I'm an instance_method!"
  end

  def self.the_method
    puts "I'm a class_method!"
  end

  def self.define_my_methods *method_names
    method_names.each do |name|
      define_method("method_#{name}") do
        the_method
      end
    end
  end

  define_my_methods :a, :b, :c
end

t = TesterA.new
t.method_a #=> I'm an instance_method!
t.method_b #=> I'm an instance_method!
t.method_c #=> I'm an instance_method!
案例2

class TesterB

  def the_method
    puts "I'm an instance_method!"
  end

  def self.the_method
    puts "I'm a class_method!"
  end

  def self.define_the_method attr
    define_method("method_#{attr}") do
      begin
        yield
      rescue
        raise $!, "method_#{attr} was called: #$!", $@
      end
    end
  end

  def self.define_my_methods *method_names
    method_names.each do |name|
      define_the_method(name) do
        the_method
      end
    end
  end

  define_my_methods :a, :b, :c
end

t = TesterB.new
t.method_a #=> I'm a class_method!
t.method_b #=> I'm a class_method!
t.method_c #=> I'm a class_method!
在第二个例子中,我介绍了一种“助手方法”
define\u方法
,我用它来定义方法,而不是
define\u方法
它自己。原因是,我想将动态方法的名称附加到这些方法中可能出现的任何异常消息中。然而,问题是,内容(使用后一种情况时)似乎是在类范围内进行评估的

这是为什么?我如何才能使其在实例范围内得到评估?

此块:

do
  the_method
end
它在类作用域中定义和作用域。没有理由期望它以某种方式注入实例范围。要解决此问题,只需显式传递接收器:

yield(self)
以及:


发生这种情况是因为您在类方法
self.define_my_methods
的范围内提供了块,其中
self
是类,而不是实例。因此,您可以做的是
yield
定义方法本身的范围:

def self.define_the_method attr
  define_method("method_#{attr}") do
    begin
      yield self
    rescue
      raise $!, "method_#{attr} was called: #$!", $@
    end
  end
end

def self.define_my_methods *method_names
  method_names.each do |name|
    define_the_method(name) do |scope|
      scope.send(:the_method)
    end
  end
end

这是因为使用
instance\u eval
将调用用
define\u方法定义的
proc

在第一个示例中,它是:

do
  the_method
end
第二:

do
  begin
    yield
  rescue
    raise $!, "method_#{attr} was called: #$!", $@
  end
end
但是,
yield
将从定义它的位置起具有父范围

以下是我的建议:

def self.define_the_method attr, &block
  define_method("method_#{attr}") do
    begin
      instance_eval(&block)
    rescue
      raise $!, "method_#{attr} was called: #$!", $@
    end
  end
end

Buuuuut,在第一段中是一样的。不(作用域),我明白了,它在snippetsAh和snippetsAh中都存在(并从类方法调用)。我会喝更多的咖啡谢谢你的回答,我选择了
实例_eval
一个,因为如果我需要显式调用作用域上的所有方法,我必须更改很多代码。“我需要显式调用作用域上的所有方法”-我不明白为什么你需要显式调用作用域上的所有方法。“您在类方法的范围内
yield
ing”-不,它是实例范围内的
yield
ing,因为
define\u method
在引擎盖下使用
instance\u eval
。这个块是类范围内的,这就是为什么。@mudasobwa:对不起,这是我最初的意思,我有一段时间没有回答了,发音有点生锈。谢谢你的回答,我去了使用
instance\u eval
一个,因为如果需要显式调用作用域上的所有方法,我必须更改很多代码。我喜欢这个!但是在我的实际用例中,我还需要能够将参数传递给这些方法,所以我最终使用了
instance\u exec
。谢谢你的提示!
def self.define_the_method attr, &block
  define_method("method_#{attr}") do
    begin
      instance_eval(&block)
    rescue
      raise $!, "method_#{attr} was called: #$!", $@
    end
  end
end