solve 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|