aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/vendor/bundler/lib/bundler/source.rb
blob: 37828ca31696fc411af66011f51561e828718e8c (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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
module Bundler
  # Represents a source of rubygems. Initially, this is only gem repositories, but
  # eventually, this will be git, svn, HTTP
  class Source
    attr_accessor :tmp_path
  end

  class GemSource < Source
    attr_reader :uri

    def initialize(options)
      @uri = options[:uri]
      @uri = URI.parse(@uri) unless @uri.is_a?(URI)
      raise ArgumentError, "The source must be an absolute URI" unless @uri.absolute?
    end

    def gems
      @specs ||= fetch_specs
    end

    def ==(other)
      uri == other.uri
    end

    def to_s
      @uri.to_s
    end

    class RubygemsRetardation < StandardError; end

    def download(spec, repository)
      Bundler.logger.info "Downloading #{spec.full_name}.gem"

      destination = repository.download_path_for(:gem)

      unless destination.writable?
        raise RubygemsRetardation
      end

      Gem::RemoteFetcher.fetcher.download(spec, uri, repository.download_path_for(:gem))
    end

  private

    def fetch_specs
      Bundler.logger.info "Updating source: #{to_s}"

      deflated = Gem::RemoteFetcher.fetcher.fetch_path("#{uri}/Marshal.4.8.Z")
      inflated = Gem.inflate deflated

      index = Marshal.load(inflated)
      index.gems
    rescue Gem::RemoteFetcher::FetchError => e
      raise ArgumentError, "#{to_s} is not a valid source: #{e.message}"
    end
  end

  class DirectorySource < Source
    def initialize(options)
      @name          = options[:name]
      @version       = options[:version]
      @location      = options[:location]
      @require_paths = options[:require_paths] || %w(lib)
    end

    def gems
      @gems ||= begin
        specs = {}

        # Find any gemspec files in the directory and load those specs
        Dir[@location.join('**', '*.gemspec')].each do |file|
          path = Pathname.new(file).relative_path_from(@location).dirname
          spec = eval(File.read(file))
          spec.require_paths.map! { |p| path.join(p) }
          specs[spec.full_name] = spec
        end

        # If a gemspec for the dependency was not found, add it to the list
        if specs.keys.grep(/^#{Regexp.escape(@name)}/).empty?
          case
          when @version.nil?
            raise ArgumentError, "If you use :at, you must specify the gem" \
              "and version you wish to stand in for"
          when !Gem::Version.correct?(@version)
            raise ArgumentError, "If you use :at, you must specify a gem and" \
              "version. You specified #{@version} for the version"
          end

          default = Gem::Specification.new do |s|
            s.name = @name
            s.version = Gem::Version.new(@version) if @version
          end
          specs[default.full_name] = default
        end

        specs
      end
    end

    def ==(other)
      # TMP HAX
      other.is_a?(DirectorySource)
    end

    def to_s
      "#{@name} (#{@version}) Located at: '#{@location}'"
    end

    def download(spec, repository)
      spec.require_paths.map! { |p| File.join(@location, p) }
      repository.add_spec(:directory, spec)
    end
  end

  class GitSource < DirectorySource
    def initialize(options)
      super
      @uri = options[:uri]
      @ref = options[:ref]
      @branch = options[:branch]
    end

    def gems
      FileUtils.mkdir_p(tmp_path.join("gitz"))

      # TMP HAX to get the *.gemspec reading to work
      @location = tmp_path.join("gitz", @name)

      Bundler.logger.info "Cloning git repository at: #{@uri}"
      `git clone #{@uri} #{@location} --no-hardlinks`

      if @ref
        Dir.chdir(@location) { `git checkout #{@ref}` }
      elsif @branch && @branch != "master"
        Dir.chdir(@location) { `git checkout origin/#{@branch}` }
      end
      super
    end

    def download(spec, repository)
      dest = repository.download_path_for(:directory).join(@name)
      spec.require_paths.map! { |p| File.join(dest, p) }
      repository.add_spec(:directory, spec)
      if spec.name == @name
        FileUtils.mkdir_p(dest.dirname)
        FileUtils.mv(tmp_path.join("gitz", spec.name), dest)
      end
    end
  end
end