class Librarian::Resolver::Implementation

Attributes

cyclic[RW]
resolver[RW]
spec[RW]

Public Class Methods

new(resolver, spec, options = { }) click to toggle source
# File lib/librarian/resolver/implementation.rb, line 36
def initialize(resolver, spec, options = { })
  unrecognized_options = options.keys - [:cyclic]
  unrecognized_options.empty? or raise Error,
    "unrecognized options: #{unrecognized_options.join(", ")}"
  self.resolver = resolver
  self.spec = spec
  self.cyclic = !!options[:cyclic]
  @level = 0
end

Public Instance Methods

resolve(manifests) click to toggle source
# File lib/librarian/resolver/implementation.rb, line 46
def resolve(manifests)
  manifests = index_by(manifests, &:name) if manifests.kind_of?(Array)
  queue = spec.dependencies + sourced_dependencies_for_manifests(manifests)
  state = State.new(manifests.dup, [], queue)
  recursive_resolve(state)
end

Private Instance Methods

check_manifest(state, manifest) click to toggle source

When using this method, you are required to check the return value. Returns true if the manifest satisfies all of the dependencies. Returns false if there was a dependency that the manifest does not satisfy.

# File lib/librarian/resolver/implementation.rb, line 102
def check_manifest(state, manifest)
  violation = lambda{|d| d.name == manifest.name && !d.satisfied_by?(manifest)}
  if q = state.dependencies.find(&violation) || state.queue.find(&violation)
    debug_conflict manifest, q
    return false
  end
  true
end
check_manifest_for_cycles(state, manifest) click to toggle source

When using this method, you are required to check the return value. Returns true if the manifest does not introduce a cycle. Returns false if the manifest introduces a cycle.

# File lib/librarian/resolver/implementation.rb, line 114
def check_manifest_for_cycles(state, manifest)
  manifests = state.manifests.merge(manifest.name => manifest)
  known = manifests.keys
  graph = Hash[manifests.map{|n, m| [n, m.dependencies.map(&:name) & known]}]
  if Algorithms::AdjacencyListDirectedGraph.cyclic?(graph)
    debug_cycle manifest
    return false
  end
  true
end
debug() { || ... } click to toggle source
# File lib/librarian/resolver/implementation.rb, line 228
def debug
  environment.logger.debug { '  ' * @level + yield }
end
debug_conflict(dependency, conflict) click to toggle source
# File lib/librarian/resolver/implementation.rb, line 201
def debug_conflict(dependency, conflict)
  debug { "Conflict between #{dependency} and #{conflict}" }
end
debug_cycle(manifest) click to toggle source
# File lib/librarian/resolver/implementation.rb, line 205
def debug_cycle(manifest)
  debug { "Cycle with #{manifest}" }
end
debug_schedule(dependency) click to toggle source
# File lib/librarian/resolver/implementation.rb, line 197
def debug_schedule(dependency)
  debug { "Scheduling #{dependency}" }
end
default_source() click to toggle source
# File lib/librarian/resolver/implementation.rb, line 125
def default_source
  @default_source ||= MultiSource.new(spec.sources)
end
dependency_source_map() click to toggle source
# File lib/librarian/resolver/implementation.rb, line 129
def dependency_source_map
  @dependency_source_map ||=
    Hash[spec.dependencies.map{|d| [d.name, d.source]}]
end
environment() click to toggle source
# File lib/librarian/resolver/implementation.rb, line 232
def environment
  resolver.environment
end
find_inconsistency(state, dependency) click to toggle source
# File lib/librarian/resolver/implementation.rb, line 74
def find_inconsistency(state, dependency)
  m = state.manifests[dependency.name]
  dependency.satisfied_by?(m) or return m if m
  violation = lambda{|d| !dependency.consistent_with?(d)}
  state.dependencies.find(&violation) || state.queue.find(&violation)
end
index_by(enum) { |obj| ... } click to toggle source
# File lib/librarian/resolver/implementation.rb, line 217
def index_by(enum)
  Hash[enum.map{|obj| [yield(obj), obj]}]
end
map_find(enum) { |obj| ... } click to toggle source
# File lib/librarian/resolver/implementation.rb, line 209
def map_find(enum)
  enum.each do |obj|
    res = yield(obj)
    res.nil? or return res
  end
  nil
end
recursive_resolve(state) click to toggle source
# File lib/librarian/resolver/implementation.rb, line 55
def recursive_resolve(state)
  shift_resolved_enqueued_dependencies(state) or return
  state.queue.empty? and return state.manifests

  state.dependencies << state.queue.shift
  dependency = state.dependencies.last

  resolving_dependency_map_find_manifests(dependency) do |manifest|
    check_manifest(state, manifest) or next
    check_manifest_for_cycles(state, manifest) or next unless cyclic

    m = state.manifests.merge(dependency.name => manifest)
    a = sourced_dependencies_for_manifest(manifest)
    s = State.new(m, state.dependencies.dup, state.queue + a)

    recursive_resolve(s)
  end
end
resolving_dependency_map_find_manifests(dependency) { |manifest| ... } click to toggle source
# File lib/librarian/resolver/implementation.rb, line 150
def resolving_dependency_map_find_manifests(dependency)
  scope_resolving_dependency dependency do
    map_find(dependency.manifests) do |manifest|
      scope_checking_manifest dependency, manifest do
        yield manifest
      end
    end
  end
end
scope() { || ... } click to toggle source
# File lib/librarian/resolver/implementation.rb, line 221
def scope
  @level += 1
  yield
ensure
  @level -= 1
end
scope_checking_manifest(dependency, manifest) { || ... } click to toggle source
# File lib/librarian/resolver/implementation.rb, line 183
def scope_checking_manifest(dependency, manifest)
  debug { "Checking #{manifest}" }
  resolution = nil
  scope do
    resolution = yield
    if resolution
      debug { "Resolved #{dependency} at #{manifest}" }
    else
      debug { "Backtracking from #{manifest}" }
    end
  end
  resolution
end
scope_checking_manifests() { || ... } click to toggle source
# File lib/librarian/resolver/implementation.rb, line 176
def scope_checking_manifests
  debug { "Checking manifests" }
  scope do
    yield
  end
end
scope_resolving_dependency(dependency) { || ... } click to toggle source
# File lib/librarian/resolver/implementation.rb, line 160
def scope_resolving_dependency(dependency)
  debug { "Resolving #{dependency}" }
  resolution = nil
  scope do
    scope_checking_manifests do
      resolution = yield
    end
    if resolution
      debug { "Resolved #{dependency}" }
    else
      debug { "Failed to resolve #{dependency}" }
    end
  end
  resolution
end
shift_resolved_enqueued_dependencies(state) click to toggle source

When using this method, you are required to check the return value. Returns true if the resolved enqueued dependencies at the front of the queue could all be moved to the resolved dependencies list. Returns false if there was an inconsistency when trying to move one or more of them. This modifies queue and dependencies.

# File lib/librarian/resolver/implementation.rb, line 87
def shift_resolved_enqueued_dependencies(state)
  while (d = state.queue.first) && state.manifests[d.name]
    if q = find_inconsistency(state, d)
      debug_conflict d, q
      return false
    end
    state.dependencies << state.queue.shift
  end
  true
end
sourced_dependencies_for_manifest(manifest) click to toggle source
# File lib/librarian/resolver/implementation.rb, line 141
def sourced_dependencies_for_manifest(manifest)
  manifest.dependencies.map{|d| sourced_dependency_for(d)}
end
sourced_dependencies_for_manifests(manifests) click to toggle source
# File lib/librarian/resolver/implementation.rb, line 145
def sourced_dependencies_for_manifests(manifests)
  manifests = manifests.values if manifests.kind_of?(Hash)
  manifests.map{|m| sourced_dependencies_for_manifest(m)}.flatten(1)
end
sourced_dependency_for(dependency) click to toggle source
# File lib/librarian/resolver/implementation.rb, line 134
def sourced_dependency_for(dependency)
  return dependency if dependency.source

  source = dependency_source_map[dependency.name] || default_source
  Dependency.new(dependency.name, dependency.requirement, source)
end