123456789_123456789_123456789_123456789_123456789_

Class: Gem::SpecFetcher

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
Inherits: Object
Defined in: lib/rubygems/spec_fetcher.rb

Overview

SpecFetcher handles metadata updates from remote gem repositories.

Class Attribute Summary

Class Method Summary

Instance Attribute Summary

  • #latest_specs readonly Internal use only

    Cache of latest specs.

  • #prerelease_specs readonly Internal use only

    Cache of prerelease specs.

  • #sources readonly Internal use only

    Sources for this SpecFetcher.

  • #specs readonly Internal use only

    Cache of all released specs.

DefaultUserInteraction - Included

Instance Method Summary

UserInteraction - Included

#alert

Displays an alert statement.

#alert_error

Displays an error statement to the error output location.

#alert_warning

Displays a warning statement to the warning output location.

#ask

Asks a question and returns the answer.

#ask_for_password

Asks for a password with a prompt

#ask_yes_no

Asks a yes or no question.

#choose_from_list

Asks the user to answer question with an answer from the given list.

#say

Displays the given statement on the standard output (or equivalent).

#terminate_interaction

Terminates the RubyGems process with the given exit_code

#verbose

Calls say with msg or the results of the block if really_verbose is true.

DefaultUserInteraction - Included

Text - Included

#clean_text

Remove any non-printable characters and make the text suitable for printing.

#format_text

Wraps text to wrap characters and optionally indents by indent characters.

#levenshtein_distance

Returns a value representing the “cost” of transforming str1 into str2 Vendored version of DidYouMean::Levenshtein.distance from the ruby/did_you_mean gem @ 1.4.0 github.com/ruby/did_you_mean/blob/2ddf39b874808685965dbc47d344cf6c7651807c/lib/did_you_mean/levenshtein.rb#L7-L37.

#truncate_text, #min3

Constructor Details

.new(sources = nil) ⇒ SpecFetcher

Creates a new SpecFetcher. Ordinarily you want to use the default fetcher from .fetcher which uses the Gem.sources.

If you need to retrieve specifications from a different source, you can send it as an argument.

[ GitHub ]

  
# File 'lib/rubygems/spec_fetcher.rb', line 57

def initialize(sources = nil)
  @sources = sources || Gem.sources

  @update_cache =
    begin
      File.stat(Gem.user_home).uid == Process.uid
    rescue Errno::EACCES, Errno::ENOENT
      false
    end

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

  @caches = {
    :latest => @latest_specs,
    :prerelease => @prerelease_specs,
    :released => @specs,
  }

  @fetcher = Gem::RemoteFetcher.fetcher
end

Class Attribute Details

.fetcher (rw)

Default fetcher instance. Use this instead of .new to reduce object allocation.

[ GitHub ]

  
# File 'lib/rubygems/spec_fetcher.rb', line 42

def self.fetcher
  @fetcher ||= new
end

.fetcher=(fetcher) (rw)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubygems/spec_fetcher.rb', line 46

def self.fetcher=(fetcher) # :nodoc:
  @fetcher = fetcher
end

Instance Attribute Details

#latest_specs (readonly)

This method is for internal use only.

Cache of latest specs

[ GitHub ]

  
# File 'lib/rubygems/spec_fetcher.rb', line 19

attr_reader :latest_specs # :nodoc:

#prerelease_specs (readonly)

This method is for internal use only.

Cache of prerelease specs

[ GitHub ]

  
# File 'lib/rubygems/spec_fetcher.rb', line 34

attr_reader :prerelease_specs # :nodoc:

#sources (readonly)

This method is for internal use only.

Sources for this SpecFetcher

[ GitHub ]

  
# File 'lib/rubygems/spec_fetcher.rb', line 24

attr_reader :sources # :nodoc:

#specs (readonly)

This method is for internal use only.

Cache of all released specs

[ GitHub ]

  
# File 'lib/rubygems/spec_fetcher.rb', line 29

attr_reader :specs # :nodoc:

Instance Method Details

#available_specs(type)

Returns a list of gems available for each source in Gem.sources.

type can be one of 3 values: :released => Return the list of all released specs :complete => Return the list of all specs :latest => Return the list of only the highest version of each gem :prerelease => Return the list of all prerelease only specs

[ GitHub ]

  
# File 'lib/rubygems/spec_fetcher.rb', line 213

def available_specs(type)
  errors = []
  list = {}

  @sources.each_source do |source|
    begin
      names = case type
      when :latest
        tuples_for source, :latest
      when :released
        tuples_for source, :released
      when :complete
        names =
          tuples_for(source, :prerelease, true) +
          tuples_for(source, :released)

        names.sort
      when :abs_latest
        names =
          tuples_for(source, :prerelease, true) +
          tuples_for(source, :latest)

        names.sort
      when :prerelease
        tuples_for(source, :prerelease)
      else
        raise Gem::Exception, "Unknown type - :#{type}"
      end
    rescue Gem::RemoteFetcher::FetchError => e
      errors << Gem::SourceFetchProblem.new(source, e)
    else
      list[source] = names
    end
  end

  [list, errors]
end

#detect(type = :complete)

Return all gem name tuples who’s names match obj

[ GitHub ]

  
# File 'lib/rubygems/spec_fetcher.rb', line 133

def detect(type=:complete)
  tuples = []

  list, _ = available_specs(type)
  list.each do |source, specs|
    specs.each do |tup|
      if yield(tup)
        tuples << [tup, source]
      end
    end
  end

  tuples
end

#search_for_dependency(dependency, matching_platform = true)

Find and fetch gem name tuples that match dependency.

If matching_platform is false, gems for all platforms are returned.

[ GitHub ]

  
# File 'lib/rubygems/spec_fetcher.rb', line 86

def search_for_dependency(dependency, matching_platform=true)
  found = {}

  rejected_specs = {}

  list, errors = available_specs(dependency.identity)

  list.each do |source, specs|
    if dependency.name.is_a?(String) && specs.respond_to?(:bsearch)
      start_index = (0 ... specs.length).bsearch {|i| specs[i].name >= dependency.name }
      end_index   = (0 ... specs.length).bsearch {|i| specs[i].name > dependency.name }
      specs = specs[start_index ... end_index] if start_index && end_index
    end

    found[source] = specs.select do |tup|
      if dependency.match?(tup)
        if matching_platform && !Gem::Platform.match_gem?(tup.platform, tup.name)
          pm = (
            rejected_specs[dependency] ||= \
              Gem::PlatformMismatch.new(tup.name, tup.version))
          pm.add_platform tup.platform
          false
        else
          true
        end
      end
    end
  end

  errors += rejected_specs.values

  tuples = []

  found.each do |source, specs|
    specs.each do |s|
      tuples << [s, source]
    end
  end

  tuples = tuples.sort_by {|x| x[0].version }

  return [tuples, errors]
end

#spec_for_dependency(dependency, matching_platform = true)

Find and fetch specs that match dependency.

If matching_platform is false, gems for all platforms are returned.

[ GitHub ]

  
# File 'lib/rubygems/spec_fetcher.rb', line 153

def spec_for_dependency(dependency, matching_platform=true)
  tuples, errors = search_for_dependency(dependency, matching_platform)

  specs = []
  tuples.each do |tup, source|
    begin
      spec = source.fetch_spec(tup)
    rescue Gem::RemoteFetcher::FetchError => e
      errors << Gem::SourceFetchProblem.new(source, e)
    else
      specs << [spec, source]
    end
  end

  return [specs, errors]
end

#suggest_gems_from_name(gem_name, type = :latest, num_results = 5)

Suggests gems based on the supplied gem_name. Returns an array of alternative gem names.

[ GitHub ]

  
# File 'lib/rubygems/spec_fetcher.rb', line 174

def suggest_gems_from_name(gem_name, type = :latest, num_results = 5)
  gem_name        = gem_name.downcase.tr("_-", "")
  max             = gem_name.size / 2
  names           = available_specs(type).first.values.flatten(1)

  matches = names.map do |n|
    next unless n.match_platform?
    [n.name, 0] if n.name.downcase.tr("_-", "").include?(gem_name)
  end.compact

  if matches.length < num_results
    matches += names.map do |n|
      next unless n.match_platform?
      distance = levenshtein_distance gem_name, n.name.downcase.tr("_-", "")
      next if distance >= max
      return [n.name] if distance == 0
      [n.name, distance]
    end.compact
  end

  matches = if matches.empty? && type != :prerelease
    suggest_gems_from_name gem_name, :prerelease
  else
    matches.uniq.sort_by {|name, dist| dist }
  end

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

#tuples_for(source, type, gracefully_ignore = false)

This method is for internal use only.

Retrieves NameTuples from source of the given type (:prerelease, etc.). If gracefully_ignore is true, errors are ignored.

[ GitHub ]

  
# File 'lib/rubygems/spec_fetcher.rb', line 255

def tuples_for(source, type, gracefully_ignore=false) # :nodoc:
  @caches[type][source.uri] ||=
    source.load_specs(type).sort_by {|tup| tup.name }
rescue Gem::RemoteFetcher::FetchError
  raise unless gracefully_ignore
  []
end