In Files

  • rubygems/spec_fetcher.rb

Class/Module Index [+]

Quicksearch

Gem::SpecFetcher

SpecFetcher handles metadata updates from remote gem repositories.

Public Class Methods

fetcher() click to toggle source
 
               # File rubygems/spec_fetcher.rb, line 37
def self.fetcher
  @fetcher ||= new
end
            
new() click to toggle source
 
               # File rubygems/spec_fetcher.rb, line 45
def initialize
  @dir = File.join Gem.user_home, '.gem', 'specs'
  @update_cache = File.stat(Gem.user_home).uid == Process.uid

  @specs = {}
  @latest_specs = {}
  @prerelease_specs = {}

  @fetcher = Gem::RemoteFetcher.fetcher
end
            

Public Instance Methods

cache_dir(uri) click to toggle source

Returns the local directory to write uri to.

 
               # File rubygems/spec_fetcher.rb, line 59
def cache_dir(uri)
  File.join @dir, "#{uri.host}%#{uri.port}", File.dirname(uri.path)
end
            
fetch(*args) click to toggle source
 
               # File rubygems/spec_fetcher.rb, line 87
def fetch(*args)
  fetch_with_errors(*args).first
end
            
fetch_spec(spec, source_uri) click to toggle source
 
               # File rubygems/spec_fetcher.rb, line 91
def fetch_spec(spec, source_uri)
  spec = spec - [nil, 'ruby', '']
  spec_file_name = "#{spec.join '-'}.gemspec"

  uri = source_uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"

  cache_dir = cache_dir uri

  local_spec = File.join cache_dir, spec_file_name

  if File.exist? local_spec then
    spec = Gem.read_binary local_spec
  else
    uri.path << '.rz'

    spec = @fetcher.fetch_path uri
    spec = Gem.inflate spec

    if @update_cache then
      FileUtils.mkdir_p cache_dir

      open local_spec, 'wb' do |io|
        io.write spec
      end
    end
  end

  # TODO: Investigate setting Gem::Specification#loaded_from to a URI
  Marshal.load spec
end
            
fetch_with_errors(dependency, all = false, matching_platform = true, prerelease = false) click to toggle source

Fetch specs matching dependency. If all is true, all matching (released) versions are returned. If matching_platform is false, all platforms are returned. If prerelease is true, prerelease versions are included.

 
               # File rubygems/spec_fetcher.rb, line 69
def fetch_with_errors(dependency, all = false, matching_platform = true, prerelease = false)
  specs_and_sources, errors = find_matching_with_errors dependency, all, matching_platform, prerelease

  ss = specs_and_sources.map do |spec_tuple, source_uri|
    [fetch_spec(spec_tuple, URI.parse(source_uri)), source_uri]
  end

  return [ss, errors]

rescue Gem::RemoteFetcher::FetchError => e
  raise unless warn_legacy e do
    require 'rubygems/source_info_cache'

    return [Gem::SourceInfoCache.search_with_source(dependency,
                                                   matching_platform, all), nil]
  end
end
            
find_matching(*args) click to toggle source
 
               # File rubygems/spec_fetcher.rb, line 158
def find_matching(*args)
  find_matching_with_errors(*args).first
end
            
find_matching_with_errors(dependency, all = false, matching_platform = true, prerelease = false) click to toggle source

Find spec names that match dependency. If all is true, all matching released versions are returned. If matching_platform is false, gems for all platforms are returned.

 
               # File rubygems/spec_fetcher.rb, line 127
def find_matching_with_errors(dependency, all = false, matching_platform = true, prerelease = false)
  found = {}

  rejected_specs = {}

  list(all, prerelease).each do |source_uri, specs|
    found[source_uri] = specs.select do |spec_name, version, spec_platform|
      if dependency.match?(spec_name, version)
        if matching_platform and !Gem::Platform.match(spec_platform)
          pm = (rejected_specs[dependency] ||= Gem::PlatformMismatch.new(spec_name, version))
          pm.add_platform spec_platform
          false
        else
          true
        end
      end
    end
  end

  errors = rejected_specs.values

  specs_and_sources = []

  found.each do |source_uri, specs|
    uri_str = source_uri.to_s
    specs_and_sources.push(*specs.map { |spec| [spec, uri_str] })
  end

  [specs_and_sources, errors]
end
            
legacy_repos() click to toggle source

Returns Array of gem repositories that were generated with RubyGems less than 1.2.

 
               # File rubygems/spec_fetcher.rb, line 166
def legacy_repos
  Gem.sources.reject do |source_uri|
    source_uri = URI.parse source_uri
    spec_path = source_uri + "specs.#{Gem.marshal_version}.gz"

    begin
      @fetcher.fetch_size spec_path
    rescue Gem::RemoteFetcher::FetchError
      begin
        @fetcher.fetch_size(source_uri + 'yaml') # re-raise if non-repo
      rescue Gem::RemoteFetcher::FetchError
        alert_error "#{source_uri} does not appear to be a repository"
        raise
      end
      false
    end
  end
end
            
list(all = false, prerelease = false) click to toggle source

Returns a list of gems available for each source in Gem.sources. If all is true, all released versions are returned instead of only latest versions. If prerelease is true, include prerelease versions.

 
               # File rubygems/spec_fetcher.rb, line 190
def list(all = false, prerelease = false)
  # TODO: make type the only argument
  type = if all
           :all
         elsif prerelease
           :prerelease
         else
           :latest
         end

  list = {}

  file = { :latest => 'latest_specs',
    :prerelease => 'prerelease_specs',
    :all => 'specs' }[type]

  cache = { :latest => @latest_specs,
    :prerelease => @prerelease_specs,
    :all => @specs }[type]

  Gem.sources.each do |source_uri|
    source_uri = URI.parse source_uri

    unless cache.include? source_uri
      cache[source_uri] = load_specs source_uri, file
    end

    list[source_uri] = cache[source_uri]
  end

  if type == :all
    list.values.map do |gems|
      gems.reject! { |g| !g[1] || g[1].prerelease? }
    end
  end

  list
end
            
load_specs(source_uri, file) click to toggle source

Loads specs in file, fetching from source_uri if the on-disk cache is out of date.

 
               # File rubygems/spec_fetcher.rb, line 233
def load_specs(source_uri, file)
  file_name  = "#{file}.#{Gem.marshal_version}"
  spec_path  = source_uri + "#{file_name}.gz"
  cache_dir  = cache_dir spec_path
  local_file = File.join(cache_dir, file_name)
  loaded     = false

  if File.exist? local_file then
    spec_dump = @fetcher.fetch_path spec_path, File.mtime(local_file)

    if spec_dump.nil? then
      spec_dump = Gem.read_binary local_file
    else
      loaded = true
    end
  else
    spec_dump = @fetcher.fetch_path spec_path
    loaded = true
  end

  specs = begin
            Marshal.load spec_dump
          rescue ArgumentError
            spec_dump = @fetcher.fetch_path spec_path
            loaded = true

            Marshal.load spec_dump
          end

  if loaded and @update_cache then
    begin
      FileUtils.mkdir_p cache_dir

      open local_file, 'wb' do |io|
        io << spec_dump
      end
    rescue
    end
  end

  specs
end
            
warn_legacy(exception) click to toggle source

Warn about legacy repositories if exception indicates only legacy repositories are available, and yield to the block. Returns false if the exception indicates some other FetchError.

 
               # File rubygems/spec_fetcher.rb, line 281
  def warn_legacy(exception)
    uri = exception.uri.to_s
    if uri =~ /specs\.#{Regexp.escape Gem.marshal_version}\.gz$/ then
      alert_warning <<-EOF
RubyGems 1.2+ index not found for:
\t#{legacy_repos.join "\n\t"}

RubyGems will revert to legacy indexes degrading performance.
      EOF

      yield

      return true
    end

    false
  end