Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override Kernel.load

Tags:

ruby

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:

  • "Ways to load code"
  • "Ruby Require VS Load VS Include VS Extend"
  • And Jörg's amazing answer in "When monkey patching a method, can you call the overridden method from the new implementation?"

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.

  • http://ruby-doc.org/core-2.2.2/Module.html#method-i-prepend
  • http://ruby-doc.org/core-2.2.2/Module.html#method-i-prepend_features
  • http://ruby-doc.org/core-2.2.2/Module.html#method-i-append_features
like image 924
the Tin Man Avatar asked Aug 05 '15 18:08

the Tin Man


1 Answers

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.

like image 70
the Tin Man Avatar answered Oct 01 '22 04:10

the Tin Man