solve 0.2.1

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.
@@ -0,0 +1,73 @@
1
+ module Solve
2
+ class Constraint
3
+ class << self
4
+ # @param [#to_s] string
5
+ #
6
+ # @return [Array, nil]
7
+ def parse(string)
8
+ if string =~ /^[0-9]/
9
+ op = "="
10
+ ver = string
11
+ else
12
+ _, op, ver = REGEXP.match(string).to_a
13
+ end
14
+
15
+ return nil unless op || ver
16
+
17
+ [ op, ver ]
18
+ end
19
+ end
20
+
21
+ OPERATORS = [
22
+ "=",
23
+ ">",
24
+ "<",
25
+ ">=",
26
+ "<=",
27
+ "~>"
28
+ ]
29
+ REGEXP = /^(#{OPERATORS.join('|')}) (.+)$/
30
+
31
+ attr_reader :operator
32
+ attr_reader :version
33
+
34
+ # @param [#to_s] constraint
35
+ def initialize(constraint = ">= 0.0.0")
36
+ @operator, ver_str = self.class.parse(constraint)
37
+ if @operator.nil? || ver_str.nil?
38
+ raise InvalidConstraintFormat.new(constraint)
39
+ end
40
+
41
+ @version = Version.new(ver_str)
42
+ @dep_constraint = DepSelector::VersionConstraint.new(constraint)
43
+ end
44
+
45
+ # Returns true or false if the given version would be satisfied by
46
+ # the version constraint.
47
+ #
48
+ # @param [Version, String] version
49
+ #
50
+ # @return [Boolean]
51
+ def satisfies?(version)
52
+ dep_constraint.include?(version)
53
+ end
54
+
55
+ # @param [Object] other
56
+ #
57
+ # @return [Boolean]
58
+ def ==(other)
59
+ other.is_a?(self.class) &&
60
+ self.operator == other.operator &&
61
+ self.version == other.version
62
+ end
63
+ alias_method :eql?, :==
64
+
65
+ def to_s
66
+ "#{operator} #{version}"
67
+ end
68
+
69
+ private
70
+
71
+ attr_reader :dep_constraint
72
+ end
73
+ end
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].sort.each do |path|
2
+ require "solve/core_ext/#{File.basename(path, '.rb')}"
3
+ end
@@ -0,0 +1,33 @@
1
+ # From ActiveSupport: https://github.com/rails/rails/blob/c2c8ef57d6f00d1c22743dc43746f95704d67a95/activesupport/lib/active_support/core_ext/kernel/reporting.rb#L39
2
+
3
+ require 'rbconfig'
4
+
5
+ module Kernel
6
+ # Silences any stream for the duration of the block.
7
+ #
8
+ # silence_stream(STDOUT) do
9
+ # puts 'This will never be seen'
10
+ # end
11
+ #
12
+ # puts 'But this will'
13
+ def silence_stream(stream)
14
+ old_stream = stream.dup
15
+ stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
16
+ stream.sync = true
17
+ yield
18
+ ensure
19
+ stream.reopen(old_stream)
20
+ end
21
+
22
+ # Silences both STDOUT and STDERR, even for subprocesses.
23
+ #
24
+ # quietly { system 'bundle install' }
25
+ #
26
+ def quietly
27
+ silence_stream(STDOUT) do
28
+ silence_stream(STDERR) do
29
+ yield
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,38 @@
1
+ module Solve
2
+ class Demand
3
+ attr_reader :graph
4
+ attr_reader :name
5
+ attr_reader :constraint
6
+
7
+ # @param [Solve::Graph] graph
8
+ # @param [#to_s] name
9
+ # @param [Solve::Constraint, #to_s] constraint
10
+ def initialize(graph, name, constraint = nil)
11
+ @graph = graph
12
+ @name = name
13
+
14
+ if constraint
15
+ @constraint = if constraint.is_a?(Solve::Constraint)
16
+ constraint
17
+ else
18
+ Constraint.new(constraint.to_s)
19
+ end
20
+ end
21
+ end
22
+
23
+ # @return [Solve::Demand, nil]
24
+ def delete
25
+ unless graph.nil?
26
+ result = graph.remove_demand(self)
27
+ @graph = nil
28
+ result
29
+ end
30
+ end
31
+
32
+ def to_s
33
+ s = "#{name}"
34
+ s << "(#{constraint})" if constraint
35
+ s
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,40 @@
1
+ module Solve
2
+ class Dependency
3
+ attr_reader :artifact
4
+ attr_reader :name
5
+ attr_reader :constraint
6
+
7
+ # @param [Solve::Artifact] artifact
8
+ # @param [#to_s] name
9
+ # @param [Solve::Constraint, #to_s] constraint
10
+ def initialize(artifact, name, constraint)
11
+ @artifact = artifact
12
+ @name = name
13
+ @constraint = case constraint
14
+ when Solve::Constraint
15
+ constraint
16
+ else
17
+ Constraint.new(constraint)
18
+ end
19
+ end
20
+
21
+ # @return [Solve::Dependency, nil]
22
+ def delete
23
+ unless artifact.nil?
24
+ result = artifact.remove_dependency(self)
25
+ @artifact = nil
26
+ result
27
+ end
28
+ end
29
+
30
+ # @param [Object] other
31
+ #
32
+ # @return [Boolean]
33
+ def ==(other)
34
+ other.is_a?(self.class) &&
35
+ self.artifact == other.artifact &&
36
+ self.constraint == other.constraint
37
+ end
38
+ alias_method :eql?, :==
39
+ end
40
+ end
@@ -0,0 +1,31 @@
1
+ module Solve
2
+ class SolveError < StandardError; end
3
+
4
+ class InvalidVersionFormat < SolveError
5
+ attr_reader :version
6
+
7
+ # @param [#to_s] version
8
+ def initialize(version)
9
+ @version = version
10
+ end
11
+
12
+ def message
13
+ "'#{version}' did not contain a valid version string: 'x.y.z' or 'x.y'."
14
+ end
15
+ end
16
+
17
+ class InvalidConstraintFormat < SolveError
18
+ attr_reader :constraint
19
+
20
+ # @param [#to_s] constraint
21
+ def initialize(constraint)
22
+ @constraint = constraint
23
+ end
24
+
25
+ def message
26
+ "'#{constraint}' did not contain a valid operator or a valid version string."
27
+ end
28
+ end
29
+
30
+ class NoSolutionError < SolveError; end
31
+ end
@@ -0,0 +1,3 @@
1
+ module Solve
2
+ VERSION = "0.2.1"
3
+ end
@@ -0,0 +1,152 @@
1
+ module Solve
2
+ class Graph
3
+ def initialize
4
+ @artifacts = Hash.new
5
+ @demands = Hash.new
6
+ @dep_graph = DepSelector::DependencyGraph.new
7
+ end
8
+
9
+ # @overload artifacts(name, version)
10
+ # Return the Solve::Artifact from the collection of artifacts
11
+ # with the given name and version.
12
+ #
13
+ # @param [#to_s]
14
+ # @param [Solve::Version, #to_s]
15
+ #
16
+ # @return [Solve::Artifact]
17
+ # @overload artifacts
18
+ # Return the collection of artifacts
19
+ #
20
+ # @return [Array<Solve::Artifact>]
21
+ def artifacts(*args)
22
+ if args.empty?
23
+ return artifact_collection
24
+ end
25
+ unless args.length == 2
26
+ raise ArgumentError, "Unexpected number of arguments. You gave: #{args.length}. Expected: 0 or 2."
27
+ end
28
+
29
+ name, version = args
30
+
31
+ if name.nil? || version.nil?
32
+ raise ArgumentError, "A name and version must be specified. You gave: #{args}."
33
+ end
34
+
35
+ artifact = Artifact.new(self, name, version)
36
+ add_artifact(artifact)
37
+ end
38
+
39
+ # Add a Solve::Artifact to the collection of artifacts and
40
+ # return the added Solve::Artifact. No change will be made
41
+ # if the artifact is already a member of the collection.
42
+ #
43
+ # @param [Solve::Artifact] artifact
44
+ #
45
+ # @return [Solve::Artifact]
46
+ def add_artifact(artifact)
47
+ unless has_artifact?(artifact)
48
+ @dep_graph.package(artifact.name).add_version(DepSelector::Version.new(artifact.version.to_s))
49
+ @artifacts[artifact.to_s] = artifact
50
+ end
51
+
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)
68
+ end
69
+
70
+ # @overload demands(name, constraint)
71
+ # Return the Solve::Demand from the collection of demands
72
+ # with the given name and constraint.
73
+ #
74
+ # @param [#to_s]
75
+ # @param [Solve::Constraint, #to_s]
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
87
+ #
88
+ # @return [Array<Solve::Demand>]
89
+ def demands(*args)
90
+ if args.empty?
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)
106
+ end
107
+
108
+ # Add a Solve::Demand to the collection of demands and
109
+ # return the added Solve::Demand. No change will be made
110
+ # if the demand is already a member of the collection.
111
+ #
112
+ # @param [Solve::Demand] demand
113
+ #
114
+ # @return [Solve::Demand]
115
+ def add_demand(demand)
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)
128
+ end
129
+ end
130
+
131
+ # @param [Solve::Demand] demand
132
+ #
133
+ # @return [Boolean]
134
+ def has_demand?(demand)
135
+ @demands.has_key?(demand.to_s)
136
+ end
137
+
138
+ private
139
+
140
+ attr_reader :dep_graph
141
+
142
+ # @return [Array<Solve::Artifact>]
143
+ def artifact_collection
144
+ @artifacts.collect { |name, artifact| artifact }
145
+ end
146
+
147
+ # @return [Array<Solve::Demand>]
148
+ def demand_collection
149
+ @demands.collect { |name, demand| demand }
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,47 @@
1
+ module Solve
2
+ class Version
3
+ include Comparable
4
+
5
+ attr_reader :major
6
+ attr_reader :minor
7
+ attr_reader :patch
8
+
9
+ # @param [#to_s] ver_str
10
+ def initialize(ver_str = String.new)
11
+ @major, @minor, @patch = parse(ver_str)
12
+ end
13
+
14
+ def <=>(other)
15
+ [:major, :minor, :patch].each do |method|
16
+ ans = (self.send(method) <=> other.send(method))
17
+ return ans if ans != 0
18
+ end
19
+ 0
20
+ end
21
+
22
+ def eql?(other)
23
+ other.is_a?(Version) && self == other
24
+ end
25
+
26
+ def inspect
27
+ to_s
28
+ end
29
+
30
+ def to_s
31
+ "#{major}.#{minor}.#{patch}"
32
+ end
33
+
34
+ private
35
+
36
+ def parse(ver_str = String.new)
37
+ @major, @minor, @patch = case ver_str.to_s
38
+ when /^(\d+)\.(\d+)\.(\d+)$/
39
+ [ $1.to_i, $2.to_i, $3.to_i ]
40
+ when /^(\d+)\.(\d+)$/
41
+ [ $1.to_i, $2.to_i, 0 ]
42
+ else
43
+ raise InvalidVersionFormat.new(ver_str)
44
+ end
45
+ end
46
+ end
47
+ end
data/solve.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/solve/gem_version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.authors = ["Jamie Winsor"]
6
+ s.email = ["jamie@vialstudios.com"]
7
+ s.description = %q{A Ruby constraint solver}
8
+ s.summary = s.description
9
+ s.homepage = "https://github.com/reset/solve"
10
+
11
+ s.files = `git ls-files`.split($\)
12
+ s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ s.test_files = s.files.grep(%r{^spec/})
14
+ s.name = "solve"
15
+ s.require_paths = ["lib"]
16
+ s.version = Solve::VERSION
17
+ s.required_ruby_version = ">= 1.9.1"
18
+
19
+ s.add_runtime_dependency 'dep_selector', '~> 0.0.8'
20
+
21
+ s.add_development_dependency 'thor'
22
+ s.add_development_dependency 'rspec'
23
+ s.add_development_dependency 'fuubar'
24
+ s.add_development_dependency 'spork'
25
+ s.add_development_dependency 'yard'
26
+ s.add_development_dependency 'redcarpet'
27
+ s.add_development_dependency 'guard'
28
+ s.add_development_dependency 'guard-rspec'
29
+ s.add_development_dependency 'guard-spork'
30
+ s.add_development_dependency 'guard-yard'
31
+ s.add_development_dependency 'coolline'
32
+ end