Ruby 元编程:define_method 块不维护范围
Ruby metaprogramming: define_method block not maintaining scope
我正在努力动态修补一堆 classes 和方法(大多数时候这些方法并不简单 "puts" 就像我能找到的很多例子一样在互联网上)
例如我有以下代码:
foo.rb
module Base
class Foo
def info
puts 'Foo#info called'
end
end
end
&我还有以下class:
test.rb
module Base
class Test
def print
puts "Test#print called"
Foo.new.info
end
end
end
然后在 main.rb 中,我想在同一模块中添加一个使用 class 的方法(在本例中为 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 应该能够找到我想要的 class Foo。
Base::Test.instance_eval do
def asdf
puts "Test#asdf called"
Foo.new.info
end
end
我已经进行了相当多的谷歌搜索和 SO'ing,但没有找到任何关于如何在 class_eval/instance_eval/module_eval/define_method 期间保持恒定查找范围的信息(我已经尝试了很多 Ruby各种黑魔法都以不同程度的失败告终 lol)
https://cirw.in/blog/constant-lookup
Confusingly however, if you pass a String to these methods, then the String is evaluated with Module.nesting containing just the class itself (for class_eval) or just the singleton class of the object (for instance_eval).
& 还有这个:
https://bugs.ruby-lang.org/issues/6838
Evaluates the string or block in the context of mod, except that when
a block is given, constant/class variable lookup is not affected.
所以我的问题是:
如何重新定义方法但保持 constant/class 范围?
我一直在尝试其他一些事情(在 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 ,因此它会在模块树上寻找存在的 class(Base::Foo))
当您像这样引用 class Base::Test
时,ruby 不会将 Base::
作为查找常量的模块上下文。这是正常行为,如果您直接定义方法,也不会起作用。
但是你可以这样做:
module Base
Test.instance_eval do
def asdf
puts "Test#asdf called"
Foo.new.info
end
end
end
我正在努力动态修补一堆 classes 和方法(大多数时候这些方法并不简单 "puts" 就像我能找到的很多例子一样在互联网上)
例如我有以下代码: foo.rb
module Base
class Foo
def info
puts 'Foo#info called'
end
end
end
&我还有以下class: test.rb
module Base
class Test
def print
puts "Test#print called"
Foo.new.info
end
end
end
然后在 main.rb 中,我想在同一模块中添加一个使用 class 的方法(在本例中为 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 应该能够找到我想要的 class Foo。
Base::Test.instance_eval do
def asdf
puts "Test#asdf called"
Foo.new.info
end
end
我已经进行了相当多的谷歌搜索和 SO'ing,但没有找到任何关于如何在 class_eval/instance_eval/module_eval/define_method 期间保持恒定查找范围的信息(我已经尝试了很多 Ruby各种黑魔法都以不同程度的失败告终 lol)
https://cirw.in/blog/constant-lookup
Confusingly however, if you pass a String to these methods, then the String is evaluated with Module.nesting containing just the class itself (for class_eval) or just the singleton class of the object (for instance_eval).
& 还有这个: https://bugs.ruby-lang.org/issues/6838
Evaluates the string or block in the context of mod, except that when a block is given, constant/class variable lookup is not affected.
所以我的问题是: 如何重新定义方法但保持 constant/class 范围?
我一直在尝试其他一些事情(在 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 ,因此它会在模块树上寻找存在的 class(Base::Foo))
当您像这样引用 class Base::Test
时,ruby 不会将 Base::
作为查找常量的模块上下文。这是正常行为,如果您直接定义方法,也不会起作用。
但是你可以这样做:
module Base
Test.instance_eval do
def asdf
puts "Test#asdf called"
Foo.new.info
end
end
end