solve 0.6.1 → 0.7.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.
- checksums.yaml +4 -4
- data/README.md +5 -0
- data/lib/solve.rb +7 -3
- data/lib/solve/errors.rb +14 -0
- data/lib/solve/gem_version.rb +1 -1
- data/lib/solve/solver.rb +41 -6
- data/spec/acceptance/solutions_spec.rb +65 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 868022031f083b30e7c32a6d424896fb2fb08367
|
4
|
+
data.tar.gz: 5d50e154ab203dbddd6888616295fc683f26672c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56abef15a6d5da9a0e31e1ed88cc46ce9acb7aab8526ec7aaf0c615e8f09cfa1fbb0b361349282640813328fecc954f9eb75fea5d73764176964b771c1a467ec
|
7
|
+
data.tar.gz: 0a079122af8824f23e23c69649092b4f32c7cbb44f72e4fc9d754350c45a48580280e758bb8e94269d6edba99ed026c379399d1edd38fe49afa53b70acfbc84c
|
data/README.md
CHANGED
@@ -31,6 +31,11 @@ And now solve the graph with some demands
|
|
31
31
|
|
32
32
|
Solve.it!(graph, ['nginx', '>= 0.100.0'])
|
33
33
|
|
34
|
+
Or, if you want a topologically sorted solution
|
35
|
+
NOTE: This will raise Solve::Errors::UnsortableSolutionError if the solution contains a cycle (which can happen with ruby packages)
|
36
|
+
|
37
|
+
Solve.it!(graph, ['nginx', '>= 0.100.0'], sorted: true)
|
38
|
+
|
34
39
|
### Removing an artifact, or dependency from the graph
|
35
40
|
|
36
41
|
graph.artifacts("nginx", "1.0.0").delete
|
data/lib/solve.rb
CHANGED
@@ -18,13 +18,17 @@ module Solve
|
|
18
18
|
#
|
19
19
|
# @param [Solve::Graph] graph
|
20
20
|
# @param [Array<Solve::Demand>, Array<String, String>] demands
|
21
|
-
#
|
21
|
+
#
|
22
|
+
# @option options [#say] :ui (nil)
|
23
|
+
# a ui object for output
|
24
|
+
# @option options [Boolean] :sorted (false)
|
25
|
+
# should the output be a sorted list rather than a Hash
|
22
26
|
#
|
23
27
|
# @raise [NoSolutionError]
|
24
28
|
#
|
25
29
|
# @return [Hash]
|
26
|
-
def it!(graph, demands,
|
27
|
-
Solver.new(graph, demands, ui).resolve
|
30
|
+
def it!(graph, demands, options = {})
|
31
|
+
Solver.new(graph, demands, options[:ui]).resolve(options)
|
28
32
|
end
|
29
33
|
end
|
30
34
|
end
|
data/lib/solve/errors.rb
CHANGED
@@ -31,5 +31,19 @@ module Solve
|
|
31
31
|
end
|
32
32
|
|
33
33
|
class NoSolutionError < SolveError; end
|
34
|
+
|
35
|
+
class UnsortableSolutionError < SolveError
|
36
|
+
attr_reader :internal_exception
|
37
|
+
attr_reader :unsorted_solution
|
38
|
+
|
39
|
+
def initialize(internal_exception, unsorted_solution)
|
40
|
+
@internal_exception = internal_exception
|
41
|
+
@unsorted_solution = unsorted_solution
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
"The solution contains a cycle and cannot be topologically sorted. See #unsorted_solution on this exception for the unsorted solution"
|
46
|
+
end
|
47
|
+
end
|
34
48
|
end
|
35
49
|
end
|
data/lib/solve/gem_version.rb
CHANGED
data/lib/solve/solver.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'tsort'
|
1
2
|
require_relative 'solver/variable_table'
|
2
3
|
require_relative 'solver/variable_row'
|
3
4
|
require_relative 'solver/constraint_table'
|
@@ -87,8 +88,12 @@ module Solve
|
|
87
88
|
end
|
88
89
|
end
|
89
90
|
|
90
|
-
# @
|
91
|
-
|
91
|
+
# @option options [Boolean] :sorted
|
92
|
+
# return the solution as a sorted list instead of a Hash
|
93
|
+
#
|
94
|
+
# @return [Hash, List] Returns a hash like { "Artifact Name" => "Version",... }
|
95
|
+
# unless options[:sorted], then it returns a list like [["Artifact Name", "Version],...]
|
96
|
+
def resolve(options = {})
|
92
97
|
trace("Attempting to find a solution")
|
93
98
|
seed_demand_dependencies
|
94
99
|
|
@@ -119,16 +124,46 @@ module Solve
|
|
119
124
|
end
|
120
125
|
end
|
121
126
|
|
122
|
-
solution =
|
127
|
+
solution = (options[:sorted]) ? build_sorted_solution : build_unsorted_solution
|
128
|
+
|
129
|
+
trace("Found Solution")
|
130
|
+
trace(solution)
|
131
|
+
|
132
|
+
solution
|
133
|
+
end
|
134
|
+
|
135
|
+
def build_unsorted_solution
|
136
|
+
{}.tap do |solution|
|
123
137
|
variable_table.rows.each do |variable|
|
124
138
|
solution[variable.artifact] = variable.value.version.to_s
|
125
139
|
end
|
126
140
|
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def build_sorted_solution
|
144
|
+
unsorted_solution = build_unsorted_solution
|
145
|
+
nodes = Hash.new
|
146
|
+
unsorted_solution.each do |name, version|
|
147
|
+
nodes[name] = @graph.get_artifact(name, version).dependencies.map(&:name)
|
148
|
+
end
|
127
149
|
|
128
|
-
|
129
|
-
|
150
|
+
# Modified from http://ruby-doc.org/stdlib-1.9.3/libdoc/tsort/rdoc/TSort.html
|
151
|
+
class << nodes
|
152
|
+
include TSort
|
153
|
+
alias tsort_each_node each_key
|
154
|
+
def tsort_each_child(node, &block)
|
155
|
+
fetch(node).each(&block)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
begin
|
159
|
+
sorted_names = nodes.tsort
|
160
|
+
rescue TSort::Cyclic => e
|
161
|
+
raise Solve::Errors::UnsortableSolutionError.new(e, unsorted_solution)
|
162
|
+
end
|
130
163
|
|
131
|
-
|
164
|
+
sorted_names.map do |artifact|
|
165
|
+
[artifact, unsorted_solution[artifact]]
|
166
|
+
end
|
132
167
|
end
|
133
168
|
|
134
169
|
# @overload demands(name, constraint)
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe "Solutions" do
|
4
|
-
|
5
4
|
it "chooses the correct artifact for the demands" do
|
6
5
|
graph = Solve::Graph.new
|
7
6
|
graph.artifacts("mysql", "2.0.0")
|
@@ -191,4 +190,69 @@ describe "Solutions" do
|
|
191
190
|
"old-bottom" => "2.0.0"
|
192
191
|
})
|
193
192
|
end
|
193
|
+
|
194
|
+
describe "when options[:sorted] is true" do
|
195
|
+
describe "with a simple list of dependencies" do
|
196
|
+
it "returns a sorted list of dependencies" do
|
197
|
+
graph = Solve::Graph.new
|
198
|
+
|
199
|
+
graph.artifacts("A", "1.0.0").depends("B", "= 1.0.0")
|
200
|
+
graph.artifacts("B", "1.0.0").depends("C", "= 1.0.0")
|
201
|
+
graph.artifacts("C", "1.0.0")
|
202
|
+
|
203
|
+
demands = [["A"]]
|
204
|
+
|
205
|
+
result = Solve.it!(graph, demands, { :sorted => true })
|
206
|
+
|
207
|
+
result.should eql([
|
208
|
+
["C", "1.0.0"],
|
209
|
+
["B", "1.0.0"],
|
210
|
+
["A", "1.0.0"]
|
211
|
+
])
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# The order that the demands come in determines the order of artifacts
|
216
|
+
# in the solver's variable_table. This must not determine the sort order
|
217
|
+
describe "with a constraint that depends upon an earlier constrained artifact" do
|
218
|
+
it "returns a sorted list of dependencies" do
|
219
|
+
graph = Solve::Graph.new
|
220
|
+
|
221
|
+
graph.artifacts("B", "1.0.0").depends("A", "= 1.0.0")
|
222
|
+
graph.artifacts("A", "1.0.0").depends("C", "= 1.0.0")
|
223
|
+
graph.artifacts("C", "1.0.0")
|
224
|
+
|
225
|
+
demands = [["A"],["B"]]
|
226
|
+
|
227
|
+
result = Solve.it!(graph, demands, { :sorted => true } )
|
228
|
+
|
229
|
+
result.should eql([
|
230
|
+
["C", "1.0.0"],
|
231
|
+
["A", "1.0.0"],
|
232
|
+
["B", "1.0.0"]
|
233
|
+
])
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
describe "when the solution is cyclic" do
|
238
|
+
it "raises a Solve::Errors::UnsortableSolutionError which contains the unsorted solution" do
|
239
|
+
graph = Solve::Graph.new
|
240
|
+
|
241
|
+
graph.artifacts("A", "1.0.0").depends("B", "= 1.0.0")
|
242
|
+
graph.artifacts("B", "1.0.0").depends("C", "= 1.0.0")
|
243
|
+
graph.artifacts("C", "1.0.0").depends("A", "= 1.0.0")
|
244
|
+
|
245
|
+
demands = [["A"]]
|
246
|
+
|
247
|
+
expect { Solve.it!(graph, demands, { :sorted => true } ) }.to raise_error { |error|
|
248
|
+
error.should be_a(Solve::Errors::UnsortableSolutionError)
|
249
|
+
error.unsorted_solution.should eql({
|
250
|
+
"A" => "1.0.0",
|
251
|
+
"B" => "1.0.0",
|
252
|
+
"C" => "1.0.0",
|
253
|
+
})
|
254
|
+
}
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
194
258
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solve
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jamie Winsor
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-07-
|
13
|
+
date: 2013-07-29 00:00:00.000000000 Z
|
14
14
|
dependencies: []
|
15
15
|
description: A Ruby version constraint solver
|
16
16
|
email:
|