Module: RequireHooks

Defined in:
lib/require-hooks/api.rb,
lib/require-hooks/iseq.rb,
lib/require-hooks/version.rb,
lib/require-hooks/mode/bootsnap.rb,
lib/require-hooks/mode/load_iseq.rb,
lib/require-hooks/mode/kernel_patch.rb

Defined Under Namespace

Modules: Bootsnap, Iseq, KernelPatch, LoadIseq Classes: Context

Constant Summary collapse

VERSION =
"0.4.0"
EMPTY_ISEQ =
RubyVM::InstructionSequence.compile("").freeze
@@default_context =
Context.new
@@noop_context =
Context.new
@@contexts =
{}

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

Returns the value of attribute print_warnings.



93
94
95
# File 'lib/require-hooks/api.rb', line 93

def print_warnings
  @print_warnings
end

Class Method Details

.around_load(patterns: nil, exclude_patterns: nil, &block) ⇒ Object

Define a block to wrap the code loading. The return value MUST be a result of calling the passed block. For example, you can use such hooks for instrumentation, debugging purposes.

RequireHooks.around_load do |path, &block|
  puts "Loading #{path}"
  block.call.tap { puts "Loaded #{path}" }
end


103
104
105
# File 'lib/require-hooks/api.rb', line 103

def around_load(patterns: nil, exclude_patterns: nil, &block)
  register_hook(:around_load, block, patterns: patterns, exclude_patterns: exclude_patterns)
end

.context_for(path) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/require-hooks/api.rb', line 139

def context_for(path)
  # Fast-track in case we have just a single non-global context defined
  if @@default_context
    return @@default_context if @@default_context.match?(path)

    return @@noop_context
  end

  matching = @@contexts.values.select { |ctx| ctx.match?(path) }

  return matching[0] || @@noop_context if matching.size < 2

  ctx = Context.new
  matching.each { |mctx| ctx.merge!(mctx) }

  ctx
end

.contextsObject



168
169
170
# File 'lib/require-hooks/api.rb', line 168

def contexts
  @@contexts
end

.hijack_load(patterns: nil, exclude_patterns: nil, &block) ⇒ Object

This hook should be used to manually compile byte code to be loaded by the VM. The arguments are (path, source = nil), where source is only defined if transformations took place. Otherwise, you MUST read the source code from the file yourself.

The return value MUST be either nil (continue to the next hook or default behavior) or a platform-specific bytecode object (e.g., RubyVM::InstructionSequence).

RequireHooks.hijack_load do |path, source|
  source ||= File.read(path)
  if defined?(RubyVM::InstructionSequence)
    RubyVM::InstructionSequence.compile(source)
  elsif defined?(JRUBY_VERSION)
    JRuby.compile(source)
  end
end


135
136
137
# File 'lib/require-hooks/api.rb', line 135

def hijack_load(patterns: nil, exclude_patterns: nil, &block)
  register_hook(:hijack_load, block, patterns: patterns, exclude_patterns: exclude_patterns)
end

.setup_path_coverage(path, contents = nil) ⇒ Object

Hack to enable coverage for hooked files. Requires eval coverage to be on. See bugs.ruby-lang.org/issues/22018 (github.com/ruby/ruby/pull/16805)



160
161
162
163
164
165
166
# File 'lib/require-hooks/api.rb', line 160

def setup_path_coverage(path, contents = nil)
  return unless defined?(Coverage) && Coverage.running?

  return unless eval_coverage_enabled?

  Kernel.eval("\n" * (contents || File.read(path)).lines.size, TOPLEVEL_BINDING, path, 1) # rubocop:disable Style/EvalWithLocation,Security/Eval
end

.source_transform(patterns: nil, exclude_patterns: nil, &block) ⇒ Object

Define hooks to perform source-to-source transformations. The return value MUST be either String (new source code) or nil (indicating that no transformations were performed).

NOTE: The second argument (‘source`) MAY be nil, indicating that no transformer tried to transform the source code.

For example, you can prepend each file with ‘# frozen_string_literal: true` pragma:

RequireHooks.source_transform do |path, source|
  "# frozen_string_literal: true\n#{source}"
end


117
118
119
# File 'lib/require-hooks/api.rb', line 117

def source_transform(patterns: nil, exclude_patterns: nil, &block)
  register_hook(:source_transform, block, patterns: patterns, exclude_patterns: exclude_patterns)
end