簡體   English   中英

在 Ruby 中為類的實例動態定義超級方法

[英]Dynamically define a super method for an instance of a class in Ruby

假設我們有一個我們不能改變的類,

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, ...]

我想出了一個丑陋的解決方法,即在我們的單例類中重新定義我們類中的方法,即

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!

並使其輸出不受其他實例的污染。

既然我知道您正在嘗試解決某些第三方代碼中的問題,我可以建議一個更合理的解決方案。 在下面的代碼中,我認為BC是由 gem 定義的,您不想更改它們的源代碼,但是您想在C#foo調用B#foo的地方注入一些代碼。

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!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM