Module: RequireHooks

Defined in:
lib/require-hooks/api.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, KernelPatch, LoadIseq Classes: Context

Constant Summary collapse

VERSION =
"0.3.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.



80
81
82
# File 'lib/require-hooks/api.rb', line 80

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


90
91
92
93
94
95
96
97
98
# File 'lib/require-hooks/api.rb', line 90

def around_load(patterns: nil, exclude_patterns: nil, &block)
  @@default_context = nil
  ctx = Context.new(patterns: patterns, exclude_patterns: exclude_patterns)

  @@contexts[ctx.to_key] ||= ctx
  @@contexts[ctx.to_key].around_load << block

  @@default_context = @@contexts.values.first if @@contexts.size == 1
end

.context_for(path) ⇒ Object



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/require-hooks/api.rb', line 144

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

    return @@default_context
  end

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

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

  ctx = Context.new
  matching.each do |mctx|
    ctx.around_load.concat(mctx.around_load)
    ctx.source_transform.concat(mctx.source_transform)
    ctx.hijack_load.concat(mctx.hijack_load)
  end

  ctx
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


134
135
136
137
138
139
140
141
142
# File 'lib/require-hooks/api.rb', line 134

def hijack_load(patterns: nil, exclude_patterns: nil, &block)
  @@default_context = nil
  ctx = Context.new(patterns: patterns, exclude_patterns: exclude_patterns)

  @@contexts[ctx.to_key] ||= ctx
  @@contexts[ctx.to_key].hijack_load << block

  @@default_context = @@contexts.values.first if @@contexts.size == 1
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


110
111
112
113
114
115
116
117
118
# File 'lib/require-hooks/api.rb', line 110

def source_transform(patterns: nil, exclude_patterns: nil, &block)
  @@default_context = nil
  ctx = Context.new(patterns: patterns, exclude_patterns: exclude_patterns)

  @@contexts[ctx.to_key] ||= ctx
  @@contexts[ctx.to_key].source_transform << block

  @@default_context = @@contexts.values.first if @@contexts.size == 1
end