Module: Bumbler::Hooks

Defined in:
lib/bumbler/hooks.rb

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.slow_requiresObject (readonly)

Returns the value of attribute slow_requires.



12
13
14
# File 'lib/bumbler/hooks.rb', line 12

def slow_requires
  @slow_requires
end

.slow_threshold=(value) ⇒ Object (writeonly)

Sets the attribute slow_threshold

Parameters:

  • value

    the value to set the attribute slow_threshold to.



10
11
12
# File 'lib/bumbler/hooks.rb', line 10

def slow_threshold=(value)
  @slow_threshold = value
end

Class Method Details

.benchmark(key) ⇒ Object



78
79
80
81
82
83
84
# File 'lib/bumbler/hooks.rb', line 78

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, &block) ⇒ Object

Actually do something about a require here.



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/bumbler/hooks.rb', line 58

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_require!Object

Inject our custom handling of require into the Kernel.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/bumbler/hooks.rb', line 15

def hook_require!
  @hooking_require = true

  # There are two independent require methods.  Joy!
  ::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

    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
  end

  @hooking_require = nil
end

.hooking_require?Boolean

Returns:

  • (Boolean)


53
54
55
# File 'lib/bumbler/hooks.rb', line 53

def hooking_require?
  @hooking_require
end

.watch_require!Object

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



41
42
43
44
45
46
47
48
49
50
51
# File 'lib/bumbler/hooks.rb', line 41

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)
      if method_name == :require && !::Bumbler::Hooks.hooking_require?
        # Fix those hooks.
        ::Bumbler::Hooks.hook_require!
      end
    end
  end
end