ruby 中未定义的调用方法

calling method that was undefined in ruby

使用已定义为特殊 class 的外部 API,其中几乎所有标准方法都未定义用于构建 xml。其中 #method_missing 负责根据对象调用的缺失方法名称生成元素。

基本上在 class 正文中有一些效果:

undef_method :send

现在我想通过名称以编程方式调用方法。我想我可以使用 eval "obj.#{something}" 但我真的不喜欢 eval。 我在想一定有一些阴暗面的技术来恢复方法 #send 的取消定义,所以我可以将它别名为 #__send__ 并再次取消定义。这样我就可以愉快地按名称调用方法而无需评估。例如obj.__send__(:something, params)

所以我的问题是如何使用猴子修补来恢复 #send 方法。我找不到任何那样的效果。甚至也找不到人问。

更新:我原来的问题是没有问题的,因为无论如何有一个方法#__send__,我只知道#send。问题的另一部分是如何恢复未定义的方法。在@philomory asnwer 的帮助下,这就是我得到的:

[46] pry(#<CucuShift::DefaultWorld>)> class A
[46] pry(#<CucuShift::DefaultWorld>)*   def gah
[46] pry(#<CucuShift::DefaultWorld>)*     puts "gah"
[46] pry(#<CucuShift::DefaultWorld>)*   end  
[46] pry(#<CucuShift::DefaultWorld>)* end  
=> :gah
[49] pry(#<CucuShift::DefaultWorld>)> class C < A
[49] pry(#<CucuShift::DefaultWorld>)*   undef_method :gah
[49] pry(#<CucuShift::DefaultWorld>)* end  
=> C
[50] pry(#<CucuShift::DefaultWorld>)> C.new.gah
NoMethodError: undefined method `gah' for #<C:0x000000070d7918>
[57] pry(#<CucuShift::DefaultWorld>)> class C
[57] pry(#<CucuShift::DefaultWorld>)*   define_method :fff , A.instance_method(:gah)
[57] pry(#<CucuShift::DefaultWorld>)* end  
=> :fff
[58] pry(#<CucuShift::DefaultWorld>)> C.new.fff
gah

您正在寻找的黑魔法是 UnboundMethod class。像这样使用它(假设 obj 是您的构建器 API 对象):

send_method = BasicObject.instance_method(:__send__)
bound_method = send_method.bind(obj)
bound_method.call(:method_you_are_calling_dynamically,*arguments)

当然,根据你想要完成的事情,你可以直接获取你想要的特定方法并直接绑定它,比如

id_method = BasicObject.intance_method(:__id__)
bound_method = id_method.bind(obj)
bound_method.call

或者,如果构建器 class 正在使用 method_missing 而您只想动态分派给它,则根本不需要 send 或类似的东西,只需致电 obj.method_missing(:my_dynamic_method_name)


更新:

值得注意的是,您不需要将 send 别名为 __send__,因为 __send__ 已作为 BasicObject 的一部分提供,取消定义它是非常不常见的。虽然我想如果你使用的是早期版本的 Ruby 没有 BasicObject 和 __send__ 你这样做:

def obj.__send__(*args,&blk)
  Object.instance_method(:send).bind(self).call(*args,&blk)
end