Extended maintenance of Ruby 1.9.3 ended on February 23, 2015. Read more

In Files

  • rubygems/spec_fetcher.rb

Class/Module Index [+]

Quicksearch

Gem::SpecFetcher

SpecFetcher handles metadata updates from remote gem repositories.

Constants

FILES

Public Class Methods

fetcher() click to toggle source
 
               # File rubygems/spec_fetcher.rb, line 42
def self.fetcher
  @fetcher ||= new
end
            
new() click to toggle source
 
               # File rubygems/spec_fetcher.rb, line 50
def initialize
  require 'fileutils'

  @dir = File.join Gem.user_home, '.gem', 'specs'
  @update_cache = File.stat(Gem.user_home).uid == Process.uid

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

  @caches = {
    :latest => @latest_specs,
    :prerelease => @prerelease_specs,
    :all => @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 72
def cache_dir(uri)
  # Correct for windows paths
  escaped_path = uri.path.sub(/^\/([a-z]):\//i, '/\1-/')
  File.join @dir, "#{uri.host}%#{uri.port}", File.dirname(escaped_path)
end
            
fetch(*args) click to toggle source
 
               # File rubygems/spec_fetcher.rb, line 101
def fetch(*args)
  fetch_with_errors(*args).first
end
            
fetch_spec(spec, source_uri) click to toggle source
 
               # File rubygems/spec_fetcher.rb, line 105
def fetch_spec(spec, source_uri)
  source_uri = URI.parse source_uri if String === 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 84
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]
end
            
find_matching(*args) click to toggle source
 
               # File rubygems/spec_fetcher.rb, line 176
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 142
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.concat(specs.map { |spec| [spec, uri_str] })
  end

  [specs_and_sources, errors]
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 213
def list(all = false, prerelease = false)
  # TODO: make type the only argument
  type = if all
           :all
         elsif prerelease
           :prerelease
         else
           :latest
         end

  list  = {}
  file  = FILES[type]
  cache = @caches[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 250
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
    begin
      spec_dump =
        @fetcher.fetch_path(spec_path, File.mtime(local_file))
    rescue Gem::RemoteFetcher::FetchError => e
      alert_warning "Error fetching data: #{e.message}"
    end

    loaded = true if spec_dump

    spec_dump ||= Gem.read_binary local_file
  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
            
suggest_gems_from_name(gem_name) click to toggle source

Suggests a gem based on the supplied gem_name. Returns a string of the gem name if an approximate match can be found or nil otherwise. NOTE: for performance reasons only gems which exactly match the first character of gem_name are considered.

 
               # File rubygems/spec_fetcher.rb, line 186
def suggest_gems_from_name gem_name
  gem_name        = gem_name.downcase
  max             = gem_name.size / 2
  specs           = list.values.flatten 1

  matches = specs.map { |name, version, platform|
    next unless Gem::Platform.match platform

    distance = levenshtein_distance gem_name, name.downcase

    next if distance >= max

    return [name] if distance == 0

    [name, distance]
  }.compact

  matches = matches.uniq.sort_by { |name, dist| dist }

  matches.first(5).map { |name, dist| name }
end
            

Commenting is here to help enhance the documentation. For example, code samples, or clarification of the documentation.

If you have questions about Ruby or the documentation, please post to one of the Ruby mailing lists. You will get better, faster, help that way.

If you wish to post a correction of the docs, please do so, but also file bug report so that it can be corrected for the next release. Thank you.

If you want to help improve the Ruby documentation, please visit Documenting-ruby.org.