aboutsummaryrefslogtreecommitdiffstats
path: root/lib/bundler/spec_set.rb
blob: 9d0a771e4251096c0f8715b119f65e0e9a3d9a0c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
require 'tsort'

module Bundler
  class SpecSet
    include TSort, Enumerable

    def initialize(specs)
      @specs = specs.sort_by { |s| s.name }
    end

    def each
      sorted.each { |s| yield s }
    end

    def length
      @specs.length
    end

    # TODO: Handle platform filtering
    def for(deps, skip = [])
      specs = {}
      deps.each do |dep|
        name = dep.respond_to?(:name) ? dep.name : dep
        current = lookup[name].first
        append_subgraph(specs, current, skip)
      end

      SpecSet.new(sorted.select { |s| specs[s.name] })
    end

    def valid_for?(deps)
      deps = deps.dup
      until deps.empty?
        specs = lookup[deps.shift.name]
        return false unless specs.any?
        specs.each { |s| deps.concat s.dependencies }
      end
      true
    end

    def to_a
      sorted.dup
    end

    def delete_if(&blk)
      @lookup = nil
      @sorted = nil
      @specs.delete_if(&blk)
    end

    def __materialize__(type)
      materialized = @specs.map do |s|
        next s unless s.is_a?(LazySpecification)
        s.__materialize__(s.source.send(type))
      end
      SpecSet.new(materialized)
    end

  private

    def append_subgraph(specs, current, skip)
      return unless current
      return if specs[current.name] || skip.include?(current.name)
      specs[current.name] = true
      current.dependencies.each do |dep|
        next if dep.type == :development
        s = lookup[dep.name].first
        append_subgraph(specs, s, skip)
      end
    end

    def sorted
      rake = @specs.find { |s| s.name == 'rake' }
      @sorted ||= ([rake] + tsort).compact.uniq
    end

    def lookup
      @lookup ||= begin
        lookup = Hash.new { |h,k| h[k] = [] }
        @specs.each do |s|
          lookup[s.name] << s
        end
        lookup
      end
    end

    def tsort_each_node
      @specs.each { |s| yield s }
    end

    def tsort_each_child(s)
      s.dependencies.sort_by { |d| d.name }.each do |d|
        next if d.type == :development
        lookup[d.name].each { |s| yield s }
      end
    end
  end
end