Permalink
Browse files

Teach Gem::Collection about dependency order.

  • Loading branch information...
1 parent 6c75927 commit 16131c6bcdb25c6cb3941a8977468c774c85ba02 @jbarnette committed Nov 22, 2010
Showing with 112 additions and 1 deletion.
  1. +47 −0 lib/rubygems/collection.rb
  2. +65 −1 test/test_gem_collection.rb
View
47 lib/rubygems/collection.rb
@@ -1,3 +1,5 @@
+require "tsort"
+
module Gem
# Wraps an Enumerable collection of Gem::Info or Gem::Specification
@@ -11,6 +13,7 @@ module Gem
class Collection
include Enumerable
+ include TSort
# The Enumerable wrapped by this collection.
@@ -75,6 +78,36 @@ def latest!
nil
end
+ # Returns an Array containing the filtered entries in this
+ # collection ordered by dependency, so that no entry depends on an
+ # entry earlier in the list. This will not add entries for missing
+ # dependencies: It only orders the entries in this
+ # collection. Passing +development+ as +true+ will consult
+ # development dependencies as well as runtime.
+
+ def ordered development = false
+ @development = development
+
+ sorted = strongly_connected_components.flatten
+ result = []
+ seen = {}
+
+ sorted.each do |entry|
+ index = seen[entry.name]
+
+ if index
+ if result[index].version < entry.version
+ result[index] = entry
+ end
+ else
+ seen[entry.name] = result.length
+ result << entry
+ end
+ end
+
+ result
+ end
+
# Return a new collection exposing only entries with prerelease
# versions.
@@ -202,5 +235,19 @@ def to_s
arr = [self.class.name, features, names].reject { |e| e.empty? }
"#<#{arr.join ': '}>"
end
+
+ def tsort_each_child current, &block
+ allowed = [nil, :runtime]
+ allowed << :development if @development
+
+ deps = current.dependencies.select { |d| allowed.include? d.type }
+
+ deps.each do |dep|
+ target = detect { |e| dep.matches_spec? e }
+ yield target if target
+ end
+ end
+
+ alias_method :tsort_each_node, :each
end
end
View
66 test/test_gem_collection.rb
@@ -2,7 +2,7 @@
require "rubygems/collection"
require "rubygems/info"
-class TestGemFilter < Gem::Future::Test
+class TestGemCollection < Gem::Future::Test
def test_by
a = entry "foo", "1.0.0"
b = entry "foo", "2.0.0"
@@ -51,6 +51,70 @@ def test_latest!
assert_equal [b], c.entries
end
+ def test_ordered
+ repo do
+ foo = gem "foo", "1.0.0" do |s|
+ s.add_dependency "bar"
+ end
+
+ bar = gem "bar", "1.0.0"
+
+ coll = Gem::Collection.new [foo, bar]
+ assert_equal [bar, foo], coll.ordered
+ end
+ end
+
+ def test_ordered_cyclic
+ repo do
+ foo = gem "foo", "1.0.0" do |s|
+ s.add_dependency "bar"
+ end
+
+ bar = gem "bar", "1.0.0" do |s|
+ s.add_dependency "foo"
+ end
+
+ coll = Gem::Collection.new [foo, bar]
+ assert_equal [foo, bar], coll.ordered
+ end
+
+ def test_ordered_indirect
+ repo do
+ foo = gem "foo", "1.0.0"
+
+ bar = gem "bar", "1.0.0" do |s|
+ s.add_dependency "foo"
+ end
+
+ baz = gem "baz", "1.0.0" do |s|
+ s.add_dependency "bar"
+ s.add_dependency "foo"
+ end
+
+ coll = Gem::Collection.new [bar, baz, foo]
+ assert_equal [foo, bar, baz], coll.ordered
+ end
+
+ def test_ordered_versions
+ repo do
+ f1 = gem "foo", "1.0.0"
+ f2 = gem "foo", "2.0.0"
+
+ bar = gem "bar", "1.0.0" do |s|
+ s.add_dependency "foo", "> 1"
+ end
+
+ baz = gem "baz", "1.0.0" do |s|
+ s.add_dependency "bar"
+ end
+
+ coll = Gem::Collection.new [baz, f1, bar, f2]
+ assert_equal [f2, bar, baz, f1], coll.ordered
+ end
+ end
+ end
+ end
+
def test_prerelease
a = entry "foo", "1.0.0"
b = entry "bar", "1.0.0.pre"

0 comments on commit 16131c6

Please sign in to comment.