module Bumbler::Hooks

Attributes

slow_requires[R]
slow_threshold[W]

Public Class Methods

benchmark(key) { || ... } click to toggle source
# File lib/bumbler/hooks.rb, line 100
def benchmark(key)
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  result = yield
  time = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000 # ms
  @slow_requires[key] = time if time > @slow_threshold
  [time, result]
end
handle_require(path) { || ... } click to toggle source

Actually do something about a require here.

# File lib/bumbler/hooks.rb, line 80
def handle_require(path, &block)
  # break out early if we're already handling the path
  return yield if path == @previous_require
  @previous_require = path

  # ignore untracked gem
  return yield unless (gem_name = Bumbler::Bundler.gem_for_require(path))

  # track load starts
  Bumbler::Bundler.require_started(gem_name) unless @started_items[gem_name]
  @started_items[gem_name] = true

  time, result = benchmark(path, &block)

  # TODO: for items with multiple paths we need to add the times
  Bumbler::Bundler.require_finished(gem_name, path, time) if result

  result
end
hook_instance_require!() click to toggle source
# File lib/bumbler/hooks.rb, line 20
def hook_instance_require!
  @hooking_instance_require = true

  ::Kernel.module_eval do
    orig_instance_require = instance_method(:require)
    define_method(:require) do |path, *args|
      ::Bumbler::Hooks.handle_require(path) do
        orig_instance_require.bind(self).call(path, *args)
      end
    end
    private :require
  end

  @hooking_instance_require = nil
end
hook_require!() click to toggle source

Inject our custom handling of require into the Kernel.

# File lib/bumbler/hooks.rb, line 15
def hook_require!
  hook_instance_require!
  hook_singleton_require!
end
hook_singleton_require!() click to toggle source
# File lib/bumbler/hooks.rb, line 36
def hook_singleton_require!
  @hooking_singleton_require = true

  ::Kernel.module_eval do
    class << self
      orig_public_require = Kernel.public_method(:require)
      define_method(:require) do |path, *args|
        ::Bumbler::Hooks.handle_require(path) do
          orig_public_require.call(path, *args)
        end
      end
    end
  end

  @hooking_singleton_require = nil
end
hooking_instance_require?() click to toggle source
# File lib/bumbler/hooks.rb, line 71
def hooking_instance_require?
  @hooking_instance_require
end
hooking_singleton_require?() click to toggle source
# File lib/bumbler/hooks.rb, line 75
def hooking_singleton_require?
  @hooking_singleton_require
end
method_added(method_name, *_args) click to toggle source

It isn’t previously defined in Kernel. This could be a bit dangerous, though.

# File lib/bumbler/hooks.rb, line 57
def self.method_added(method_name, *_args) # rubocop:disable Lint/MissingSuper
  if method_name == :require && !Bumbler::Hooks.hooking_instance_require?
    ::Bumbler::Hooks.hook_instance_require!
  end
end
singleton_method_added(method_name, *_args) click to toggle source
# File lib/bumbler/hooks.rb, line 63
def self.singleton_method_added(method_name, *_args) # rubocop:disable Lint/MissingSuper
  if method_name == :require && !Bumbler::Hooks.hooking_singleton_require?
    ::Bumbler::Hooks.hook_singleton_require!
  end
end
watch_require!() click to toggle source

Even better: Other gems hook require as well. The instance method one at least.

# File lib/bumbler/hooks.rb, line 54
def watch_require!
  ::Kernel.module_eval do
    # It isn't previously defined in Kernel.  This could be a bit dangerous, though.
    def self.method_added(method_name, *_args) # rubocop:disable Lint/MissingSuper
      if method_name == :require && !Bumbler::Hooks.hooking_instance_require?
        ::Bumbler::Hooks.hook_instance_require!
      end
    end

    def self.singleton_method_added(method_name, *_args) # rubocop:disable Lint/MissingSuper
      if method_name == :require && !Bumbler::Hooks.hooking_singleton_require?
        ::Bumbler::Hooks.hook_singleton_require!
      end
    end
  end
end