solve 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b23c8ac10998066c9ecea0480dfa83bc74266a20
4
- data.tar.gz: ec933cf811cffa4abcb405ab6d9092e8a34b980b
3
+ metadata.gz: 868022031f083b30e7c32a6d424896fb2fb08367
4
+ data.tar.gz: 5d50e154ab203dbddd6888616295fc683f26672c
5
5
  SHA512:
6
- metadata.gz: 4b19ad0f653833739926bd31dde7d774ff134728855ec43be35eeec4945721e798d0e75e04c28071baaacaad191f61115ffcb22623bf2140fdbba770a7e5eaec
7
- data.tar.gz: a9bf1c0f0160e9a2d2dbe0489d25a99a7bed8df34c6d9fce67958731cb734d6427b46101f3668e84b91954edcb4ff9a6881de5252715b9b791a8d3b0b94744cd
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
- # @param [#say, nil] ui (nil)
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, ui = nil)
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
@@ -1,3 +1,3 @@
1
1
  module Solve
2
- VERSION = "0.6.1"
2
+ VERSION = "0.7.0"
3
3
  end
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
- # @return [Hash]
91
- def resolve
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 = {}.tap do |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
- trace("Found Solution")
129
- trace(solution)
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
- solution
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.6.1
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-16 00:00:00.000000000 Z
13
+ date: 2013-07-29 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description: A Ruby version constraint solver
16
16
  email: