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)
  do_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 109
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 121
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 235
def debug
  environment.logger.debug { '  ' * @level + yield }
end
debug_conflict(dependency, conflict) click to toggle source
# File lib/librarian/resolver/implementation.rb, line 208
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 212
def debug_cycle(manifest)
  debug { "Cycle with #{manifest}" }
end
debug_schedule(dependency) click to toggle source
# File lib/librarian/resolver/implementation.rb, line 204
def debug_schedule(dependency)
  debug { "Scheduling #{dependency}" }
end
default_source() click to toggle source
# File lib/librarian/resolver/implementation.rb, line 132
def default_source
  @default_source ||= MultiSource.new(spec.sources)
end
dependency_source_map() click to toggle source
# File lib/librarian/resolver/implementation.rb, line 136
def dependency_source_map
  @dependency_source_map ||=
    Hash[spec.dependencies.map{|d| [d.name, d.source]}]
end
do_resolve(state) click to toggle source
# File lib/librarian/resolver/implementation.rb, line 55
def do_resolve(state)
  stack = [state]
  while !stack.empty? do
    state = stack.pop
    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
      manifest.exclude_dependencies!(spec.exclusions).each do |d|
        debug { "Excluding dependency #{d.name} from #{manifest.name}" }
      end
      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)

      stack.push(s)
    end
  end
end
environment() click to toggle source
# File lib/librarian/resolver/implementation.rb, line 239
def environment
  resolver.environment
end
find_inconsistency(state, dependency) click to toggle source
# File lib/librarian/resolver/implementation.rb, line 81
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) click to toggle source
# File lib/librarian/resolver/implementation.rb, line 224
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 216
def map_find(enum)
  enum.each do |obj|
    res = yield(obj)
    res.nil? or return res
  end
  nil
end
resolving_dependency_map_find_manifests(dependency) { |manifest| ... } click to toggle source
# File lib/librarian/resolver/implementation.rb, line 157
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 228
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 190
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 183
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 167
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 94
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 148
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 152
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 141
def sourced_dependency_for(dependency)
  return dependency if dependency.source

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