I need to override Kernel.load
in order to watch and process some Ruby files we've written for monitoring. However, it seems immune to such shenanigans.
It's easy to override require
and require_relative
, but load
sits under them and bottlenecks the actual file read if I remember right.
Here's why it seems to be protected from overriding:
Kernel.module_eval do
alias_method :original_require, :require
def require(filename)
require_result = original_require(filename)
puts "required #{filename}"
require_result
end
alias_method :original_load, :load
def load(filename, wrap=true)
load_result = original_load(filename, wrap)
puts "loaded #{filename}"
load_result
end
end
include Kernel
require 'open-uri'
puts 'done'
Running that outputs:
required uri/rfc2396_parser
required uri/rfc3986_parser
required uri/common
required uri/common
required uri/generic
required uri/generic
required uri/ftp
required uri/generic
required uri/http
required uri/http
required uri/https
required uri/generic
required uri/ldap
required uri/ldap
required uri/ldaps
required uri/generic
required uri/mailto
required uri
required stringio
required date_core
required date
required time
required open-uri
done
I'm content to only override require
and require_relative
. However, I'm curious what's going on with load
.
Afterthoughts:
It looks like load
isn't called by require
or require_relative
. Mea culpa. Good catch Matt.
This question is similar to "How to override require in Ruby?".
Good reading:
Jörg's comment
I also want to give some love to Module#prepend, which would allow you to simply use super instead of that ugly alias_method stuff, with the additional bonus that your modifications would actually show up in the ancestry chain and thus much easier to debug.
is very sensible and worth using.
Here are two simple examples that seem to work for overriding require
and require_relative
, based on examples in "When monkey patching a method, can you call the overridden method from the new implementation?".
module Kernel
old_require = method(:require)
define_method(:require) do |filename|
puts "require #{filename}"
old_require.call(filename)
end
old_require_relative = method(:require_relative)
define_method(:require_relative) do |filename|
puts "require_relative #{filename}"
old_require_relative.call(filename)
end
end
or
module KernelExtensions
def require(filename)
puts "require #{filename}"
super
end
def require_relative(filename)
puts "require_relative #{filename}"
super
end
end
class Object
prepend KernelExtensions
end
Running the second using
module Kernel
prepend KernelExtensions
end
didn't work, but since Object includes Kernel, using class Object
overriding seems to work cleanly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With