solve 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +5 -0
- data/README.md +8 -7
- data/lib/solve.rb +10 -29
- data/lib/solve/artifact.rb +91 -52
- data/lib/solve/constraint.rb +109 -24
- data/lib/solve/demand.rb +26 -17
- data/lib/solve/dependency.rb +15 -1
- data/lib/solve/errors.rb +23 -20
- data/lib/solve/gem_version.rb +1 -1
- data/lib/solve/graph.rb +76 -84
- data/lib/solve/solver.rb +246 -0
- data/lib/solve/solver/constraint_row.rb +17 -0
- data/lib/solve/solver/constraint_table.rb +27 -0
- data/lib/solve/solver/variable.rb +41 -0
- data/lib/solve/solver/variable_table.rb +50 -0
- data/lib/solve/version.rb +53 -16
- data/solve.gemspec +2 -3
- data/spec/acceptance/solutions_spec.rb +81 -8
- data/spec/unit/solve/artifact_spec.rb +73 -49
- data/spec/unit/solve/constraint_spec.rb +36 -30
- data/spec/unit/solve/demand_spec.rb +22 -13
- data/spec/unit/solve/dependency_spec.rb +15 -0
- data/spec/unit/solve/graph_spec.rb +98 -142
- data/spec/unit/solve/solver_spec.rb +302 -0
- data/spec/unit/solve/version_spec.rb +110 -0
- metadata +42 -96
- data/lib/solve/core_ext.rb +0 -3
- data/lib/solve/core_ext/kernel.rb +0 -33
data/lib/solve/demand.rb
CHANGED
@@ -1,38 +1,47 @@
|
|
1
1
|
module Solve
|
2
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
2
3
|
class Demand
|
3
|
-
|
4
|
+
# A reference to the solver this demand belongs to
|
5
|
+
#
|
6
|
+
# @return [Solve::Solver]
|
7
|
+
attr_reader :solver
|
8
|
+
|
9
|
+
# The name of the artifact this demand is for
|
10
|
+
#
|
11
|
+
# @return [String]
|
4
12
|
attr_reader :name
|
13
|
+
|
14
|
+
# The acceptable constraint of the artifact this demand is for
|
15
|
+
#
|
16
|
+
# @return [Solve::Constraint]
|
5
17
|
attr_reader :constraint
|
6
18
|
|
7
|
-
# @param [Solve::
|
19
|
+
# @param [Solve::Solver] solver
|
8
20
|
# @param [#to_s] name
|
9
21
|
# @param [Solve::Constraint, #to_s] constraint
|
10
|
-
def initialize(
|
11
|
-
@
|
22
|
+
def initialize(solver, name, constraint = ">= 0.0.0")
|
23
|
+
@solver = solver
|
12
24
|
@name = name
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
else
|
18
|
-
Constraint.new(constraint.to_s)
|
19
|
-
end
|
25
|
+
@constraint = if constraint.is_a?(Solve::Constraint)
|
26
|
+
constraint
|
27
|
+
else
|
28
|
+
Constraint.new(constraint.to_s)
|
20
29
|
end
|
21
30
|
end
|
22
31
|
|
32
|
+
# Remove this demand from the solver it belongs to
|
33
|
+
#
|
23
34
|
# @return [Solve::Demand, nil]
|
24
35
|
def delete
|
25
|
-
unless
|
26
|
-
result =
|
27
|
-
@
|
36
|
+
unless solver.nil?
|
37
|
+
result = solver.remove_demand(self)
|
38
|
+
@solver = nil
|
28
39
|
result
|
29
40
|
end
|
30
41
|
end
|
31
42
|
|
32
43
|
def to_s
|
33
|
-
|
34
|
-
s << "(#{constraint})" if constraint
|
35
|
-
s
|
44
|
+
"#{name} (#{constraint})"
|
36
45
|
end
|
37
46
|
end
|
38
47
|
end
|
data/lib/solve/dependency.rb
CHANGED
@@ -1,13 +1,25 @@
|
|
1
1
|
module Solve
|
2
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
2
3
|
class Dependency
|
4
|
+
# A reference to the artifact this dependency belongs to
|
5
|
+
#
|
6
|
+
# @return [Solve::Artifact]
|
3
7
|
attr_reader :artifact
|
8
|
+
|
9
|
+
# The name of the artifact this dependency represents
|
10
|
+
#
|
11
|
+
# @return [String]
|
4
12
|
attr_reader :name
|
13
|
+
|
14
|
+
# The constraint requirement of this dependency
|
15
|
+
#
|
16
|
+
# @return [Solve::Constraint]
|
5
17
|
attr_reader :constraint
|
6
18
|
|
7
19
|
# @param [Solve::Artifact] artifact
|
8
20
|
# @param [#to_s] name
|
9
21
|
# @param [Solve::Constraint, #to_s] constraint
|
10
|
-
def initialize(artifact, name, constraint)
|
22
|
+
def initialize(artifact, name, constraint = ">= 0.0.0")
|
11
23
|
@artifact = artifact
|
12
24
|
@name = name
|
13
25
|
@constraint = case constraint
|
@@ -18,6 +30,8 @@ module Solve
|
|
18
30
|
end
|
19
31
|
end
|
20
32
|
|
33
|
+
# Remove this dependency from the artifact it belongs to
|
34
|
+
#
|
21
35
|
# @return [Solve::Dependency, nil]
|
22
36
|
def delete
|
23
37
|
unless artifact.nil?
|
data/lib/solve/errors.rb
CHANGED
@@ -1,31 +1,34 @@
|
|
1
1
|
module Solve
|
2
|
-
|
2
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
3
|
+
module Errors
|
4
|
+
class SolveError < StandardError; end
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
+
class InvalidVersionFormat < SolveError
|
7
|
+
attr_reader :version
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
# @param [#to_s] version
|
10
|
+
def initialize(version)
|
11
|
+
@version = version
|
12
|
+
end
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
+
def message
|
15
|
+
"'#{version}' did not contain a valid version string: 'x.y.z' or 'x.y'."
|
16
|
+
end
|
14
17
|
end
|
15
|
-
end
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
+
class InvalidConstraintFormat < SolveError
|
20
|
+
attr_reader :constraint
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
# @param [#to_s] constraint
|
23
|
+
def initialize(constraint)
|
24
|
+
@constraint = constraint
|
25
|
+
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
+
def message
|
28
|
+
"'#{constraint}' did not contain a valid operator or a valid version string."
|
29
|
+
end
|
27
30
|
end
|
28
|
-
end
|
29
31
|
|
30
|
-
|
32
|
+
class NoSolutionError < SolveError; end
|
33
|
+
end
|
31
34
|
end
|
data/lib/solve/gem_version.rb
CHANGED
data/lib/solve/graph.rb
CHANGED
@@ -1,9 +1,48 @@
|
|
1
1
|
module Solve
|
2
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
2
3
|
class Graph
|
4
|
+
class << self
|
5
|
+
# Create a key for a graph from an instance of an Artifact or Dependency
|
6
|
+
#
|
7
|
+
# @param [Solve::Artifact, Solve::Dependency] object
|
8
|
+
#
|
9
|
+
# @raise [ArgumentError] if an instance of an object of an unknown type is given
|
10
|
+
#
|
11
|
+
# @return [Symbol]
|
12
|
+
def key_for(object)
|
13
|
+
case object
|
14
|
+
when Solve::Artifact
|
15
|
+
artifact_key(object.name, object.version)
|
16
|
+
when Solve::Dependency
|
17
|
+
dependency_key(object.name, object.constraint)
|
18
|
+
else
|
19
|
+
raise ArgumentError, "Could not generate graph key for Class: #{object.class}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Create a key representing an artifact for an instance of Graph
|
24
|
+
#
|
25
|
+
# @param [#to_s] name
|
26
|
+
# @param [#to_s] version
|
27
|
+
#
|
28
|
+
# @return [Symbol]
|
29
|
+
def artifact_key(name, version)
|
30
|
+
"#{name}-#{version}".to_sym
|
31
|
+
end
|
32
|
+
|
33
|
+
# Create a key representing an dependency for an instance of Graph
|
34
|
+
#
|
35
|
+
# @param [#to_s] name
|
36
|
+
# @param [#to_s] constraint
|
37
|
+
#
|
38
|
+
# @return [Symbol]
|
39
|
+
def dependency_key(name, constraint)
|
40
|
+
"#{name}-#{constraint}".to_sym
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
3
44
|
def initialize
|
4
45
|
@artifacts = Hash.new
|
5
|
-
@demands = Hash.new
|
6
|
-
@dep_graph = DepSelector::DependencyGraph.new
|
7
46
|
end
|
8
47
|
|
9
48
|
# @overload artifacts(name, version)
|
@@ -36,6 +75,20 @@ module Solve
|
|
36
75
|
add_artifact(artifact)
|
37
76
|
end
|
38
77
|
|
78
|
+
# Return all the artifacts from the collection of artifacts
|
79
|
+
# with the given name.
|
80
|
+
#
|
81
|
+
# @param [String] name
|
82
|
+
#
|
83
|
+
# @return [Array<Solve::Artifact>]
|
84
|
+
def versions(name, constraint = ">= 0.0.0")
|
85
|
+
constraint = constraint.is_a?(Constraint) ? constraint : Constraint.new(constraint)
|
86
|
+
|
87
|
+
artifacts.select do |art|
|
88
|
+
art.name == name && constraint.satisfies?(art.version)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
39
92
|
# Add a Solve::Artifact to the collection of artifacts and
|
40
93
|
# return the added Solve::Artifact. No change will be made
|
41
94
|
# if the artifact is already a member of the collection.
|
@@ -44,109 +97,48 @@ module Solve
|
|
44
97
|
#
|
45
98
|
# @return [Solve::Artifact]
|
46
99
|
def add_artifact(artifact)
|
47
|
-
unless has_artifact?(artifact)
|
48
|
-
@
|
49
|
-
@artifacts[artifact.to_s] = artifact
|
100
|
+
unless has_artifact?(artifact.name, artifact.version)
|
101
|
+
@artifacts[self.class.key_for(artifact)] = artifact
|
50
102
|
end
|
51
103
|
|
52
|
-
artifact
|
53
|
-
end
|
54
|
-
|
55
|
-
# @param [Solve::Artifact, nil] artifact
|
56
|
-
def remove_artifact(artifact)
|
57
|
-
if has_artifact?(artifact)
|
58
|
-
@dep_graph.packages.delete(artifact.to_s)
|
59
|
-
@artifacts.delete(artifact.to_s)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# @param [Solve::Artifact] artifact
|
64
|
-
#
|
65
|
-
# @return [Boolean]
|
66
|
-
def has_artifact?(artifact)
|
67
|
-
@artifacts.has_key?(artifact.to_s)
|
104
|
+
get_artifact(artifact.name, artifact.version)
|
68
105
|
end
|
69
106
|
|
70
|
-
#
|
71
|
-
# Return the Solve::Demand from the collection of demands
|
72
|
-
# with the given name and constraint.
|
107
|
+
# Retrieve the artifact from the graph with the matching name and version
|
73
108
|
#
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
# @return [Solve::Demand]
|
78
|
-
# @overload demands(name)
|
79
|
-
# Return the Solve::Demand from the collection of demands
|
80
|
-
# with the given name.
|
81
|
-
#
|
82
|
-
# @param [#to_s]
|
83
|
-
#
|
84
|
-
# @return [Solve::Demand]
|
85
|
-
# @overload demands
|
86
|
-
# Return the collection of demands
|
109
|
+
# @param [String] name
|
110
|
+
# @param [Solve::Version, #to_s] version
|
87
111
|
#
|
88
|
-
#
|
89
|
-
def
|
90
|
-
|
91
|
-
return demand_collection
|
92
|
-
end
|
93
|
-
if args.length > 2
|
94
|
-
raise ArgumentError, "Unexpected number of arguments. You gave: #{args.length}. Expected: 2 or less."
|
95
|
-
end
|
96
|
-
|
97
|
-
name, constraint = args
|
98
|
-
constraint ||= ">= 0.0.0"
|
99
|
-
|
100
|
-
if name.nil?
|
101
|
-
raise ArgumentError, "A name must be specified. You gave: #{args}."
|
102
|
-
end
|
103
|
-
|
104
|
-
demand = Demand.new(self, name, constraint)
|
105
|
-
add_demand(demand)
|
112
|
+
# @return [Solve::Artifact, nil]
|
113
|
+
def get_artifact(name, version)
|
114
|
+
@artifacts.fetch(self.class.artifact_key(name, version.to_s), nil)
|
106
115
|
end
|
107
116
|
|
108
|
-
#
|
109
|
-
# return the added Solve::Demand. No change will be made
|
110
|
-
# if the demand is already a member of the collection.
|
117
|
+
# Remove the given instance of artifact from the graph
|
111
118
|
#
|
112
|
-
# @param [Solve::
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
unless has_demand?(demand)
|
117
|
-
@demands[demand.to_s] = demand
|
118
|
-
end
|
119
|
-
|
120
|
-
demand
|
121
|
-
end
|
122
|
-
alias_method :demand, :add_demand
|
123
|
-
|
124
|
-
# @param [Solve::Demand, nil] demand
|
125
|
-
def remove_demand(demand)
|
126
|
-
if has_demand?(demand)
|
127
|
-
@demands.delete(demand.to_s)
|
119
|
+
# @param [Solve::Artifact, nil] artifact
|
120
|
+
def remove_artifact(artifact)
|
121
|
+
if has_artifact?(artifact.name, artifact.version)
|
122
|
+
@artifacts.delete(self.class.key_for(artifact))
|
128
123
|
end
|
129
124
|
end
|
130
125
|
|
131
|
-
#
|
126
|
+
# Check if an artifact with a matching name and version is a member of this instance
|
127
|
+
# of graph
|
128
|
+
#
|
129
|
+
# @param [String] name
|
130
|
+
# @param [Solve::Version, #to_s] version
|
132
131
|
#
|
133
132
|
# @return [Boolean]
|
134
|
-
def
|
135
|
-
|
133
|
+
def has_artifact?(name, version)
|
134
|
+
!get_artifact(name, version).nil?
|
136
135
|
end
|
137
136
|
|
138
137
|
private
|
139
138
|
|
140
|
-
attr_reader :dep_graph
|
141
|
-
|
142
139
|
# @return [Array<Solve::Artifact>]
|
143
140
|
def artifact_collection
|
144
141
|
@artifacts.collect { |name, artifact| artifact }
|
145
142
|
end
|
146
|
-
|
147
|
-
# @return [Array<Solve::Demand>]
|
148
|
-
def demand_collection
|
149
|
-
@demands.collect { |name, demand| demand }
|
150
|
-
end
|
151
143
|
end
|
152
144
|
end
|
data/lib/solve/solver.rb
ADDED
@@ -0,0 +1,246 @@
|
|
1
|
+
module Solve
|
2
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
3
|
+
class Solver
|
4
|
+
autoload :VariableTable, 'solve/solver/variable_table'
|
5
|
+
autoload :Variable, 'solve/solver/variable'
|
6
|
+
autoload :ConstraintTable, 'solve/solver/constraint_table'
|
7
|
+
autoload :ConstraintRow, 'solve/solver/constraint_row'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# Create a key to identify a demand on a Solver.
|
11
|
+
#
|
12
|
+
# @param [Solve::Demand] demand
|
13
|
+
#
|
14
|
+
# @raise [NoSolutionError]
|
15
|
+
#
|
16
|
+
# @return [Symbol]
|
17
|
+
def demand_key(demand)
|
18
|
+
"#{demand.name}-#{demand.constraint}".to_sym
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns all of the versions which satisfy all of the given constraints
|
22
|
+
#
|
23
|
+
# @param [Array<Solve::Constraint>, Array<String>] constraints
|
24
|
+
# @param [Array<Solve::Version>, Array<String>] versions
|
25
|
+
#
|
26
|
+
# @return [Array<Solve::Version>]
|
27
|
+
def satisfy_all(constraints, versions)
|
28
|
+
constraints = Array(constraints).collect do |con|
|
29
|
+
con.is_a?(Constraint) ? con : Constraint.new(con.to_s)
|
30
|
+
end.uniq
|
31
|
+
|
32
|
+
versions = Array(versions).collect do |ver|
|
33
|
+
ver.is_a?(Version) ? ver : Version.new(ver.to_s)
|
34
|
+
end.uniq
|
35
|
+
|
36
|
+
versions.select do |ver|
|
37
|
+
constraints.all? { |constraint| constraint.satisfies?(ver) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return the best version from the given list of versions for the given list of constraints
|
42
|
+
#
|
43
|
+
# @param [Array<Solve::Constraint>, Array<String>] constraints
|
44
|
+
# @param [Array<Solve::Version>, Array<String>] versions
|
45
|
+
#
|
46
|
+
# @raise [NoSolutionError] if version matches the given constraints
|
47
|
+
#
|
48
|
+
# @return [Solve::Version]
|
49
|
+
def satisfy_best(constraints, versions)
|
50
|
+
solution = satisfy_all(constraints, versions)
|
51
|
+
|
52
|
+
if solution.empty?
|
53
|
+
raise Errors::NoSolutionError
|
54
|
+
end
|
55
|
+
|
56
|
+
solution.sort.last
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# The world as we know it
|
61
|
+
#
|
62
|
+
# @return [Solve::Graph]
|
63
|
+
attr_reader :graph
|
64
|
+
|
65
|
+
attr_reader :domain
|
66
|
+
attr_reader :variable_table
|
67
|
+
attr_reader :constraint_table
|
68
|
+
attr_reader :possible_values
|
69
|
+
|
70
|
+
# @param [Solve::Graph] graph
|
71
|
+
# @param [Array<String>, Array<Array<String, String>>] demands
|
72
|
+
def initialize(graph, demands = Array.new)
|
73
|
+
@graph = graph
|
74
|
+
@domain = Hash.new
|
75
|
+
@demands = Hash.new
|
76
|
+
@possible_values = Hash.new
|
77
|
+
@constraint_table = ConstraintTable.new
|
78
|
+
@variable_table = VariableTable.new
|
79
|
+
|
80
|
+
Array(demands).each do |l_demand|
|
81
|
+
demands(*l_demand)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [Hash]
|
86
|
+
def resolve
|
87
|
+
seed_demand_dependencies
|
88
|
+
|
89
|
+
while unbound_variable = variable_table.first_unbound
|
90
|
+
possible_values_for_unbound = possible_values_for(unbound_variable)
|
91
|
+
|
92
|
+
while possible_value = possible_values_for_unbound.shift
|
93
|
+
possible_artifact = graph.get_artifact(unbound_variable.package, possible_value.version)
|
94
|
+
possible_dependencies = possible_artifact.dependencies
|
95
|
+
all_ok = possible_dependencies.all? { |dependency| can_add_new_constraint?(dependency) }
|
96
|
+
if all_ok
|
97
|
+
add_dependencies(possible_dependencies, possible_artifact)
|
98
|
+
unbound_variable.bind(possible_value)
|
99
|
+
break
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
unless unbound_variable.bound?
|
104
|
+
backtrack(unbound_variable)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
{}.tap do |solution|
|
109
|
+
variable_table.rows.each do |variable|
|
110
|
+
solution[variable.package] = variable.value.version.to_s
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# @overload demands(name, constraint)
|
116
|
+
# Return the Solve::Demand from the collection of demands
|
117
|
+
# with the given name and constraint.
|
118
|
+
#
|
119
|
+
# @param [#to_s]
|
120
|
+
# @param [Solve::Constraint, #to_s]
|
121
|
+
#
|
122
|
+
# @return [Solve::Demand]
|
123
|
+
# @overload demands(name)
|
124
|
+
# Return the Solve::Demand from the collection of demands
|
125
|
+
# with the given name.
|
126
|
+
#
|
127
|
+
# @param [#to_s]
|
128
|
+
#
|
129
|
+
# @return [Solve::Demand]
|
130
|
+
# @overload demands
|
131
|
+
# Return the collection of demands
|
132
|
+
#
|
133
|
+
# @return [Array<Solve::Demand>]
|
134
|
+
def demands(*args)
|
135
|
+
if args.empty?
|
136
|
+
return demand_collection
|
137
|
+
end
|
138
|
+
if args.length > 2
|
139
|
+
raise ArgumentError, "Unexpected number of arguments. You gave: #{args.length}. Expected: 2 or less."
|
140
|
+
end
|
141
|
+
|
142
|
+
name, constraint = args
|
143
|
+
constraint ||= ">= 0.0.0"
|
144
|
+
|
145
|
+
if name.nil?
|
146
|
+
raise ArgumentError, "A name must be specified. You gave: #{args}."
|
147
|
+
end
|
148
|
+
|
149
|
+
demand = Demand.new(self, name, constraint)
|
150
|
+
add_demand(demand)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Add a Solve::Demand to the collection of demands and
|
154
|
+
# return the added Solve::Demand. No change will be made
|
155
|
+
# if the demand is already a member of the collection.
|
156
|
+
#
|
157
|
+
# @param [Solve::Demand] demand
|
158
|
+
#
|
159
|
+
# @return [Solve::Demand]
|
160
|
+
def add_demand(demand)
|
161
|
+
unless has_demand?(demand)
|
162
|
+
@demands[self.class.demand_key(demand)] = demand
|
163
|
+
end
|
164
|
+
|
165
|
+
demand
|
166
|
+
end
|
167
|
+
alias_method :demand, :add_demand
|
168
|
+
|
169
|
+
# @param [Solve::Demand, nil] demand
|
170
|
+
def remove_demand(demand)
|
171
|
+
if has_demand?(demand)
|
172
|
+
@demands.delete(self.class.demand_key(demand))
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# @param [Solve::Demand] demand
|
177
|
+
#
|
178
|
+
# @return [Boolean]
|
179
|
+
def has_demand?(demand)
|
180
|
+
@demands.has_key?(self.class.demand_key(demand))
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
# @return [Array<Solve::Demand>]
|
186
|
+
def demand_collection
|
187
|
+
@demands.collect { |name, demand| demand }
|
188
|
+
end
|
189
|
+
|
190
|
+
def seed_demand_dependencies
|
191
|
+
add_dependencies(demands, :root)
|
192
|
+
end
|
193
|
+
|
194
|
+
def can_add_new_constraint?(dependency)
|
195
|
+
current_binding = variable_table.find_package(dependency.name)
|
196
|
+
#haven't seen it before, haven't bound it yet or the binding is ok
|
197
|
+
current_binding.nil? || current_binding.value.nil? || dependency.constraint.satisfies?(current_binding.value.version)
|
198
|
+
end
|
199
|
+
|
200
|
+
def possible_values_for(variable)
|
201
|
+
possible_values_for_variable = possible_values[variable.package]
|
202
|
+
if possible_values_for_variable.nil?
|
203
|
+
constraints_for_variable = constraint_table.constraints_on_package(variable.package)
|
204
|
+
all_values_for_variable = domain[variable.package]
|
205
|
+
possible_values_for_variable = constraints_for_variable.inject(all_values_for_variable) do |remaining_values, constraint|
|
206
|
+
remaining_values.reject { |value| !constraint.satisfies?(value.version) }
|
207
|
+
end
|
208
|
+
possible_values[variable.package] = possible_values_for_variable
|
209
|
+
end
|
210
|
+
possible_values_for_variable
|
211
|
+
end
|
212
|
+
|
213
|
+
def add_dependencies(dependencies, source)
|
214
|
+
dependencies.each do |dependency|
|
215
|
+
variable_table.add(dependency.name, source)
|
216
|
+
constraint_table.add(dependency.name, dependency.constraint, source)
|
217
|
+
dependency_domain = graph.versions(dependency.name, dependency.constraint)
|
218
|
+
domain[dependency.name] = [(domain[dependency.name] || []), dependency_domain]
|
219
|
+
.flatten
|
220
|
+
.uniq
|
221
|
+
.sort { |left, right| right.version <=> left.version }
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def reset_possible_values_for(variable)
|
226
|
+
possible_values[variable.package] = nil
|
227
|
+
possible_values_for(variable)
|
228
|
+
end
|
229
|
+
|
230
|
+
def backtrack(unbound_variable)
|
231
|
+
previous_variable = variable_table.before(unbound_variable.package)
|
232
|
+
|
233
|
+
if previous_variable.nil?
|
234
|
+
raise Errors::NoSolutionError
|
235
|
+
end
|
236
|
+
|
237
|
+
source = previous_variable.value
|
238
|
+
variable_table.remove_all_with_only_this_source!(source)
|
239
|
+
constraint_table.remove_constraints_from_source!(source)
|
240
|
+
previous_variable.unbind
|
241
|
+
variable_table.all_after(previous_variable.package).each do |variable|
|
242
|
+
new_possibles = reset_possible_values_for(variable)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|