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
使用已定义为特殊 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