pub_grub 0.3.2 → 0.4.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
  SHA256:
3
- metadata.gz: 312b4d6ab0235f22ead56c025d6ae557043af8f4e4b0ac7bbed1e0a219151a29
4
- data.tar.gz: 7b9de23d87e9918d4a97b24ded5d1db9573e7693af34b737c2b66b46f93a9e76
3
+ metadata.gz: 906d1263305a0df7b8082cf01fd1abb3ddbd92f294744b1d9131603396be31a1
4
+ data.tar.gz: 6566971cf3a8ed458d15d21233614351d12b407c3e056f3bc580b0efe810fef0
5
5
  SHA512:
6
- metadata.gz: 2ceeb01137803054fb77b1fba533e21880c49b4ae3e4cbaed52887543137a641f9e4527a86e5840df725e96c9ed021b14f1dcf10233a8608f8eb1f84eb7c4e97
7
- data.tar.gz: ce803cc3420bef9d78bd4745ad2e3829272dabb2b1083659e4de712f3a8ab1db4a9f990589a9405e251d0ed2f24d4e466fd035bea6d429ee366d05735cc1777c
6
+ metadata.gz: 2d793abfc4b0481e6a056f2355310c60e8450058f777754284e011369d2e420a14c37939a8631b3d1ae01ec3f7b1f7c4250234a245ba4b2b6d6d4fc4ea90a5ee
7
+ data.tar.gz: 526cc6588093a2ec82b44fdad8a2bf6c09f3520c02d1c9a248e11576ca80e6c2817dc8564573b95d8da637e29ea5bed80379632f176a8729bb7c4eecba227950
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pub_grub (0.3.2)
4
+ pub_grub (0.4.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -16,5 +16,5 @@ module PubGrub
16
16
  attr_accessor :logger
17
17
  end
18
18
  self.logger = Logger.new(STDERR)
19
- self.logger.level = Logger::WARN
19
+ self.logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN
20
20
  end
@@ -0,0 +1,166 @@
1
+ require 'pub_grub/version_constraint'
2
+ require 'pub_grub/incompatibility'
3
+
4
+ module PubGrub
5
+ # Types:
6
+ #
7
+ # Where possible, PubGrub will accept user-defined types, so long as they quack.
8
+ #
9
+ # ## "Package":
10
+ #
11
+ # This class will be used to represent the various packages being solved for.
12
+ # .to_s will be called when displaying errors and debugging info, it should
13
+ # probably return the package's name.
14
+ # It must also have a reasonable definition of #== and #hash
15
+ #
16
+ # Example classes: String ("rails")
17
+ #
18
+ #
19
+ # ## "Version":
20
+ #
21
+ # This class will be used to represent a single version number.
22
+ #
23
+ # Versions don't need to store their associated package, however they will
24
+ # only be compared against other versions of the same package.
25
+ #
26
+ # It must be Comparible (and implement <=> reasonably)
27
+ #
28
+ # Example classes: Gem::Version, Integer
29
+ #
30
+ #
31
+ # ## "Dependency"
32
+ #
33
+ # This class represents the requirement one package has on another. It is
34
+ # returned by dependencies_for(package, version) and will be passed to
35
+ # parse_dependency to convert it to a format PubGrub understands.
36
+ #
37
+ # It must also have a reasonable definition of #==
38
+ #
39
+ # Example classes: String ("~> 1.0"), Gem::Requirement
40
+ #
41
+ class BasicPackageSource
42
+ # Override me!
43
+ #
44
+ # This is called per package to find all possible versions of a package.
45
+ #
46
+ # It is called at most once per-package
47
+ #
48
+ # Returns: Array of versions for a package, in preferred order of selection
49
+ def all_versions_for(package)
50
+ raise NotImplementedError
51
+ end
52
+
53
+ # Override me!
54
+ #
55
+ # Returns: Hash in the form of { package => requirement, ... }
56
+ def dependencies_for(package, version)
57
+ raise NotImplementedError
58
+ end
59
+
60
+ # Override me!
61
+ #
62
+ # Convert a (user-defined) dependency into a format PubGrub understands.
63
+ #
64
+ # Package is passed to this method but for many implementations is not
65
+ # needed.
66
+ #
67
+ # Returns: either a PubGrub::VersionRange, PubGrub::VersionUnion, or a
68
+ # PubGrub::VersionConstraint
69
+ def parse_dependency(package, dependency)
70
+ raise NotImplementedError
71
+ end
72
+
73
+ # Override me!
74
+ #
75
+ # If not overridden, this will call dependencies_for with the root package.
76
+ #
77
+ # Returns: Hash in the form of { package => requirement, ... } (see dependencies_for)
78
+ def root_dependencies
79
+ dependencies_for(@root_package, @root_version)
80
+ end
81
+
82
+ def initialize
83
+ @root_package = Package.root
84
+ @root_version = Package.root_version
85
+
86
+ @cached_versions = Hash.new do |h,k|
87
+ if k == @root_package
88
+ h[k] = [0]
89
+ else
90
+ h[k] = all_versions_for(k)
91
+ end
92
+ end
93
+ @sorted_versions = Hash.new { |h,k| h[k] = @cached_versions[k].sort }
94
+
95
+ @cached_dependencies = Hash.new do |packages, package|
96
+ if package == @root_package
97
+ packages[package] = {
98
+ @root_version => root_dependencies
99
+ }
100
+ else
101
+ packages[package] = Hash.new do |versions, version|
102
+ versions[version] = dependencies_for(package, version)
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ def versions_for(package, range=VersionRange.any)
109
+ @cached_versions[package].select do |version|
110
+ range.include?(version)
111
+ end
112
+ end
113
+
114
+ def incompatibilities_for(package, version)
115
+ package_deps = @cached_dependencies[package]
116
+ sorted_versions = @sorted_versions[package]
117
+ package_deps[version].map do |dep_package, dep_constraint_name|
118
+ low = high = sorted_versions.index(version)
119
+
120
+ # find version low such that all >= low share the same dep
121
+ while low > 0 &&
122
+ package_deps[sorted_versions[low - 1]][dep_package] == dep_constraint_name
123
+ low -= 1
124
+ end
125
+ low =
126
+ if low == 0
127
+ nil
128
+ else
129
+ sorted_versions[low]
130
+ end
131
+
132
+ # find version high such that all < high share the same dep
133
+ while high < sorted_versions.length &&
134
+ package_deps[sorted_versions[high]][dep_package] == dep_constraint_name
135
+ high += 1
136
+ end
137
+ high =
138
+ if high == sorted_versions.length
139
+ nil
140
+ else
141
+ sorted_versions[high]
142
+ end
143
+
144
+ range = VersionRange.new(min: low, max: high, include_min: true)
145
+
146
+ self_constraint = VersionConstraint.new(package, range: range)
147
+
148
+ if !@packages.include?(dep_package)
149
+ # no such package -> this version is invalid
150
+ end
151
+
152
+ dep_constraint = parse_dependency(dep_package, dep_constraint_name)
153
+ if !dep_constraint
154
+ # falsey indicates this dependency was invalid
155
+ cause = PubGrub::Incompatibility::InvalidDependency.new(dep_package, dep_constraint_name)
156
+ return [Incompatibility.new([Term.new(self_constraint, true)], cause: cause)]
157
+ elsif !dep_constraint.is_a?(VersionConstraint)
158
+ # Upgrade range/union to VersionConstraint
159
+ dep_constraint = VersionConstraint.new(dep_package, range: dep_constraint)
160
+ end
161
+
162
+ Incompatibility.new([Term.new(self_constraint, true), Term.new(dep_constraint, false)], cause: :dependency)
163
+ end
164
+ end
165
+ end
166
+ end
@@ -16,6 +16,8 @@ module PubGrub
16
16
  end
17
17
 
18
18
  def write
19
+ return @root.to_s unless @root.conflict?
20
+
19
21
  visit(@root)
20
22
 
21
23
  padding = @line_numbers.empty? ? 0 : "(#{@line_numbers.values.last}) ".length
@@ -16,6 +16,10 @@ module PubGrub
16
16
  def initialize(terms, cause:)
17
17
  @cause = cause
18
18
  @terms = cleanup_terms(terms)
19
+
20
+ if cause == :dependency && @terms.length != 2
21
+ raise ArgumentError, "a dependency Incompatibility must have exactly two terms. Got #{terms.inspect}"
22
+ end
19
23
  end
20
24
 
21
25
  def failure?
@@ -44,7 +48,6 @@ module PubGrub
44
48
  when :root
45
49
  "(root dependency)"
46
50
  when :dependency
47
- raise unless terms.length == 2
48
51
  "#{terms[0].to_s(allow_every: true)} depends on #{terms[1].invert}"
49
52
  when PubGrub::Incompatibility::InvalidDependency
50
53
  "#{terms[0].to_s(allow_every: true)} depends on unknown package #{cause.package}"
@@ -27,5 +27,9 @@ module PubGrub
27
27
  def self.root_version
28
28
  ROOT_VERSION
29
29
  end
30
+
31
+ def to_s
32
+ name
33
+ end
30
34
  end
31
35
  end
@@ -1,9 +1,10 @@
1
1
  require 'pub_grub/package'
2
2
  require 'pub_grub/version_constraint'
3
3
  require 'pub_grub/incompatibility'
4
+ require 'pub_grub/basic_package_source'
4
5
 
5
6
  module PubGrub
6
- class StaticPackageSource
7
+ class StaticPackageSource < BasicPackageSource
7
8
  class DSL
8
9
  def initialize(packages, root_deps)
9
10
  @packages = packages
@@ -15,95 +16,38 @@ module PubGrub
15
16
  end
16
17
 
17
18
  def add(name, version, deps: {})
18
- @packages << [name, version, deps]
19
+ version = Gem::Version.new(version)
20
+ @packages[name] ||= {}
21
+ raise ArgumentError, "#{name} #{version} declared twice" if @packages[name].key?(version)
22
+ @packages[name][version] = deps
19
23
  end
20
24
  end
21
25
 
22
26
  def initialize
23
27
  @root_deps = {}
24
- @package_list = []
25
- yield DSL.new(@package_list, @root_deps)
26
-
27
- @packages = {
28
- root: Package.root
29
- }
30
- @package_versions = Hash.new{ |h, k| h[k] = [] }
31
-
32
- @deps_by_version = Hash.new { |h, k| h[k] = {} }
33
-
34
- root_version = Package.root_version
35
- @package_versions[Package.root] = [root_version]
36
- @deps_by_version[Package.root][root_version] = @root_deps
28
+ @packages = {}
37
29
 
38
- @package_list.each do |name, version, deps|
39
- @packages[name] ||= Package.new(name)
40
- package = @packages[name]
30
+ yield DSL.new(@packages, @root_deps)
41
31
 
42
- version = Gem::Version.new(version)
43
- @package_versions[package] << version
44
- @deps_by_version[package][version] = deps
45
- end
32
+ super()
46
33
  end
47
34
 
48
- def get_package(name)
49
- @packages[name] ||
50
- raise("No such package in source: #{name.inspect}")
35
+ def all_versions_for(package)
36
+ @packages[package].keys
51
37
  end
52
- alias_method :package, :get_package
53
38
 
54
- def versions_for(package, range=VersionRange.any)
55
- @package_versions[package].select do |version|
56
- range.include?(version)
57
- end
39
+ def root_dependencies
40
+ @root_deps
58
41
  end
59
42
 
60
- def incompatibilities_for(package, version)
61
- package_deps = @deps_by_version[package]
62
- package_versions = @package_versions[package]
63
- package_deps[version].map do |dep_package_name, dep_constraint_name|
64
- sorted_versions = package_versions.sort
65
- low = high = sorted_versions.index(version)
66
-
67
- # find version low such that all >= low share the same dep
68
- while low > 0 &&
69
- package_deps[sorted_versions[low - 1]][dep_package_name] == dep_constraint_name
70
- low -= 1
71
- end
72
- low =
73
- if low == 0
74
- nil
75
- else
76
- sorted_versions[low]
77
- end
78
-
79
- # find version high such that all < high share the same dep
80
- while high < sorted_versions.length &&
81
- package_deps[sorted_versions[high]][dep_package_name] == dep_constraint_name
82
- high += 1
83
- end
84
- high =
85
- if high == sorted_versions.length
86
- nil
87
- else
88
- sorted_versions[high]
89
- end
90
-
91
- range = VersionRange.new(min: low, max: high, include_min: true)
92
-
93
- self_constraint = VersionConstraint.new(package, range: range)
94
-
95
- dep_package = @packages[dep_package_name]
96
-
97
- if !dep_package
98
- # no such package -> this version is invalid
99
- cause = PubGrub::Incompatibility::InvalidDependency.new(dep_package_name, dep_constraint_name)
100
- return [Incompatibility.new([Term.new(self_constraint, true)], cause: cause)]
101
- end
43
+ def dependencies_for(package, version)
44
+ @packages[package][version]
45
+ end
102
46
 
103
- dep_constraint = PubGrub::RubyGems.parse_constraint(dep_package, dep_constraint_name)
47
+ def parse_dependency(package, dependency)
48
+ return false unless @packages.key?(package)
104
49
 
105
- Incompatibility.new([Term.new(self_constraint, true), Term.new(dep_constraint, false)], cause: :dependency)
106
- end
50
+ PubGrub::RubyGems.parse_constraint(package, dependency)
107
51
  end
108
52
  end
109
53
  end
@@ -85,9 +85,9 @@ module PubGrub
85
85
  if package == Package.root
86
86
  "root"
87
87
  elsif allow_every && any?
88
- "every version of #{package.name}"
88
+ "every version of #{package}"
89
89
  else
90
- "#{package.name} #{constraint_string}"
90
+ "#{package} #{constraint_string}"
91
91
  end
92
92
  end
93
93
 
@@ -8,7 +8,7 @@ module PubGrub
8
8
  attr_reader :source
9
9
  attr_reader :solution
10
10
 
11
- def initialize(source:)
11
+ def initialize(source:, root: Package.root)
12
12
  @source = source
13
13
 
14
14
  # { package => [incompatibility, ...]}
@@ -19,29 +19,43 @@ module PubGrub
19
19
  @solution = PartialSolution.new
20
20
 
21
21
  add_incompatibility Incompatibility.new([
22
- Term.new(VersionConstraint.any(Package.root), false)
22
+ Term.new(VersionConstraint.any(root), false)
23
23
  ], cause: :root)
24
+
25
+ propagate(root)
24
26
  end
25
27
 
26
- def solve
27
- next_package = Package.root
28
+ def solved?
29
+ solution.unsatisfied.empty?
30
+ end
28
31
 
29
- while next_package
30
- propagate(next_package)
32
+ # Returns true if there is more work to be done, false otherwise
33
+ def work
34
+ return false if solved?
31
35
 
32
- next_package = choose_package_version
33
- end
36
+ next_package = choose_package_version
37
+ propagate(next_package)
34
38
 
35
- result = solution.decisions
39
+ if solved?
40
+ logger.info "Solution found after #{solution.attempted_solutions} attempts:"
41
+ solution.decisions.each do |package, version|
42
+ logger.info "* #{package} #{version}"
43
+ end
36
44
 
37
- logger.info "Solution found after #{solution.attempted_solutions} attempts:"
38
- result.each do |package, version|
39
- logger.info "* #{package.name} #{version}"
45
+ false
46
+ else
47
+ true
40
48
  end
49
+ end
50
+
51
+ def solve
52
+ work until solved?
41
53
 
42
- result
54
+ solution.decisions
43
55
  end
44
56
 
57
+ alias_method :result, :solve
58
+
45
59
  private
46
60
 
47
61
  def propagate(initial_package)
@@ -121,7 +135,7 @@ module PubGrub
121
135
  end
122
136
 
123
137
  unless conflict
124
- logger.info("selecting #{package.name} #{version}")
138
+ logger.info("selecting #{package} #{version}")
125
139
 
126
140
  solution.decide(package, version)
127
141
  end
@@ -55,7 +55,7 @@ module PubGrub
55
55
  def select_versions(all_versions)
56
56
  versions = []
57
57
  ranges.inject(all_versions) do |acc, range|
58
- lower, matching, higher = range.partition_versions(acc)
58
+ _, matching, higher = range.partition_versions(acc)
59
59
  versions.concat matching
60
60
  higher
61
61
  end
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "pub_grub"
7
- spec.version = "0.3.2"
7
+ spec.version = "0.4.0"
8
8
  spec.authors = ["John Hawthorn"]
9
9
  spec.email = ["john@hawthorn.email"]
10
10
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pub_grub
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Hawthorn
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-12-08 00:00:00.000000000 Z
11
+ date: 2018-12-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -99,6 +99,7 @@ files:
99
99
  - bin/setup
100
100
  - lib/pub_grub.rb
101
101
  - lib/pub_grub/assignment.rb
102
+ - lib/pub_grub/basic_package_source.rb
102
103
  - lib/pub_grub/failure_writer.rb
103
104
  - lib/pub_grub/incompatibility.rb
104
105
  - lib/pub_grub/package.rb
@@ -132,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
133
  version: '0'
133
134
  requirements: []
134
135
  rubyforge_project:
135
- rubygems_version: 2.7.7
136
+ rubygems_version: 2.7.6
136
137
  signing_key:
137
138
  specification_version: 4
138
139
  summary: A version solver based on dart's PubGrub