I want to do some experiments which involve hooking into invocations of GC.start
.
Ruby tells me GC
is not a class when I run this:
class GC
def self.start
puts "hello"
super
end
end
But running this, Ruby tells me GC.start
has no superclass, so I think I'm not actually hooking into the original one, but just somehow taking over that name:
module GC
def self.start
puts "hello"
super
end
end
GC.start
How can I monkeypatch GC.start
?
Let's first redefine GC::start
so we can see when it is invoked.
module GC
def self.start(full_mark: true, immediate_sweep: true)
puts "old start, full_mark: #{full_mark}, " +
"immediate_sweep: #{immediate_sweep}"
end
end
Here are two ways to obtain the desired result.
1. Use Module#prepend from within GC
's singleton class
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
Note:
GC.singleton_class.ancestors
#=> [X, #<Class:GC>, Module, ...]
Using Module#prepend
within GC
's singleton class is like GC.extend X
except it places X
ahead of GC
's singleton class among GC
's ancestors. See also Method#super_method, Object#method, Kernel#__method__ and Method#call.
Observe also that:
GC.singleton_class.public_send(:prepend, X)
can be used in place of:
module GC
class << self
prepend X
end
end
2. Use aliasing
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
Aliasing was commonly used before Module#prepend
made its debut in Ruby v2.0.