Class: Bundler::Overrule::Registry

Inherits:
Object
  • Object
show all
Defined in:
lib/bundler/overrule/registry.rb

Overview

Holds the rules declared in the Gemfile and the edges they rewrote.

There is one of these per process (see Overrule.registry). It is not thread-safe by design: rules are declared during single-threaded Gemfile evaluation. Edge recording does happen from the parallel installer, so that path appends to a plain array and de-duplicates on read rather than locking on every dependency lookup.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeRegistry

Returns a new instance of Registry.



21
22
23
# File 'lib/bundler/overrule/registry.rb', line 21

def initialize
  clear!
end

Instance Attribute Details

#rulesObject (readonly)

Returns the value of attribute rules.



19
20
21
# File 'lib/bundler/overrule/registry.rb', line 19

def rules
  @rules
end

#rules_by_nameObject (readonly)

Fast lookup used on the hot path (once per dependency, per spec).



41
42
43
# File 'lib/bundler/overrule/registry.rb', line 41

def rules_by_name
  @rules_by_name
end

Instance Method Details

#add(rule) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/bundler/overrule/registry.rb', line 43

def add(rule)
  existing = @rules_by_name[rule.name]

  if existing
    # Bundler evaluates the Gemfile more than once per invocation, so an
    # identical re-declaration is expected and must be a no-op. Only a
    # genuinely different rule for the same gem is an error (B4).
    return existing if existing == rule

    raise DuplicateRuleError, <<~MSG
      duplicate overrule for `#{rule.name}`

        already declared: #{existing}
        then declared:    #{rule}

      Each gem may have at most one overrule rule. Pick one and delete the other.
    MSG
  end

  @rules << rule
  @rules_by_name[rule.name] = rule
  rule
end

#any?Boolean

Returns:

  • (Boolean)


36
37
38
# File 'lib/bundler/overrule/registry.rb', line 36

def any?
  !empty?
end

#bansObject



75
76
77
# File 'lib/bundler/overrule/registry.rb', line 75

def bans
  @rules.select { |r| r.type == :ban }
end

#clear!Object



25
26
27
28
29
30
# File 'lib/bundler/overrule/registry.rb', line 25

def clear!
  @rules = []
  @rules_by_name = {}
  @edges = []
  self
end

#edgesObject

Every edge a rule actually changed, de-duplicated. Bundler asks specs for their dependencies many times per run, so the raw list is noisy.



90
91
92
# File 'lib/bundler/overrule/registry.rb', line 90

def edges
  @edges.uniq { |e| [e.rule.name, e.owner, e.was, e.now] }
end

#edges_for(rule) ⇒ Object



94
95
96
# File 'lib/bundler/overrule/registry.rb', line 94

def edges_for(rule)
  edges.select { |e| e.rule.name == rule.name }
end

#empty?Boolean

Returns:

  • (Boolean)


32
33
34
# File 'lib/bundler/overrule/registry.rb', line 32

def empty?
  @rules.empty?
end

#filter(dependencies, owner: nil) ⇒ Object

Rewrite a dependency list and remember what changed.



80
81
82
83
84
85
86
# File 'lib/bundler/overrule/registry.rb', line 80

def filter(dependencies, owner: nil)
  return dependencies if empty?

  rewritten, edges = DependencyRewriter.rewrite(dependencies, @rules_by_name, owner: owner)
  @edges.concat(edges) unless edges.empty?
  rewritten
end

#fingerprintObject

Identifies the current rule set. Written to the state file so we can tell when rules changed and a re-resolve is required (see Patcher).



106
107
108
109
110
111
112
# File 'lib/bundler/overrule/registry.rb', line 106

def fingerprint
  return nil if empty?

  payload = @rules.map { |r| "#{r.type}:#{r.name}:#{r.comparable_requirement}" }.sort.join("\n")
  # ::Digest — Bundler vendors its own Bundler::Digest, which would win here.
  ::Digest::SHA256.hexdigest(payload)[0, 16]
end

#forcesObject



71
72
73
# File 'lib/bundler/overrule/registry.rb', line 71

def forces
  @rules.select { |r| r.type == :force }
end

#rule_for(name) ⇒ Object



67
68
69
# File 'lib/bundler/overrule/registry.rb', line 67

def rule_for(name)
  @rules_by_name[name]
end

#stale_rulesObject

Rules that matched nothing at all (B3) — doctor flags these.



99
100
101
102
# File 'lib/bundler/overrule/registry.rb', line 99

def stale_rules
  matched = edges.map { |e| e.rule.name }.uniq
  @rules.reject { |r| matched.include?(r.name) }
end