aboutsummaryrefslogtreecommitdiffstats
path: root/lib/rubygems/spec_fetcher.rb
diff options
context:
space:
mode:
authordrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-11-29 06:52:18 +0000
committerdrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-11-29 06:52:18 +0000
commit9694bb8cac12969300692dac5a1cf7aa4e3a46cd (patch)
treec3cb423d701f7049ba9382de052e2a937cd1302d /lib/rubygems/spec_fetcher.rb
parent3f606b7063fc7a8b191556365ad343a314719a8d (diff)
downloadruby-9694bb8cac12969300692dac5a1cf7aa4e3a46cd.tar.gz
* lib/rubygems*: Updated to RubyGems 2.0
* test/rubygems*: ditto. * common.mk (prelude): Updated for RubyGems 2.0 source rearrangement. * tool/change_maker.rb: Allow invalid UTF-8 characters in source files. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37976 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/rubygems/spec_fetcher.rb')
-rw-r--r--lib/rubygems/spec_fetcher.rb298
1 files changed, 110 insertions, 188 deletions
diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb
index 7302ad9ffa..531d023b2f 100644
--- a/lib/rubygems/spec_fetcher.rb
+++ b/lib/rubygems/spec_fetcher.rb
@@ -2,6 +2,7 @@ require 'rubygems/remote_fetcher'
require 'rubygems/user_interaction'
require 'rubygems/errors'
require 'rubygems/text'
+require 'rubygems/name_tuple'
##
# SpecFetcher handles metadata updates from remote gem repositories.
@@ -11,17 +12,6 @@ class Gem::SpecFetcher
include Gem::UserInteraction
include Gem::Text
- FILES = {
- :all => 'specs',
- :latest => 'latest_specs',
- :prerelease => 'prerelease_specs',
- }
-
- ##
- # The SpecFetcher cache dir.
-
- attr_reader :dir # :nodoc:
-
##
# Cache of latest specs
@@ -48,8 +38,6 @@ class Gem::SpecFetcher
end
def initialize
- require 'fileutils'
-
@dir = File.join Gem.user_home, '.gem', 'specs'
@update_cache = File.stat(Gem.user_home).uid == Process.uid
@@ -60,144 +48,124 @@ class Gem::SpecFetcher
@caches = {
:latest => @latest_specs,
:prerelease => @prerelease_specs,
- :all => @specs
+ :released => @specs,
}
@fetcher = Gem::RemoteFetcher.fetcher
end
##
- # Returns the local directory to write +uri+ to.
+ #
+ # Find and fetch gem name tuples that match +dependency+.
+ #
+ # If +matching_platform+ is false, gems for all platforms are returned.
- 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
+ def search_for_dependency(dependency, matching_platform=true)
+ found = {}
- ##
- # 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.
-
- 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]
+ rejected_specs = {}
+
+ if dependency.prerelease?
+ type = :complete
+ elsif dependency.latest_version?
+ type = :latest
+ else
+ type = :released
end
- return [ss, errors]
- end
+ list, errors = available_specs(type)
+ list.each do |source, specs|
+ found[source] = specs.select do |tup|
+ if dependency.match?(tup)
+ if matching_platform and !Gem::Platform.match(tup.platform)
+ pm = (
+ rejected_specs[dependency] ||= \
+ Gem::PlatformMismatch.new(tup.name, tup.version))
+ pm.add_platform tup.platform
+ false
+ else
+ true
+ end
+ end
+ end
+ end
- def fetch(*args)
- fetch_with_errors(*args).first
- end
+ errors += rejected_specs.values
- 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"
+ tuples = []
- uri = source_uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"
+ found.each do |source, specs|
+ specs.each do |s|
+ tuples << [s, source]
+ end
+ end
- cache_dir = cache_dir uri
+ tuples = tuples.sort_by { |x| x[0] }
- local_spec = File.join cache_dir, spec_file_name
+ return [tuples, errors]
+ end
- 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
+ ##
+ # Return all gem name tuples who's names match +obj+
- if @update_cache then
- FileUtils.mkdir_p cache_dir
+ def detect(type=:complete)
+ tuples = []
- open local_spec, 'wb' do |io|
- io.write spec
+ list, _ = available_specs(type)
+ list.each do |source, specs|
+ specs.each do |tup|
+ if yield(tup)
+ tuples << [tup, source]
end
end
end
- # TODO: Investigate setting Gem::Specification#loaded_from to a URI
- Marshal.load spec
+ tuples
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.
-
- def find_matching_with_errors(dependency,
- all = false,
- matching_platform = true,
- prerelease = false)
- found = {}
+ # Find and fetch specs that match +dependency+.
+ #
+ # If +matching_platform+ is false, gems for all platforms are returned.
- rejected_specs = {}
+ def spec_for_dependency(dependency, matching_platform=true)
+ tuples, errors = search_for_dependency(dependency, matching_platform)
- 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
+ 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
- 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
-
- def find_matching(*args)
- find_matching_with_errors(*args).first
+ return [specs, errors]
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.
+ # Suggests gems based on the supplied +gem_name+. Returns an array of
+ # alternative gem names.
def suggest_gems_from_name gem_name
- gem_name = gem_name.downcase
+ gem_name = gem_name.downcase.tr('_-', '')
max = gem_name.size / 2
- specs = list.values.flatten 1
+ names = available_specs(:complete).first.values.flatten(1)
- matches = specs.map { |name, version, platform|
- next unless Gem::Platform.match platform
+ matches = names.map { |n|
+ next unless n.match_platform?
- distance = levenshtein_distance gem_name, name.downcase
+ distance = levenshtein_distance gem_name, n.name.downcase.tr('_-', '')
next if distance >= max
- return [name] if distance == 0
+ return [n.name] if distance == 0
- [name, distance]
+ [n.name, distance]
}.compact
matches = matches.uniq.sort_by { |name, dist| dist }
@@ -206,92 +174,46 @@ class Gem::SpecFetcher
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.
-
- 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.
-
- 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
+ # 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
+ #
+
+ def available_specs(type)
+ errors = []
+ list = {}
+
+ Gem.sources.each_source do |source|
begin
- spec_dump =
- @fetcher.fetch_path(spec_path, File.mtime(local_file))
+ names = case type
+ when :latest
+ tuples_for source, :latest
+ when :released
+ tuples_for source, :released
+ when :complete
+ tuples_for(source, :prerelease) + tuples_for(source, :released)
+ when :prerelease
+ tuples_for(source, :prerelease)
+ else
+ raise Gem::Exception, "Unknown type - :#{type}"
+ end
rescue Gem::RemoteFetcher::FetchError => e
- alert_warning "Error fetching data: #{e.message}"
+ errors << Gem::SourceFetchProblem.new(source, e)
+ else
+ list[source] = names
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
+ [list, errors]
end
+ def tuples_for(source, type)
+ cache = @caches[type]
+ cache[source.uri] ||= source.load_specs(type)
+ end
end