Class: Solve::RubySolver

Inherits:
Object
  • Object
show all
Includes:
Molinillo::SpecificationProvider
Defined in:
lib/solve/ruby_solver.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(graph, demands, options = {}) ⇒ RubySolver

Returns a new instance of RubySolver.

Examples:

Basic use:

graph = Solve::Graph.new
graph.artifacts("mysql", "1.2.0")
demands = [["mysql"]]
RubySolver.new(graph, demands)

Parameters:

  • graph (Solve::Graph)
  • demands (Array<String>, Array<Array<String, String>>)


43
44
45
46
47
48
49
50
51
52
53
# File 'lib/solve/ruby_solver.rb', line 43

def initialize(graph, demands, options = {})
  @graph         = graph
  @demands_array = demands
  @timeout_ms    = self.class.timeout

  @ui = options[:ui] # could be nil, but that's okay
  @dependency_source = options[:dependency_source] || "user-specified dependency"

  @molinillo_graph = Molinillo::DependencyGraph.new
  @resolver = Molinillo::Resolver.new(self, self)
end

Instance Attribute Details

#demands_arrayArray<String>, Array<Array<String, String>> (readonly)

Returns demands.

Examples:

Demands are Arrays of Arrays with an artifact name and optional constraint:

[['nginx', '= 1.0.0'], ['mysql']]

Returns:

  • (Array<String>, Array<Array<String, String>>)

    demands



34
35
36
# File 'lib/solve/ruby_solver.rb', line 34

def demands_array
  @demands_array
end

#graphSolve::Graph (readonly)

Graph object with references to all known artifacts and dependency constraints.

Returns:



29
30
31
# File 'lib/solve/ruby_solver.rb', line 29

def graph
  @graph
end

Class Method Details

.activateObject

For optional solver engines, this attempts to load depenencies. The RubySolver is a non-optional component, so this is a no-op



20
21
22
# File 'lib/solve/ruby_solver.rb', line 20

def activate
  true
end

.timeoutInteger

The timeout (in seconds) to use when resolving graphs. Default is 10. This can be configured by setting the SOLVE_TIMEOUT environment variable.

Returns:

  • (Integer)


13
14
15
16
# File 'lib/solve/ruby_solver.rb', line 13

def timeout
  seconds = 30 unless ( seconds = ENV["SOLVE_TIMEOUT"] )
  seconds.to_i * 1_000
end

Instance Method Details

#after_resolutionObject

Callback required by Molinillo, called when the solve is complete.

Returns:

  • nil



115
116
117
# File 'lib/solve/ruby_solver.rb', line 115

def after_resolution
  @ui.say("Finished dependency resolution") if @ui
end

#allow_missing?(dependency) ⇒ Boolean

Callback required by Molinillo Returns whether this dependency, which has no possible matching specifications, can safely be ignored.

Parameters:

  • dependency (Object)

Returns:

  • (Boolean)

    whether this dependency can safely be skipped.



253
254
255
# File 'lib/solve/ruby_solver.rb', line 253

def allow_missing?(dependency)
  false
end

#before_resolutionObject

Callback required by Molinillo, called when the solve starts

Returns:

  • nil



109
110
111
# File 'lib/solve/ruby_solver.rb', line 109

def before_resolution
  @ui.say("Starting dependency resolution") if @ui
end

#debug(current_resolver_depth = 0) ⇒ Object

Callback required by Molinillo, gives debug information about the solution

Returns:

  • nil



127
128
129
130
131
# File 'lib/solve/ruby_solver.rb', line 127

def debug(current_resolver_depth = 0)
  # debug info will be returned if you call yield here, but it seems to be
  # broken in current Molinillo
  @ui.say(yield) if @ui
end

#demandsArray<Solve::Demand>

The problem demands given as Demand model objects

Returns:



57
58
59
60
61
# File 'lib/solve/ruby_solver.rb', line 57

def demands
  demands_array.map do |name, constraint|
    Demand.new(self, name, constraint)
  end
end

#dependencies_for(specification) ⇒ Array<Solve::Dependency>

Note:

This method should be ‘pure’, i.e. the return value should depend only on the ‘specification` parameter.

Callback required by Molinillo Returns the dependencies of ‘specification`.

Parameters:

  • specification (Object)

Returns:



157
158
159
# File 'lib/solve/ruby_solver.rb', line 157

def dependencies_for(specification)
  specification.dependencies
end

#indicate_progressObject

Callback required by Molinillo, called when resolving every progress_rate

Returns:

  • nil



121
122
123
# File 'lib/solve/ruby_solver.rb', line 121

def indicate_progress
  nil
end

#name_for(dependency) ⇒ String

Note:

This method should be ‘pure’, i.e. the return value should depend only on the ‘dependency` parameter.

Callback required by Molinillo Returns the name for the given ‘dependency`.

Parameters:

  • dependency (Object)

Returns:

  • (String)

    the name for the given ‘dependency`.



212
213
214
# File 'lib/solve/ruby_solver.rb', line 212

def name_for(dependency)
  dependency.name
end

#name_for_explicit_dependency_sourceString

Callback required by Molinillo

Returns:

  • (String)

    the name of the source of explicit dependencies, i.e. those passed to Resolver#resolve directly.



219
220
221
# File 'lib/solve/ruby_solver.rb', line 219

def name_for_explicit_dependency_source
  @dependency_source
end

#progress_rateInteger

Callback required by Molinillo, called when the solve starts

Returns:

  • (Integer)


103
104
105
# File 'lib/solve/ruby_solver.rb', line 103

def progress_rate
  1
end

#requirement_satisfied_by?(requirement, activated, spec) ⇒ Boolean

Callback required by Molinillo Determines whether the given ‘requirement` is satisfied by the given `spec`, in the context of the current `activated` dependency graph.

Parameters:

  • requirement (Object)
  • activated (DependencyGraph)

    the current dependency graph in the resolution process.

  • spec (Object)

Returns:

  • (Boolean)

    whether ‘requirement` is satisfied by `spec` in the context of the current `activated` dependency graph.



171
172
173
174
175
176
177
178
179
# File 'lib/solve/ruby_solver.rb', line 171

def requirement_satisfied_by?(requirement, activated, spec)
  version = spec.version
  return false unless requirement.constraint.satisfies?(version)

  shared_possibility_versions = possibility_versions(requirement, activated)
  return false if !shared_possibility_versions.empty? && !shared_possibility_versions.include?(version)

  true
end

#resolve(options = {}) ⇒ Hash, List

Returns a hash like { “Artifact Name” => “Version”,… } unless the :sorted option is true, then it returns a list like [[“Artifact Name”, “Version],…]

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :sorted (Boolean)

    return the solution as a sorted list instead of a Hash

Returns:

  • (Hash, List)

    Returns a hash like { “Artifact Name” => “Version”,… } unless the :sorted option is true, then it returns a list like [[“Artifact Name”, “Version],…]

Raises:



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/solve/ruby_solver.rb', line 73

def resolve(options = {})
  @ui = options[:ui] if options[:ui]

  solved_graph = resolve_with_error_wrapping

  solution = solved_graph.map(&:payload)

  unsorted_solution = solution.inject({}) do |stringified_soln, artifact|
    stringified_soln[artifact.name] = artifact.version.to_s
    stringified_soln
  end

  if options[:sorted]
    build_sorted_solution(unsorted_solution)
  else
    unsorted_solution
  end
end

#search_for(dependency) ⇒ Array<Solve::Artifact>

Note:

This method should be ‘pure’, i.e. the return value should depend only on the ‘dependency` parameter.

Callback required by Molinillo Search for the specifications that match the given dependency. The specifications in the returned array will be considered in reverse order, so the latest version ought to be last.

Parameters:

  • dependency (Object)

Returns:



144
145
146
147
148
# File 'lib/solve/ruby_solver.rb', line 144

def search_for(dependency)
  # This array gets mutated by Molinillo; it's okay because sort returns a
  # new array.
  graph.versions(dependency.name, dependency.constraint).sort
end

#sort_dependencies(dependencies, activated, conflicts) ⇒ Array<Solve::Dependency>

Callback required by Molinillo Sort dependencies so that the ones that are easiest to resolve are first. Easiest to resolve is (usually) defined by:

1) Is this dependency already activated?
2) How relaxed are the requirements?
3) Are there any conflicts for this dependency?
4) How many possibilities are there to satisfy this dependency?

Parameters:

  • dependencies (Array<Object>)
  • activated (DependencyGraph)

    the current dependency graph in the resolution process.

  • conflicts ({String => Array<Conflict>})

Returns:



236
237
238
239
240
241
242
243
244
245
# File 'lib/solve/ruby_solver.rb', line 236

def sort_dependencies(dependencies, activated, conflicts)
  dependencies.sort_by do |dependency|
    name = name_for(dependency)
    [
      activated.vertex_named(name).payload ? 0 : 1,
      conflicts[name] ? 0 : 1,
      search_for(dependency).count,
    ]
  end
end