1class Gem::AvailableSet
2  Tuple = Struct.new(:spec, :source)
3
4  def initialize
5    @set = []
6    @sorted = nil
7  end
8
9  attr_reader :set
10
11  def add(spec, source)
12    @set << Tuple.new(spec, source)
13    @sorted = nil
14    self
15  end
16
17  def <<(o)
18    case o
19    when Gem::AvailableSet
20      s = o.set
21    when Array
22      s = o.map do |sp,so|
23        if !sp.kind_of?(Gem::Specification) or !so.kind_of?(Gem::Source)
24          raise TypeError, "Array must be in [[spec, source], ...] form"
25        end
26
27        Tuple.new(sp,so)
28      end
29    else
30      raise TypeError, "must be a Gem::AvailableSet"
31    end
32
33    @set += s
34    @sorted = nil
35
36    self
37  end
38
39  def empty?
40    @set.empty?
41  end
42
43  def all_specs
44    @set.map { |t| t.spec }
45  end
46
47  def match_platform!
48    @set.reject! { |t| !Gem::Platform.match(t.spec.platform) }
49    @sorted = nil
50    self
51  end
52
53  def sorted
54    @sorted ||= @set.sort do |a,b|
55      i = b.spec <=> a.spec
56      i != 0 ? i : (a.source <=> b.source)
57    end
58  end
59
60  def size
61    @set.size
62  end
63
64  def source_for(spec)
65    f = @set.find { |t| t.spec == spec }
66    f.source
67  end
68
69  def pick_best!
70    return self if empty?
71
72    @set = [sorted.first]
73    @sorted = nil
74    self
75  end
76
77  def remove_installed!(dep)
78    @set.reject! do |t|
79      # already locally installed
80      Gem::Specification.any? do |installed_spec|
81        dep.name == installed_spec.name and
82          dep.requirement.satisfied_by? installed_spec.version
83      end
84    end
85
86    @sorted = nil
87    self
88  end
89
90  def inject_into_list(dep_list)
91    @set.each { |t| dep_list.add t.spec }
92  end
93end
94