pub_grub 0.3.2 → 0.4.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 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