Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
AvailableSet abstracts tracking and picking which gems to install from which sources. Previously, the [[spec, source], ...] array had an implicit order that was required in order for RubyGems to function properly. This is now properly documented and maintained by AvailableSet.
- Loading branch information
Showing
6 changed files
with
382 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,95 @@ | |||
module Gem | |||
class AvailableSet | |||
Tuple = Struct.new(:spec, :source) | |||
|
|||
def initialize | |||
@set = [] | |||
@sorted = nil | |||
end | |||
|
|||
attr_reader :set | |||
|
|||
def add(spec, source) | |||
@set << Tuple.new(spec, source) | |||
@sorted = nil | |||
self | |||
end | |||
|
|||
def <<(o) | |||
case o | |||
when AvailableSet | |||
s = o.set | |||
when Array | |||
s = o.map do |sp,so| | |||
if !sp.kind_of?(Specification) or !so.kind_of?(Source) | |||
raise TypeError, "Array must be in [[spec, source], ...] form" | |||
end | |||
|
|||
Tuple.new(sp,so) | |||
end | |||
else | |||
raise TypeError, "Must be an AvailableSet" | |||
end | |||
|
|||
@set += s | |||
@sorted = nil | |||
|
|||
self | |||
end | |||
|
|||
def empty? | |||
@set.empty? | |||
end | |||
|
|||
def all_specs | |||
@set.map { |t| t.spec } | |||
end | |||
|
|||
def match_platform! | |||
@set.reject! { |t| !Gem::Platform.match(t.spec.platform) } | |||
@sorted = nil | |||
self | |||
end | |||
|
|||
def sorted | |||
@sorted ||= @set.sort do |a,b| | |||
i = b.spec <=> a.spec | |||
i != 0 ? i : (a.source <=> b.source) | |||
end | |||
end | |||
|
|||
def size | |||
@set.size | |||
end | |||
|
|||
def source_for(spec) | |||
f = @set.find { |t| t.spec == spec } | |||
f.source | |||
end | |||
|
|||
def pick_best! | |||
return self if empty? | |||
|
|||
@set = [sorted.first] | |||
@sorted = nil | |||
self | |||
end | |||
|
|||
def remove_installed!(dep) | |||
@set.reject! do |t| | |||
# already locally installed | |||
Gem::Specification.any? do |installed_spec| | |||
dep.name == installed_spec.name and | |||
dep.requirement.satisfied_by? installed_spec.version | |||
end | |||
end | |||
|
|||
@sorted = nil | |||
self | |||
end | |||
|
|||
def inject_into_list(dep_list) | |||
@set.each { |t| dep_list.add t.spec } | |||
end | |||
end | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,106 @@ | |||
require 'rubygems/test_case' | |||
require 'rubygems/available_set' | |||
require 'rubygems/security' | |||
|
|||
class TestGemAvailableSet < Gem::TestCase | |||
def setup | |||
super | |||
|
|||
@source = Gem::Source.new(@gem_repo) | |||
end | |||
|
|||
def test_add_and_empty | |||
a1, _ = util_gem 'a', '1' | |||
|
|||
set = Gem::AvailableSet.new | |||
assert set.empty? | |||
|
|||
set.add a1, @source | |||
|
|||
refute set.empty? | |||
|
|||
assert_equal [a1], set.all_specs | |||
end | |||
|
|||
def test_match_platform | |||
a1, _ = util_gem 'a', '1' do |g| | |||
g.platform = "something-weird-yep" | |||
end | |||
|
|||
a1c, _ = util_gem 'a', '2' do |g| | |||
g.platform = Gem::Platform.local | |||
end | |||
|
|||
a2, _ = util_gem 'a', '2' | |||
|
|||
set = Gem::AvailableSet.new | |||
set.add a1, @source | |||
set.add a1c, @source | |||
set.add a2, @source | |||
|
|||
set.match_platform! | |||
|
|||
assert_equal [a1c, a2], set.all_specs | |||
end | |||
|
|||
def test_best | |||
a1, _ = util_gem 'a', '1' | |||
a2, _ = util_gem 'a', '2' | |||
|
|||
set = Gem::AvailableSet.new | |||
set.add a1, @source | |||
set.add a2, @source | |||
|
|||
set.pick_best! | |||
|
|||
assert_equal [a2], set.all_specs | |||
end | |||
|
|||
def test_remove_installed_bang | |||
a1, _ = util_gem 'a', '1' | |||
|
|||
a1.activate | |||
|
|||
set = Gem::AvailableSet.new | |||
set.add a1, @source | |||
|
|||
dep = Gem::Dependency.new "a", ">= 0" | |||
|
|||
set.remove_installed! dep | |||
|
|||
assert set.empty? | |||
end | |||
|
|||
def test_sorted_normal_versions | |||
a1, _ = util_gem 'a', '1' | |||
a2, _ = util_gem 'a', '2' | |||
|
|||
set = Gem::AvailableSet.new | |||
set.add a1, @source | |||
set.add a2, @source | |||
|
|||
g = set.sorted | |||
|
|||
assert_equal a2, g[0].spec | |||
assert_equal a1, g[1].spec | |||
end | |||
|
|||
def test_sorted_respect_pre | |||
a1a, _ = util_gem 'a', '1.a' | |||
a1, _ = util_gem 'a', '1' | |||
a2a, _ = util_gem 'a', '2.a' | |||
a2, _ = util_gem 'a', '2' | |||
a3a, _ = util_gem 'a', '3.a' | |||
|
|||
set = Gem::AvailableSet.new | |||
set.add a1, @source | |||
set.add a1a, @source | |||
set.add a3a, @source | |||
set.add a2a, @source | |||
set.add a2, @source | |||
|
|||
g = set.sorted.map { |t| t.spec } | |||
|
|||
assert_equal [a3a, a2, a2a, a1, a1a], g | |||
end | |||
end |
Oops, something went wrong.