Module: Bundler::Overrule::Patcher

Defined in:
lib/bundler/overrule/patcher.rb

Overview

Applies the monkey-patches, and refuses to run if Bundler's internals have moved somewhere we do not recognise.

Two seams carry the whole feature:

1. Bundler::Dsl#to_definition — the Gemfile's own dependency list.
 This is what makes `force` beat a `gem 'x', '1.5'` line (B2) and
 what keeps the lockfile's DEPENDENCIES section honest.

2. #dependencies / #runtime_dependencies on the spec classes — every
 transitive edge. BOTH readers matter: `runtime_dependencies` feeds
 resolution (via MatchMetadata#expanded_dependencies) and
 SpecSet#validate_deps, while `dependencies` feeds
 ParallelInstaller::SpecInstallation#all_dependencies. Filtering only
 the former resolves correctly and then deadlocks the installer,
 which sits waiting for a banned gem that will never arrive.

Patching the resolver itself (Resolver#to_dependency_hash) also works for resolution and is tempting as a single choke point, but it does not cover installation, so it buys nothing over seam 2 and adds a patch point.

Defined Under Namespace

Modules: GemfileDependencies, ResolveFreshness, SpecDependencies

Constant Summary collapse

SPEC_CLASSES =

Spec classes whose dependency readers feed resolution and installation.

%w[
  Gem::Specification
  Bundler::RemoteSpecification
  Bundler::EndpointSpecification
  Bundler::StubSpecification
  Bundler::LazySpecification
].freeze

Class Method Summary collapse

Class Method Details

.applied?Boolean

Returns:

  • (Boolean)


38
39
40
# File 'lib/bundler/overrule/patcher.rb', line 38

def applied?
  @applied ||= false
end

.apply!Object

Idempotent: Bundler 4.0 may re-load plugins.rb during a single run, and the Gemfile itself is evaluated more than once.



44
45
46
47
48
49
50
51
52
# File 'lib/bundler/overrule/patcher.rb', line 44

def apply!
  return if applied?

  verify_supported!
  patch_dsl!
  patch_spec_classes!
  patch_resolve_short_circuit!
  @applied = true
end

.reset!Object

Test hook. Prepended modules cannot be un-prepended; this only resets the "have we done it" flag, which is all the specs need.



56
57
58
# File 'lib/bundler/overrule/patcher.rb', line 56

def reset!
  @applied = false
end

.verify_supported!Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/bundler/overrule/patcher.rb', line 60

def verify_supported!
  unless ::Gem::Requirement.create(SUPPORTED_BUNDLER).satisfied_by?(::Gem::Version.new(::Bundler::VERSION))
    raise UnsupportedBundlerError, "Bundler #{::Bundler::VERSION} is outside the tested range."
  end

  missing = SPEC_CLASSES.reject { |name| resolve_constant(name) }
  unless missing.empty?
    raise UnsupportedBundlerError,
          "Expected these classes to exist, but they do not: #{missing.join(", ")}."
  end

  return if ::Bundler::Dsl.method_defined?(:to_definition)

  raise UnsupportedBundlerError, "Bundler::Dsl#to_definition is missing."
end