Как сделать monkeypatch "G C .start" - PullRequest
1 голос
/ 08 февраля 2020

Я хочу провести несколько экспериментов, которые включают подключение к вызовам GC.start.

Ruby говорит мне, GC это не класс, когда я запускаю это:

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

Но, выполнив это, Ruby говорит мне, что GC.start не имеет суперкласса, поэтому я думаю, что на самом деле я не привязываюсь к оригинальному, а просто каким-то образом принимаю это имя:

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

Как можно Я обезьяна GC.start?

1 Ответ

2 голосов
/ 08 февраля 2020

Давайте сначала переопределим 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. Используйте Модуль # prepend в пределах одноэлементного класса GC

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

Использование Module#prepend в синглтон-классе GC похоже на GC.extend X, за исключением того, что X опережает синглтон-класс GC среди предков GC. См. Также Метод # super_method , Объект # метод , Ядро #__ метод __ и Вызов метода # .

Соблюдайте также что:

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.

...