Skip to content

Commit 24c604b

Browse files
committed
Even more docs and cleanup. Yay!
1 parent 59dee98 commit 24c604b

File tree

5 files changed

+63
-23
lines changed

5 files changed

+63
-23
lines changed

lib/semantic/dependency.rb

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,64 @@ module Dependency
77
autoload :ModuleRelease, 'semantic/dependency/module_release'
88
autoload :Source, 'semantic/dependency/source'
99

10+
# @!group Sources
11+
12+
# @return [Array<Source>] a frozen copy of the {Source} list
1013
def sources
1114
(@sources ||= []).dup.freeze
1215
end
1316

17+
# Appends a new {Source} to the current list.
18+
# @param source [Source] the {Source} to add
19+
# @return [void]
1420
def add_source(source)
1521
sources
1622
@sources << source
23+
nil
1724
end
1825

26+
# Clears the current list of {Source}s.
27+
# @return [void]
1928
def clear_sources
2029
sources
2130
@sources.clear
31+
nil
2232
end
2333

34+
# @!endgroup
35+
2436
# Fetches a graph of modules and their dependencies from the currently
2537
# configured list of {Source}s.
2638
#
27-
# @param modules [{ String => String }]
39+
# @todo Return a specialized "Graph" object.
40+
# @todo Allow for external constraints to be added to the graph.
2841
# @see #sources
2942
# @see #add_source
3043
# @see #clear_sources
44+
#
45+
# @param modules [{ String => String }]
46+
# @return [ModuleRelease] the root of a dependency graph
3147
def query(modules)
32-
release = Source::ROOT_CAUSE.create_release('', nil, modules)
48+
graph = Source::ROOT_CAUSE.create_release('', nil, modules)
3349

34-
modules = fetch release
35-
releases = modules.values.flatten << release
50+
modules = fetch(graph)
51+
releases = modules.values.flatten << graph
3652
releases.each do |rel|
3753
rel.dependencies.each do |name|
38-
rel.satisfy_dependencies modules[name]
54+
rel.satisfy_dependencies(modules[name])
3955
end
4056
end
4157

42-
return release
58+
return graph
4359
end
4460

61+
# Given a graph result from {#query}, this method will resolve the graph of
62+
# dependencies, if possible, into a flat list of the best suited modules. If
63+
# the dependency graph does not have a suitable resolution, this method will
64+
# raise an exception to that effect.
65+
#
66+
# @param graph [ModuleRelease] the root of a dependency graph
67+
# @return [Array<ModuleRelease>] the list of releases to act on
4568
def resolve(graph)
4669
catch :next do
4770
return walk(graph.depends_on)
@@ -56,9 +79,11 @@ def resolve(graph)
5679
# placed on it, being {ModuleRelease#satisfied? satisfied}, and having the
5780
# greatest version number (with stability being preferred over prereleases).
5881
#
59-
# @param dependencies [{ String => Array(ModuleRelease) }] the dependencies
60-
# @param considering [Array(ModuleRelease)] the set of releases being tested
61-
# @return [Array(ModuleRelease)] the list of releases to use, if successful
82+
# @todo Traversal order is not presently guaranteed.
83+
#
84+
# @param dependencies [{ String => Array<ModuleRelease> }] the dependencies
85+
# @param considering [Array<ModuleRelease>] the set of releases being tested
86+
# @return [Array<ModuleRelease>] the list of releases to use, if successful
6287
def walk(dependencies, *considering)
6388
return considering if dependencies.empty?
6489

@@ -80,7 +105,7 @@ def walk(dependencies, *considering)
80105
# will return a completed dependency list. If there were problems
81106
# resolving our dependencies, we'll catch `:next`, which will cause
82107
# us to move to the next possibility.
83-
return walk(merged, dep, *considering)
108+
return walk(merged, *considering, dep)
84109
end
85110
end
86111

@@ -90,15 +115,20 @@ def walk(dependencies, *considering)
90115
throw :next
91116
end
92117

93-
# @param module [ModuleRelease] the release to detail
118+
# Given a {ModuleRelease}, this method will iterate through the current
119+
# list of {Source}s to find the complete list of versions available for its
120+
# dependencies.
121+
#
122+
# @param release [ModuleRelease] the release to fetch details for
123+
# @return [{ String => [ModuleRelease] }] the fetched dependency information
94124
def fetch(release, cache = Hash.new { |h,k| h[k] = {} })
95125
release.dependencies.each do |mod|
96126
next if cache.key? mod
97127
releases = cache[mod]
98128
sources.each do |source|
99129
source.fetch(mod).each do |dependency|
100130
releases[dependency.version] ||= dependency
101-
fetch dependency, cache
131+
fetch(dependency, cache)
102132
end
103133
end
104134
end
@@ -108,13 +138,19 @@ def fetch(release, cache = Hash.new { |h,k| h[k] = {} })
108138
end
109139
end
110140

141+
# Given a list of potential releases, this method returns the most suitable
142+
# releases for exploration. Only {ModuleRelease#satisfied? satisfied}
143+
# releases are considered, and releases with stable versions are preferred.
144+
#
145+
# @param releases [Array<ModuleRelease>] a list of potential releases
146+
# @return [Array<ModuleRelease>] releases open for consideration
111147
def preferred_releases(releases)
112-
stable = proc { |x| x.version.prerelease.nil? }
148+
satisfied = releases.select { |x| x.satisfied? }
113149

114-
if releases.none?(&stable)
115-
return releases.select { |r| r.satisfied? }
150+
if satisfied.any? { |x| x.version.stable? }
151+
return satisfied.select { |x| x.version.stable? }
116152
else
117-
return releases.select(&stable).select { |r| r.satisfied? }
153+
return satisfied
118154
end
119155
end
120156
end

lib/semantic/dependency/module_release.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def satisfied?
6060
# end
6161

6262
def to_s
63-
"<#{name}@#{version}>"
63+
"#<#{self.class} #{name}@#{version}>"
6464
end
6565
end
6666
end

lib/semantic/dependency/source.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ class Source
66
def create_release(name, version, dependencies = {})
77
version = Version.parse(version) if version.is_a? String
88
dependencies = dependencies.inject({}) do |hash, (key, value)|
9-
hash[key] = VersionRange.parse(value); hash
9+
hash[key] = VersionRange.parse(value || '>= 0')
10+
hash[key] ||= VersionRange::EMPTY_RANGE
11+
hash
1012
end
1113
ModuleRelease.new(self, name, version, dependencies)
1214
end

lib/semantic/version_range.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class << self
2626
def parse(range)
2727
partial = '\d+(?:[.]\d+)?(?:[.][xX*]|[.]\d+(?:[-][0-9a-zA-Z-]*)?)?'
2828

29-
range = range.sub(/([><=~])[ ]/, '\1')
29+
range = range.gsub(/([><=~])[ ]/, '\1')
3030

3131
return case range
3232
when /\A(#{partial})\Z/
@@ -251,9 +251,6 @@ def process_loose_expr(expr)
251251
end
252252
end
253253

254-
# A range that matches no versions
255-
EMPTY_RANGE = VersionRange.new(Version.parse('0.0.0'), Version.parse('0.0.0'), true).freeze
256-
257254
# Computes the intersection of a pair of ranges. If the ranges have no
258255
# useful intersection, an empty range is returned.
259256
#
@@ -293,5 +290,10 @@ def intersection(other)
293290
def ends_before?(other)
294291
self.end < other.end || (self.end == other.end && self.exclude_end?)
295292
end
293+
294+
public
295+
296+
# A range that matches no versions
297+
EMPTY_RANGE = VersionRange.new(MIN_VERSION, MIN_VERSION, true).freeze
296298
end
297299
end

spec/unit/semantic/version_range_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ def self.range(x, y, ex = false)
273273
Semantic::VersionRange.new(v(x), v(y), ex)
274274
end
275275

276-
EMPTY_RANGE = range(0, 0, true)
276+
EMPTY_RANGE = Semantic::VersionRange::EMPTY_RANGE
277277

278278
tests = {
279279
# This falls entirely before the target range

0 commit comments

Comments
 (0)