Ruby元编程:define_method块不维护范围

时间:2018-11-20 17:02:04

标签: ruby metaprogramming

我正在动态修补一堆类和方法(大多数情况下,这些方法不是简单的“输入”,就像我在互联网上可以找到的许多示例一样)

例如,我有以下代码: foo.rb

module Base
  class Foo
    def info
      puts 'Foo#info called'
    end
  end 
 end

&我也有以下课程: test.rb

module Base
  class Test
    def print
      puts "Test#print called"
      Foo.new.info
    end
  end
end

然后在main.rb中,我想在以下位置添加一个在同一模块(本例中为Foo)中使用类的方法

require_relative './foo'
require_relative './test'


new_method_content = "puts 'hi'
                      Foo.new.info"

Base::Test.instance_eval do
  def asdf
    puts "Test#asdf called"
    Foo.new.info
   end
end

执行时将清除以下内容:

Uncaught exception: uninitialized constant Foo

这对我来说很有意义,因为main.rb文件不知道我想要Base :: Foo,但是,我需要一种维护查找范围的方法,因为Base :: Test应该能够找到该类我想要的。

Base::Test.instance_eval do
  def asdf
    puts "Test#asdf called"
    Foo.new.info
   end
end

我已经做了大量的谷歌搜索和搜索操作,但是在class_eval / instance_eval / module_eval / define_method时,关于如何保持恒定的查找范围,我一无所获。其中已经以不同程度的失败而结束大声笑)

https://cirw.in/blog/constant-lookup

  

但是,令人困惑的是,如果将String传递给这些方法,则使用Module.nesting对字符串进行评估,该模块仅包含类本身(对于class_eval)或仅对象的单例类(对于instance_eval)。

&还有: https://bugs.ruby-lang.org/issues/6838

  

mod 的上下文中计算字符串或块,除了   给定一个块,不会影响常量/类变量的查找。

所以我的问题是: 如何重新定义方法,但要保持常量/类范围?

我一直在尝试其他事情(在main.rb的上下文中):

Base::Test.class_eval('def asdf; puts "Test#asdf called"; Foo.new.info; end')
Base::Test.new.asdf
=>
Test#asdf called
Uncaught exception: uninitialized constant Base::Test::Foo
Did you mean?  Base::Foo

(这是一个差异问题,因为它试图从评估的模块嵌套中查找它?我不确定为什么它不尝试从Base :: Test获得的所有模块路径,我想这会尝试不存在的Base :: Test :: Foo,因此它将在模块树中向上查找要存在的类(Base :: Foo)

1 个答案:

答案 0 :(得分:1)

当您像这样引用类Base::Test时,ruby不会将Base::作为查找常量的模块上下文。如果您直接定义方法,那是正常行为,也行不通。

但是您可以通过以下方式做到:

module Base
  Test.instance_eval do
    def asdf
      puts "Test#asdf called"
      Foo.new.info
    end
  end
end