solve 0.7.0 → 0.8.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/lib/solve.rb +9 -1
- data/lib/solve/gem_version.rb +1 -1
- data/lib/solve/solver.rb +13 -24
- data/lib/solve/tracers.rb +50 -0
- data/lib/solve/tracers/human_readable.rb +67 -0
- data/lib/solve/tracers/silent.rb +17 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 96b860730518abd99eb79c2d9b847fc6b4b13d5c
|
4
|
+
data.tar.gz: 95635f8ef60cce43ff1b92e7800e749ef1666659
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5742cdbaecdfae7a0dac68f377331b96ee1200ca469e4317a647ec4fb23710520afd46051468ce9509ba81952c508cbb12dccc50323f35c00aa75d2c6f20df43
|
7
|
+
data.tar.gz: f6602ad5b6a9e9e07e052e779727b9a8f24abc267443af2e9902f6536dcb99b430841f1d72e8ea62b2edfa20b789740675326c6a2499ce4e5bdd35a9a117f61a
|
data/lib/solve.rb
CHANGED
@@ -8,8 +8,12 @@ module Solve
|
|
8
8
|
require_relative 'solve/graph'
|
9
9
|
require_relative 'solve/solver'
|
10
10
|
require_relative 'solve/version'
|
11
|
+
require_relative 'solve/tracers'
|
11
12
|
|
12
13
|
class << self
|
14
|
+
# @return [Solve::Formatter]
|
15
|
+
attr_reader :tracer
|
16
|
+
|
13
17
|
# A quick solve. Given the "world" as we know it (the graph) and a list of
|
14
18
|
# requirements (demands) which must be met. Return me the best solution of
|
15
19
|
# artifacts and verisons that I should use.
|
@@ -20,7 +24,10 @@ module Solve
|
|
20
24
|
# @param [Array<Solve::Demand>, Array<String, String>] demands
|
21
25
|
#
|
22
26
|
# @option options [#say] :ui (nil)
|
23
|
-
# a ui object for output
|
27
|
+
# a ui object for output, this will be used to output from a Solve::Tracers::HumanReadable if
|
28
|
+
# no other tracer is provided in options[:tracer]
|
29
|
+
# @option options [AbstractTracer] :tracer (nil)
|
30
|
+
# a Tracer object that is used to format and output tracing information
|
24
31
|
# @option options [Boolean] :sorted (false)
|
25
32
|
# should the output be a sorted list rather than a Hash
|
26
33
|
#
|
@@ -28,6 +35,7 @@ module Solve
|
|
28
35
|
#
|
29
36
|
# @return [Hash]
|
30
37
|
def it!(graph, demands, options = {})
|
38
|
+
@tracer = options[:tracer] || Solve::Tracers.build(options[:ui])
|
31
39
|
Solver.new(graph, demands, options[:ui]).resolve(options)
|
32
40
|
end
|
33
41
|
end
|
data/lib/solve/gem_version.rb
CHANGED
data/lib/solve/solver.rb
CHANGED
@@ -94,24 +94,20 @@ module Solve
|
|
94
94
|
# @return [Hash, List] Returns a hash like { "Artifact Name" => "Version",... }
|
95
95
|
# unless options[:sorted], then it returns a list like [["Artifact Name", "Version],...]
|
96
96
|
def resolve(options = {})
|
97
|
-
|
97
|
+
Solve.tracer.start
|
98
98
|
seed_demand_dependencies
|
99
99
|
|
100
100
|
while unbound_variable = variable_table.first_unbound
|
101
101
|
possible_values_for_unbound = possible_values_for(unbound_variable)
|
102
|
-
|
103
|
-
|
104
|
-
constraint_table.constraints_on_artifact(unbound_variable.artifact).each do |constraint|
|
105
|
-
trace("\t#{constraint}")
|
106
|
-
end
|
107
|
-
trace("Possible values are #{possible_values_for_unbound}")
|
102
|
+
constraints = constraint_table.constraints_on_artifact(unbound_variable.artifact)
|
103
|
+
Solve.tracer.searching_for(unbound_variable, constraints, possible_values)
|
108
104
|
|
109
105
|
while possible_value = possible_values_for_unbound.shift
|
110
106
|
possible_artifact = graph.get_artifact(unbound_variable.artifact, possible_value.version)
|
111
107
|
possible_dependencies = possible_artifact.dependencies
|
112
108
|
all_ok = possible_dependencies.all? { |dependency| can_add_new_constraint?(dependency) }
|
113
109
|
if all_ok
|
114
|
-
|
110
|
+
Solve.tracer.trying(possible_artifact)
|
115
111
|
add_dependencies(possible_dependencies, possible_artifact)
|
116
112
|
unbound_variable.bind(possible_value)
|
117
113
|
break
|
@@ -119,15 +115,13 @@ module Solve
|
|
119
115
|
end
|
120
116
|
|
121
117
|
unless unbound_variable.bound?
|
122
|
-
trace("Could not find an acceptable value for #{unbound_variable.artifact}")
|
123
118
|
backtrack(unbound_variable)
|
124
119
|
end
|
125
120
|
end
|
126
121
|
|
127
122
|
solution = (options[:sorted]) ? build_sorted_solution : build_unsorted_solution
|
128
123
|
|
129
|
-
|
130
|
-
trace(solution)
|
124
|
+
Solve.tracer.solution(solution)
|
131
125
|
|
132
126
|
solution
|
133
127
|
end
|
@@ -266,7 +260,7 @@ module Solve
|
|
266
260
|
|
267
261
|
def add_dependencies(dependencies, source)
|
268
262
|
dependencies.each do |dependency|
|
269
|
-
|
263
|
+
Solve.tracer.add_constraint(dependency, source)
|
270
264
|
variable_table.add(dependency.name, source)
|
271
265
|
constraint_table.add(dependency, source)
|
272
266
|
dependency_domain = graph.versions(dependency.name, dependency.constraint)
|
@@ -289,41 +283,36 @@ module Solve
|
|
289
283
|
end
|
290
284
|
|
291
285
|
def reset_possible_values_for(variable)
|
292
|
-
|
286
|
+
Solve.tracer.reset_domain(variable)
|
293
287
|
possible_values[variable.artifact] = nil
|
294
|
-
|
295
|
-
trace("Possible values are #{x}")
|
296
|
-
x
|
288
|
+
possible_values_for(variable).tap { |values| Solve.tracer.possible_values(values) }
|
297
289
|
end
|
298
290
|
|
299
291
|
def backtrack(unbound_variable)
|
292
|
+
Solve.tracer.backtrack(unbound_variable)
|
300
293
|
previous_variable = variable_table.before(unbound_variable.artifact)
|
301
294
|
|
302
295
|
if previous_variable.nil?
|
303
|
-
|
296
|
+
Solve.tracer.cannot_backtrack
|
304
297
|
raise Errors::NoSolutionError
|
305
298
|
end
|
306
299
|
|
307
|
-
|
300
|
+
Solve.tracer.unbind(previous_variable)
|
308
301
|
|
309
302
|
source = previous_variable.value
|
310
303
|
removed_variables = variable_table.remove_all_with_only_this_source!(source)
|
311
304
|
removed_variables.each do |removed_variable|
|
312
305
|
possible_values[removed_variable.artifact] = nil
|
313
|
-
|
306
|
+
Solve.tracer.remove_variable(removed_variable)
|
314
307
|
end
|
315
308
|
removed_constraints = constraint_table.remove_constraints_from_source!(source)
|
316
309
|
removed_constraints.each do |removed_constraint|
|
317
|
-
|
310
|
+
Solve.tracer.remove_constraint(removed_constraint)
|
318
311
|
end
|
319
312
|
previous_variable.unbind
|
320
313
|
variable_table.all_after(previous_variable.artifact).each do |variable|
|
321
314
|
new_possibles = reset_possible_values_for(variable)
|
322
315
|
end
|
323
316
|
end
|
324
|
-
|
325
|
-
def trace(message)
|
326
|
-
ui.say(message) unless ui.nil?
|
327
|
-
end
|
328
317
|
end
|
329
318
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Solve
|
2
|
+
module Tracers
|
3
|
+
class << self
|
4
|
+
# @param [#say] ui
|
5
|
+
#
|
6
|
+
# @returns [AbstractTracer]
|
7
|
+
def build(ui)
|
8
|
+
unless ui.respond_to?(:say)
|
9
|
+
Solve::Tracers::Silent.new
|
10
|
+
else
|
11
|
+
Solve::Tracers::HumanReadable.new(ui)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class AbstractTracer
|
17
|
+
class << self
|
18
|
+
private
|
19
|
+
|
20
|
+
def must_define(*args)
|
21
|
+
args.each do |method|
|
22
|
+
define_method(method.to_sym) do |*args|
|
23
|
+
raise AbstractFunction, "##{method} must be defined on #{self.class}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
TRACER_METHODS = [
|
30
|
+
:start,
|
31
|
+
:searching_for,
|
32
|
+
:add_constraint,
|
33
|
+
:possible_values,
|
34
|
+
:trying,
|
35
|
+
:backtrack,
|
36
|
+
:cannot_backtrack,
|
37
|
+
:solution,
|
38
|
+
:reset_domain,
|
39
|
+
:unbind,
|
40
|
+
:remove_variable,
|
41
|
+
:remove_constraint,
|
42
|
+
]
|
43
|
+
|
44
|
+
must_define *TRACER_METHODS
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
require_relative 'tracers/human_readable'
|
50
|
+
require_relative 'tracers/silent'
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Solve
|
2
|
+
module Tracers
|
3
|
+
class HumanReadable < AbstractTracer
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
attr_reader :ui
|
7
|
+
|
8
|
+
def_delegator :ui, :say
|
9
|
+
|
10
|
+
# @param [#say] ui
|
11
|
+
def initialize(ui)
|
12
|
+
@ui = ui
|
13
|
+
end
|
14
|
+
|
15
|
+
def start
|
16
|
+
say("Attempting to find a solution")
|
17
|
+
end
|
18
|
+
|
19
|
+
def searching_for(unbound_variable, constraints, possible_values)
|
20
|
+
say("Searching for a value for #{unbound_variable.artifact}")
|
21
|
+
say("Constraints are #{constraints.join("\n\t")}")
|
22
|
+
possible_values(possible_values)
|
23
|
+
end
|
24
|
+
|
25
|
+
def possible_values(possible_values)
|
26
|
+
say("Possible values are #{possible_values.map(&:to_s).join("\n\t")}")
|
27
|
+
end
|
28
|
+
|
29
|
+
def trying(artifact)
|
30
|
+
say("Attempting to use #{artifact.to_s}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def backtrack(unbound_variable)
|
34
|
+
say("Could not find an acceptable value for #{unbound_variable.artifact.to_s}")
|
35
|
+
end
|
36
|
+
|
37
|
+
def cannot_backtrack
|
38
|
+
say("Cannot backtrack any further")
|
39
|
+
end
|
40
|
+
|
41
|
+
def solution(solution)
|
42
|
+
say("Found Solution")
|
43
|
+
say(solution)
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_constraint(dependency, source)
|
47
|
+
say("Adding constraint #{dependency.name} #{dependency.constraint} from #{source.to_s}")
|
48
|
+
end
|
49
|
+
|
50
|
+
def reset_domain(variable)
|
51
|
+
say("Resetting possible values for #{variable.artifact.to_s}")
|
52
|
+
end
|
53
|
+
|
54
|
+
def unbind(variable)
|
55
|
+
say("Unbinding #{variable.artifact.to_s}")
|
56
|
+
end
|
57
|
+
|
58
|
+
def remove_variable(variable)
|
59
|
+
say("Removed variable #{variable.artifact.to_s}")
|
60
|
+
end
|
61
|
+
|
62
|
+
def remove_constraint(constraint)
|
63
|
+
say("Removed constraint #{constraint.name} #{constraint.constraint}")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Solve
|
2
|
+
module Tracers
|
3
|
+
class Silent < AbstractTracer
|
4
|
+
class << self
|
5
|
+
def empty_method(*args)
|
6
|
+
args.each do |method|
|
7
|
+
define_method(method.to_sym) do |*args|
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
empty_method *AbstractTracer::TRACER_METHODS
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
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.8.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-31 00:00:00.000000000 Z
|
14
14
|
dependencies: []
|
15
15
|
description: A Ruby version constraint solver
|
16
16
|
email:
|
@@ -43,6 +43,9 @@ files:
|
|
43
43
|
- lib/solve/solver/serializer.rb
|
44
44
|
- lib/solve/solver/variable_row.rb
|
45
45
|
- lib/solve/solver/variable_table.rb
|
46
|
+
- lib/solve/tracers.rb
|
47
|
+
- lib/solve/tracers/human_readable.rb
|
48
|
+
- lib/solve/tracers/silent.rb
|
46
49
|
- lib/solve/version.rb
|
47
50
|
- solve.gemspec
|
48
51
|
- spec/acceptance/solutions_spec.rb
|