gpt4 book ai didi

ruby - 覆盖使用#define_singleton_method 定义的实例方法并调用原始方法

转载 作者:太空宇宙 更新时间:2023-11-03 16:12:37 24 4
gpt4 key购买 nike

我有一个像这样在其实例上动态定义方法的类

obj = Object.new
obj.define_singleton_method(:foo) { "foo" }

稍后,我想重新定义 #foo,但能够调用原始实现。

当使用普通类时,这可以通过 prepend MyModule 并在前置方法中调用 super 来实现。但是 #prepend 在实例级别不可用。

我用 #extend 试过了,但它似乎根本没有覆盖 #foo 方法:

obj = Object.new
obj.define_singleton_method(:foo) { "foo" }
mod = Module.new
mod.define_method(:foo) { "module foo" }
obj.extend(mod)
obj.foo
# => "foo"

我查看了 RSpec gem,因为它们使用 #and_wrap_original 实现了类似的行为(参见 https://relishapp.com/rspec/rspec-mocks/v/3-8/docs/configuring-responses/wrapping-the-original-implementation)但我不在测试环境中 ,看起来需要很多设置代码才能实现此行为(跟踪 stub 、在重置它们时进行回调等)。

那么知道如何在纯 Ruby 中做到这一点吗?

最佳答案

查看 obj.singleton_class.ancestors 以查看实际发生的情况。默认情况下,它类似于

[#<Class:#<Object:0x00007febcf103160>>, Object, Kernel, BasicObject]

如果你执行obj.extend(mod),你会得到这个:

[#<Class:#<Object:0x00007febcf103160>>, #<Module:0x00007febcd0bb088>, Object, Kernel, BasicObject]

所以顺序是错误的,因为模块出现在单例类“后面”。为了替换它,它需要在祖先链中更早。您可以通过 obj.singleton_class.prepend(mod) 来完成此操作。这种情况下的祖先链是:

[#<Module:0x00007febcd0bb088>, #<Class:#<Object:0x00007febcf103160>>, Object, Kernel, BasicObject]

并且输出显示该方法被覆盖:

obj.foo
# => "module foo"

关于ruby - 覆盖使用#define_singleton_method 定义的实例方法并调用原始方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58231223/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com