【问题标题】:Dynamically define a super method for an instance of a class in Ruby在 Ruby 中为类的实例动态定义一个超级方法
【发布时间】:2020-03-10 23:06:43
【问题描述】:

假设我们有一个无法更改的类,

class C
  def foo
    super
    puts "Low!"
  end
end

我们需要动态定义 foo 方法,以便我们能够注入到 C 的祖先链中。 super 的行为必须特定于给定对象,而不是类范围的。我们将能够将该逻辑封装到一个匿名模块中(让我们暂时命名它):

module M
  def foo
    puts "High!"
  end
end

使用模块扩展C 的实例:

c = C.new
c.extend(M)
c.foo
# High!

将不起作用,因为我们已将模块中的方法放在我们在类中定义的方法之前。查看我们对象的祖先

c.singleton_class.ancestors
# => [#<Class:#<C:0x00005652be630b20>>, M, C, ...]

我想出了一个丑陋的解决方法,即在我们的 singleton_class 中重新定义我们类的方法,即

c.define_singleton_method(:foo, c.class.instance_method(:foo))
c.foo
# High!
# Low!

虽然这有效(有效吗?我已经对其进行了一些测试,似乎可以,但我不再确定),我想知道我是否遗漏了一些明显的东西,并且有一种更简单的方法来动态定义类实例的“超级”方法。

明确地说,我们希望能够使用另一个模块扩展 C 的另一个实例,即

C.new.extend(Module.new do
  def foo
    puts "Medium!"
  end
end).foo
# Medium!
# Low!

并且其输出不会被其他实例污染。

【问题讨论】:

  • 我认为没有什么好的方法可以做到这一点。我建议您不要像这样使用super,而只是调用一个具有一些有意义名称的方法。或者,如果您需要更灵活的东西,您可以让对象在实例变量中保存一个 Proc 对象数组。 @procs.each { |proc| proc.call }
  • @DavidGrayson 我知道这是多么的骇人听闻。该示例已简化,实际上我正在尝试解决已停产的 gem。这不是我的首选解决方案,但我仍然认为这是一个有趣的独立问题,在 Ruby 的可能性范围内。

标签: ruby


【解决方案1】:

既然我了解到您正在尝试解决某些第三方代码中的问题,我可以建议一个更合理的解决方案。在下面的代码中,我认为BC 是由gem 定义的,您不想更改它们的源代码,但是您想在C#foo 调用@ 的地方注入一些代码987654324@.

class B
  def foo
    puts "Highest!"
  end
end

class C < B
  def foo
    super
    puts "Low!"
  end
end

module CE
  def foo
    super
    foo_injected
  end
end

C.include(CE)

module M
  def foo_injected
    puts "High!"
  end
end

c = C.new
c.extend(M)
p c.singleton_class.ancestors
c.foo

输出是:

[#<Class:#<C:0x000055ce443366a8>>, M, C, CE, B, Object, Kernel, BasicObject]
Highest!
High!
Low!

【讨论】:

    猜你喜欢
    • 2012-07-04
    • 1970-01-01
    • 1970-01-01
    • 2017-09-01
    • 1970-01-01
    • 2011-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多