Extended maintenance of Ruby 1.9.3 ended on February 23, 2015. Read more
SpecFetcher handles metadata updates from remote gem repositories.
# File rubygems/spec_fetcher.rb, line 42
def self.fetcher
@fetcher ||= new
end
# 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
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
# File rubygems/spec_fetcher.rb, line 101
def fetch(*args)
fetch_with_errors(*args).first
end
# 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 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
# File rubygems/spec_fetcher.rb, line 176
def find_matching(*args)
find_matching_with_errors(*args).first
end
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
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
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
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.