Module: Bundler::Overrule::DependencyRewriter
- Defined in:
- lib/bundler/overrule/dependency_rewriter.rb
Overview
The heart of the gem: given a list of Gem::Dependency objects and a set of rules, return the rewritten list plus the edges that changed.
Deliberately pure — no Bundler objects, no global state, no I/O — so the behavior contract can be unit-tested without running Bundler at all. Both the resolution seam and the installation seam call this same function, which is what keeps the lockfile and the installed gems from ever disagreeing about what a dependency list contains.
Constant Summary collapse
- LOCATOR_OPTIONS =
Options on a Gemfile dependency that describe which gem, as opposed to how it is used. A swap points at a different gem, so these must not be carried over —
gem 'httpclient', git: '...'swapped to a fork would otherwise fetch the fork's name from the old gem's git remote. %w[ source git github gist bitbucket path branch ref tag glob submodules ].freeze
Class Method Summary collapse
-
.apply_rule(dependency, rule, owner) ⇒ Array(Gem::Dependency, Edge)
Decide what one dependency becomes under one rule.
- .development?(dependency) ⇒ Boolean
-
.replace(dependency, requirement) ⇒ Object
Swap in a new requirement while keeping everything else about the dependency intact.
- .requirement_string(dependency) ⇒ Object
- .rewrite(dependencies, rules_by_name, owner: nil) ⇒ Array(Array<Gem::Dependency>, Array<Edge>)
-
.substitute(dependency, name, requirement) ⇒ Object
Point a dependency at a different gem entirely, keeping the options that still make sense (groups, platforms, the require path).
Class Method Details
.apply_rule(dependency, rule, owner) ⇒ Array(Gem::Dependency, Edge)
Decide what one dependency becomes under one rule.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/bundler/overrule/dependency_rewriter.rb', line 57 def apply_rule(dependency, rule, owner) # Development dependencies never reach the resolver, but spec objects # still report them. Rewriting or dropping one would be a lie about # what the gem declares, for no benefit. return [dependency, nil] if rule.nil? || development?(dependency) case rule.type when :ban [nil, Edge.new(rule, owner, requirement_string(dependency), nil)] when :force return [dependency, nil] if dependency.requirement == rule.requirement [replace(dependency, rule.requirement), Edge.new(rule, owner, requirement_string(dependency), rule.requirement.to_s)] when :swap [substitute(dependency, rule.replacement, rule.requirement), Edge.new(rule, owner, requirement_string(dependency), "#{rule.replacement} #{rule.requirement}")] else [dependency, nil] end end |
.development?(dependency) ⇒ Boolean
80 81 82 |
# File 'lib/bundler/overrule/dependency_rewriter.rb', line 80 def development?(dependency) dependency.respond_to?(:type) && dependency.type == :development end |
.replace(dependency, requirement) ⇒ Object
Swap in a new requirement while keeping everything else about the dependency intact.
We copy-and-mutate rather than construct a fresh object on purpose.
Bundler::Dependency derives #groups, #source, #git, #path, #github,
#branch and #ref lazily from an options hash it keeps privately, and
the shape of that hash has changed across Bundler versions. Rebuilding
from public readers silently drops whichever of those we forgot — a
force on a gem declared with git: would quietly lose its source.
Duplicating preserves the exact class and every option, forever.
98 99 100 101 102 |
# File 'lib/bundler/overrule/dependency_rewriter.rb', line 98 def replace(dependency, requirement) replacement = dependency.dup replacement.instance_variable_set(:@requirement, requirement) replacement end |
.requirement_string(dependency) ⇒ Object
84 85 86 |
# File 'lib/bundler/overrule/dependency_rewriter.rb', line 84 def requirement_string(dependency) dependency.requirement.to_s end |
.rewrite(dependencies, rules_by_name, owner: nil) ⇒ Array(Array<Gem::Dependency>, Array<Edge>)
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/bundler/overrule/dependency_rewriter.rb', line 36 def rewrite(dependencies, rules_by_name, owner: nil) return [dependencies, []] if rules_by_name.empty? || dependencies.nil? || dependencies.empty? edges = [] rewritten = [] dependencies.each do |dependency| rule = rules_by_name[dependency.name] replacement, edge = apply_rule(dependency, rule, owner) rewritten << replacement unless replacement.nil? edges << edge if edge end [rewritten, edges] end |
.substitute(dependency, name, requirement) ⇒ Object
Point a dependency at a different gem entirely, keeping the options that still make sense (groups, platforms, the require path).
114 115 116 117 118 119 120 121 122 |
# File 'lib/bundler/overrule/dependency_rewriter.rb', line 114 def substitute(dependency, name, requirement) unless dependency.is_a?(::Bundler::Dependency) return ::Gem::Dependency.new(name, requirement, dependency.type) end = dependency.instance_variable_get(:@options) || {} kept = .reject { |key, _| LOCATOR_OPTIONS.include?(key.to_s) } ::Bundler::Dependency.new(name, requirement.as_list, kept) end |