aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/rails/vendor_gem_source_index.rb
blob: e188f07895a09200e7d02967e997d1f790de348a (plain) (blame)
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
require 'rubygems'
require 'yaml'

module Rails

  class VendorGemSourceIndex
    # VendorGemSourceIndex acts as a proxy for the Gem source index, allowing
    # gems to be loaded from vendor/gems. Rather than the standard gem repository format,
    # vendor/gems contains unpacked gems, with YAML specifications in .specification in
    # each gem directory.
    include Enumerable

    attr_reader :installed_source_index
    attr_reader :vendor_source_index

    def initialize(installed_index, vendor_dir=Rails::GemDependency.unpacked_path)
      @installed_source_index = installed_index
      @vendor_dir = vendor_dir
      refresh!
    end

    def refresh!
      # reload the installed gems
      @installed_source_index.refresh!
      vendor_gems = {}

      # handle vendor Rails gems - they are identified by having loaded_from set to ""
      # we add them manually to the list, so that other gems can find them via dependencies
      Gem.loaded_specs.each do |n, s|
        next unless s.loaded_from.empty?
        vendor_gems[s.full_name] = s
      end

      # load specifications from vendor/gems
      Dir[File.join(Rails::GemDependency.unpacked_path, '*')].each do |d|
        spec = load_specification(d)
        next unless spec
        # NOTE: this is a bit of a hack - the gem system expects a different structure
        # than we have.
        # It's looking for:
        # repository
        #   -> specifications
        #      - gem_name.spec      <= loaded_from points to this
        #   -> gems
        #      - gem_name           <= gem files here
        # and therefore goes up one directory from loaded_from, then adds gems/gem_name
        # to the path.
        # But we have:
        # vendor
        #   -> gems
        #      -> gem_name          <= gem files here
        #         - .specification
        # so we set loaded_from to vendor/gems/.specification (not a real file) to
        # get the correct behavior.
        spec.loaded_from = File.join(Rails::GemDependency.unpacked_path, '.specification')
        vendor_gems[File.basename(d)] = spec
      end
      @vendor_source_index = Gem::SourceIndex.new(vendor_gems)
    end

    def load_specification(gem_dir)
      spec_file = File.join(gem_dir, '.specification')
      YAML.load_file(spec_file) if File.exist?(spec_file)
    end

    def find_name(*args)
      @installed_source_index.find_name(*args) + @vendor_source_index.find_name(*args)
    end

    def search(*args)
      # look for vendor gems, and then installed gems - later elements take priority
      @installed_source_index.search(*args) + @vendor_source_index.search(*args)
    end

    def each(&block)
      @vendor_source_index.each(&block)
      @installed_source_index.each(&block)
    end

    def add_spec(spec)
      @vendor_source_index.add_spec spec
    end

    def remove_spec(spec)
      @vendor_source_index.remove_spec spec
    end

    def size
      @vendor_source_index.size + @installed_source_index.size
    end

  end
end