如何 monkeypatch "GC.start"

How to monkeypatch "GC.start"

我想做一些涉及挂钩 GC.start 调用的实验。

Ruby 告诉我 GC 不是 class 当我 运行 这个:

class GC
  def self.start
    puts "hello"
    super
  end
end

但是 运行 宁这个,Ruby 告诉我 GC.start 没有超级 class,所以我想我实际上并没有连接到原来的那个,但是只是以某种方式接管了那个名字:

module GC
  def self.start
    puts "hello"
    super
  end
end
GC.start

如何使用 monkeypatch GC.start

我们先重新定义 GC::start 这样我们就可以看到它什么时候被调用了。

module GC
  def self.start(full_mark: true, immediate_sweep: true)
    puts "old start, full_mark: #{full_mark}, " +
      "immediate_sweep: #{immediate_sweep}"
  end
end  

这里有两种方法可以获得想要的结果。

1.在 GC 的单例 class

中使用 Module#prepend
module X 
  def start(full_mark: true, immediate_sweep: true)
    puts "new start, full_mark: #{full_mark}, " +
      "immediate_sweep: #{immediate_sweep}"
    method(__method__).super_method.call(full_mark: full_mark,
      immediate_sweep: immediate_sweep)  
  end
end

module GC
  class << self
    prepend X
  end
end

GC.start(full_mark: 'cat')
new start, full_mark: cat, immediate_sweep: true
old start, full_mark: cat, immediate_sweep: true

注:

GC.singleton_class.ancestors
  #=> [X, #<Class:GC>, Module, ...] 

GC 的单例 class 中使用 Module#prepend 类似于 GC.extend X 除了它把 X 放在 GC 的单例之前class 在 GC 的祖先中。另见 Method#super_method, Object#method, Kernel#__method__ and Method#call.

还请注意:

GC.singleton_class.public_send(:prepend, X)

可用于代替:

module GC
  class << self
    prepend X
  end
end

2。使用别名

module GC
  class << self
    alias old_start start
  end

  def self.start(full_mark: true, immediate_sweep: true)
    puts "new start, full_mark: #{full_mark}, " +
      "immediate_sweep: #{immediate_sweep}"
    old_start(full_mark: full_mark, immediate_sweep: immediate_sweep)
  end
end

GC.start(full_mark: 'cat')
new start, full_mark: cat, immediate_sweep: true
old start, full_mark: cat, immediate_sweep: true

别名在 Module#prepend 在 Ruby v2.0 中首次亮相之前很常用。