Update RubyGems to 1.1.1 r1778 (almost 1.2)

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@17392 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
drbrain 2008-06-17 22:04:18 +00:00
parent f98e6b91de
commit 9d4f37f51f
71 changed files with 3765 additions and 1127 deletions

View File

@ -1,3 +1,7 @@
Wed Jun 18 07:03:30 2008 Eric Hodel <drbrain@egment7.net>
* lib/rubygems/*: Update to RubyGems r1778 (pre 1.2).
Wed Jun 18 04:27:58 2008 Koichi Sasada <ko1@atdot.net> Wed Jun 18 04:27:58 2008 Koichi Sasada <ko1@atdot.net>
* KNOWNBUGS.rb, bootstraptest/pending.rb: move pending bug. * KNOWNBUGS.rb, bootstraptest/pending.rb: move pending bug.

View File

@ -67,11 +67,14 @@ module Gem
:RUBY_SO_NAME => RbConfig::CONFIG["RUBY_SO_NAME"], :RUBY_SO_NAME => RbConfig::CONFIG["RUBY_SO_NAME"],
:arch => RbConfig::CONFIG["arch"], :arch => RbConfig::CONFIG["arch"],
:bindir => RbConfig::CONFIG["bindir"], :bindir => RbConfig::CONFIG["bindir"],
:datadir => RbConfig::CONFIG["datadir"],
:libdir => RbConfig::CONFIG["libdir"], :libdir => RbConfig::CONFIG["libdir"],
:ruby_install_name => RbConfig::CONFIG["ruby_install_name"], :ruby_install_name => RbConfig::CONFIG["ruby_install_name"],
:ruby_version => RbConfig::CONFIG["ruby_version"], :ruby_version => RbConfig::CONFIG["ruby_version"],
:sitedir => RbConfig::CONFIG["sitedir"], :sitedir => RbConfig::CONFIG["sitedir"],
:sitelibdir => RbConfig::CONFIG["sitelibdir"] :sitelibdir => RbConfig::CONFIG["sitelibdir"],
:vendordir => RbConfig::CONFIG["vendordir"] ,
:vendorlibdir => RbConfig::CONFIG["vendorlibdir"]
) )
DIRECTORIES = %w[cache doc gems specifications] unless defined?(DIRECTORIES) DIRECTORIES = %w[cache doc gems specifications] unless defined?(DIRECTORIES)
@ -137,7 +140,7 @@ module Gem
unless matches.any? { |spec| spec.version == existing_spec.version } then unless matches.any? { |spec| spec.version == existing_spec.version } then
raise Gem::Exception, raise Gem::Exception,
"can't activate #{gem}, already activated #{existing_spec.full_name}]" "can't activate #{gem}, already activated #{existing_spec.full_name}"
end end
return false return false
@ -151,7 +154,7 @@ module Gem
@loaded_specs[spec.name] = spec @loaded_specs[spec.name] = spec
# Load dependent gems first # Load dependent gems first
spec.dependencies.each do |dep_gem| spec.runtime_dependencies.each do |dep_gem|
activate dep_gem activate dep_gem
end end
@ -203,6 +206,19 @@ module Gem
private_class_method :all_partials private_class_method :all_partials
##
# See if a given gem is available.
def self.available?(gem, *requirements)
requirements = Gem::Requirement.default if requirements.empty?
unless gem.respond_to?(:name) && gem.respond_to?(:version_requirements)
gem = Gem::Dependency.new(gem, requirements)
end
!Gem.source_index.search(gem).empty?
end
## ##
# The mode needed to read a file as straight binary. # The mode needed to read a file as straight binary.
@ -267,6 +283,13 @@ module Gem
File.join(spec.full_gem_path, 'data', gem_name) File.join(spec.full_gem_path, 'data', gem_name)
end end
##
# A Zlib::Deflate.deflate wrapper
def self.deflate(data)
Zlib::Deflate.deflate data
end
## ##
# The path where gems are to be installed. # The path where gems are to be installed.
@ -345,6 +368,33 @@ module Gem
private_class_method :find_home private_class_method :find_home
##
# Zlib::GzipReader wrapper that unzips +data+.
def self.gunzip(data)
data = StringIO.new data
Zlib::GzipReader.new(data).read
end
##
# Zlib::GzipWriter wrapper that zips +data+.
def self.gzip(data)
zipped = StringIO.new
Zlib::GzipWriter.wrap zipped do |io| io.write data end
zipped.string
end
##
# A Zlib::Inflate#inflate wrapper
def self.inflate(data)
Zlib::Inflate.inflate data
end
## ##
# Return a list of all possible load paths for the latest version for all # Return a list of all possible load paths for the latest version for all
# gems in the Gem installation. # gems in the Gem installation.
@ -438,7 +488,11 @@ module Gem
@gem_path ||= nil @gem_path ||= nil
unless @gem_path then unless @gem_path then
paths = [ENV['GEM_PATH']] || [default_path] paths = if ENV['GEM_PATH'] then
[ENV['GEM_PATH']]
else
[default_path]
end
if defined?(APPLE_GEM_HOME) and not ENV['GEM_PATH'] then if defined?(APPLE_GEM_HOME) and not ENV['GEM_PATH'] then
paths << APPLE_GEM_HOME paths << APPLE_GEM_HOME
@ -459,7 +513,7 @@ module Gem
## ##
# Array of platforms this RubyGems supports. # Array of platforms this RubyGems supports.
def self.platforms def self.platforms
@platforms ||= [] @platforms ||= []
if @platforms.empty? if @platforms.empty?
@ -586,13 +640,13 @@ module Gem
def self.set_paths(gpaths) def self.set_paths(gpaths)
if gpaths if gpaths
@gem_path = gpaths.split(File::PATH_SEPARATOR) @gem_path = gpaths.split(File::PATH_SEPARATOR)
if File::ALT_SEPARATOR then if File::ALT_SEPARATOR then
@gem_path.map! do |path| @gem_path.map! do |path|
path.gsub File::ALT_SEPARATOR, File::SEPARATOR path.gsub File::ALT_SEPARATOR, File::SEPARATOR
end end
end end
@gem_path << Gem.dir @gem_path << Gem.dir
else else
@gem_path = [Gem.dir] @gem_path = [Gem.dir]
@ -683,24 +737,25 @@ module Gem
end end
MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/"
YAML_SPEC_DIR = 'quick/'
end end
# Modify the non-gem version of datadir to handle gem package names. module Config
# :stopdoc:
require 'rbconfig/datadir'
module Config # :nodoc:
class << self class << self
alias gem_original_datadir datadir
# Return the path to the data directory associated with the named # Return the path to the data directory associated with the named
# package. If the package is loaded as a gem, return the gem # package. If the package is loaded as a gem, return the gem
# specific data directory. Otherwise return a path to the share # specific data directory. Otherwise return a path to the share
# area as define by "#{ConfigMap[:datadir]}/#{package_name}". # area as define by "#{ConfigMap[:datadir]}/#{package_name}".
def datadir(package_name) def datadir(package_name)
Gem.datadir(package_name) || Config.gem_original_datadir(package_name) Gem.datadir(package_name) ||
File.join(Gem::ConfigMap[:datadir], package_name)
end end
end end
# :startdoc:
end end
require 'rubygems/exceptions' require 'rubygems/exceptions'
@ -712,6 +767,18 @@ require 'rubygems/source_index' # Needed for Kernel#gem
require 'rubygems/platform' require 'rubygems/platform'
require 'rubygems/builder' # HACK: Needed for rake's package task. require 'rubygems/builder' # HACK: Needed for rake's package task.
begin
require 'rubygems/defaults/operating_system'
rescue LoadError
end
if defined?(RUBY_ENGINE) then
begin
require "rubygems/defaults/#{RUBY_ENGINE}"
rescue LoadError
end
end
if RUBY_VERSION < '1.9' then if RUBY_VERSION < '1.9' then
require 'rubygems/custom_require' require 'rubygems/custom_require'
end end

View File

@ -46,6 +46,7 @@ module Gem
register_command :server register_command :server
register_command :sources register_command :sources
register_command :specification register_command :specification
register_command :stale
register_command :uninstall register_command :uninstall
register_command :unpack register_command :unpack
register_command :update register_command :update

View File

@ -46,37 +46,67 @@ class Gem::Commands::DependencyCommand < Gem::Command
options[:args] << '.' if options[:args].empty? options[:args] << '.' if options[:args].empty?
specs = {} specs = {}
source_indexes = [] source_indexes = Hash.new do |h, source_uri|
h[source_uri] = Gem::SourceIndex.new
end
pattern = /\A#{Regexp.union(*options[:args])}/
dependency = Gem::Dependency.new pattern, options[:version]
if options[:reverse_dependencies] and remote? and not local? then
alert_error 'Only reverse dependencies for local gems are supported.'
terminate_interaction 1
end
if local? then if local? then
source_indexes << Gem::SourceIndex.from_installed_gems Gem.source_index.search(dependency).each do |spec|
end source_indexes[:local].add_spec spec
if remote? then
Gem::SourceInfoCache.cache_data.map do |_, sice|
source_indexes << sice.source_index
end end
end end
options[:args].each do |name| if remote? and not options[:reverse_dependencies] then
new_specs = nil fetcher = Gem::SpecFetcher.fetcher
source_indexes.each do |source_index|
new_specs = find_gems(name, source_index) begin
fetcher.find_matching(dependency).each do |spec_tuple, source_uri|
spec = fetcher.fetch_spec spec_tuple, URI.parse(source_uri)
source_indexes[source_uri].add_spec spec
end
rescue Gem::RemoteFetcher::FetchError => e
raise unless fetcher.warn_legacy e do
require 'rubygems/source_info_cache'
specs = Gem::SourceInfoCache.search_with_source dependency, false
specs.each do |spec, source_uri|
source_indexes[source_uri].add_spec spec
end
end
end end
say "No match found for #{name} (#{options[:version]})" if
new_specs.empty?
specs = specs.merge new_specs
end end
terminate_interaction 1 if specs.empty? if source_indexes.empty? then
patterns = options[:args].join ','
say "No gems found matching #{patterns} (#{options[:version]})" if
Gem.configuration.verbose
terminate_interaction 1
end
specs = {}
source_indexes.values.each do |source_index|
source_index.gems.each do |name, spec|
specs[spec.full_name] = [source_index, spec]
end
end
reverse = Hash.new { |h, k| h[k] = [] } reverse = Hash.new { |h, k| h[k] = [] }
if options[:reverse_dependencies] then if options[:reverse_dependencies] then
specs.values.each do |source_index, spec| specs.values.each do |_, spec|
reverse[spec.full_name] = find_reverse_dependencies spec, source_index reverse[spec.full_name] = find_reverse_dependencies spec
end end
end end
@ -118,10 +148,10 @@ class Gem::Commands::DependencyCommand < Gem::Command
end end
# Retuns list of [specification, dep] that are satisfied by spec. # Retuns list of [specification, dep] that are satisfied by spec.
def find_reverse_dependencies(spec, source_index) def find_reverse_dependencies(spec)
result = [] result = []
source_index.each do |name, sp| Gem.source_index.each do |name, sp|
sp.dependencies.each do |dep| sp.dependencies.each do |dep|
dep = Gem::Dependency.new(*dep) unless Gem::Dependency === dep dep = Gem::Dependency.new(*dep) unless Gem::Dependency === dep
@ -146,5 +176,6 @@ class Gem::Commands::DependencyCommand < Gem::Command
specs specs
end end
end end

View File

@ -51,6 +51,8 @@ class Gem::Commands::EnvironmentCommand < Gem::Command
out << " - RUBY EXECUTABLE: #{Gem.ruby}\n" out << " - RUBY EXECUTABLE: #{Gem.ruby}\n"
out << " - EXECUTABLE DIRECTORY: #{Gem.bindir}\n"
out << " - RUBYGEMS PLATFORMS:\n" out << " - RUBYGEMS PLATFORMS:\n"
Gem.platforms.each do |platform| Gem.platforms.each do |platform|
out << " - #{platform}\n" out << " - #{platform}\n"

View File

@ -33,12 +33,14 @@ class Gem::Commands::FetchCommand < Gem::Command
def execute def execute
version = options[:version] || Gem::Requirement.default version = options[:version] || Gem::Requirement.default
all = Gem::Requirement.default
gem_names = get_all_gem_names gem_names = get_all_gem_names
gem_names.each do |gem_name| gem_names.each do |gem_name|
dep = Gem::Dependency.new gem_name, version dep = Gem::Dependency.new gem_name, version
specs_and_sources = Gem::SourceInfoCache.search_with_source dep, true
specs_and_sources = Gem::SpecFetcher.fetcher.fetch dep, all
specs_and_sources.sort_by { |spec,| spec.version } specs_and_sources.sort_by { |spec,| spec.version }

View File

@ -16,7 +16,6 @@ class Gem::Commands::InstallCommand < Gem::Command
defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({ defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
:generate_rdoc => true, :generate_rdoc => true,
:generate_ri => true, :generate_ri => true,
:install_dir => Gem.dir,
:format_executable => false, :format_executable => false,
:test => false, :test => false,
:version => Gem::Requirement.default, :version => Gem::Requirement.default,
@ -62,7 +61,8 @@ class Gem::Commands::InstallCommand < Gem::Command
:install_dir => options[:install_dir], :install_dir => options[:install_dir],
:security_policy => options[:security_policy], :security_policy => options[:security_policy],
:wrappers => options[:wrappers], :wrappers => options[:wrappers],
:bin_dir => options[:bin_dir] :bin_dir => options[:bin_dir],
:development => options[:development],
} }
exit_code = 0 exit_code = 0

View File

@ -1,33 +1,35 @@
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/commands/query_command' require 'rubygems/commands/query_command'
module Gem ##
module Commands # An alternate to Gem::Commands::QueryCommand that searches for gems starting
class ListCommand < QueryCommand # with the the supplied argument.
def initialize class Gem::Commands::ListCommand < Gem::Commands::QueryCommand
super 'list', 'Display gems whose name starts with STRING'
remove_option('--name-matches') def initialize
end super 'list', 'Display gems whose name starts with STRING'
def arguments # :nodoc: remove_option('--name-matches')
"STRING start of gem name to look for"
end
def defaults_str # :nodoc:
"--local --no-details"
end
def usage # :nodoc:
"#{program_name} [STRING]"
end
def execute
string = get_one_optional_argument || ''
options[:name] = /^#{string}/i
super
end
end
end end
def arguments # :nodoc:
"STRING start of gem name to look for"
end
def defaults_str # :nodoc:
"--local --no-details"
end
def usage # :nodoc:
"#{program_name} [STRING]"
end
def execute
string = get_one_optional_argument || ''
options[:name] = /^#{string}/i
super
end
end end

View File

@ -80,7 +80,7 @@ lock it down to the exact version.
say "gem '#{spec.name}', '= #{spec.version}'" unless locked[spec.name] say "gem '#{spec.name}', '= #{spec.version}'" unless locked[spec.name]
locked[spec.name] = true locked[spec.name] = true
spec.dependencies.each do |dep| spec.runtime_dependencies.each do |dep|
next if locked[dep.name] next if locked[dep.name]
candidates = Gem.source_index.search dep.name, dep.requirement_list candidates = Gem.source_index.search dep.name, dep.requirement_list

View File

@ -1,6 +1,6 @@
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/local_remote_options' require 'rubygems/local_remote_options'
require 'rubygems/source_info_cache' require 'rubygems/spec_fetcher'
require 'rubygems/version_option' require 'rubygems/version_option'
class Gem::Commands::OutdatedCommand < Gem::Command class Gem::Commands::OutdatedCommand < Gem::Command
@ -20,8 +20,11 @@ class Gem::Commands::OutdatedCommand < Gem::Command
locals.outdated.sort.each do |name| locals.outdated.sort.each do |name|
local = locals.search(/^#{name}$/).last local = locals.search(/^#{name}$/).last
remotes = Gem::SourceInfoCache.search_with_source(/^#{name}$/, true)
dep = Gem::Dependency.new local.name, ">= #{local.version}"
remotes = Gem::SpecFetcher.fetcher.fetch dep
remote = remotes.last.first remote = remotes.last.first
say "#{local.name} (#{local.version} < #{remote.version})" say "#{local.name} (#{local.version} < #{remote.version})"
end end
end end

View File

@ -82,51 +82,10 @@ revert the gem.
end end
# TODO use installer options # TODO use installer options
installer = Gem::Installer.new gem, :wrappers => true installer = Gem::Installer.new gem, :wrappers => true, :force => true
installer.install
gem_file = File.join install_dir, "cache", "#{spec.full_name}.gem" say "Restored #{spec.full_name}"
security_policy = nil # TODO use installer option
format = Gem::Format.from_file_by_path gem_file, security_policy
target_directory = File.join(install_dir, "gems", format.spec.full_name)
target_directory.untaint
pristine_files = format.file_entries.collect { |data| data[0]["path"] }
file_map = {}
format.file_entries.each do |entry, file_data|
file_map[entry["path"]] = file_data
end
Dir.chdir target_directory do
deployed_files = Dir.glob(File.join("**", "*")) +
Dir.glob(File.join("**", ".*"))
pristine_files = pristine_files.map { |f| File.expand_path f }
deployed_files = deployed_files.map { |f| File.expand_path f }
to_redeploy = (pristine_files - deployed_files)
to_redeploy = to_redeploy.map { |path| path.untaint}
if to_redeploy.length > 0 then
say "Restoring #{to_redeploy.length} file#{to_redeploy.length == 1 ? "" : "s"} to #{spec.full_name}..."
to_redeploy.each do |path|
say " #{path}"
FileUtils.mkdir_p File.dirname(path)
File.open(path, "wb") do |out|
out.write file_map[path]
end
end
else
say "#{spec.full_name} is in pristine condition"
end
end
installer.generate_bin
installer.build_extensions
end end
end end

View File

@ -1,6 +1,6 @@
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/local_remote_options' require 'rubygems/local_remote_options'
require 'rubygems/source_info_cache' require 'rubygems/spec_fetcher'
require 'rubygems/version_option' require 'rubygems/version_option'
class Gem::Commands::QueryCommand < Gem::Command class Gem::Commands::QueryCommand < Gem::Command
@ -74,7 +74,13 @@ class Gem::Commands::QueryCommand < Gem::Command
say "*** LOCAL GEMS ***" say "*** LOCAL GEMS ***"
say say
output_query_results Gem.source_index.search(name) specs = Gem.source_index.search name
spec_tuples = specs.map do |spec|
[[spec.name, spec.version, spec.original_platform, spec], :local]
end
output_query_results spec_tuples
end end
if remote? then if remote? then
@ -84,13 +90,26 @@ class Gem::Commands::QueryCommand < Gem::Command
all = options[:all] all = options[:all]
dep = Gem::Dependency.new name, Gem::Requirement.default
begin begin
Gem::SourceInfoCache.cache all fetcher = Gem::SpecFetcher.fetcher
rescue Gem::RemoteFetcher::FetchError spec_tuples = fetcher.find_matching dep, all, false
# no network rescue Gem::RemoteFetcher::FetchError => e
raise unless fetcher.warn_legacy e do
require 'rubygems/source_info_cache'
dep.name = '' if dep.name == //
specs = Gem::SourceInfoCache.search_with_source dep, false, all
spec_tuples = specs.map do |spec, source_uri|
[[spec.name, spec.version, spec.original_platform, spec],
source_uri]
end
end
end end
output_query_results Gem::SourceInfoCache.search(name, false, all) output_query_results spec_tuples
end end
end end
@ -104,28 +123,30 @@ class Gem::Commands::QueryCommand < Gem::Command
!Gem.source_index.search(dep).empty? !Gem.source_index.search(dep).empty?
end end
def output_query_results(gemspecs) def output_query_results(spec_tuples)
output = [] output = []
gem_list_with_version = {} versions = Hash.new { |h,name| h[name] = [] }
gemspecs.flatten.each do |gemspec| spec_tuples.each do |spec_tuple, source_uri|
gem_list_with_version[gemspec.name] ||= [] versions[spec_tuple.first] << [spec_tuple, source_uri]
gem_list_with_version[gemspec.name] << gemspec
end end
gem_list_with_version = gem_list_with_version.sort_by do |name, spec| versions = versions.sort_by do |(name,),|
name.downcase name.downcase
end end
gem_list_with_version.each do |gem_name, list_of_matching| versions.each do |gem_name, matching_tuples|
list_of_matching = list_of_matching.sort_by { |x| x.version.to_ints }.reverse matching_tuples = matching_tuples.sort_by do |(name, version,),|
seen_versions = {} version
end.reverse
list_of_matching.delete_if do |item| seen = {}
if seen_versions[item.version] then
matching_tuples.delete_if do |(name, version,),|
if seen[version] then
true true
else else
seen_versions[item.version] = true seen[version] = true
false false
end end
end end
@ -133,12 +154,50 @@ class Gem::Commands::QueryCommand < Gem::Command
entry = gem_name.dup entry = gem_name.dup
if options[:versions] then if options[:versions] then
versions = list_of_matching.map { |s| s.version }.uniq versions = matching_tuples.map { |(name, version,),| version }.uniq
entry << " (#{versions.join ', '})" entry << " (#{versions.join ', '})"
end end
entry << "\n" << format_text(list_of_matching[0].summary, 68, 4) if if options[:details] then
options[:details] detail_tuple = matching_tuples.first
spec = if detail_tuple.first.length == 4 then
detail_tuple.first.last
else
uri = URI.parse detail_tuple.last
Gem::SpecFetcher.fetcher.fetch_spec detail_tuple.first, uri
end
entry << "\n"
authors = "Author#{spec.authors.length > 1 ? 's' : ''}: "
authors << spec.authors.join(', ')
entry << format_text(authors, 68, 4)
if spec.rubyforge_project and not spec.rubyforge_project.empty? then
rubyforge = "Rubyforge: http://rubyforge.org/projects/#{spec.rubyforge_project}"
entry << "\n" << format_text(rubyforge, 68, 4)
end
if spec.homepage and not spec.homepage.empty? then
entry << "\n" << format_text("Homepage: #{spec.homepage}", 68, 4)
end
if spec.loaded_from then
if matching_tuples.length == 1 then
loaded_from = File.dirname File.dirname(spec.loaded_from)
entry << "\n" << " Installed at: #{loaded_from}"
else
label = 'Installed at'
matching_tuples.each do |(_,version,_,s),|
loaded_from = File.dirname File.dirname(s.loaded_from)
entry << "\n" << " #{label} (#{version}): #{loaded_from}"
label = ' ' * label.length
end
end
end
entry << "\n\n" << format_text(spec.summary, 68, 4)
end
output << entry output << entry
end end

View File

@ -1,7 +1,8 @@
require 'fileutils'
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/remote_fetcher' require 'rubygems/remote_fetcher'
require 'rubygems/source_info_cache' require 'rubygems/source_info_cache'
require 'rubygems/source_info_cache_entry' require 'rubygems/spec_fetcher'
class Gem::Commands::SourcesCommand < Gem::Command class Gem::Commands::SourcesCommand < Gem::Command
@ -21,14 +22,14 @@ class Gem::Commands::SourcesCommand < Gem::Command
options[:remove] = value options[:remove] = value
end end
add_option '-u', '--update', 'Update source cache' do |value, options|
options[:update] = value
end
add_option '-c', '--clear-all', add_option '-c', '--clear-all',
'Remove all sources (clear the cache)' do |value, options| 'Remove all sources (clear the cache)' do |value, options|
options[:clear_all] = value options[:clear_all] = value
end end
add_option '-u', '--update', 'Update source cache' do |value, options|
options[:update] = value
end
end end
def defaults_str def defaults_str
@ -36,9 +37,23 @@ class Gem::Commands::SourcesCommand < Gem::Command
end end
def execute def execute
options[:list] = !(options[:add] || options[:remove] || options[:clear_all] || options[:update]) options[:list] = !(options[:add] ||
options[:clear_all] ||
options[:remove] ||
options[:update])
if options[:clear_all] then if options[:clear_all] then
path = Gem::SpecFetcher.fetcher.dir
FileUtils.rm_rf path
if not File.exist?(path) then
say "*** Removed specs cache ***"
elsif not File.writable?(path) then
say "*** Unable to remove source cache (write protected) ***"
else
say "*** Unable to remove source cache ***"
end
sic = Gem::SourceInfoCache sic = Gem::SourceInfoCache
remove_cache_file 'user', sic.user_cache_file remove_cache_file 'user', sic.user_cache_file
remove_cache_file 'latest user', sic.latest_user_cache_file remove_cache_file 'latest user', sic.latest_user_cache_file
@ -48,15 +63,10 @@ class Gem::Commands::SourcesCommand < Gem::Command
if options[:add] then if options[:add] then
source_uri = options[:add] source_uri = options[:add]
uri = URI.parse source_uri
sice = Gem::SourceInfoCacheEntry.new nil, nil
begin begin
sice.refresh source_uri, true Gem::SpecFetcher.fetcher.load_specs uri, 'specs'
Gem::SourceInfoCache.cache_data[source_uri] = sice
Gem::SourceInfoCache.cache.update
Gem::SourceInfoCache.cache.flush
Gem.sources << source_uri Gem.sources << source_uri
Gem.configuration.write Gem.configuration.write
@ -64,31 +74,32 @@ class Gem::Commands::SourcesCommand < Gem::Command
rescue URI::Error, ArgumentError rescue URI::Error, ArgumentError
say "#{source_uri} is not a URI" say "#{source_uri} is not a URI"
rescue Gem::RemoteFetcher::FetchError => e rescue Gem::RemoteFetcher::FetchError => e
say "Error fetching #{source_uri}:\n\t#{e.message}" yaml_uri = uri + 'yaml'
gem_repo = Gem::RemoteFetcher.fetcher.fetch_size yaml_uri rescue false
if e.uri =~ /specs\.#{Regexp.escape Gem.marshal_version}\.gz$/ and
gem_repo then
alert_warning <<-EOF
RubyGems 1.2+ index not found for:
\t#{source_uri}
Will cause RubyGems to revert to legacy indexes, degrading performance.
EOF
say "#{source_uri} added to sources"
else
say "Error fetching #{source_uri}:\n\t#{e.message}"
end
end end
end end
if options[:update] then
Gem::SourceInfoCache.cache true
Gem::SourceInfoCache.cache.flush
say "source cache successfully updated"
end
if options[:remove] then if options[:remove] then
source_uri = options[:remove] source_uri = options[:remove]
unless Gem.sources.include? source_uri then unless Gem.sources.include? source_uri then
say "source #{source_uri} not present in cache" say "source #{source_uri} not present in cache"
else else
begin # HACK figure out how to get the cache w/o update
Gem::SourceInfoCache.cache
rescue Gem::RemoteFetcher::FetchError
end
Gem::SourceInfoCache.cache_data.delete source_uri
Gem::SourceInfoCache.cache.update
Gem::SourceInfoCache.cache.flush
Gem.sources.delete source_uri Gem.sources.delete source_uri
Gem.configuration.write Gem.configuration.write
@ -96,6 +107,23 @@ class Gem::Commands::SourcesCommand < Gem::Command
end end
end end
if options[:update] then
fetcher = Gem::SpecFetcher.fetcher
if fetcher.legacy_repos.empty? then
Gem.sources.each do |source_uri|
source_uri = URI.parse source_uri
fetcher.load_specs source_uri, 'specs'
fetcher.load_specs source_uri, 'latest_specs'
end
else
Gem::SourceInfoCache.cache true
Gem::SourceInfoCache.cache.flush
end
say "source cache successfully updated"
end
if options[:list] then if options[:list] then
say "*** CURRENT SOURCES ***" say "*** CURRENT SOURCES ***"
say say

View File

@ -52,9 +52,10 @@ class Gem::Commands::SpecificationCommand < Gem::Command
end end
if remote? then if remote? then
Gem::SourceInfoCache.cache_data.each do |_,sice| dep = Gem::Dependency.new gem, options[:version]
specs.push(*sice.source_index.search(gem, options[:version])) found = Gem::SpecFetcher.fetcher.fetch dep
end
specs.push(*found.map { |spec,| spec })
end end
if specs.empty? then if specs.empty? then

View File

@ -0,0 +1,27 @@
require 'rubygems/command'
class Gem::Commands::StaleCommand < Gem::Command
def initialize
super('stale', 'List gems along with access times')
end
def usage # :nodoc:
"#{program_name}"
end
def execute
gem_to_atime = {}
Gem.source_index.each do |name, spec|
Dir["#{spec.full_gem_path}/**/*.*"].each do |file|
next if File.directory?(file)
stat = File.stat(file)
gem_to_atime[name] ||= stat.atime
gem_to_atime[name] = stat.atime if gem_to_atime[name] < stat.atime
end
end
gem_to_atime.sort_by { |_, atime| atime }.each do |name, atime|
say "#{name} at #{atime.strftime '%c'}"
end
end
end

View File

@ -2,7 +2,7 @@ require 'rubygems/command'
require 'rubygems/command_manager' require 'rubygems/command_manager'
require 'rubygems/install_update_options' require 'rubygems/install_update_options'
require 'rubygems/local_remote_options' require 'rubygems/local_remote_options'
require 'rubygems/source_info_cache' require 'rubygems/spec_fetcher'
require 'rubygems/version_option' require 'rubygems/version_option'
require 'rubygems/commands/install_command' require 'rubygems/commands/install_command'
@ -15,11 +15,10 @@ class Gem::Commands::UpdateCommand < Gem::Command
def initialize def initialize
super 'update', super 'update',
'Update the named gems (or all installed gems) in the local repository', 'Update the named gems (or all installed gems) in the local repository',
:generate_rdoc => true, :generate_rdoc => true,
:generate_ri => true, :generate_ri => true,
:force => false, :force => false,
:test => false, :test => false
:install_dir => Gem.dir
add_install_update_options add_install_update_options
@ -60,21 +59,13 @@ class Gem::Commands::UpdateCommand < Gem::Command
hig = {} # highest installed gems hig = {} # highest installed gems
Gem::SourceIndex.from_installed_gems.each do |name, spec| Gem.source_index.each do |name, spec|
if hig[spec.name].nil? or hig[spec.name].version < spec.version then if hig[spec.name].nil? or hig[spec.name].version < spec.version then
hig[spec.name] = spec hig[spec.name] = spec
end end
end end
pattern = if options[:args].empty? then gems_to_update = which_to_update hig, options[:args]
//
else
Regexp.union(*options[:args])
end
remote_gemspecs = Gem::SourceInfoCache.search pattern
gems_to_update = which_to_update hig, remote_gemspecs
updated = [] updated = []
@ -135,20 +126,42 @@ class Gem::Commands::UpdateCommand < Gem::Command
end end
end end
def which_to_update(highest_installed_gems, remote_gemspecs) def which_to_update(highest_installed_gems, gem_names)
result = [] result = []
highest_installed_gems.each do |l_name, l_spec| highest_installed_gems.each do |l_name, l_spec|
matching_gems = remote_gemspecs.select do |spec| next if not gem_names.empty? and
spec.name == l_name and Gem.platforms.any? do |platform| gem_names.all? { |name| /#{name}/ !~ l_spec.name }
platform == spec.platform
dependency = Gem::Dependency.new l_spec.name, "> #{l_spec.version}"
begin
fetcher = Gem::SpecFetcher.fetcher
spec_tuples = fetcher.find_matching dependency
rescue Gem::RemoteFetcher::FetchError => e
raise unless fetcher.warn_legacy e do
require 'rubygems/source_info_cache'
dependency.name = '' if dependency.name == //
specs = Gem::SourceInfoCache.search_with_source dependency
spec_tuples = specs.map do |spec, source_uri|
[[spec.name, spec.version, spec.original_platform], source_uri]
end
end end
end end
highest_remote_gem = matching_gems.sort_by { |spec| spec.version }.last matching_gems = spec_tuples.select do |(name, version, platform),|
name == l_name and Gem::Platform.match platform
end
highest_remote_gem = matching_gems.sort_by do |(name, version),|
version
end.last
if highest_remote_gem and if highest_remote_gem and
l_spec.version < highest_remote_gem.version then l_spec.version < highest_remote_gem.first[1] then
result << l_name result << l_name
end end
end end

View File

@ -18,6 +18,22 @@ class Gem::ConfigFile
DEFAULT_VERBOSITY = true DEFAULT_VERBOSITY = true
DEFAULT_UPDATE_SOURCES = true DEFAULT_UPDATE_SOURCES = true
system_config_path =
begin
require 'Win32API'
CSIDL_COMMON_APPDATA = 0x0023
path = 0.chr * 260
SHGetFolderPath = Win32API.new 'shell32', 'SHGetFolderPath', 'LLLLP', 'L'
SHGetFolderPath.call 0, CSIDL_COMMON_APPDATA, 0, 1, path
path.strip
rescue LoadError
'/etc'
end
SYSTEM_WIDE_CONFIG_FILE = File.join system_config_path, 'gemrc'
# List of arguments supplied to the config file object. # List of arguments supplied to the config file object.
attr_reader :args attr_reader :args
@ -81,18 +97,8 @@ class Gem::ConfigFile
@verbose = DEFAULT_VERBOSITY @verbose = DEFAULT_VERBOSITY
@update_sources = DEFAULT_UPDATE_SOURCES @update_sources = DEFAULT_UPDATE_SOURCES
begin @hash = load_file(SYSTEM_WIDE_CONFIG_FILE)
# HACK $SAFE ok? @hash.merge!(load_file(config_file_name.dup.untaint))
@hash = open(config_file_name.dup.untaint) {|f| YAML.load(f) }
rescue ArgumentError
warn "Failed to load #{config_file_name}"
rescue Errno::ENOENT
# Ignore missing config file error.
rescue Errno::EACCES
warn "Failed to load #{config_file_name} due to permissions problem."
end
@hash ||= {}
# HACK these override command-line args, which is bad # HACK these override command-line args, which is bad
@backtrace = @hash[:backtrace] if @hash.key? :backtrace @backtrace = @hash[:backtrace] if @hash.key? :backtrace
@ -105,6 +111,16 @@ class Gem::ConfigFile
handle_arguments arg_list handle_arguments arg_list
end end
def load_file(filename)
begin
YAML.load(File.read(filename)) if filename and File.exist?(filename)
rescue ArgumentError
warn "Failed to load #{config_file_name}"
rescue Errno::EACCES
warn "Failed to load #{config_file_name} due to permissions problem."
end or {}
end
# True if the backtrace option has been specified, or debug is on. # True if the backtrace option has been specified, or debug is on.
def backtrace def backtrace
@backtrace or $DEBUG @backtrace or $DEBUG

View File

@ -26,7 +26,7 @@ module Kernel
def require(path) # :nodoc: def require(path) # :nodoc:
gem_original_require path gem_original_require path
rescue LoadError => load_error rescue LoadError => load_error
if load_error.message =~ /\A[Nn]o such file to load -- #{Regexp.escape path}\z/ and if load_error.message =~ /#{Regexp.escape path}\z/ and
spec = Gem.searcher.find(path) then spec = Gem.searcher.find(path) then
Gem.activate(spec.name, "= #{spec.version}") Gem.activate(spec.name, "= #{spec.version}")
gem_original_require path gem_original_require path

View File

@ -2,7 +2,7 @@ module Gem
# An Array of the default sources that come with RubyGems. # An Array of the default sources that come with RubyGems.
def self.default_sources def self.default_sources
%w[http://gems.rubyforge.org] %w[http://gems.rubyforge.org/]
end end
# Default home directory path to be used if an alternate value is not # Default home directory path to be used if an alternate value is not

View File

@ -8,24 +8,54 @@ require 'rubygems'
## ##
# The Dependency class holds a Gem name and a Gem::Requirement # The Dependency class holds a Gem name and a Gem::Requirement
class Gem::Dependency class Gem::Dependency
##
# Valid dependency types.
#--
# When this list is updated, be sure to change
# Gem::Specification::CURRENT_SPECIFICATION_VERSION as well.
TYPES = [
:development,
:runtime,
]
##
# Dependency name or regular expression.
attr_accessor :name attr_accessor :name
##
# Dependency type.
attr_reader :type
##
# Dependent versions.
attr_writer :version_requirements attr_writer :version_requirements
##
# Orders dependencies by name only.
def <=>(other) def <=>(other)
[@name] <=> [other.name] [@name] <=> [other.name]
end end
## ##
# Constructs the dependency # Constructs a dependency with +name+ and +requirements+.
#
# name:: [String] name of the Gem def initialize(name, version_requirements, type=:runtime)
# version_requirements:: [String Array] version requirement (e.g. ["> 1.2"])
#
def initialize(name, version_requirements)
@name = name @name = name
unless TYPES.include? type
raise ArgumentError, "Valid types are #{TYPES.inspect}, not #{@type.inspect}"
end
@type = type
@version_requirements = Gem::Requirement.create version_requirements @version_requirements = Gem::Requirement.create version_requirements
@version_requirement = nil # Avoid warnings. @version_requirement = nil # Avoid warnings.
end end
@ -48,17 +78,41 @@ class Gem::Dependency
end end
def to_s # :nodoc: def to_s # :nodoc:
"#{name} (#{version_requirements})" "#{name} (#{version_requirements}, #{@type || :runtime})"
end end
def ==(other) # :nodoc: def ==(other) # :nodoc:
self.class === other && self.class === other &&
self.name == other.name && self.name == other.name &&
self.type == other.type &&
self.version_requirements == other.version_requirements self.version_requirements == other.version_requirements
end end
def hash ##
name.hash + version_requirements.hash # Uses this dependency as a pattern to compare to the dependency +other+.
# This dependency will match if the name matches the other's name, and other
# has only an equal version requirement that satisfies this dependency.
def =~(other)
return false unless self.class === other
pattern = @name
pattern = /\A#{@name}\Z/ unless Regexp === pattern
return false unless pattern =~ other.name
reqs = other.version_requirements.requirements
return false unless reqs.length == 1
return false unless reqs.first.first == '='
version = reqs.first.last
version_requirements.satisfied_by? version
end
def hash # :nodoc:
name.hash + type.hash + version_requirements.hash
end end
end end

View File

@ -1,9 +1,12 @@
require 'rubygems' require 'rubygems'
require 'rubygems/dependency_list' require 'rubygems/dependency_list'
require 'rubygems/installer' require 'rubygems/installer'
require 'rubygems/source_info_cache' require 'rubygems/spec_fetcher'
require 'rubygems/user_interaction' require 'rubygems/user_interaction'
##
# Installs a gem along with all its dependencies from local and remote gems.
class Gem::DependencyInstaller class Gem::DependencyInstaller
include Gem::UserInteraction include Gem::UserInteraction
@ -25,36 +28,50 @@ class Gem::DependencyInstaller
# Creates a new installer instance. # Creates a new installer instance.
# #
# Options are: # Options are:
# :env_shebang:: See Gem::Installer::new. # :cache_dir:: Alternate repository path to store .gem files in.
# :domain:: :local, :remote, or :both. :local only searches gems in the # :domain:: :local, :remote, or :both. :local only searches gems in the
# current directory. :remote searches only gems in Gem::sources. # current directory. :remote searches only gems in Gem::sources.
# :both searches both. # :both searches both.
# :env_shebang:: See Gem::Installer::new.
# :force:: See Gem::Installer#install. # :force:: See Gem::Installer#install.
# :format_executable:: See Gem::Installer#initialize. # :format_executable:: See Gem::Installer#initialize.
# :ignore_dependencies: Don't install any dependencies. # :ignore_dependencies:: Don't install any dependencies.
# :install_dir: See Gem::Installer#install. # :install_dir:: See Gem::Installer#install.
# :security_policy: See Gem::Installer::new and Gem::Security. # :security_policy:: See Gem::Installer::new and Gem::Security.
# :wrappers: See Gem::Installer::new # :wrappers:: See Gem::Installer::new
def initialize(options = {}) def initialize(options = {})
options = DEFAULT_OPTIONS.merge options options = DEFAULT_OPTIONS.merge options
@env_shebang = options[:env_shebang]
@bin_dir = options[:bin_dir]
@development = options[:development]
@domain = options[:domain] @domain = options[:domain]
@env_shebang = options[:env_shebang]
@force = options[:force] @force = options[:force]
@format_executable = options[:format_executable] @format_executable = options[:format_executable]
@ignore_dependencies = options[:ignore_dependencies] @ignore_dependencies = options[:ignore_dependencies]
@install_dir = options[:install_dir] || Gem.dir
@security_policy = options[:security_policy] @security_policy = options[:security_policy]
@wrappers = options[:wrappers] @wrappers = options[:wrappers]
@bin_dir = options[:bin_dir]
@installed_gems = [] @installed_gems = []
@install_dir = options[:install_dir] || Gem.dir
@cache_dir = options[:cache_dir] || @install_dir
if options[:install_dir] then
spec_dir = File.join @install_dir, 'specifications'
@source_index = Gem::SourceIndex.from_gems_in spec_dir
else
@source_index = Gem.source_index
end
end end
## ##
# Returns a list of pairs of gemspecs and source_uris that match # Returns a list of pairs of gemspecs and source_uris that match
# Gem::Dependency +dep+ from both local (Dir.pwd) and remote (Gem.sources) # Gem::Dependency +dep+ from both local (Dir.pwd) and remote (Gem.sources)
# sources. Gems are sorted with newer gems preferred over older gems, and # sources. Gems are sorted with newer gems prefered over older gems, and
# local gems preferred over remote gems. # local gems preferred over remote gems.
def find_gems_with_sources(dep) def find_gems_with_sources(dep)
gems_and_sources = [] gems_and_sources = []
@ -74,8 +91,7 @@ class Gem::DependencyInstaller
all = requirements.length > 1 || all = requirements.length > 1 ||
(requirements.first != ">=" and requirements.first != ">") (requirements.first != ">=" and requirements.first != ">")
found = Gem::SourceInfoCache.search_with_source dep, true, all found = Gem::SpecFetcher.fetcher.fetch dep, all
gems_and_sources.push(*found) gems_and_sources.push(*found)
rescue Gem::RemoteFetcher::FetchError => e rescue Gem::RemoteFetcher::FetchError => e
@ -95,6 +111,7 @@ class Gem::DependencyInstaller
## ##
# Gathers all dependencies necessary for the installation from local and # Gathers all dependencies necessary for the installation from local and
# remote sources unless the ignore_dependencies was given. # remote sources unless the ignore_dependencies was given.
def gather_dependencies def gather_dependencies
specs = @specs_and_sources.map { |spec,_| spec } specs = @specs_and_sources.map { |spec,_| spec }
@ -110,8 +127,18 @@ class Gem::DependencyInstaller
next if spec.nil? or seen[spec.name] next if spec.nil? or seen[spec.name]
seen[spec.name] = true seen[spec.name] = true
spec.dependencies.each do |dep| deps = spec.runtime_dependencies
results = find_gems_with_sources(dep).reverse # local gems first deps |= spec.development_dependencies if @development
deps.each do |dep|
results = find_gems_with_sources(dep).reverse
results.reject! do |spec,|
@source_index.any? do |_, installed_spec|
dep.name == installed_spec.name and
dep.version_requirements.satisfied_by? installed_spec.version
end
end
results.each do |dep_spec, source_uri| results.each do |dep_spec, source_uri|
next if seen[dep_spec.name] next if seen[dep_spec.name]
@ -126,6 +153,11 @@ class Gem::DependencyInstaller
@gems_to_install = dependency_list.dependency_order.reverse @gems_to_install = dependency_list.dependency_order.reverse
end end
##
# Finds a spec and the source_uri it came from for gem +gem_name+ and
# +version+. Returns an Array of specs and sources required for
# installation of the gem.
def find_spec_by_name_and_version gem_name, version = Gem::Requirement.default def find_spec_by_name_and_version gem_name, version = Gem::Requirement.default
spec_and_source = nil spec_and_source = nil
@ -160,14 +192,16 @@ class Gem::DependencyInstaller
if spec_and_source.nil? then if spec_and_source.nil? then
raise Gem::GemNotFoundException, raise Gem::GemNotFoundException,
"could not find #{gem_name} locally or in a repository" "could not find gem #{gem_name} locally or in a repository"
end end
@specs_and_sources = [spec_and_source] @specs_and_sources = [spec_and_source]
end end
## ##
# Installs the gem and all its dependencies. # Installs the gem and all its dependencies. Returns an Array of installed
# gems specifications.
def install dep_or_name, version = Gem::Requirement.default def install dep_or_name, version = Gem::Requirement.default
if String === dep_or_name then if String === dep_or_name then
find_spec_by_name_and_version dep_or_name, version find_spec_by_name_and_version dep_or_name, version
@ -175,15 +209,14 @@ class Gem::DependencyInstaller
@specs_and_sources = [find_gems_with_sources(dep_or_name).last] @specs_and_sources = [find_gems_with_sources(dep_or_name).last]
end end
gather_dependencies @installed_gems = []
spec_dir = File.join @install_dir, 'specifications' gather_dependencies
source_index = Gem::SourceIndex.from_gems_in spec_dir
@gems_to_install.each do |spec| @gems_to_install.each do |spec|
last = spec == @gems_to_install.last last = spec == @gems_to_install.last
# HACK is this test for full_name acceptable? # HACK is this test for full_name acceptable?
next if source_index.any? { |n,_| n == spec.full_name } and not last next if @source_index.any? { |n,_| n == spec.full_name } and not last
# TODO: make this sorta_verbose so other users can benefit from it # TODO: make this sorta_verbose so other users can benefit from it
say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose
@ -191,7 +224,7 @@ class Gem::DependencyInstaller
_, source_uri = @specs_and_sources.assoc spec _, source_uri = @specs_and_sources.assoc spec
begin begin
local_gem_path = Gem::RemoteFetcher.fetcher.download spec, source_uri, local_gem_path = Gem::RemoteFetcher.fetcher.download spec, source_uri,
@install_dir @cache_dir
rescue Gem::RemoteFetcher::FetchError rescue Gem::RemoteFetcher::FetchError
next if @force next if @force
raise raise
@ -205,12 +238,15 @@ class Gem::DependencyInstaller
:install_dir => @install_dir, :install_dir => @install_dir,
:security_policy => @security_policy, :security_policy => @security_policy,
:wrappers => @wrappers, :wrappers => @wrappers,
:bin_dir => @bin_dir :bin_dir => @bin_dir,
:development => @development
spec = inst.install spec = inst.install
@installed_gems << spec @installed_gems << spec
end end
@installed_gems
end end
end end

View File

@ -69,7 +69,7 @@ class Gem::DependencyList
# Are all the dependencies in the list satisfied? # Are all the dependencies in the list satisfied?
def ok? def ok?
@specs.all? do |spec| @specs.all? do |spec|
spec.dependencies.all? do |dep| spec.runtime_dependencies.all? do |dep|
@specs.find { |s| s.satisfies_requirement? dep } @specs.find { |s| s.satisfies_requirement? dep }
end end
end end

View File

@ -9,9 +9,9 @@ require 'fileutils'
module Gem module Gem
class DocManager class DocManager
include UserInteraction include UserInteraction
# Create a document manager for the given gem spec. # Create a document manager for the given gem spec.
# #
# spec:: The Gem::Specification object representing the gem. # spec:: The Gem::Specification object representing the gem.
@ -22,12 +22,12 @@ module Gem
@doc_dir = File.join(spec.installation_path, "doc", spec.full_name) @doc_dir = File.join(spec.installation_path, "doc", spec.full_name)
@rdoc_args = rdoc_args.nil? ? [] : rdoc_args.split @rdoc_args = rdoc_args.nil? ? [] : rdoc_args.split
end end
# Is the RDoc documentation installed? # Is the RDoc documentation installed?
def rdoc_installed? def rdoc_installed?
return File.exist?(File.join(@doc_dir, "rdoc")) return File.exist?(File.join(@doc_dir, "rdoc"))
end end
# Generate the RI documents for this gem spec. # Generate the RI documents for this gem spec.
# #
# Note that if both RI and RDoc documents are generated from the # Note that if both RI and RDoc documents are generated from the
@ -102,7 +102,7 @@ module Gem
args << '--quiet' args << '--quiet'
args << @spec.require_paths.clone args << @spec.require_paths.clone
args << @spec.extra_rdoc_files args << @spec.extra_rdoc_files
args.flatten! args = args.flatten.map do |arg| arg.to_s end
r = RDoc::RDoc.new r = RDoc::RDoc.new

View File

@ -1,5 +1,6 @@
require 'fileutils' require 'fileutils'
require 'tmpdir' require 'tmpdir'
require 'zlib'
require 'rubygems' require 'rubygems'
require 'rubygems/format' require 'rubygems/format'
@ -40,104 +41,43 @@ class Gem::Indexer
marshal_name = "Marshal.#{Gem.marshal_version}" marshal_name = "Marshal.#{Gem.marshal_version}"
@master_index = Gem::Indexer::MasterIndexBuilder.new "yaml", @directory @master_index = File.join @directory, 'yaml'
@marshal_index = Gem::Indexer::MarshalIndexBuilder.new marshal_name, @directory @marshal_index = File.join @directory, marshal_name
@quick_index = Gem::Indexer::QuickIndexBuilder.new 'index', @directory
quick_dir = File.join @directory, 'quick' @quick_dir = File.join @directory, 'quick'
@latest_index = Gem::Indexer::LatestIndexBuilder.new 'latest_index', quick_dir
@quick_marshal_dir = File.join @quick_dir, marshal_name
@quick_index = File.join @quick_dir, 'index'
@latest_index = File.join @quick_dir, 'latest_index'
@specs_index = File.join @directory, "specs.#{Gem.marshal_version}"
@latest_specs_index = File.join @directory,
"latest_specs.#{Gem.marshal_version}"
files = [
@specs_index,
"#{@specs_index}.gz",
@latest_specs_index,
"#{@latest_specs_index}.gz",
@quick_dir,
@master_index,
"#{@master_index}.Z",
@marshal_index,
"#{@marshal_index}.Z",
]
@files = files.map do |path|
path.sub @directory, ''
end
end end
## ##
# Build the index. # Abbreviate the spec for downloading. Abbreviated specs are only used for
# searching, downloading and related activities and do not need deployment
# specific information (e.g. list of files). So we abbreviate the spec,
# making it much smaller for quicker downloads.
def build_index
@master_index.build do
@quick_index.build do
@marshal_index.build do
@latest_index.build do
progress = ui.progress_reporter gem_file_list.size,
"Generating index for #{gem_file_list.size} gems in #{@dest_directory}",
"Loaded all gems"
gem_file_list.each do |gemfile|
if File.size(gemfile.to_s) == 0 then
alert_warning "Skipping zero-length gem: #{gemfile}"
next
end
begin
spec = Gem::Format.from_file_by_path(gemfile).spec
unless gemfile =~ /\/#{Regexp.escape spec.original_name}.*\.gem\z/i then
alert_warning "Skipping misnamed gem: #{gemfile} => #{spec.full_name} (#{spec.original_name})"
next
end
abbreviate spec
sanitize spec
@master_index.add spec
@quick_index.add spec
@marshal_index.add spec
@latest_index.add spec
progress.updated spec.original_name
rescue SignalException => e
alert_error "Received signal, exiting"
raise
rescue Exception => e
alert_error "Unable to process #{gemfile}\n#{e.message} (#{e.class})\n\t#{e.backtrace.join "\n\t"}"
end
end
progress.done
say "Generating master indexes (this may take a while)"
end
end
end
end
end
def install_index
verbose = Gem.configuration.really_verbose
say "Moving index into production dir #{@dest_directory}" if verbose
files = @master_index.files + @quick_index.files + @marshal_index.files +
@latest_index.files
files.each do |file|
src_name = File.join @directory, file
dst_name = File.join @dest_directory, file
FileUtils.rm_rf dst_name, :verbose => verbose
FileUtils.mv src_name, @dest_directory, :verbose => verbose
end
end
def generate_index
FileUtils.rm_rf @directory
FileUtils.mkdir_p @directory, :mode => 0700
build_index
install_index
rescue SignalException
ensure
FileUtils.rm_rf @directory
end
# List of gem file names to index.
def gem_file_list
Dir.glob(File.join(@dest_directory, "gems", "*.gem"))
end
# Abbreviate the spec for downloading. Abbreviated specs are only
# used for searching, downloading and related activities and do not
# need deployment specific information (e.g. list of files). So we
# abbreviate the spec, making it much smaller for quicker downloads.
def abbreviate(spec) def abbreviate(spec)
spec.files = [] spec.files = []
spec.test_files = [] spec.test_files = []
@ -147,9 +87,257 @@ class Gem::Indexer
spec spec
end end
##
# Build various indicies
def build_indicies(index)
progress = ui.progress_reporter index.size,
"Generating quick index gemspecs for #{index.size} gems",
"Complete"
index.each do |original_name, spec|
spec_file_name = "#{original_name}.gemspec.rz"
yaml_name = File.join @quick_dir, spec_file_name
marshal_name = File.join @quick_marshal_dir, spec_file_name
yaml_zipped = Gem.deflate spec.to_yaml
open yaml_name, 'wb' do |io| io.write yaml_zipped end
marshal_zipped = Gem.deflate Marshal.dump(spec)
open marshal_name, 'wb' do |io| io.write marshal_zipped end
progress.updated original_name
end
progress.done
say "Generating specs index"
open @specs_index, 'wb' do |io|
specs = index.sort.map do |_, spec|
platform = spec.original_platform
platform = Gem::Platform::RUBY if platform.nil?
[spec.name, spec.version, platform]
end
specs = compact_specs specs
Marshal.dump specs, io
end
say "Generating latest specs index"
open @latest_specs_index, 'wb' do |io|
specs = index.latest_specs.sort.map do |spec|
[spec.name, spec.version, spec.original_platform]
end
specs = compact_specs specs
Marshal.dump specs, io
end
say "Generating quick index"
quick_index = File.join @quick_dir, 'index'
open quick_index, 'wb' do |io|
io.puts index.sort.map { |_, spec| spec.original_name }
end
say "Generating latest index"
latest_index = File.join @quick_dir, 'latest_index'
open latest_index, 'wb' do |io|
io.puts index.latest_specs.sort.map { |spec| spec.original_name }
end
say "Generating Marshal master index"
open @marshal_index, 'wb' do |io|
io.write index.dump
end
progress = ui.progress_reporter index.size,
"Generating YAML master index for #{index.size} gems (this may take a while)",
"Complete"
open @master_index, 'wb' do |io|
io.puts "--- !ruby/object:#{index.class}"
io.puts "gems:"
gems = index.sort_by { |name, gemspec| gemspec.sort_obj }
gems.each do |original_name, gemspec|
yaml = gemspec.to_yaml.gsub(/^/, ' ')
yaml = yaml.sub(/\A ---/, '') # there's a needed extra ' ' here
io.print " #{original_name}:"
io.puts yaml
progress.updated original_name
end
end
progress.done
say "Compressing indicies"
# use gzip for future files.
compress quick_index, 'rz'
paranoid quick_index, 'rz'
compress latest_index, 'rz'
paranoid latest_index, 'rz'
compress @marshal_index, 'Z'
paranoid @marshal_index, 'Z'
compress @master_index, 'Z'
paranoid @master_index, 'Z'
gzip @specs_index
gzip @latest_specs_index
end
##
# Collect specifications from .gem files from the gem directory.
def collect_specs
index = Gem::SourceIndex.new
progress = ui.progress_reporter gem_file_list.size,
"Loading #{gem_file_list.size} gems from #{@dest_directory}",
"Loaded all gems"
gem_file_list.each do |gemfile|
if File.size(gemfile.to_s) == 0 then
alert_warning "Skipping zero-length gem: #{gemfile}"
next
end
begin
spec = Gem::Format.from_file_by_path(gemfile).spec
unless gemfile =~ /\/#{Regexp.escape spec.original_name}.*\.gem\z/i then
alert_warning "Skipping misnamed gem: #{gemfile} => #{spec.full_name} (#{spec.original_name})"
next
end
abbreviate spec
sanitize spec
index.gems[spec.original_name] = spec
progress.updated spec.original_name
rescue SignalException => e
alert_error "Received signal, exiting"
raise
rescue Exception => e
alert_error "Unable to process #{gemfile}\n#{e.message} (#{e.class})\n\t#{e.backtrace.join "\n\t"}"
end
end
progress.done
index
end
##
# Compacts Marshal output for the specs index data source by using identical
# objects as much as possible.
def compact_specs(specs)
names = {}
versions = {}
platforms = {}
specs.map do |(name, version, platform)|
names[name] = name unless names.include? name
versions[version] = version unless versions.include? version
platforms[platform] = platform unless platforms.include? platform
[names[name], versions[version], platforms[platform]]
end
end
##
# Compress +filename+ with +extension+.
def compress(filename, extension)
data = Gem.read_binary filename
zipped = Gem.deflate data
open "#{filename}.#{extension}", 'wb' do |io|
io.write zipped
end
end
##
# List of gem file names to index.
def gem_file_list
Dir.glob(File.join(@dest_directory, "gems", "*.gem"))
end
##
# Builds and installs indexicies.
def generate_index
FileUtils.rm_rf @directory
FileUtils.mkdir_p @directory, :mode => 0700
FileUtils.mkdir_p @quick_marshal_dir
index = collect_specs
build_indicies index
install_indicies
rescue SignalException
ensure
FileUtils.rm_rf @directory
end
##
# Zlib::GzipWriter wrapper that gzips +filename+ on disk.
def gzip(filename)
Zlib::GzipWriter.open "#{filename}.gz" do |io|
io.write Gem.read_binary(filename)
end
end
##
# Install generated indicies into the destination directory.
def install_indicies
verbose = Gem.configuration.really_verbose
say "Moving index into production dir #{@dest_directory}" if verbose
@files.each do |file|
src_name = File.join @directory, file
dst_name = File.join @dest_directory, file
FileUtils.rm_rf dst_name, :verbose => verbose
FileUtils.mv src_name, @dest_directory, :verbose => verbose
end
end
##
# Ensure +path+ and path with +extension+ are identical.
def paranoid(path, extension)
data = Gem.read_binary path
compressed_data = Gem.read_binary "#{path}.#{extension}"
unless data == Gem.inflate(compressed_data) then
raise "Compressed file #{compressed_path} does not match uncompressed file #{path}"
end
end
##
# Sanitize the descriptive fields in the spec. Sometimes non-ASCII # Sanitize the descriptive fields in the spec. Sometimes non-ASCII
# characters will garble the site index. Non-ASCII characters will # characters will garble the site index. Non-ASCII characters will
# be replaced by their XML entity equivalent. # be replaced by their XML entity equivalent.
def sanitize(spec) def sanitize(spec)
spec.summary = sanitize_string(spec.summary) spec.summary = sanitize_string(spec.summary)
spec.description = sanitize_string(spec.description) spec.description = sanitize_string(spec.description)
@ -158,7 +346,9 @@ class Gem::Indexer
spec spec
end end
##
# Sanitize a single string. # Sanitize a single string.
def sanitize_string(string) def sanitize_string(string)
# HACK the #to_s is in here because RSpec has an Array of Arrays of # HACK the #to_s is in here because RSpec has an Array of Arrays of
# Strings for authors. Need a way to disallow bad values on gempsec # Strings for authors. Need a way to disallow bad values on gempsec
@ -168,9 +358,3 @@ class Gem::Indexer
end end
require 'rubygems/indexer/abstract_index_builder'
require 'rubygems/indexer/master_index_builder'
require 'rubygems/indexer/quick_index_builder'
require 'rubygems/indexer/marshal_index_builder'
require 'rubygems/indexer/latest_index_builder'

View File

@ -89,6 +89,12 @@ module Gem::InstallUpdateOptions
'foo_exec18') do |value, options| 'foo_exec18') do |value, options|
options[:format_executable] = value options[:format_executable] = value
end end
add_option(:"Install/Update", "--development",
"Install any additional development",
"dependencies") do |value, options|
options[:development] = true
end
end end
# Default options for the gem install command. # Default options for the gem install command.

View File

@ -56,6 +56,7 @@ class Gem::Installer
# foo_exec18. # foo_exec18.
# :security_policy:: Use the specified security policy. See Gem::Security # :security_policy:: Use the specified security policy. See Gem::Security
# :wrappers:: Install wrappers if true, symlinks if false. # :wrappers:: Install wrappers if true, symlinks if false.
def initialize(gem, options={}) def initialize(gem, options={})
@gem = gem @gem = gem
@ -76,6 +77,7 @@ class Gem::Installer
@security_policy = options[:security_policy] @security_policy = options[:security_policy]
@wrappers = options[:wrappers] @wrappers = options[:wrappers]
@bin_dir = options[:bin_dir] @bin_dir = options[:bin_dir]
@development = options[:development]
begin begin
@format = Gem::Format.from_file_by_path @gem, @security_policy @format = Gem::Format.from_file_by_path @gem, @security_policy
@ -98,6 +100,7 @@ class Gem::Installer
# cache/<gem-version>.gem #=> a cached copy of the installed gem # cache/<gem-version>.gem #=> a cached copy of the installed gem
# gems/<gem-version>/... #=> extracted files # gems/<gem-version>/... #=> extracted files
# specifications/<gem-version>.gemspec #=> the Gem::Specification # specifications/<gem-version>.gemspec #=> the Gem::Specification
def install def install
# If we're forcing the install then disable security unless the security # If we're forcing the install then disable security unless the security
# policy says that we only install singed gems. # policy says that we only install singed gems.
@ -119,7 +122,10 @@ class Gem::Installer
end end
unless @ignore_dependencies then unless @ignore_dependencies then
@spec.dependencies.each do |dep_gem| deps = @spec.runtime_dependencies
deps |= @spec.development_dependencies if @development
deps.each do |dep_gem|
ensure_dependency @spec, dep_gem ensure_dependency @spec, dep_gem
end end
end end
@ -150,6 +156,8 @@ class Gem::Installer
@spec.loaded_from = File.join(@gem_home, 'specifications', @spec.loaded_from = File.join(@gem_home, 'specifications',
"#{@spec.full_name}.gemspec") "#{@spec.full_name}.gemspec")
Gem.source_index.add_spec @spec
return @spec return @spec
rescue Zlib::GzipFile::Error rescue Zlib::GzipFile::Error
raise Gem::InstallError, "gzip error installing #{@gem}" raise Gem::InstallError, "gzip error installing #{@gem}"
@ -161,6 +169,7 @@ class Gem::Installer
# #
# spec :: Gem::Specification # spec :: Gem::Specification
# dependency :: Gem::Dependency # dependency :: Gem::Dependency
def ensure_dependency(spec, dependency) def ensure_dependency(spec, dependency)
unless installation_satisfies_dependency? dependency then unless installation_satisfies_dependency? dependency then
raise Gem::InstallError, "#{spec.name} requires #{dependency}" raise Gem::InstallError, "#{spec.name} requires #{dependency}"
@ -170,17 +179,15 @@ class Gem::Installer
end end
## ##
# True if the current installed gems satisfy the given dependency. # True if the gems in Gem.source_index satisfy +dependency+.
#
# dependency :: Gem::Dependency
def installation_satisfies_dependency?(dependency) def installation_satisfies_dependency?(dependency)
current_index = Gem::SourceIndex.from_installed_gems Gem.source_index.find_name(dependency.name, dependency.version_requirements).size > 0
current_index.find_name(dependency.name, dependency.version_requirements).size > 0
end end
## ##
# Unpacks the gem into the given directory. # Unpacks the gem into the given directory.
#
def unpack(directory) def unpack(directory)
@gem_dir = directory @gem_dir = directory
@format = Gem::Format.from_file_by_path @gem, @security_policy @format = Gem::Format.from_file_by_path @gem, @security_policy
@ -193,7 +200,7 @@ class Gem::Installer
# #
# spec:: [Gem::Specification] The Gem specification to output # spec:: [Gem::Specification] The Gem specification to output
# spec_path:: [String] The location (path) to write the gemspec to # spec_path:: [String] The location (path) to write the gemspec to
#
def write_spec def write_spec
rubycode = @spec.to_ruby rubycode = @spec.to_ruby
@ -208,7 +215,7 @@ class Gem::Installer
## ##
# Creates windows .bat files for easy running of commands # Creates windows .bat files for easy running of commands
#
def generate_windows_script(bindir, filename) def generate_windows_script(bindir, filename)
if Gem.win_platform? then if Gem.win_platform? then
script_name = filename + ".bat" script_name = filename + ".bat"
@ -227,7 +234,7 @@ class Gem::Installer
# If the user has asked for the gem to be installed in a directory that is # If the user has asked for the gem to be installed in a directory that is
# the system gem directory, then use the system bin directory, else create # the system gem directory, then use the system bin directory, else create
# (or use) a new bin dir under the gem_home. # (or use) a new bin dir under the gem_home.
bindir = @bin_dir ? @bin_dir : (Gem.bindir @gem_home) bindir = @bin_dir ? @bin_dir : Gem.bindir(@gem_home)
Dir.mkdir bindir unless File.exist? bindir Dir.mkdir bindir unless File.exist? bindir
raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir
@ -252,7 +259,7 @@ class Gem::Installer
# The Windows script is generated in addition to the regular one due to a # The Windows script is generated in addition to the regular one due to a
# bug or misfeature in the Windows shell's pipe. See # bug or misfeature in the Windows shell's pipe. See
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/193379 # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/193379
#
def generate_bin_script(filename, bindir) def generate_bin_script(filename, bindir)
bin_script_path = File.join bindir, formatted_program_filename(filename) bin_script_path = File.join bindir, formatted_program_filename(filename)
@ -260,6 +267,8 @@ class Gem::Installer
# HACK some gems don't have #! in their executables, restore 2008/06 # HACK some gems don't have #! in their executables, restore 2008/06
#if File.read(exec_path, 2) == '#!' then #if File.read(exec_path, 2) == '#!' then
FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers
File.open bin_script_path, 'w', 0755 do |file| File.open bin_script_path, 'w', 0755 do |file|
file.print app_script_text(filename) file.print app_script_text(filename)
end end
@ -277,7 +286,7 @@ class Gem::Installer
## ##
# Creates the symlinks to run the applications in the gem. Moves # Creates the symlinks to run the applications in the gem. Moves
# the symlink if the gem being installed has a newer version. # the symlink if the gem being installed has a newer version.
#
def generate_bin_symlink(filename, bindir) def generate_bin_symlink(filename, bindir)
if Gem.win_platform? then if Gem.win_platform? then
alert_warning "Unable to use symlinks on Windows, installing wrapper" alert_warning "Unable to use symlinks on Windows, installing wrapper"
@ -303,6 +312,7 @@ class Gem::Installer
## ##
# Generates a #! line for +bin_file_name+'s wrapper copying arguments if # Generates a #! line for +bin_file_name+'s wrapper copying arguments if
# necessary. # necessary.
def shebang(bin_file_name) def shebang(bin_file_name)
if @env_shebang then if @env_shebang then
"#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name] "#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name]
@ -324,7 +334,9 @@ class Gem::Installer
end end
end end
##
# Return the text for an application file. # Return the text for an application file.
def app_script_text(bin_file_name) def app_script_text(bin_file_name)
<<-TEXT <<-TEXT
#{shebang bin_file_name} #{shebang bin_file_name}
@ -349,7 +361,9 @@ load '#{bin_file_name}'
TEXT TEXT
end end
##
# return the stub script text used to launch the true ruby script # return the stub script text used to launch the true ruby script
def windows_stub_script(bindir, bin_file_name) def windows_stub_script(bindir, bin_file_name)
<<-TEXT <<-TEXT
@ECHO OFF @ECHO OFF
@ -361,8 +375,10 @@ GOTO :EOF
TEXT TEXT
end end
##
# Builds extensions. Valid types of extensions are extconf.rb files, # Builds extensions. Valid types of extensions are extconf.rb files,
# configure scripts and rakefiles or mkrf_conf files. # configure scripts and rakefiles or mkrf_conf files.
def build_extensions def build_extensions
return if @spec.extensions.empty? return if @spec.extensions.empty?
say "Building native extensions. This could take a while..." say "Building native extensions. This could take a while..."
@ -418,6 +434,7 @@ Results logged to #{File.join(Dir.pwd, 'gem_make.out')}
# Reads the file index and extracts each file into the gem directory. # Reads the file index and extracts each file into the gem directory.
# #
# Ensures that files can't be installed outside the gem directory. # Ensures that files can't be installed outside the gem directory.
def extract_files def extract_files
expand_and_validate_gem_dir expand_and_validate_gem_dir
@ -445,11 +462,15 @@ Results logged to #{File.join(Dir.pwd, 'gem_make.out')}
out.write file_data out.write file_data
end end
FileUtils.chmod entry['mode'], path
say path if Gem.configuration.really_verbose say path if Gem.configuration.really_verbose
end end
end end
##
# Prefix and suffix the program filename the same as ruby. # Prefix and suffix the program filename the same as ruby.
def formatted_program_filename(filename) def formatted_program_filename(filename)
if @format_executable then if @format_executable then
self.class.exec_format % File.basename(filename) self.class.exec_format % File.basename(filename)
@ -460,7 +481,9 @@ Results logged to #{File.join(Dir.pwd, 'gem_make.out')}
private private
##
# HACK Pathname is broken on windows. # HACK Pathname is broken on windows.
def absolute_path? pathname def absolute_path? pathname
pathname.absolute? or (Gem.win_platform? and pathname.to_s =~ /\A[a-z]:/i) pathname.absolute? or (Gem.win_platform? and pathname.to_s =~ /\A[a-z]:/i)
end end

View File

@ -4,27 +4,34 @@
# See LICENSE.txt for permissions. # See LICENSE.txt for permissions.
#++ #++
require 'uri'
require 'rubygems' require 'rubygems'
##
# Mixin methods for local and remote Gem::Command options. # Mixin methods for local and remote Gem::Command options.
module Gem::LocalRemoteOptions module Gem::LocalRemoteOptions
##
# Allows OptionParser to handle HTTP URIs. # Allows OptionParser to handle HTTP URIs.
def accept_uri_http def accept_uri_http
OptionParser.accept URI::HTTP do |value| OptionParser.accept URI::HTTP do |value|
begin begin
value = URI.parse value uri = URI.parse value
rescue URI::InvalidURIError rescue URI::InvalidURIError
raise OptionParser::InvalidArgument, value raise OptionParser::InvalidArgument, value
end end
raise OptionParser::InvalidArgument, value unless value.scheme == 'http' raise OptionParser::InvalidArgument, value unless uri.scheme == 'http'
value value
end end
end end
##
# Add local/remote options to the command line parser. # Add local/remote options to the command line parser.
def add_local_remote_options def add_local_remote_options
add_option(:"Local/Remote", '-l', '--local', add_option(:"Local/Remote", '-l', '--local',
'Restrict operations to the LOCAL domain') do |value, options| 'Restrict operations to the LOCAL domain') do |value, options|
@ -47,7 +54,9 @@ module Gem::LocalRemoteOptions
add_update_sources_option add_update_sources_option
end end
##
# Add the --bulk-threshold option # Add the --bulk-threshold option
def add_bulk_threshold_option def add_bulk_threshold_option
add_option(:"Local/Remote", '-B', '--bulk-threshold COUNT', add_option(:"Local/Remote", '-B', '--bulk-threshold COUNT',
"Threshold for switching to bulk", "Threshold for switching to bulk",
@ -57,7 +66,9 @@ module Gem::LocalRemoteOptions
end end
end end
##
# Add the --http-proxy option # Add the --http-proxy option
def add_proxy_option def add_proxy_option
accept_uri_http accept_uri_http
@ -68,22 +79,28 @@ module Gem::LocalRemoteOptions
end end
end end
##
# Add the --source option # Add the --source option
def add_source_option def add_source_option
accept_uri_http accept_uri_http
add_option(:"Local/Remote", '--source URL', URI::HTTP, add_option(:"Local/Remote", '--source URL', URI::HTTP,
'Use URL as the remote source for gems') do |value, options| 'Use URL as the remote source for gems') do |source, options|
source << '/' if source !~ /\/\z/
if options[:added_source] then if options[:added_source] then
Gem.sources << value Gem.sources << source
else else
options[:added_source] = true options[:added_source] = true
Gem.sources.replace [value] Gem.sources.replace [source]
end end
end end
end end
##
# Add the --source option # Add the --source option
def add_update_sources_option def add_update_sources_option
add_option(:"Local/Remote", '-u', '--[no-]update-sources', add_option(:"Local/Remote", '-u', '--[no-]update-sources',
@ -92,12 +109,16 @@ module Gem::LocalRemoteOptions
end end
end end
##
# Is local fetching enabled? # Is local fetching enabled?
def local? def local?
options[:domain] == :local || options[:domain] == :both options[:domain] == :local || options[:domain] == :both
end end
##
# Is remote fetching enabled? # Is remote fetching enabled?
def remote? def remote?
options[:domain] == :remote || options[:domain] == :both options[:domain] == :remote || options[:domain] == :both
end end

View File

@ -1,7 +1,8 @@
require 'rubygems' require 'rubygems'
##
# Available list of platforms for targeting Gem installations. # Available list of platforms for targeting Gem installations.
#
class Gem::Platform class Gem::Platform
@local = nil @local = nil
@ -122,11 +123,20 @@ class Gem::Platform
to_a.compact.join '-' to_a.compact.join '-'
end end
##
# Is +other+ equal to this platform? Two platforms are equal if they have
# the same CPU, OS and version.
def ==(other) def ==(other)
self.class === other and self.class === other and
@cpu == other.cpu and @os == other.os and @version == other.version @cpu == other.cpu and @os == other.os and @version == other.version
end end
##
# Does +other+ match this platform? Two platforms match if they have the
# same CPU, or either has a CPU of 'universal', they have the same OS, and
# they have the same version, or either has no version.
def ===(other) def ===(other)
return nil unless Gem::Platform === other return nil unless Gem::Platform === other
@ -140,6 +150,10 @@ class Gem::Platform
(@version.nil? or other.version.nil? or @version == other.version) (@version.nil? or other.version.nil? or @version == other.version)
end end
##
# Does +other+ match this platform? If +other+ is a String it will be
# converted to a Gem::Platform first. See #=== for matching rules.
def =~(other) def =~(other)
case other case other
when Gem::Platform then # nop when Gem::Platform then # nop

View File

@ -1,4 +1,5 @@
require 'net/http' require 'net/http'
require 'stringio'
require 'uri' require 'uri'
require 'rubygems' require 'rubygems'
@ -11,15 +12,38 @@ class Gem::RemoteFetcher
include Gem::UserInteraction include Gem::UserInteraction
class FetchError < Gem::Exception; end ##
# A FetchError exception wraps up the various possible IO and HTTP failures
# that could happen while downloading from the internet.
class FetchError < Gem::Exception
##
# The URI which was being accessed when the exception happened.
attr_accessor :uri
def initialize(message, uri)
super message
@uri = uri
end
def to_s # :nodoc:
"#{super} (#{uri})"
end
end
@fetcher = nil @fetcher = nil
##
# Cached RemoteFetcher instance. # Cached RemoteFetcher instance.
def self.fetcher def self.fetcher
@fetcher ||= self.new Gem.configuration[:http_proxy] @fetcher ||= self.new Gem.configuration[:http_proxy]
end end
##
# Initialize a remote fetcher using the source URI and possible proxy # Initialize a remote fetcher using the source URI and possible proxy
# information. # information.
# #
@ -29,6 +53,7 @@ class Gem::RemoteFetcher
# * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER, # * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER,
# HTTP_PROXY_PASS) # HTTP_PROXY_PASS)
# * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy # * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy
def initialize(proxy) def initialize(proxy)
Socket.do_not_reverse_lookup = true Socket.do_not_reverse_lookup = true
@ -47,11 +72,13 @@ class Gem::RemoteFetcher
# Moves the gem +spec+ from +source_uri+ to the cache dir unless it is # Moves the gem +spec+ from +source_uri+ to the cache dir unless it is
# already there. If the source_uri is local the gem cache dir copy is # already there. If the source_uri is local the gem cache dir copy is
# always replaced. # always replaced.
def download(spec, source_uri, install_dir = Gem.dir)
gem_file_name = "#{spec.full_name}.gem"
local_gem_path = File.join install_dir, 'cache', gem_file_name
Gem.ensure_gem_subdirectories install_dir def download(spec, source_uri, install_dir = Gem.dir)
cache_dir = File.join install_dir, 'cache'
gem_file_name = "#{spec.full_name}.gem"
local_gem_path = File.join cache_dir, gem_file_name
FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir
source_uri = URI.parse source_uri unless URI::Generic === source_uri source_uri = URI.parse source_uri unless URI::Generic === source_uri
scheme = source_uri.scheme scheme = source_uri.scheme
@ -102,21 +129,26 @@ class Gem::RemoteFetcher
local_gem_path local_gem_path
end end
# Downloads +uri+. ##
# Downloads +uri+ and returns it as a String.
def fetch_path(uri) def fetch_path(uri)
open_uri_or_path(uri) do |input| open_uri_or_path(uri) do |input|
input.read input.read
end end
rescue FetchError
raise
rescue Timeout::Error rescue Timeout::Error
raise FetchError, "timed out fetching #{uri}" raise FetchError.new('timed out', uri)
rescue IOError, SocketError, SystemCallError => e rescue IOError, SocketError, SystemCallError => e
raise FetchError, "#{e.class}: #{e} reading #{uri}" raise FetchError.new("#{e.class}: #{e}", uri)
rescue => e rescue => e
message = "#{e.class}: #{e} reading #{uri}" raise FetchError.new("#{e.class}: #{e}", uri)
raise FetchError, message
end end
##
# Returns the size of +uri+ in bytes. # Returns the size of +uri+ in bytes.
def fetch_size(uri) def fetch_size(uri)
return File.size(get_file_uri_path(uri)) if file_uri? uri return File.size(get_file_uri_path(uri)) if file_uri? uri
@ -124,30 +156,21 @@ class Gem::RemoteFetcher
raise ArgumentError, 'uri is not an HTTP URI' unless URI::HTTP === uri raise ArgumentError, 'uri is not an HTTP URI' unless URI::HTTP === uri
http = connect_to uri.host, uri.port response = request uri, Net::HTTP::Head
request = Net::HTTP::Head.new uri.request_uri if response.code !~ /^2/ then
raise FetchError.new("bad response #{response.message} #{response.code}", uri)
request.basic_auth unescape(uri.user), unescape(uri.password) unless
uri.user.nil? or uri.user.empty?
resp = http.request request
if resp.code !~ /^2/ then
raise Gem::RemoteSourceException,
"HTTP Response #{resp.code} fetching #{uri}"
end end
if resp['content-length'] then if response['content-length'] then
return resp['content-length'].to_i return response['content-length'].to_i
else else
resp = http.get uri.request_uri response = http.get uri.request_uri
return resp.body.size return response.body.size
end end
rescue SocketError, SystemCallError, Timeout::Error => e rescue SocketError, SystemCallError, Timeout::Error => e
raise Gem::RemoteFetcher::FetchError, raise FetchError.new("#{e.message} (#{e.class})\n\tfetching size", uri)
"#{e.message} (#{e.class})\n\tgetting size of #{uri}"
end end
private private
@ -162,7 +185,9 @@ class Gem::RemoteFetcher
URI.unescape(str) URI.unescape(str)
end end
##
# Returns an HTTP proxy URI if one is set in the environment variables. # Returns an HTTP proxy URI if one is set in the environment variables.
def get_proxy_from_env def get_proxy_from_env
env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY'] env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
@ -179,104 +204,129 @@ class Gem::RemoteFetcher
uri uri
end end
##
# Normalize the URI by adding "http://" if it is missing. # Normalize the URI by adding "http://" if it is missing.
def normalize_uri(uri) def normalize_uri(uri)
(uri =~ /^(https?|ftp|file):/) ? uri : "http://#{uri}" (uri =~ /^(https?|ftp|file):/) ? uri : "http://#{uri}"
end end
# Connect to the source host/port, using a proxy if needed. ##
def connect_to(host, port) # Creates or an HTTP connection based on +uri+, or retrieves an existing
if @proxy_uri # connection, using a proxy if needed.
Net::HTTP::Proxy(@proxy_uri.host, @proxy_uri.port, unescape(@proxy_uri.user), unescape(@proxy_uri.password)).new(host, port)
else def connection_for(uri)
Net::HTTP.new(host, port) net_http_args = [uri.host, uri.port]
if @proxy_uri then
net_http_args += [
@proxy_uri.host,
@proxy_uri.port,
@proxy_uri.user,
@proxy_uri.password
]
end end
connection_id = net_http_args.join ':'
@connections[connection_id] ||= Net::HTTP.new(*net_http_args)
connection = @connections[connection_id]
if uri.scheme == 'https' and not connection.started? then
http_obj.use_ssl = true
http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
connection.start unless connection.started?
connection
end end
##
# Read the data from the (source based) URI, but if it is a file:// URI, # Read the data from the (source based) URI, but if it is a file:// URI,
# read from the filesystem instead. # read from the filesystem instead.
def open_uri_or_path(uri, depth = 0, &block) def open_uri_or_path(uri, depth = 0, &block)
if file_uri?(uri) if file_uri?(uri)
open(get_file_uri_path(uri), &block) open(get_file_uri_path(uri), &block)
else else
uri = URI.parse uri unless URI::Generic === uri uri = URI.parse uri unless URI::Generic === uri
net_http_args = [uri.host, uri.port]
if @proxy_uri then response = request uri
net_http_args += [ @proxy_uri.host,
@proxy_uri.port,
@proxy_uri.user,
@proxy_uri.password
]
end
connection_id = net_http_args.join ':'
@connections[connection_id] ||= Net::HTTP.new(*net_http_args)
connection = @connections[connection_id]
if uri.scheme == 'https' && ! connection.started?
http_obj.use_ssl = true
http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
connection.start unless connection.started?
request = Net::HTTP::Get.new(uri.request_uri)
unless uri.nil? || uri.user.nil? || uri.user.empty? then
request.basic_auth(uri.user, uri.password)
end
ua = "RubyGems/#{Gem::RubyGemsVersion} #{Gem::Platform.local}"
ua << " Ruby/#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}"
ua << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
ua << ")"
request.add_field 'User-Agent', ua
request.add_field 'Connection', 'keep-alive'
request.add_field 'Keep-Alive', '30'
# HACK work around EOFError bug in Net::HTTP
# NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
# to install gems.
retried = false
begin
@requests[connection_id] += 1
response = connection.request(request)
rescue EOFError, Errno::ECONNABORTED
requests = @requests[connection_id]
say "connection reset after #{requests} requests, retrying" if
Gem.configuration.really_verbose
raise Gem::RemoteFetcher::FetchError, 'too many connection resets' if
retried
@requests[connection_id] = 0
connection.finish
connection.start
retried = true
retry
end
case response case response
when Net::HTTPOK then when Net::HTTPOK then
block.call(StringIO.new(response.body)) if block block.call(StringIO.new(response.body)) if block
when Net::HTTPRedirection then when Net::HTTPRedirection then
raise Gem::RemoteFetcher::FetchError, "too many redirects" if depth > 10 raise FetchError.new('too many redirects', uri) if depth > 10
open_uri_or_path(response['Location'], depth + 1, &block) open_uri_or_path(response['Location'], depth + 1, &block)
else else
raise Gem::RemoteFetcher::FetchError, raise FetchError.new("bad response #{response.message} #{response.code}", uri)
"bad response #{response.message} #{response.code}"
end end
end end
end end
##
# Performs a Net::HTTP request of type +request_class+ on +uri+ returning
# a Net::HTTP response object. request maintains a table of persistent
# connections to reduce connect overhead.
def request(uri, request_class = Net::HTTP::Get)
request = request_class.new uri.request_uri
unless uri.nil? || uri.user.nil? || uri.user.empty? then
request.basic_auth uri.user, uri.password
end
ua = "RubyGems/#{Gem::RubyGemsVersion} #{Gem::Platform.local}"
ua << " Ruby/#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}"
ua << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
ua << ")"
request.add_field 'User-Agent', ua
request.add_field 'Connection', 'keep-alive'
request.add_field 'Keep-Alive', '30'
connection = connection_for uri
retried = false
# HACK work around EOFError bug in Net::HTTP
# NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
# to install gems.
begin
@requests[connection.object_id] += 1
response = connection.request request
say "#{request.method} #{response.code} #{response.message}: #{uri}" if
Gem.configuration.really_verbose
rescue EOFError, Errno::ECONNABORTED, Errno::ECONNRESET
requests = @requests[connection.object_id]
say "connection reset after #{requests} requests, retrying" if
Gem.configuration.really_verbose
raise FetchError.new('too many connection resets', uri) if retried
@requests.delete connection.object_id
connection.finish
connection.start
retried = true
retry
end
response
end
##
# Checks if the provided string is a file:// URI. # Checks if the provided string is a file:// URI.
def file_uri?(uri) def file_uri?(uri)
uri =~ %r{\Afile://} uri =~ %r{\Afile://}
end end
##
# Given a file:// URI, returns its local path. # Given a file:// URI, returns its local path.
def get_file_uri_path(uri) def get_file_uri_path(uri)
uri.sub(%r{\Afile://}, '') uri.sub(%r{\Afile://}, '')
end end

View File

@ -12,6 +12,7 @@ require 'rubygems/version'
# #
# A Requirement object can actually contain multiple, er, # A Requirement object can actually contain multiple, er,
# requirements, as in (> 1.2, < 2.0). # requirements, as in (> 1.2, < 2.0).
class Gem::Requirement class Gem::Requirement
include Comparable include Comparable
@ -35,7 +36,7 @@ class Gem::Requirement
# Version, a String, or nil. Intended to simplify client code. # Version, a String, or nil. Intended to simplify client code.
# #
# If the input is "weird", the default version requirement is returned. # If the input is "weird", the default version requirement is returned.
#
def self.create(input) def self.create(input)
case input case input
when Gem::Requirement then when Gem::Requirement then
@ -57,6 +58,7 @@ class Gem::Requirement
# This comment once said: # This comment once said:
# #
# "A default "version requirement" can surely _only_ be '> 0'." # "A default "version requirement" can surely _only_ be '> 0'."
def self.default def self.default
self.new ['>= 0'] self.new ['>= 0']
end end
@ -65,6 +67,7 @@ class Gem::Requirement
# Constructs a Requirement from +requirements+ which can be a String, a # Constructs a Requirement from +requirements+ which can be a String, a
# Gem::Version, or an Array of those. See parse for details on the # Gem::Version, or an Array of those. See parse for details on the
# formatting of requirement strings. # formatting of requirement strings.
def initialize(requirements) def initialize(requirements)
@requirements = case requirements @requirements = case requirements
when Array then when Array then
@ -77,13 +80,17 @@ class Gem::Requirement
@version = nil # Avoid warnings. @version = nil # Avoid warnings.
end end
##
# Marshal raw requirements, rather than the full object # Marshal raw requirements, rather than the full object
def marshal_dump
def marshal_dump # :nodoc:
[@requirements] [@requirements]
end end
##
# Load custom marshal format # Load custom marshal format
def marshal_load(array)
def marshal_load(array) # :nodoc:
@requirements = array[0] @requirements = array[0]
@version = nil @version = nil
end end
@ -108,20 +115,16 @@ class Gem::Requirement
end end
## ##
# Is the requirement satisfied by +version+. # True if this requirement satisfied by the Gem::Version +version+.
#
# version:: [Gem::Version] the version to compare against
# return:: [Boolean] true if this requirement is satisfied by
# the version, otherwise false
#
def satisfied_by?(version) def satisfied_by?(version)
normalize normalize
@requirements.all? { |op, rv| satisfy?(op, version, rv) } @requirements.all? { |op, rv| satisfy?(op, version, rv) }
end end
## ##
# Is "version op required_version" satisfied? # Is "+version+ +op+ +required_version+" satisfied?
#
def satisfy?(op, version, required_version) def satisfy?(op, version, required_version)
OPS[op].call(version, required_version) OPS[op].call(version, required_version)
end end
@ -132,6 +135,7 @@ class Gem::Requirement
# The requirement can be a String or a Gem::Version. A String can be an # The requirement can be a String or a Gem::Version. A String can be an
# operator (<, <=, =, =>, >, !=, ~>), a version number, or both, operator # operator (<, <=, =, =>, >, !=, ~>), a version number, or both, operator
# first. # first.
def parse(obj) def parse(obj)
case obj case obj
when /^\s*(#{OP_RE})\s*([0-9.]+)\s*$/o then when /^\s*(#{OP_RE})\s*([0-9.]+)\s*$/o then
@ -147,7 +151,7 @@ class Gem::Requirement
end end
end end
def <=>(other) def <=>(other) # :nodoc:
to_s <=> other.to_s to_s <=> other.to_s
end end

View File

@ -2,5 +2,5 @@
# This file is auto-generated by build scripts. # This file is auto-generated by build scripts.
# See: rake update_version # See: rake update_version
module Gem module Gem
RubyGemsVersion = '1.1.1' RubyGemsVersion = '1.1.1.1778'
end end

View File

@ -4,6 +4,7 @@ require 'zlib'
require 'erb' require 'erb'
require 'rubygems' require 'rubygems'
require 'rubygems/doc_manager'
## ##
# Gem::Server and allows users to serve gems for consumption by # Gem::Server and allows users to serve gems for consumption by
@ -11,18 +12,24 @@ require 'rubygems'
# #
# gem_server starts an HTTP server on the given port and serves the following: # gem_server starts an HTTP server on the given port and serves the following:
# * "/" - Browsing of gem spec files for installed gems # * "/" - Browsing of gem spec files for installed gems
# * "/Marshal" - Full SourceIndex dump of metadata for installed gems # * "/specs.#{Gem.marshal_version}.gz" - specs name/version/platform index
# * "/yaml" - YAML dump of metadata for installed gems - deprecated # * "/latest_specs.#{Gem.marshal_version}.gz" - latest specs
# name/version/platform index
# * "/quick/" - Individual gemspecs
# * "/gems" - Direct access to download the installable gems # * "/gems" - Direct access to download the installable gems
# * legacy indexes:
# * "/Marshal.#{Gem.marshal_version}" - Full SourceIndex dump of metadata
# for installed gems
# * "/yaml" - YAML dump of metadata for installed gems - deprecated
# #
# == Usage # == Usage
# #
# gem server [-p portnum] [-d gem_path] # gem_server = Gem::Server.new Gem.dir, 8089, false
# gem_server.run
# #
# port_num:: The TCP port the HTTP server will bind to #--
# gem_path:: # TODO Refactor into a real WEBrick servlet to remove code duplication.
# Root gem directory containing both "cache" and "specifications"
# subdirectories.
class Gem::Server class Gem::Server
include Gem::UserInteraction include Gem::UserInteraction
@ -36,7 +43,6 @@ class Gem::Server
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head> <head>
<title>RubyGems Documentation Index</title> <title>RubyGems Documentation Index</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" /> <link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" />
</head> </head>
<body> <body>
@ -325,32 +331,99 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
new(options[:gemdir], options[:port], options[:daemon]).run new(options[:gemdir], options[:port], options[:daemon]).run
end end
def initialize(gemdir, port, daemon) def initialize(gem_dir, port, daemon)
Socket.do_not_reverse_lookup = true Socket.do_not_reverse_lookup = true
@gemdir = gemdir @gem_dir = gem_dir
@port = port @port = port
@daemon = daemon @daemon = daemon
logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL
@server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger @server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger
@spec_dir = File.join @gemdir, "specifications" @spec_dir = File.join @gem_dir, 'specifications'
unless File.directory? @spec_dir then
raise ArgumentError, "#{@gem_dir} does not appear to be a gem repository"
end
@source_index = Gem::SourceIndex.from_gems_in @spec_dir @source_index = Gem::SourceIndex.from_gems_in @spec_dir
end end
def Marshal(req, res)
@source_index.refresh!
res['date'] = File.stat(@spec_dir).mtime
index = Marshal.dump @source_index
if req.request_method == 'HEAD' then
res['content-length'] = index.length
return
end
if req.path =~ /Z$/ then
res['content-type'] = 'application/x-deflate'
index = Gem.deflate index
else
res['content-type'] = 'application/octet-stream'
end
res.body << index
end
def latest_specs(req, res)
@source_index.refresh!
res['content-type'] = 'application/x-gzip'
res['date'] = File.stat(@spec_dir).mtime
specs = @source_index.latest_specs.sort.map do |spec|
platform = spec.original_platform
platform = Gem::Platform::RUBY if platform.nil?
[spec.name, spec.version, platform]
end
specs = Marshal.dump specs
if req.path =~ /\.gz$/ then
specs = Gem.gzip specs
res['content-type'] = 'application/x-gzip'
else
res['content-type'] = 'application/octet-stream'
end
if req.request_method == 'HEAD' then
res['content-length'] = specs.length
else
res.body << specs
end
end
def quick(req, res) def quick(req, res)
@source_index.refresh!
res['content-type'] = 'text/plain' res['content-type'] = 'text/plain'
res['date'] = File.stat(@spec_dir).mtime res['date'] = File.stat(@spec_dir).mtime
case req.request_uri.request_uri case req.request_uri.path
when '/quick/index' then when '/quick/index' then
res.body << @source_index.map { |name,_| name }.join("\n") res.body << @source_index.map { |name,| name }.sort.join("\n")
when '/quick/index.rz' then when '/quick/index.rz' then
index = @source_index.map { |name,_| name }.join("\n") index = @source_index.map { |name,| name }.sort.join("\n")
res.body << Zlib::Deflate.deflate(index) res['content-type'] = 'application/x-deflate'
res.body << Gem.deflate(index)
when '/quick/latest_index' then
index = @source_index.latest_specs.map { |spec| spec.full_name }
res.body << index.sort.join("\n")
when '/quick/latest_index.rz' then
index = @source_index.latest_specs.map { |spec| spec.full_name }
res['content-type'] = 'application/x-deflate'
res.body << Gem.deflate(index.sort.join("\n"))
when %r|^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)-([0-9.]+)(-.*?)?\.gemspec\.rz$| then when %r|^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)-([0-9.]+)(-.*?)?\.gemspec\.rz$| then
dep = Gem::Dependency.new $2, $3 dep = Gem::Dependency.new $2, $3
specs = @source_index.search dep specs = @source_index.search dep
marshal_format = $1
selector = [$2, $3, $4].map { |s| s.inspect }.join ' ' selector = [$2, $3, $4].map { |s| s.inspect }.join ' '
@ -368,17 +441,98 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
elsif specs.length > 1 then elsif specs.length > 1 then
res.status = 500 res.status = 500
res.body = "Multiple gems found matching #{selector}" res.body = "Multiple gems found matching #{selector}"
elsif $1 then # marshal quickindex instead of YAML elsif marshal_format then
res.body << Zlib::Deflate.deflate(Marshal.dump(specs.first)) res['content-type'] = 'application/x-deflate'
res.body << Gem.deflate(Marshal.dump(specs.first))
else # deprecated YAML format else # deprecated YAML format
res.body << Zlib::Deflate.deflate(specs.first.to_yaml) res['content-type'] = 'application/x-deflate'
res.body << Gem.deflate(specs.first.to_yaml)
end end
else else
res.status = 404 raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
res.body = "#{req.request_uri} not found"
end end
end end
def root(req, res)
@source_index.refresh!
res['date'] = File.stat(@spec_dir).mtime
raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." unless
req.path == '/'
specs = []
total_file_count = 0
@source_index.each do |path, spec|
total_file_count += spec.files.size
deps = spec.dependencies.map do |dep|
{ "name" => dep.name,
"type" => dep.type,
"version" => dep.version_requirements.to_s, }
end
deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] }
deps.last["is_last"] = true unless deps.empty?
# executables
executables = spec.executables.sort.collect { |exec| {"executable" => exec} }
executables = nil if executables.empty?
executables.last["is_last"] = true if executables
specs << {
"authors" => spec.authors.sort.join(", "),
"date" => spec.date.to_s,
"dependencies" => deps,
"doc_path" => "/doc_root/#{spec.full_name}/rdoc/index.html",
"executables" => executables,
"only_one_executable" => (executables && executables.size == 1),
"full_name" => spec.full_name,
"has_deps" => !deps.empty?,
"homepage" => spec.homepage,
"name" => spec.name,
"rdoc_installed" => Gem::DocManager.new(spec).rdoc_installed?,
"summary" => spec.summary,
"version" => spec.version.to_s,
}
end
specs << {
"authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others",
"dependencies" => [],
"doc_path" => "/doc_root/rubygems-#{Gem::RubyGemsVersion}/rdoc/index.html",
"executables" => [{"executable" => 'gem', "is_last" => true}],
"only_one_executable" => true,
"full_name" => "rubygems-#{Gem::RubyGemsVersion}",
"has_deps" => false,
"homepage" => "http://rubygems.org/",
"name" => 'rubygems',
"rdoc_installed" => true,
"summary" => "RubyGems itself",
"version" => Gem::RubyGemsVersion,
}
specs = specs.sort_by { |spec| [spec["name"].downcase, spec["version"]] }
specs.last["is_last"] = true
# tag all specs with first_name_entry
last_spec = nil
specs.each do |spec|
is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase)
spec["first_name_entry"] = is_first
last_spec = spec
end
# create page from template
template = ERB.new(DOC_TEMPLATE)
res['content-type'] = 'text/html'
values = { "gem_count" => specs.size.to_s, "specs" => specs,
"total_file_count" => total_file_count.to_s }
result = template.result binding
res.body = result
end
def run def run
@server.listen nil, @port @server.listen nil, @port
@ -386,27 +540,21 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
WEBrick::Daemon.start if @daemon WEBrick::Daemon.start if @daemon
@server.mount_proc("/yaml") do |req, res| @server.mount_proc "/yaml", method(:yaml)
res['content-type'] = 'text/plain' @server.mount_proc "/yaml.Z", method(:yaml)
res['date'] = File.stat(@spec_dir).mtime
if req.request_method == 'HEAD' then
res['content-length'] = @source_index.to_yaml.length
else
res.body << @source_index.to_yaml
end
end
@server.mount_proc("/Marshal") do |req, res| @server.mount_proc "/Marshal.#{Gem.marshal_version}", method(:Marshal)
res['content-type'] = 'text/plain' @server.mount_proc "/Marshal.#{Gem.marshal_version}.Z", method(:Marshal)
res['date'] = File.stat(@spec_dir).mtime
if req.request_method == 'HEAD' then
res['content-length'] = Marshal.dump(@source_index).length
else
res.body << Marshal.dump(@source_index)
end
end
@server.mount_proc("/quick/", &method(:quick)) @server.mount_proc "/specs.#{Gem.marshal_version}", method(:specs)
@server.mount_proc "/specs.#{Gem.marshal_version}.gz", method(:specs)
@server.mount_proc "/latest_specs.#{Gem.marshal_version}",
method(:latest_specs)
@server.mount_proc "/latest_specs.#{Gem.marshal_version}.gz",
method(:latest_specs)
@server.mount_proc "/quick/", method(:quick)
@server.mount_proc("/gem-server-rdoc-style.css") do |req, res| @server.mount_proc("/gem-server-rdoc-style.css") do |req, res|
res['content-type'] = 'text/css' res['content-type'] = 'text/css'
@ -414,80 +562,12 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
res.body << RDOC_CSS res.body << RDOC_CSS
end end
@server.mount_proc("/") do |req, res| @server.mount_proc "/", method(:root)
specs = []
total_file_count = 0
@source_index.each do |path, spec|
total_file_count += spec.files.size
deps = spec.dependencies.collect { |dep|
{ "name" => dep.name,
"version" => dep.version_requirements.to_s, }
}
deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] }
deps.last["is_last"] = true unless deps.empty?
# executables
executables = spec.executables.sort.collect { |exec| {"executable" => exec} }
executables = nil if executables.empty?
executables.last["is_last"] = true if executables
specs << {
"authors" => spec.authors.sort.join(", "),
"date" => spec.date.to_s,
"dependencies" => deps,
"doc_path" => ('/doc_root/' + spec.full_name + '/rdoc/index.html'),
"executables" => executables,
"only_one_executable" => (executables && executables.size==1),
"full_name" => spec.full_name,
"has_deps" => !deps.empty?,
"homepage" => spec.homepage,
"name" => spec.name,
"rdoc_installed" => Gem::DocManager.new(spec).rdoc_installed?,
"summary" => spec.summary,
"version" => spec.version.to_s,
}
end
specs << {
"authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others",
"dependencies" => [],
"doc_path" => "/doc_root/rubygems-#{Gem::RubyGemsVersion}/rdoc/index.html",
"executables" => [{"executable" => 'gem', "is_last" => true}],
"only_one_executable" => true,
"full_name" => "rubygems-#{Gem::RubyGemsVersion}",
"has_deps" => false,
"homepage" => "http://rubygems.org/",
"name" => 'rubygems',
"rdoc_installed" => true,
"summary" => "RubyGems itself",
"version" => Gem::RubyGemsVersion,
}
specs = specs.sort_by { |spec| [spec["name"].downcase, spec["version"]] }
specs.last["is_last"] = true
# tag all specs with first_name_entry
last_spec = nil
specs.each do |spec|
is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase)
spec["first_name_entry"] = is_first
last_spec = spec
end
# create page from template
template = ERB.new(DOC_TEMPLATE)
res['content-type'] = 'text/html'
values = { "gem_count" => specs.size.to_s, "specs" => specs,
"total_file_count" => total_file_count.to_s }
result = template.result binding
res.body = result
end
paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" } paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" }
paths.each do |mount_point, mount_dir| paths.each do |mount_point, mount_dir|
@server.mount(mount_point, WEBrick::HTTPServlet::FileHandler, @server.mount(mount_point, WEBrick::HTTPServlet::FileHandler,
File.join(@gemdir, mount_dir), true) File.join(@gem_dir, mount_dir), true)
end end
trap("INT") { @server.shutdown; exit! } trap("INT") { @server.shutdown; exit! }
@ -496,5 +576,54 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
@server.start @server.start
end end
def specs(req, res)
@source_index.refresh!
res['date'] = File.stat(@spec_dir).mtime
specs = @source_index.sort.map do |_, spec|
platform = spec.original_platform
platform = Gem::Platform::RUBY if platform.nil?
[spec.name, spec.version, platform]
end
specs = Marshal.dump specs
if req.path =~ /\.gz$/ then
specs = Gem.gzip specs
res['content-type'] = 'application/x-gzip'
else
res['content-type'] = 'application/octet-stream'
end
if req.request_method == 'HEAD' then
res['content-length'] = specs.length
else
res.body << specs
end
end
def yaml(req, res)
@source_index.refresh!
res['date'] = File.stat(@spec_dir).mtime
index = @source_index.to_yaml
if req.path =~ /Z$/ then
res['content-type'] = 'application/x-deflate'
index = Gem.deflate index
else
res['content-type'] = 'text/plain'
end
if req.request_method == 'HEAD' then
res['content-length'] = index.length
return
end
res.body << index
end
end end

View File

@ -7,6 +7,7 @@
require 'rubygems' require 'rubygems'
require 'rubygems/user_interaction' require 'rubygems/user_interaction'
require 'rubygems/specification' require 'rubygems/specification'
require 'rubygems/spec_fetcher'
## ##
# The SourceIndex object indexes all the gems available from a # The SourceIndex object indexes all the gems available from a
@ -27,6 +28,11 @@ class Gem::SourceIndex
attr_reader :gems # :nodoc: attr_reader :gems # :nodoc:
##
# Directories to use to refresh this SourceIndex when calling refresh!
attr_accessor :spec_dirs
class << self class << self
include Gem::UserInteraction include Gem::UserInteraction
@ -39,7 +45,7 @@ class Gem::SourceIndex
# +from_gems_in+. This argument is deprecated and is provided # +from_gems_in+. This argument is deprecated and is provided
# just for backwards compatibility, and should not generally # just for backwards compatibility, and should not generally
# be used. # be used.
# #
# return:: # return::
# SourceIndex instance # SourceIndex instance
@ -63,7 +69,9 @@ class Gem::SourceIndex
# +spec_dirs+. # +spec_dirs+.
def from_gems_in(*spec_dirs) def from_gems_in(*spec_dirs)
self.new.load_gems_in(*spec_dirs) source_index = new
source_index.spec_dirs = spec_dirs
source_index.refresh!
end end
## ##
@ -79,6 +87,8 @@ class Gem::SourceIndex
return gemspec return gemspec
end end
alert_warning "File '#{file_name}' does not evaluate to a gem specification" alert_warning "File '#{file_name}' does not evaluate to a gem specification"
rescue SignalException, SystemExit
raise
rescue SyntaxError => e rescue SyntaxError => e
alert_warning e alert_warning e
alert_warning spec_code alert_warning spec_code
@ -100,6 +110,7 @@ class Gem::SourceIndex
def initialize(specifications={}) def initialize(specifications={})
@gems = specifications @gems = specifications
@spec_dirs = nil
end end
## ##
@ -121,8 +132,8 @@ class Gem::SourceIndex
end end
## ##
# Returns a Hash of name => Specification of the latest versions of each # Returns an Array specifications for the latest versions of each gem in
# gem in this index. # this index.
def latest_specs def latest_specs
result = Hash.new { |h,k| h[k] = [] } result = Hash.new { |h,k| h[k] = [] }
@ -241,7 +252,9 @@ class Gem::SourceIndex
when Gem::Dependency then when Gem::Dependency then
only_platform = platform_only only_platform = platform_only
version_requirement = gem_pattern.version_requirements version_requirement = gem_pattern.version_requirements
gem_pattern = if gem_pattern.name.empty? then gem_pattern = if Regexp === gem_pattern.name then
gem_pattern.name
elsif gem_pattern.name.empty? then
// //
else else
/^#{Regexp.escape gem_pattern.name}$/ /^#{Regexp.escape gem_pattern.name}$/
@ -271,29 +284,43 @@ class Gem::SourceIndex
## ##
# Replaces the gems in the source index from specifications in the # Replaces the gems in the source index from specifications in the
# installed_spec_directories, # directories this source index was created from. Raises an exception if
# this source index wasn't created from a directory (via from_gems_in or
# from_installed_gems, or having spec_dirs set).
def refresh! def refresh!
load_gems_in(*self.class.installed_spec_directories) raise 'source index not created from disk' if @spec_dirs.nil?
load_gems_in(*@spec_dirs)
end end
## ##
# Returns an Array of Gem::Specifications that are not up to date. # Returns an Array of Gem::Specifications that are not up to date.
def outdated def outdated
dep = Gem::Dependency.new '', Gem::Requirement.default
remotes = Gem::SourceInfoCache.search dep, true
outdateds = [] outdateds = []
latest_specs.each do |local| latest_specs.each do |local|
name = local.name name = local.name
remote = remotes.select { |spec| spec.name == name }.
sort_by { |spec| spec.version.to_ints }.
last
outdateds << name if remote and local.version < remote.version dependency = Gem::Dependency.new name, ">= #{local.version}"
begin
fetcher = Gem::SpecFetcher.fetcher
remotes = fetcher.find_matching dependency
remotes = remotes.map { |(name, version,),| version }
rescue Gem::RemoteFetcher::FetchError => e
raise unless fetcher.warn_legacy e do
require 'rubygems/source_info_cache'
specs = Gem::SourceInfoCache.search_with_source dependency, true
remotes = specs.map { |spec,| spec.version }
end
end
latest = remotes.sort.last
outdateds << name if latest and local.version < latest
end end
outdateds outdateds
@ -387,7 +414,8 @@ class Gem::SourceIndex
end end
def fetch_bulk_index(source_uri) def fetch_bulk_index(source_uri)
say "Bulk updating Gem source index for: #{source_uri}" say "Bulk updating Gem source index for: #{source_uri}" if
Gem.configuration.verbose
index = fetch_index_from(source_uri) index = fetch_index_from(source_uri)
if index.nil? then if index.nil? then
@ -447,7 +475,7 @@ class Gem::SourceIndex
def unzip(string) def unzip(string)
require 'zlib' require 'zlib'
Zlib::Inflate.inflate(string) Gem.inflate string
end end
## ##

View File

@ -0,0 +1,251 @@
require 'zlib'
require 'rubygems'
require 'rubygems/remote_fetcher'
require 'rubygems/user_interaction'
##
# SpecFetcher handles metadata updates from remote gem repositories.
class Gem::SpecFetcher
include Gem::UserInteraction
##
# The SpecFetcher cache dir.
attr_reader :dir # :nodoc:
##
# Cache of latest specs
attr_reader :latest_specs # :nodoc:
##
# Cache of all spces
attr_reader :specs # :nodoc:
@fetcher = nil
def self.fetcher
@fetcher ||= new
end
def self.fetcher=(fetcher) # :nodoc:
@fetcher = fetcher
end
def initialize
@dir = File.join Gem.user_home, '.gem', 'specs'
@update_cache = File.stat(Gem.user_home).uid == Process.uid
@specs = {}
@latest_specs = {}
@fetcher = Gem::RemoteFetcher.fetcher
end
##
# Retuns the local directory to write +uri+ to.
def cache_dir(uri)
File.join @dir, "#{uri.host}%#{uri.port}", File.dirname(uri.path)
end
##
# Fetch specs matching +dependency+. If +all+ is true, all matching
# versions are returned. If +matching_platform+ is false, all platforms are
# returned.
def fetch(dependency, all = false, matching_platform = true)
specs_and_sources = find_matching dependency, all, matching_platform
specs_and_sources.map do |spec_tuple, source_uri|
[fetch_spec(spec_tuple, URI.parse(source_uri)), source_uri]
end
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)
end
end
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
##
# Find spec names that match +dependency+. If +all+ is true, all matching
# versions are returned. If +matching_platform+ is false, gems for all
# platforms are returned.
def find_matching(dependency, all = false, matching_platform = true)
found = {}
list(all).each do |source_uri, specs|
found[source_uri] = specs.select do |spec_name, version, spec_platform|
dependency =~ Gem::Dependency.new(spec_name, version) and
(not matching_platform or Gem::Platform.match(spec_platform))
end
end
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
end
##
# Returns Array of gem repositories that were generated with RubyGems less
# than 1.2.
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
##
# Returns a list of gems available for each source in Gem::sources. If
# +all+ is true, all versions are returned instead of only latest versions.
def list(all = false)
list = {}
file = all ? 'specs' : 'latest_specs'
Gem.sources.each do |source_uri|
source_uri = URI.parse source_uri
if all and @specs.include? source_uri then
list[source_uri] = @specs[source_uri]
elsif @latest_specs.include? source_uri then
list[source_uri] = @latest_specs[source_uri]
else
specs = load_specs source_uri, file
cache = all ? @specs : @latest_specs
cache[source_uri] = specs
list[source_uri] = specs
end
end
list
end
def load_specs(source_uri, file)
file_name = "#{file}.#{Gem.marshal_version}.gz"
spec_path = source_uri + file_name
cache_dir = cache_dir spec_path
local_file = File.join(cache_dir, file_name).chomp '.gz'
if File.exist? local_file then
local_size = File.stat(local_file).size
remote_file = spec_path.dup
remote_file.path = remote_file.path.chomp '.gz'
remote_size = @fetcher.fetch_size remote_file
spec_dump = Gem.read_binary local_file if remote_size == local_size
end
unless spec_dump then
loaded = true
spec_dump_gz = @fetcher.fetch_path spec_path
spec_dump = Gem.gunzip spec_dump_gz
end
specs = Marshal.load spec_dump
if loaded and @update_cache then
begin
FileUtils.mkdir_p cache_dir
open local_file, 'wb' do |io|
Marshal.dump specs, io
end
rescue
end
end
specs
end
##
# 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.
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
end

View File

@ -6,6 +6,7 @@
require 'rubygems' require 'rubygems'
require 'rubygems/version' require 'rubygems/version'
require 'rubygems/requirement'
require 'rubygems/platform' require 'rubygems/platform'
# :stopdoc: # :stopdoc:
@ -16,6 +17,9 @@ if RUBY_VERSION < '1.9' then
t - ((t.to_f + t.gmt_offset) % 86400) t - ((t.to_f + t.gmt_offset) % 86400)
end unless defined? Time.today end unless defined? Time.today
end end
class Date; end # for ruby_code if date.rb wasn't required
# :startdoc: # :startdoc:
module Gem module Gem
@ -37,22 +41,32 @@ module Gem
# #
class Specification class Specification
##
# Allows deinstallation of gems with legacy platforms. # Allows deinstallation of gems with legacy platforms.
attr_accessor :original_platform # :nodoc: attr_accessor :original_platform # :nodoc:
# ------------------------- Specification version constants. # ------------------------- Specification version constants.
##
# The the version number of a specification that does not specify one # The the version number of a specification that does not specify one
# (i.e. RubyGems 0.7 or earlier). # (i.e. RubyGems 0.7 or earlier).
NONEXISTENT_SPECIFICATION_VERSION = -1 NONEXISTENT_SPECIFICATION_VERSION = -1
##
# The specification version applied to any new Specification instances # The specification version applied to any new Specification instances
# created. This should be bumped whenever something in the spec format # created. This should be bumped whenever something in the spec format
# changes. # changes.
CURRENT_SPECIFICATION_VERSION = 2 #--
# When updating this number, be sure to also update #to_ruby.
CURRENT_SPECIFICATION_VERSION = 3
##
# An informal list of changes to the specification. The highest-valued # An informal list of changes to the specification. The highest-valued
# key should be equal to the CURRENT_SPECIFICATION_VERSION. # key should be equal to the CURRENT_SPECIFICATION_VERSION.
SPECIFICATION_VERSION_HISTORY = { SPECIFICATION_VERSION_HISTORY = {
-1 => ['(RubyGems versions up to and including 0.7 did not have versioned specifications)'], -1 => ['(RubyGems versions up to and including 0.7 did not have versioned specifications)'],
1 => [ 1 => [
@ -63,10 +77,13 @@ module Gem
'Added "required_rubygems_version"', 'Added "required_rubygems_version"',
'Now forward-compatible with future versions', 'Now forward-compatible with future versions',
], ],
3 => [
'Added dependency types',
],
} }
# :stopdoc: # :stopdoc:
MARSHAL_FIELDS = { -1 => 16, 1 => 16, 2 => 16 } MARSHAL_FIELDS = { -1 => 16, 1 => 16, 2 => 16, 3 => 16 }
now = Time.at(Time.now.to_i) now = Time.at(Time.now.to_i)
TODAY = now - ((now.to_i + now.gmt_offset) % 86400) TODAY = now - ((now.to_i + now.gmt_offset) % 86400)
@ -335,6 +352,14 @@ module Gem
read_only :dependencies read_only :dependencies
def runtime_dependencies
dependencies.select { |d| d.type == :runtime || d.type == nil }
end
def development_dependencies
dependencies.select { |d| d.type == :development }
end
# ALIASED gemspec attributes ------------------------------------- # ALIASED gemspec attributes -------------------------------------
attribute_alias_singular :executable, :executables attribute_alias_singular :executable, :executables
@ -629,27 +654,31 @@ module Gem
end end
end end
# Adds a dependency to this Gem. For example, # Adds a development dependency to this Gem. For example,
# #
# spec.add_dependency('jabber4r', '> 0.1', '<= 0.5') # spec.add_development_dependency('jabber4r', '> 0.1', '<= 0.5')
#
# Development dependencies aren't installed by default, and
# aren't activated when a gem is required.
# #
# gem:: [String or Gem::Dependency] The Gem name/dependency. # gem:: [String or Gem::Dependency] The Gem name/dependency.
# requirements:: [default=">= 0"] The version requirements. # requirements:: [default=">= 0"] The version requirements.
# def add_development_dependency(gem, *requirements)
def add_dependency(gem, *requirements) add_dependency_with_type(gem, :development, *requirements)
requirements = if requirements.empty? then
Gem::Requirement.default
else
requirements.flatten
end
unless gem.respond_to?(:name) && gem.respond_to?(:version_requirements)
gem = Dependency.new(gem, requirements)
end
dependencies << gem
end end
# Adds a runtime dependency to this Gem. For example,
#
# spec.add_runtime_dependency('jabber4r', '> 0.1', '<= 0.5')
#
# gem:: [String or Gem::Dependency] The Gem name/dependency.
# requirements:: [default=">= 0"] The version requirements.
def add_runtime_dependency(gem, *requirements)
add_dependency_with_type(gem, :runtime, *requirements)
end
alias add_dependency add_runtime_dependency
# Returns the full name (name-version) of this Gem. Platform information # Returns the full name (name-version) of this Gem. Platform information
# is included (name-version-platform) if it is specified (and not the # is included (name-version-platform) if it is specified (and not the
# default Ruby platform). # default Ruby platform).
@ -673,30 +702,31 @@ module Gem
end end
end end
##
# The full path to the gem (install path + full name). # The full path to the gem (install path + full name).
#
# return:: [String] the full gem path
#
def full_gem_path def full_gem_path
path = File.join installation_path, 'gems', full_name path = File.join installation_path, 'gems', full_name
return path if File.directory? path return path if File.directory? path
File.join installation_path, 'gems', original_name File.join installation_path, 'gems', original_name
end end
##
# The default (generated) file name of the gem. # The default (generated) file name of the gem.
def file_name def file_name
full_name + ".gem" full_name + ".gem"
end end
# The root directory that the gem was installed into. ##
# # The directory that this gem was installed into.
# return:: [String] the installation path
#
def installation_path def installation_path
(File.dirname(@loaded_from).split(File::SEPARATOR)[0..-2]). path = File.dirname(@loaded_from).split(File::SEPARATOR)[0..-2]
join(File::SEPARATOR) path = path.join File::SEPARATOR
File.expand_path path
end end
# Checks if this Specification meets the requirement of the supplied # Checks if this Specification meets the requirement of the supplied
# dependency. # dependency.
# #
@ -778,9 +808,11 @@ module Gem
self.platform = Gem::Platform.new @platform self.platform = Gem::Platform.new @platform
end end
##
# Returns a Ruby code representation of this specification, such that it # Returns a Ruby code representation of this specification, such that it
# can be eval'ed and reconstruct the same specification later. Attributes # can be eval'ed and reconstruct the same specification later. Attributes
# that still have their default values are omitted. # that still have their default values are omitted.
def to_ruby def to_ruby
mark_version mark_version
result = [] result = []
@ -792,8 +824,6 @@ module Gem
result << " s.platform = #{ruby_code original_platform}" result << " s.platform = #{ruby_code original_platform}"
end end
result << "" result << ""
result << " s.specification_version = #{specification_version} if s.respond_to? :specification_version="
result << ""
result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version=" result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version="
handled = [ handled = [
@ -816,15 +846,42 @@ module Gem
end end
end end
result << "" unless dependencies.empty? result << nil
result << " if s.respond_to? :specification_version then"
result << " current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION"
result << " s.specification_version = #{specification_version}"
result << nil
dependencies.each do |dep| result << " if current_version >= 3 then"
version_reqs_param = dep.requirements_list.inspect
result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})" unless dependencies.empty? then
dependencies.each do |dep|
version_reqs_param = dep.requirements_list.inspect
dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK
result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>, #{version_reqs_param})"
end
end end
result << " else"
unless dependencies.empty? then
dependencies.each do |dep|
version_reqs_param = dep.requirements_list.inspect
result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})"
end
end
result << ' end'
result << " else"
dependencies.each do |dep|
version_reqs_param = dep.requirements_list.inspect
result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})"
end
result << " end"
result << "end" result << "end"
result << "" result << nil
result.join "\n" result.join "\n"
end end
@ -940,6 +997,22 @@ module Gem
private private
def add_dependency_with_type(dependency, type, *requirements)
requirements = if requirements.empty? then
Gem::Requirement.default
else
requirements.flatten
end
unless dependency.respond_to?(:name) &&
dependency.respond_to?(:version_requirements)
dependency = Dependency.new(dependency, requirements, type)
end
dependencies << dependency
end
def find_all_satisfiers(dep) def find_all_satisfiers(dep)
Gem.source_index.each do |name,gem| Gem.source_index.each do |name,gem|
if(gem.satisfies_requirement?(dep)) then if(gem.satisfies_requirement?(dep)) then

View File

@ -0,0 +1,120 @@
require 'tempfile'
require 'rubygems'
require 'rubygems/remote_fetcher'
##
# A fake Gem::RemoteFetcher for use in tests or to avoid real live HTTP
# requests when testing code that uses RubyGems.
#
# Example:
#
# @fetcher = Gem::FakeFetcher.new
# @fetcher.data['http://gems.example.com/yaml'] = source_index.to_yaml
# Gem::RemoteFetcher.fetcher = @fetcher
#
# # invoke RubyGems code
#
# paths = @fetcher.paths
# assert_equal 'http://gems.example.com/yaml', paths.shift
# assert paths.empty?, paths.join(', ')
#
# See RubyGems' tests for more examples of FakeFetcher.
class Gem::FakeFetcher
attr_reader :data
attr_accessor :paths
def initialize
@data = {}
@paths = []
end
def fetch_path(path)
path = path.to_s
@paths << path
raise ArgumentError, 'need full URI' unless path =~ %r'^http://'
data = @data[path]
if data.nil? then
raise Gem::RemoteFetcher::FetchError.new('no data', path)
end
data.respond_to?(:call) ? data.call : data
end
def fetch_size(path)
path = path.to_s
@paths << path
raise ArgumentError, 'need full URI' unless path =~ %r'^http://'
data = @data[path]
if data.nil? then
raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", nil)
end
data.respond_to?(:call) ? data.call : data.length
end
def download spec, source_uri, install_dir = Gem.dir
name = "#{spec.full_name}.gem"
path = File.join(install_dir, 'cache', name)
Gem.ensure_gem_subdirectories install_dir
if source_uri =~ /^http/ then
File.open(path, "wb") do |f|
f.write fetch_path(File.join(source_uri, "gems", name))
end
else
FileUtils.cp source_uri, path
end
path
end
end
# :stopdoc:
class Gem::RemoteFetcher
def self.fetcher=(fetcher)
@fetcher = fetcher
end
end
# :startdoc:
##
# A StringIO duck-typed class that uses Tempfile instead of String as the
# backing store.
#--
# This class was added to flush out problems in Rubinius' IO implementation.
class TempIO
@@count = 0
def initialize(string = '')
@tempfile = Tempfile.new "TempIO-#{@@count += 1}"
@tempfile.binmode
@tempfile.write string
@tempfile.rewind
end
def method_missing(meth, *args, &block)
@tempfile.send(meth, *args, &block)
end
def respond_to?(meth)
@tempfile.respond_to? meth
end
def string
@tempfile.flush
Gem.read_binary @tempfile.path
end
end

View File

@ -176,9 +176,10 @@ class Gem::Uninstaller
end end
def path_ok?(spec) def path_ok?(spec)
match_path = File.join @gem_home, 'gems', spec.full_name full_path = File.join @gem_home, 'gems', spec.full_name
original_path = File.join @gem_home, 'gems', spec.original_name
match_path == spec.full_gem_path full_path == spec.full_gem_path || original_path == spec.full_gem_path
end end
def dependencies_ok?(spec) def dependencies_ok?(spec)

View File

@ -6,54 +6,71 @@
module Gem module Gem
#################################################################### ##
# Module that defines the default UserInteraction. Any class # Module that defines the default UserInteraction. Any class including this
# including this module will have access to the +ui+ method that # module will have access to the +ui+ method that returns the default UI.
# returns the default UI.
module DefaultUserInteraction module DefaultUserInteraction
##
# The default UI is a class variable of the singleton class for this
# module.
@ui = nil
##
# Return the default UI. # Return the default UI.
def self.ui
@ui ||= Gem::ConsoleUI.new
end
##
# Set the default UI. If the default UI is never explicitly set, a simple
# console based UserInteraction will be used automatically.
def self.ui=(new_ui)
@ui = new_ui
end
##
# Use +new_ui+ for the duration of +block+.
def self.use_ui(new_ui)
old_ui = @ui
@ui = new_ui
yield
ensure
@ui = old_ui
end
##
# See DefaultUserInteraction::ui
def ui def ui
DefaultUserInteraction.ui DefaultUserInteraction.ui
end end
# Set the default UI. If the default UI is never explicitly set, a ##
# simple console based UserInteraction will be used automatically. # See DefaultUserInteraction::ui=
def ui=(new_ui) def ui=(new_ui)
DefaultUserInteraction.ui = new_ui DefaultUserInteraction.ui = new_ui
end end
##
# See DefaultUserInteraction::use_ui
def use_ui(new_ui, &block) def use_ui(new_ui, &block)
DefaultUserInteraction.use_ui(new_ui, &block) DefaultUserInteraction.use_ui(new_ui, &block)
end end
# The default UI is a class variable of the singleton class for
# this module.
@ui = nil
class << self
def ui
@ui ||= Gem::ConsoleUI.new
end
def ui=(new_ui)
@ui = new_ui
end
def use_ui(new_ui)
old_ui = @ui
@ui = new_ui
yield
ensure
@ui = old_ui
end
end
end end
#################################################################### ##
# Make the default UI accessable without the "ui." prefix. Classes # Make the default UI accessable without the "ui." prefix. Classes
# including this module may use the interaction methods on the # including this module may use the interaction methods on the default UI
# default UI directly. Classes may also reference the +ui+ and # directly. Classes may also reference the ui and ui= methods.
# <tt>ui=</tt> methods.
# #
# Example: # Example:
# #
@ -64,22 +81,30 @@ module Gem
# n = ask("What is the meaning of life?") # n = ask("What is the meaning of life?")
# end # end
# end # end
module UserInteraction module UserInteraction
include DefaultUserInteraction include DefaultUserInteraction
[
:choose_from_list, :ask, :ask_yes_no, :say, :alert, :alert_warning, [:alert,
:alert_error, :terminate_interaction :alert_error,
].each do |methname| :alert_warning,
:ask,
:ask_yes_no,
:choose_from_list,
:say,
:terminate_interaction ].each do |methname|
class_eval %{ class_eval %{
def #{methname}(*args) def #{methname}(*args)
ui.#{methname}(*args) ui.#{methname}(*args)
end end
} }, __FILE__, __LINE__
end end
end end
#################################################################### ##
# StreamUI implements a simple stream based user interface. # StreamUI implements a simple stream based user interface.
class StreamUI class StreamUI
attr_reader :ins, :outs, :errs attr_reader :ins, :outs, :errs
@ -89,15 +114,19 @@ module Gem
@outs = out_stream @outs = out_stream
@errs = err_stream @errs = err_stream
end end
# Choose from a list of options. +question+ is a prompt displayed ##
# above the list. +list+ is a list of option strings. Returns # Choose from a list of options. +question+ is a prompt displayed above
# the pair [option_name, option_index]. # the list. +list+ is a list of option strings. Returns the pair
# [option_name, option_index].
def choose_from_list(question, list) def choose_from_list(question, list)
@outs.puts question @outs.puts question
list.each_with_index do |item, index| list.each_with_index do |item, index|
@outs.puts " #{index+1}. #{item}" @outs.puts " #{index+1}. #{item}"
end end
@outs.print "> " @outs.print "> "
@outs.flush @outs.flush
@ -109,28 +138,32 @@ module Gem
return list[result], result return list[result], result
end end
# Ask a question. Returns a true for yes, false for no. If not ##
# connected to a tty, raises an exception if default is nil, # Ask a question. Returns a true for yes, false for no. If not connected
# otherwise returns default. # to a tty, raises an exception if default is nil, otherwise returns
# default.
def ask_yes_no(question, default=nil) def ask_yes_no(question, default=nil)
if not @ins.tty? then unless @ins.tty? then
if default.nil? then if default.nil? then
raise( raise Gem::OperationNotSupportedError,
Gem::OperationNotSupportedError, "Not connected to a tty and no default specified"
"Not connected to a tty and no default specified")
else else
return default return default
end end
end end
qstr = case default qstr = case default
when nil when nil
'yn' 'yn'
when true when true
'Yn' 'Yn'
else else
'yN' 'yN'
end end
result = nil result = nil
while result.nil? while result.nil?
result = ask("#{question} [#{qstr}]") result = ask("#{question} [#{qstr}]")
result = case result result = case result
@ -144,51 +177,68 @@ module Gem
nil nil
end end
end end
return result return result
end end
# Ask a question. Returns an answer if connected to a tty, nil ##
# otherwise. # Ask a question. Returns an answer if connected to a tty, nil otherwise.
def ask(question) def ask(question)
return nil if not @ins.tty? return nil if not @ins.tty?
@outs.print(question + " ") @outs.print(question + " ")
@outs.flush @outs.flush
result = @ins.gets result = @ins.gets
result.chomp! if result result.chomp! if result
result result
end end
##
# Display a statement. # Display a statement.
def say(statement="") def say(statement="")
@outs.puts statement @outs.puts statement
end end
# Display an informational alert. ##
# Display an informational alert. Will ask +question+ if it is not nil.
def alert(statement, question=nil) def alert(statement, question=nil)
@outs.puts "INFO: #{statement}" @outs.puts "INFO: #{statement}"
return ask(question) if question ask(question) if question
end end
# Display a warning in a location expected to get error messages. ##
# Display a warning in a location expected to get error messages. Will
# ask +question+ if it is not nil.
def alert_warning(statement, question=nil) def alert_warning(statement, question=nil)
@errs.puts "WARNING: #{statement}" @errs.puts "WARNING: #{statement}"
ask(question) if question ask(question) if question
end end
# Display an error message in a location expected to get error ##
# messages. # Display an error message in a location expected to get error messages.
# Will ask +question+ if it is not nil.
def alert_error(statement, question=nil) def alert_error(statement, question=nil)
@errs.puts "ERROR: #{statement}" @errs.puts "ERROR: #{statement}"
ask(question) if question ask(question) if question
end end
# Terminate the application normally, running any exit handlers ##
# that might have been defined. # Terminate the application with exit code +status+, running any exit
# handlers that might have been defined.
def terminate_interaction(status = 0) def terminate_interaction(status = 0)
raise Gem::SystemExitException, status raise Gem::SystemExitException, status
end end
# Return a progress reporter object ##
# Return a progress reporter object chosen from the current verbosity.
def progress_reporter(*args) def progress_reporter(*args)
case Gem.configuration.verbose case Gem.configuration.verbose
when nil, false when nil, false
@ -200,6 +250,9 @@ module Gem
end end
end end
##
# An absolutely silent progress reporter.
class SilentProgressReporter class SilentProgressReporter
attr_reader :count attr_reader :count
@ -213,6 +266,9 @@ module Gem
end end
end end
##
# A basic dotted progress reporter.
class SimpleProgressReporter class SimpleProgressReporter
include DefaultUserInteraction include DefaultUserInteraction
@ -228,17 +284,27 @@ module Gem
@out.puts initial_message @out.puts initial_message
end end
##
# Prints out a dot and ignores +message+.
def updated(message) def updated(message)
@count += 1 @count += 1
@out.print "." @out.print "."
@out.flush @out.flush
end end
##
# Prints out the terminal message.
def done def done
@out.puts "\n#{@terminal_message}" @out.puts "\n#{@terminal_message}"
end end
end end
##
# A progress reporter that prints out messages about the current progress.
class VerboseProgressReporter class VerboseProgressReporter
include DefaultUserInteraction include DefaultUserInteraction
@ -254,32 +320,41 @@ module Gem
@out.puts initial_message @out.puts initial_message
end end
##
# Prints out the position relative to the total and the +message+.
def updated(message) def updated(message)
@count += 1 @count += 1
@out.puts "#{@count}/#{@total}: #{message}" @out.puts "#{@count}/#{@total}: #{message}"
end end
##
# Prints out the terminal message.
def done def done
@out.puts @terminal_message @out.puts @terminal_message
end end
end end
end end
#################################################################### ##
# Subclass of StreamUI that instantiates the user interaction using # Subclass of StreamUI that instantiates the user interaction using STDIN,
# standard in, out and error. # STDOUT, and STDERR.
class ConsoleUI < StreamUI class ConsoleUI < StreamUI
def initialize def initialize
super(STDIN, STDOUT, STDERR) super(STDIN, STDOUT, STDERR)
end end
end end
#################################################################### ##
# SilentUI is a UI choice that is absolutely silent. # SilentUI is a UI choice that is absolutely silent.
class SilentUI class SilentUI
def method_missing(sym, *args, &block) def method_missing(sym, *args, &block)
self self
end end
end end
end end

View File

@ -8,6 +8,7 @@ require 'rubygems'
## ##
# The Version class processes string versions into comparable values # The Version class processes string versions into comparable values
class Gem::Version class Gem::Version
include Comparable include Comparable
@ -17,11 +18,8 @@ class Gem::Version
attr_reader :version attr_reader :version
## ##
# Checks if version string is valid format # Returns true if +version+ is a valid version string.
#
# str:: [String] the version string
# return:: [Boolean] true if the string format is correct, otherwise false
#
def self.correct?(version) def self.correct?(version)
case version case version
when Integer, /\A\s*(\d+(\.\d+)*)*\s*\z/ then true when Integer, /\A\s*(\d+(\.\d+)*)*\s*\z/ then true
@ -36,7 +34,7 @@ class Gem::Version
# ver1 = Version.create('1.3.17') # -> (Version object) # ver1 = Version.create('1.3.17') # -> (Version object)
# ver2 = Version.create(ver1) # -> (ver1) # ver2 = Version.create(ver1) # -> (ver1)
# ver3 = Version.create(nil) # -> nil # ver3 = Version.create(nil) # -> nil
#
def self.create(input) def self.create(input)
if input.respond_to? :version then if input.respond_to? :version then
input input
@ -48,10 +46,9 @@ class Gem::Version
end end
## ##
# Constructs a version from the supplied string # Constructs a Version from the +version+ string. A version string is a
# # series of digits separated by dots.
# version:: [String] The version string. Format is digit.digit...
#
def initialize(version) def initialize(version)
raise ArgumentError, "Malformed version number string #{version}" unless raise ArgumentError, "Malformed version number string #{version}" unless
self.class.correct?(version) self.class.correct?(version)
@ -73,7 +70,9 @@ class Gem::Version
self.version = array[0] self.version = array[0]
end end
##
# Strip ignored trailing zeros. # Strip ignored trailing zeros.
def normalize def normalize
@ints = build_array_from_version_string @ints = build_array_from_version_string
@ -94,10 +93,8 @@ class Gem::Version
end end
## ##
# Convert version to integer array # Returns an integer array representation of this Version.
#
# return:: [Array] list of integers
#
def to_ints def to_ints
normalize unless @ints normalize unless @ints
@ints @ints
@ -117,20 +114,25 @@ class Gem::Version
end end
## ##
# Compares two versions # Compares this version with +other+ returning -1, 0, or 1 if the other
# # version is larger, the same, or smaller than this one.
# other:: [Version or .ints] other version to compare to
# return:: [Fixnum] -1, 0, 1
#
def <=>(other) def <=>(other)
return nil unless self.class === other
return 1 unless other return 1 unless other
@ints <=> other.ints @ints <=> other.ints
end end
alias eql? == # :nodoc: ##
# A Version is only eql? to another version if it has the same version
# string. "1.0" is not the same version as "1".
def eql?(other)
self.class === other and @version == other.version
end
def hash # :nodoc: def hash # :nodoc:
to_ints.inject { |hash_code, n| hash_code + n } @version.hash
end end
# Return a new version object where the next to the last revision # Return a new version object where the next to the last revision

View File

@ -10,10 +10,9 @@ at_exit { $SAFE = 1 }
require 'fileutils' require 'fileutils'
require 'test/unit' require 'test/unit'
require 'tmpdir' require 'tmpdir'
require 'tempfile'
require 'uri' require 'uri'
require 'rubygems/source_info_cache'
require 'rubygems/package' require 'rubygems/package'
require 'rubygems/test_utilities'
require File.join(File.expand_path(File.dirname(__FILE__)), 'mockgemui') require File.join(File.expand_path(File.dirname(__FILE__)), 'mockgemui')
@ -27,54 +26,6 @@ module Gem
end end
end end
class FakeFetcher
attr_reader :data
attr_accessor :uri
attr_accessor :paths
def initialize
@data = {}
@paths = []
@uri = nil
end
def fetch_path(path)
path = path.to_s
@paths << path
raise ArgumentError, 'need full URI' unless path =~ %r'^http://'
data = @data[path]
raise Gem::RemoteFetcher::FetchError, "no data for #{path}" if data.nil?
data.respond_to?(:call) ? data.call : data
end
def fetch_size(path)
path = path.to_s
@paths << path
raise ArgumentError, 'need full URI' unless path =~ %r'^http://'
data = @data[path]
raise Gem::RemoteFetcher::FetchError, "no data for #{path}" if data.nil?
data.respond_to?(:call) ? data.call : data.length
end
def download spec, source_uri, install_dir = Gem.dir
name = "#{spec.full_name}.gem"
path = File.join(install_dir, 'cache', name)
Gem.ensure_gem_subdirectories install_dir
if source_uri =~ /^http/ then
File.open(path, "wb") do |f|
f.write fetch_path(File.join(source_uri, "gems", name))
end
else
FileUtils.cp source_uri, path
end
path
end
end
class RubyGemTestCase < Test::Unit::TestCase class RubyGemTestCase < Test::Unit::TestCase
include Gem::DefaultUserInteraction include Gem::DefaultUserInteraction
@ -94,8 +45,13 @@ class RubyGemTestCase < Test::Unit::TestCase
@gemcache = File.join(@gemhome, "source_cache") @gemcache = File.join(@gemhome, "source_cache")
@usrcache = File.join(@gemhome, ".gem", "user_cache") @usrcache = File.join(@gemhome, ".gem", "user_cache")
@latest_usrcache = File.join(@gemhome, ".gem", "latest_user_cache") @latest_usrcache = File.join(@gemhome, ".gem", "latest_user_cache")
@userhome = File.join @tempdir, 'userhome'
ENV['HOME'] = @userhome
Gem.instance_variable_set :@user_home, nil
FileUtils.mkdir_p @gemhome FileUtils.mkdir_p @gemhome
FileUtils.mkdir_p @userhome
ENV['GEMCACHE'] = @usrcache ENV['GEMCACHE'] = @usrcache
Gem.use_paths(@gemhome) Gem.use_paths(@gemhome)
@ -104,9 +60,12 @@ class RubyGemTestCase < Test::Unit::TestCase
Gem.configuration.verbose = true Gem.configuration.verbose = true
Gem.configuration.update_sources = true Gem.configuration.update_sources = true
@gem_repo = "http://gems.example.com" @gem_repo = "http://gems.example.com/"
@uri = URI.parse @gem_repo
Gem.sources.replace [@gem_repo] Gem.sources.replace [@gem_repo]
Gem::SpecFetcher.fetcher = nil
@orig_BASERUBY = Gem::ConfigMap[:BASERUBY] @orig_BASERUBY = Gem::ConfigMap[:BASERUBY]
Gem::ConfigMap[:BASERUBY] = Gem::ConfigMap[:RUBY_INSTALL_NAME] Gem::ConfigMap[:BASERUBY] = Gem::ConfigMap[:RUBY_INSTALL_NAME]
@ -131,7 +90,7 @@ class RubyGemTestCase < Test::Unit::TestCase
Gem::ConfigMap[:arch] = @orig_arch Gem::ConfigMap[:arch] = @orig_arch
if defined? Gem::RemoteFetcher then if defined? Gem::RemoteFetcher then
Gem::RemoteFetcher.instance_variable_set :@fetcher, nil Gem::RemoteFetcher.fetcher = nil
end end
FileUtils.rm_rf @tempdir FileUtils.rm_rf @tempdir
@ -141,7 +100,6 @@ class RubyGemTestCase < Test::Unit::TestCase
ENV.delete 'GEM_PATH' ENV.delete 'GEM_PATH'
Gem.clear_paths Gem.clear_paths
Gem::SourceInfoCache.instance_variable_set :@cache, nil
end end
def install_gem gem def install_gem gem
@ -154,7 +112,7 @@ class RubyGemTestCase < Test::Unit::TestCase
end end
gem = File.join(@tempdir, "#{gem.full_name}.gem").untaint gem = File.join(@tempdir, "#{gem.full_name}.gem").untaint
Gem::Installer.new(gem).install Gem::Installer.new(gem, :wrappers => true).install
end end
def prep_cache_files(lc) def prep_cache_files(lc)
@ -231,6 +189,8 @@ class RubyGemTestCase < Test::Unit::TestCase
spec.loaded_from = written_path spec.loaded_from = written_path
Gem.source_index.add_spec spec
return spec return spec
end end
@ -254,6 +214,12 @@ class RubyGemTestCase < Test::Unit::TestCase
end end
end end
def util_clear_gems
FileUtils.rm_r File.join(@gemhome, 'gems')
FileUtils.rm_r File.join(@gemhome, 'specifications')
Gem.source_index.refresh!
end
def util_gem(name, version, &block) def util_gem(name, version, &block)
spec = quick_gem(name, version, &block) spec = quick_gem(name, version, &block)
@ -271,6 +237,16 @@ class RubyGemTestCase < Test::Unit::TestCase
[spec, cache_file] [spec, cache_file]
end end
def util_gzip(data)
out = StringIO.new
Zlib::GzipWriter.wrap out do |io|
io.write data
end
out.string
end
def util_make_gems def util_make_gems
init = proc do |s| init = proc do |s|
s.files = %w[lib/code.rb] s.files = %w[lib/code.rb]
@ -303,7 +279,7 @@ class RubyGemTestCase < Test::Unit::TestCase
end end
## ##
# Set the platform to +cpu+ and +os+ # Set the platform to +arch+
def util_set_arch(arch) def util_set_arch(arch)
Gem::ConfigMap[:arch] = arch Gem::ConfigMap[:arch] = arch
@ -320,9 +296,7 @@ class RubyGemTestCase < Test::Unit::TestCase
require 'socket' require 'socket'
require 'rubygems/remote_fetcher' require 'rubygems/remote_fetcher'
@uri = URI.parse @gem_repo @fetcher = Gem::FakeFetcher.new
@fetcher = FakeFetcher.new
@fetcher.uri = @uri
util_make_gems util_make_gems
@ -338,10 +312,11 @@ class RubyGemTestCase < Test::Unit::TestCase
@source_index.add_spec @a_evil9 @source_index.add_spec @a_evil9
@source_index.add_spec @c1_2 @source_index.add_spec @c1_2
Gem::RemoteFetcher.instance_variable_set :@fetcher, @fetcher Gem::RemoteFetcher.fetcher = @fetcher
end end
def util_setup_source_info_cache(*specs) def util_setup_source_info_cache(*specs)
require 'rubygems/source_info_cache'
require 'rubygems/source_info_cache_entry' require 'rubygems/source_info_cache_entry'
specs = Hash[*specs.map { |spec| [spec.full_name, spec] }.flatten] specs = Hash[*specs.map { |spec| [spec.full_name, spec] }.flatten]
@ -356,6 +331,35 @@ class RubyGemTestCase < Test::Unit::TestCase
sic.reset_cache_data sic.reset_cache_data
Gem::SourceInfoCache.instance_variable_set :@cache, sic Gem::SourceInfoCache.instance_variable_set :@cache, sic
si
end
def util_setup_spec_fetcher(*specs)
specs = Hash[*specs.map { |spec| [spec.full_name, spec] }.flatten]
si = Gem::SourceIndex.new specs
spec_fetcher = Gem::SpecFetcher.fetcher
spec_fetcher.specs[@uri] = []
si.gems.sort_by { |_, spec| spec }.each do |_, spec|
spec_tuple = [spec.name, spec.version, spec.original_platform]
spec_fetcher.specs[@uri] << spec_tuple
end
spec_fetcher.latest_specs[@uri] = []
si.latest_specs.sort.each do |spec|
spec_tuple = [spec.name, spec.version, spec.original_platform]
spec_fetcher.latest_specs[@uri] << spec_tuple
end
si.gems.sort_by { |_,spec| spec }.each do |_, spec|
path = "#{@gem_repo}quick/Marshal.#{Gem.marshal_version}/#{spec.original_name}.gemspec.rz"
data = Marshal.dump spec
data_deflate = Zlib::Deflate.deflate data
@fetcher.data[path] = data_deflate
end
si si
end end
@ -384,30 +388,3 @@ class RubyGemTestCase < Test::Unit::TestCase
end end
class TempIO
@@count = 0
def initialize(string = '')
@tempfile = Tempfile.new "TempIO-#{@@count ++ 1}"
@tempfile.binmode
@tempfile.write string
@tempfile.rewind
end
def method_missing(meth, *args, &block)
@tempfile.send(meth, *args, &block)
end
def respond_to?(meth)
@tempfile.respond_to? meth
end
def string
@tempfile.flush
Gem.read_binary @tempfile.path
end
end

View File

@ -12,11 +12,6 @@ require 'rubygems'
class TestConfig < RubyGemTestCase class TestConfig < RubyGemTestCase
def test_gem_original_datadir
datadir = Config::CONFIG['datadir']
assert_equal "#{datadir}/xyz", Config.gem_original_datadir('xyz')
end
def test_datadir def test_datadir
datadir = Config::CONFIG['datadir'] datadir = Config::CONFIG['datadir']
assert_equal "#{datadir}/xyz", Config.datadir('xyz') assert_equal "#{datadir}/xyz", Config.datadir('xyz')

View File

@ -27,6 +27,14 @@ class TestGem < RubyGemTestCase
assert_equal expected, Gem.all_load_paths.sort assert_equal expected, Gem.all_load_paths.sort
end end
def test_self_available?
util_make_gems
assert(Gem.available?("a"))
assert(Gem.available?("a", "1"))
assert(Gem.available?("a", ">1"))
assert(!Gem.available?("monkeys"))
end
def test_self_bindir def test_self_bindir
assert_equal File.join(@gemhome, 'bin'), Gem.bindir assert_equal File.join(@gemhome, 'bin'), Gem.bindir
@ -129,7 +137,7 @@ class TestGem < RubyGemTestCase
end end
def test_self_default_sources def test_self_default_sources
assert_equal %w[http://gems.rubyforge.org], Gem.default_sources assert_equal %w[http://gems.rubyforge.org/], Gem.default_sources
end end
def test_self_dir def test_self_dir
@ -237,6 +245,18 @@ class TestGem < RubyGemTestCase
assert_equal [Gem.dir], Gem.path assert_equal [Gem.dir], Gem.path
end end
def test_self_path_default
if defined? APPLE_GEM_HOME
orig_APPLE_GEM_HOME = APPLE_GEM_HOME
Object.send :remove_const, :APPLE_GEM_HOME
end
Gem.instance_variable_set :@gem_path, nil
assert_equal [Gem.default_path, Gem.dir], Gem.path
ensure
Object.const_set :APPLE_GEM_HOME, orig_APPLE_GEM_HOME
end
unless win_platform? unless win_platform?
def test_self_path_APPLE_GEM_HOME def test_self_path_APPLE_GEM_HOME
Gem.clear_paths Gem.clear_paths
@ -382,7 +402,7 @@ class TestGem < RubyGemTestCase
end end
def test_self_sources def test_self_sources
assert_equal %w[http://gems.example.com], Gem.sources assert_equal %w[http://gems.example.com/], Gem.sources
end end
def test_ssl_available_eh def test_ssl_available_eh

View File

@ -66,7 +66,7 @@ class TestGemCommandManager < RubyGemTestCase
assert_equal :both, check_options[:domain] assert_equal :both, check_options[:domain]
assert_equal true, check_options[:wrappers] assert_equal true, check_options[:wrappers]
assert_equal Gem::Requirement.default, check_options[:version] assert_equal Gem::Requirement.default, check_options[:version]
assert_equal Gem.dir, check_options[:install_dir] assert_equal nil, check_options[:install_dir]
assert_equal nil, check_options[:bin_dir] assert_equal nil, check_options[:bin_dir]
#check settings #check settings

View File

@ -9,6 +9,8 @@ class TestGemCommandsDependencyCommand < RubyGemTestCase
@cmd = Gem::Commands::DependencyCommand.new @cmd = Gem::Commands::DependencyCommand.new
@cmd.options[:domain] = :local @cmd.options[:domain] = :local
util_setup_fake_fetcher
end end
def test_execute def test_execute
@ -16,13 +18,15 @@ class TestGemCommandsDependencyCommand < RubyGemTestCase
gem.add_dependency 'bar', '> 1' gem.add_dependency 'bar', '> 1'
end end
Gem.source_index = nil
@cmd.options[:args] = %w[foo] @cmd.options[:args] = %w[foo]
use_ui @ui do use_ui @ui do
@cmd.execute @cmd.execute
end end
assert_equal "Gem foo-2\n bar (> 1)\n\n", @ui.output assert_equal "Gem foo-2\n bar (> 1, runtime)\n\n", @ui.output
assert_equal '', @ui.error assert_equal '', @ui.error
end end
@ -35,7 +39,7 @@ class TestGemCommandsDependencyCommand < RubyGemTestCase
end end
end end
assert_equal "No match found for foo (>= 0)\n", @ui.output assert_equal "No gems found matching foo (>= 0)\n", @ui.output
assert_equal '', @ui.error assert_equal '', @ui.error
end end
@ -64,6 +68,8 @@ class TestGemCommandsDependencyCommand < RubyGemTestCase
gem.add_dependency 'foo' gem.add_dependency 'foo'
end end
Gem.source_index = nil
@cmd.options[:args] = %w[foo] @cmd.options[:args] = %w[foo]
@cmd.options[:reverse_dependencies] = true @cmd.options[:reverse_dependencies] = true
@ -73,9 +79,9 @@ class TestGemCommandsDependencyCommand < RubyGemTestCase
expected = <<-EOF expected = <<-EOF
Gem foo-2 Gem foo-2
bar (> 1) bar (> 1, runtime)
Used by Used by
baz-2 (foo (>= 0)) baz-2 (foo (>= 0, runtime))
EOF EOF
@ -83,12 +89,34 @@ Gem foo-2
assert_equal '', @ui.error assert_equal '', @ui.error
end end
def test_execute_reverse_remote
@cmd.options[:args] = %w[foo]
@cmd.options[:reverse_dependencies] = true
@cmd.options[:domain] = :remote
assert_raise MockGemUi::TermError do
use_ui @ui do
@cmd.execute
end
end
expected = <<-EOF
ERROR: Only reverse dependencies for local gems are supported.
EOF
assert_equal '', @ui.output
assert_equal expected, @ui.error
end
def test_execute_remote def test_execute_remote
foo = quick_gem 'foo' do |gem| foo = quick_gem 'foo' do |gem|
gem.add_dependency 'bar', '> 1' gem.add_dependency 'bar', '> 1'
end end
util_setup_source_info_cache foo @fetcher = Gem::FakeFetcher.new
Gem::RemoteFetcher.fetcher = @fetcher
util_setup_spec_fetcher foo
FileUtils.rm File.join(@gemhome, 'specifications', FileUtils.rm File.join(@gemhome, 'specifications',
"#{foo.full_name}.gemspec") "#{foo.full_name}.gemspec")
@ -100,9 +128,48 @@ Gem foo-2
@cmd.execute @cmd.execute
end end
assert_equal "Gem foo-2\n bar (> 1)\n\n", @ui.output assert_equal "Gem foo-2\n bar (> 1, runtime)\n\n", @ui.output
assert_equal '', @ui.error assert_equal '', @ui.error
end end
def test_execute_remote_legacy
foo = quick_gem 'foo' do |gem|
gem.add_dependency 'bar', '> 1'
end
@fetcher = Gem::FakeFetcher.new
Gem::RemoteFetcher.fetcher = @fetcher
Gem::SpecFetcher.fetcher = nil
si = util_setup_source_info_cache foo
@fetcher.data["#{@gem_repo}yaml"] = YAML.dump si
@fetcher.data["#{@gem_repo}Marshal.#{Gem.marshal_version}"] =
si.dump
@fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz"] = nil
FileUtils.rm File.join(@gemhome, 'specifications',
"#{foo.full_name}.gemspec")
@cmd.options[:args] = %w[foo]
@cmd.options[:domain] = :remote
use_ui @ui do
@cmd.execute
end
assert_equal "Gem foo-2\n bar (> 1, runtime)\n\n", @ui.output
expected = <<-EOF
WARNING: RubyGems 1.2+ index not found for:
\t#{@gem_repo}
RubyGems will revert to legacy indexes degrading performance.
EOF
assert_equal expected, @ui.error
end
end end

View File

@ -27,6 +27,7 @@ class TestGemCommandsEnvironmentCommand < RubyGemTestCase
assert_match %r|RUBYGEMS PREFIX: |, @ui.output assert_match %r|RUBYGEMS PREFIX: |, @ui.output
assert_match %r|RUBY EXECUTABLE:.*#{Gem::ConfigMap[:RUBY_INSTALL_NAME]}|, assert_match %r|RUBY EXECUTABLE:.*#{Gem::ConfigMap[:RUBY_INSTALL_NAME]}|,
@ui.output @ui.output
assert_match %r|EXECUTABLE DIRECTORY:|, @ui.output
assert_match %r|RUBYGEMS PLATFORMS:|, @ui.output assert_match %r|RUBYGEMS PLATFORMS:|, @ui.output
assert_match %r|- #{Gem::Platform.local}|, @ui.output assert_match %r|- #{Gem::Platform.local}|, @ui.output
assert_match %r|GEM PATHS:|, @ui.output assert_match %r|GEM PATHS:|, @ui.output

View File

@ -14,10 +14,9 @@ class TestGemCommandsFetchCommand < RubyGemTestCase
def test_execute def test_execute
util_setup_fake_fetcher util_setup_fake_fetcher
util_setup_spec_fetcher @a2
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = @fetcher.data["#{@gem_repo}gems/#{@a2.full_name}.gem"] =
@source_index.dump
@fetcher.data["#{@gem_repo}/gems/#{@a2.full_name}.gem"] =
File.read(File.join(@gemhome, 'cache', "#{@a2.full_name}.gem")) File.read(File.join(@gemhome, 'cache', "#{@a2.full_name}.gem"))
@cmd.options[:args] = [@a2.name] @cmd.options[:args] = [@a2.name]
@ -28,7 +27,28 @@ class TestGemCommandsFetchCommand < RubyGemTestCase
end end
end end
assert File.exist?(File.join(@tempdir, "#{@a2.full_name}.gem")) assert File.exist?(File.join(@tempdir, "#{@a2.full_name}.gem")),
"#{@a2.full_name} fetched"
end
def test_execute_legacy
util_setup_fake_fetcher
util_setup_source_info_cache @a2
@fetcher.data["#{@gem_repo}yaml"] = ''
@fetcher.data["#{@gem_repo}gems/#{@a2.full_name}.gem"] =
File.read(File.join(@gemhome, 'cache', "#{@a2.full_name}.gem"))
@cmd.options[:args] = [@a2.name]
use_ui @ui do
Dir.chdir @tempdir do
@cmd.execute
end
end
assert File.exist?(File.join(@tempdir, "#{@a2.full_name}.gem")),
"#{@a2.full_name} fetched"
end end
end end

View File

@ -72,7 +72,7 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
end end
# HACK no repository was checked # HACK no repository was checked
assert_equal "ERROR: could not find no_such_gem locally or in a repository\n", assert_equal "ERROR: could not find gem no_such_gem locally or in a repository\n",
@ui.error @ui.error
end end
@ -86,8 +86,7 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
def test_execute_nonexistent def test_execute_nonexistent
util_setup_fake_fetcher util_setup_fake_fetcher
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = util_setup_spec_fetcher
@source_index.dump
@cmd.options[:args] = %w[nonexistent] @cmd.options[:args] = %w[nonexistent]
@ -98,18 +97,18 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
assert_equal 2, e.exit_code assert_equal 2, e.exit_code
end end
assert_equal "ERROR: could not find nonexistent locally or in a repository\n", assert_equal "ERROR: could not find gem nonexistent locally or in a repository\n",
@ui.error @ui.error
end end
def test_execute_remote def test_execute_remote
@cmd.options[:generate_rdoc] = true @cmd.options[:generate_rdoc] = true
@cmd.options[:generate_ri] = true @cmd.options[:generate_ri] = true
util_setup_fake_fetcher
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = util_setup_fake_fetcher
@source_index.dump util_setup_spec_fetcher @a2
@fetcher.data["#{@gem_repo}/gems/#{@a2.full_name}.gem"] =
@fetcher.data["#{@gem_repo}gems/#{@a2.full_name}.gem"] =
read_binary(File.join(@gemhome, 'cache', "#{@a2.full_name}.gem")) read_binary(File.join(@gemhome, 'cache', "#{@a2.full_name}.gem"))
@cmd.options[:args] = [@a2.name] @cmd.options[:args] = [@a2.name]
@ -122,7 +121,6 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
end end
out = @ui.output.split "\n" out = @ui.output.split "\n"
assert_match %r|Bulk updating|, out.shift
assert_equal "Successfully installed #{@a2.full_name}", out.shift assert_equal "Successfully installed #{@a2.full_name}", out.shift
assert_equal "1 gem installed", out.shift assert_equal "1 gem installed", out.shift
assert_equal "Installing ri documentation for #{@a2.full_name}...", assert_equal "Installing ri documentation for #{@a2.full_name}...",

View File

@ -0,0 +1,43 @@
require 'test/unit'
require File.join(File.expand_path(File.dirname(__FILE__)), 'gemutilities')
require 'rubygems/commands/outdated_command'
class TestGemCommandsOutdatedCommand < RubyGemTestCase
def setup
super
@cmd = Gem::Commands::OutdatedCommand.new
end
def test_initialize
assert @cmd.handles?(%W[--platform #{Gem::Platform.local}])
end
def test_execute
local_01 = quick_gem 'foo', '0.1'
local_02 = quick_gem 'foo', '0.2'
remote_10 = quick_gem 'foo', '1.0'
remote_20 = quick_gem 'foo', '2.0'
remote_spec_file = File.join @gemhome, 'specifications',
remote_10.full_name + ".gemspec"
FileUtils.rm remote_spec_file
remote_spec_file = File.join @gemhome, 'specifications',
remote_20.full_name + ".gemspec"
FileUtils.rm remote_spec_file
@fetcher = Gem::FakeFetcher.new
Gem::RemoteFetcher.fetcher = @fetcher
util_setup_spec_fetcher remote_10, remote_20
use_ui @ui do @cmd.execute end
assert_equal "foo (0.2 < 2.0)\n", @ui.output
assert_equal "", @ui.error
end
end

View File

@ -18,16 +18,24 @@ class TestGemCommandsPristineCommand < RubyGemTestCase
install_gem a install_gem a
foo_path = File.join @gemhome, 'gems', a.full_name, 'bin', 'foo'
File.open foo_path, 'w' do |io|
io.puts 'I changed it!'
end
@cmd.options[:args] = %w[a] @cmd.options[:args] = %w[a]
use_ui @ui do use_ui @ui do
@cmd.execute @cmd.execute
end end
assert_equal "#!/usr/bin/ruby\n", File.read(foo_path), foo_path
out = @ui.output.split "\n" out = @ui.output.split "\n"
assert_equal "Restoring gem(s) to pristine condition...", out.shift assert_equal "Restoring gem(s) to pristine condition...", out.shift
assert_equal "#{a.full_name} is in pristine condition", out.shift assert_equal "Restored #{a.full_name}", out.shift
assert out.empty?, out.inspect assert out.empty?, out.inspect
end end
@ -40,7 +48,7 @@ class TestGemCommandsPristineCommand < RubyGemTestCase
install_gem a install_gem a
gem_bin = File.join @gemhome, 'gems', "#{a.full_name}", 'bin', 'foo' gem_bin = File.join @gemhome, 'gems', a.full_name, 'bin', 'foo'
FileUtils.rm gem_bin FileUtils.rm gem_bin
@ -50,11 +58,12 @@ class TestGemCommandsPristineCommand < RubyGemTestCase
@cmd.execute @cmd.execute
end end
assert File.exist?(gem_bin)
out = @ui.output.split "\n" out = @ui.output.split "\n"
assert_equal "Restoring gem(s) to pristine condition...", out.shift assert_equal "Restoring gem(s) to pristine condition...", out.shift
assert_equal "Restoring 1 file to #{a.full_name}...", out.shift assert_equal "Restored #{a.full_name}", out.shift
assert_equal " #{gem_bin}", out.shift
assert out.empty?, out.inspect assert out.empty?, out.inspect
end end

View File

@ -7,33 +7,18 @@ class TestGemCommandsQueryCommand < RubyGemTestCase
def setup def setup
super super
util_make_gems
@a2.summary = 'This is a lot of text. ' * 4
@cmd = Gem::Commands::QueryCommand.new @cmd = Gem::Commands::QueryCommand.new
@si = util_setup_source_info_cache @a1, @a2, @pl1
util_setup_fake_fetcher util_setup_fake_fetcher
@fetcher.data["#{@gem_repo}/Marshal.#{Gem.marshal_version}"] = proc do @si = util_setup_spec_fetcher @a1, @a2, @pl1
@fetcher.data["#{@gem_repo}Marshal.#{Gem.marshal_version}"] = proc do
raise Gem::RemoteFetcher::FetchError raise Gem::RemoteFetcher::FetchError
end end
end end
def test_execute def test_execute
cache = Gem::SourceInfoCache.cache
cache.update
cache.write_cache
cache.reset_cache_data
Gem::SourceInfoCache.reset
a2_name = @a2.full_name
@fetcher.data["#{@gem_repo}/quick/latest_index.rz"] = util_zip a2_name
@fetcher.data["#{@gem_repo}/quick/Marshal.#{Gem.marshal_version}/#{a2_name}.gemspec.rz"] = util_zip Marshal.dump(@a2)
@fetcher.data["#{@gem_repo}/Marshal.#{Gem.marshal_version}"] =
Marshal.dump @si
@cmd.handle_options %w[-r] @cmd.handle_options %w[-r]
use_ui @ui do use_ui @ui do
@ -44,10 +29,8 @@ class TestGemCommandsQueryCommand < RubyGemTestCase
*** REMOTE GEMS *** *** REMOTE GEMS ***
Updating metadata for 1 gems from http://gems.example.com/
.
complete
a (2) a (2)
pl (1)
EOF EOF
assert_equal expected, @ui.output assert_equal expected, @ui.output
@ -55,21 +38,8 @@ a (2)
end end
def test_execute_all def test_execute_all
cache = Gem::SourceInfoCache.cache
cache.update
cache.write_cache
cache.reset_cache_data
Gem::SourceInfoCache.reset
a1_name = @a1.full_name a1_name = @a1.full_name
a2_name = @a2.full_name a2_name = @a2.full_name
@fetcher.data["#{@gem_repo}/quick/index.rz"] =
util_zip [a1_name, a2_name].join("\n")
@fetcher.data["#{@gem_repo}/quick/latest_index.rz"] = util_zip a2_name
@fetcher.data["#{@gem_repo}/quick/Marshal.#{Gem.marshal_version}/#{a1_name}.gemspec.rz"] = util_zip Marshal.dump(@a1)
@fetcher.data["#{@gem_repo}/quick/Marshal.#{Gem.marshal_version}/#{a2_name}.gemspec.rz"] = util_zip Marshal.dump(@a2)
@fetcher.data["#{@gem_repo}/Marshal.#{Gem.marshal_version}"] =
Marshal.dump @si
@cmd.handle_options %w[-r --all] @cmd.handle_options %w[-r --all]
@ -81,10 +51,8 @@ a (2)
*** REMOTE GEMS *** *** REMOTE GEMS ***
Updating metadata for 2 gems from http://gems.example.com/
..
complete
a (2, 1) a (2, 1)
pl (1)
EOF EOF
assert_equal expected, @ui.output assert_equal expected, @ui.output
@ -92,6 +60,13 @@ a (2, 1)
end end
def test_execute_details def test_execute_details
@a2.summary = 'This is a lot of text. ' * 4
@a2.authors = ['Abraham Lincoln', 'Hirohito']
@a2.homepage = 'http://a.example.com/'
@a2.rubyforge_project = 'rubygems'
@si = util_setup_spec_fetcher @a1, @a2, @pl1
@cmd.handle_options %w[-r -d] @cmd.handle_options %w[-r -d]
use_ui @ui do use_ui @ui do
@ -103,10 +78,17 @@ a (2, 1)
*** REMOTE GEMS *** *** REMOTE GEMS ***
a (2) a (2)
Authors: Abraham Lincoln, Hirohito
Rubyforge: http://rubyforge.org/projects/rubygems
Homepage: http://a.example.com/
This is a lot of text. This is a lot of text. This is a lot of text. This is a lot of text. This is a lot of text. This is a lot of text.
This is a lot of text. This is a lot of text.
pl (1) pl (1)
Author: A User
Homepage: http://example.com
this is a summary this is a summary
EOF EOF
@ -126,6 +108,7 @@ pl (1)
assert_equal 0, e.exit_code assert_equal 0, e.exit_code
assert_equal "true\n", @ui.output assert_equal "true\n", @ui.output
assert_equal '', @ui.error assert_equal '', @ui.error
end end
@ -189,6 +172,99 @@ pl (1)
assert_equal 1, e.exit_code assert_equal 1, e.exit_code
end end
def test_execute_legacy
Gem::SpecFetcher.fetcher = nil
si = util_setup_source_info_cache @a1, @a2, @pl1
@fetcher.data["#{@gem_repo}yaml"] = YAML.dump si
@fetcher.data["#{@gem_repo}Marshal.#{Gem.marshal_version}"] =
si.dump
@fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz"] = nil
@cmd.handle_options %w[-r]
use_ui @ui do
@cmd.execute
end
expected = <<-EOF
*** REMOTE GEMS ***
a (2)
pl (1)
EOF
assert_equal expected, @ui.output
expected = <<-EOF
WARNING: RubyGems 1.2+ index not found for:
\t#{@gem_repo}
RubyGems will revert to legacy indexes degrading performance.
EOF
assert_equal expected, @ui.error
end
def test_execute_local_details
@a2.summary = 'This is a lot of text. ' * 4
@a2.authors = ['Abraham Lincoln', 'Hirohito']
@a2.homepage = 'http://a.example.com/'
@a2.rubyforge_project = 'rubygems'
@cmd.handle_options %w[--local --details]
use_ui @ui do
@cmd.execute
end
expected = <<-EOF
*** LOCAL GEMS ***
a (2, 1)
Author: A User
Homepage: http://example.com
Installed at (2): #{@gemhome}
(1): #{@gemhome}
this is a summary
a_evil (9)
Author: A User
Homepage: http://example.com
Installed at: #{@gemhome}
this is a summary
b (2)
Author: A User
Homepage: http://example.com
Installed at: #{@gemhome}
this is a summary
c (1.2)
Author: A User
Homepage: http://example.com
Installed at: #{@gemhome}
this is a summary
pl (1)
Author: A User
Homepage: http://example.com
Installed at: #{@gemhome}
this is a summary
EOF
assert_equal expected, @ui.output
assert_equal '', @ui.error
end
def test_execute_no_versions def test_execute_no_versions
@cmd.handle_options %w[-r --no-versions] @cmd.handle_options %w[-r --no-versions]

View File

@ -8,10 +8,12 @@ class TestGemCommandsSourcesCommand < RubyGemTestCase
super super
@cmd = Gem::Commands::SourcesCommand.new @cmd = Gem::Commands::SourcesCommand.new
@new_repo = "http://beta-gems.example.com"
end end
def test_execute def test_execute
util_setup_source_info_cache util_setup_spec_fetcher
@cmd.handle_options [] @cmd.handle_options []
use_ui @ui do use_ui @ui do
@ -34,43 +36,49 @@ class TestGemCommandsSourcesCommand < RubyGemTestCase
si = Gem::SourceIndex.new si = Gem::SourceIndex.new
si.add_spec @a1 si.add_spec @a1
@fetcher.data["http://beta-gems.example.com/Marshal.#{@marshal_version}"] = specs = si.map do |_, spec|
si.dump [spec.name, spec.version, spec.original_platform]
end
@cmd.handle_options %w[--add http://beta-gems.example.com] specs_dump_gz = StringIO.new
Zlib::GzipWriter.wrap specs_dump_gz do |io|
Marshal.dump specs, io
end
util_setup_source_info_cache @fetcher.data["#{@new_repo}/specs.#{@marshal_version}.gz"] =
specs_dump_gz.string
@cmd.handle_options %W[--add #{@new_repo}]
util_setup_spec_fetcher
use_ui @ui do use_ui @ui do
@cmd.execute @cmd.execute
end end
assert_equal [@gem_repo, @new_repo], Gem.sources
expected = <<-EOF expected = <<-EOF
Bulk updating Gem source index for: http://beta-gems.example.com/ #{@new_repo} added to sources
http://beta-gems.example.com added to sources
EOF EOF
assert_equal expected, @ui.output assert_equal expected, @ui.output
assert_equal '', @ui.error assert_equal '', @ui.error
Gem::SourceInfoCache.cache.flush
assert_equal %W[http://beta-gems.example.com #{@gem_repo}],
Gem::SourceInfoCache.cache_data.keys.sort
end end
def test_execute_add_nonexistent_source def test_execute_add_nonexistent_source
util_setup_fake_fetcher util_setup_fake_fetcher
@fetcher.data["http://beta-gems.example.com/Marshal.#{@marshal_version}"] = uri = "http://beta-gems.example.com/specs.#{@marshal_version}.gz"
proc do @fetcher.data[uri] = proc do
raise Gem::RemoteFetcher::FetchError, 'it died' raise Gem::RemoteFetcher::FetchError.new('it died', uri)
end end
Gem::RemoteFetcher.instance_variable_set :@fetcher, @fetcher Gem::RemoteFetcher.fetcher = @fetcher
@cmd.handle_options %w[--add http://beta-gems.example.com] @cmd.handle_options %w[--add http://beta-gems.example.com]
util_setup_source_info_cache util_setup_spec_fetcher
use_ui @ui do use_ui @ui do
@cmd.execute @cmd.execute
@ -78,7 +86,7 @@ http://beta-gems.example.com added to sources
expected = <<-EOF expected = <<-EOF
Error fetching http://beta-gems.example.com: Error fetching http://beta-gems.example.com:
\tit died \tit died (#{uri})
EOF EOF
assert_equal expected, @ui.output assert_equal expected, @ui.output
@ -88,12 +96,14 @@ Error fetching http://beta-gems.example.com:
def test_execute_add_bad_uri def test_execute_add_bad_uri
@cmd.handle_options %w[--add beta-gems.example.com] @cmd.handle_options %w[--add beta-gems.example.com]
util_setup_source_info_cache util_setup_spec_fetcher
use_ui @ui do use_ui @ui do
@cmd.execute @cmd.execute
end end
assert_equal [@gem_repo], Gem.sources
expected = <<-EOF expected = <<-EOF
beta-gems.example.com is not a URI beta-gems.example.com is not a URI
EOF EOF
@ -102,6 +112,34 @@ beta-gems.example.com is not a URI
assert_equal '', @ui.error assert_equal '', @ui.error
end end
def test_execute_add_legacy
util_setup_fake_fetcher
util_setup_source_info_cache
si = Gem::SourceIndex.new
si.add_spec @a1
@fetcher.data["#{@new_repo}/yaml"] = ''
@cmd.handle_options %W[--add #{@new_repo}]
use_ui @ui do
@cmd.execute
end
assert_equal [@gem_repo], Gem.sources
expected = <<-EOF
WARNING: RubyGems 1.2+ index not found for:
\t#{@new_repo}
Will cause RubyGems to revert to legacy indexes, degrading performance.
EOF
assert_equal "#{@new_repo} added to sources\n", @ui.output
assert_equal expected, @ui.error
end
def test_execute_clear_all def test_execute_clear_all
@cmd.handle_options %w[--clear-all] @cmd.handle_options %w[--clear-all]
@ -116,11 +154,19 @@ beta-gems.example.com is not a URI
assert File.exist?(cache.latest_system_cache_file), assert File.exist?(cache.latest_system_cache_file),
'latest system cache file' 'latest system cache file'
util_setup_spec_fetcher
fetcher = Gem::SpecFetcher.fetcher
# HACK figure out how to force directory creation via fetcher
#assert File.directory?(fetcher.dir), 'cache dir exists'
use_ui @ui do use_ui @ui do
@cmd.execute @cmd.execute
end end
expected = <<-EOF expected = <<-EOF
*** Removed specs cache ***
*** Removed user source cache *** *** Removed user source cache ***
*** Removed latest user source cache *** *** Removed latest user source cache ***
*** Removed system source cache *** *** Removed system source cache ***
@ -135,12 +181,13 @@ beta-gems.example.com is not a URI
assert !File.exist?(cache.latest_system_cache_file), assert !File.exist?(cache.latest_system_cache_file),
'latest system cache file' 'latest system cache file'
assert !File.exist?(fetcher.dir), 'cache dir removed'
end end
def test_execute_remove def test_execute_remove
@cmd.handle_options %W[--remove #{@gem_repo}] @cmd.handle_options %W[--remove #{@gem_repo}]
util_setup_source_info_cache util_setup_spec_fetcher
use_ui @ui do use_ui @ui do
@cmd.execute @cmd.execute
@ -150,9 +197,6 @@ beta-gems.example.com is not a URI
assert_equal expected, @ui.output assert_equal expected, @ui.output
assert_equal '', @ui.error assert_equal '', @ui.error
Gem::SourceInfoCache.cache.flush
assert_equal [], Gem::SourceInfoCache.cache_data.keys
end end
def test_execute_remove_no_network def test_execute_remove_no_network
@ -160,7 +204,7 @@ beta-gems.example.com is not a URI
util_setup_fake_fetcher util_setup_fake_fetcher
@fetcher.data["#{@gem_repo}/Marshal.#{Gem.marshal_version}"] = proc do @fetcher.data["#{@gem_repo}Marshal.#{Gem.marshal_version}"] = proc do
raise Gem::RemoteFetcher::FetchError raise Gem::RemoteFetcher::FetchError
end end
@ -172,34 +216,60 @@ beta-gems.example.com is not a URI
assert_equal expected, @ui.output assert_equal expected, @ui.output
assert_equal '', @ui.error assert_equal '', @ui.error
Gem::SourceInfoCache.cache.flush
assert_equal [], Gem::SourceInfoCache.cache_data.keys
end end
def test_execute_update def test_execute_update
@cmd.handle_options %w[--update] @cmd.handle_options %w[--update]
util_setup_fake_fetcher
source_index = util_setup_spec_fetcher @a1
specs = source_index.map do |name, spec|
[spec.name, spec.version, spec.original_platform]
end
@fetcher.data["#{@gem_repo}specs.#{Gem.marshal_version}.gz"] =
util_gzip Marshal.dump(specs)
latest_specs = source_index.latest_specs.map do |spec|
[spec.name, spec.version, spec.original_platform]
end
@fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz"] =
util_gzip Marshal.dump(latest_specs)
use_ui @ui do
@cmd.execute
end
assert_equal "source cache successfully updated\n", @ui.output
assert_equal '', @ui.error
end
def test_execute_update_legacy
@cmd.handle_options %w[--update]
util_setup_fake_fetcher
util_setup_source_info_cache util_setup_source_info_cache
Gem::SourceInfoCache.reset Gem::SourceInfoCache.reset
util_setup_fake_fetcher
si = Gem::SourceIndex.new si = Gem::SourceIndex.new
si.add_spec @a1 si.add_spec @a1
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump @fetcher.data["#{@gem_repo}yaml"] = YAML.dump si
@fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}"] = si.dump
use_ui @ui do use_ui @ui do
@cmd.execute @cmd.execute
end end
expected = <<-EOF expected = <<-EOF
Bulk updating Gem source index for: #{@gem_repo}/ Bulk updating Gem source index for: #{@gem_repo}
source cache successfully updated source cache successfully updated
EOF EOF
assert_equal expected, @ui.output assert_equal expected, @ui.output
assert_equal '', @ui.error assert_equal '', @ui.error
end end
end end

View File

@ -74,7 +74,10 @@ class TestGemCommandsSpecificationCommand < RubyGemTestCase
def test_execute_remote def test_execute_remote
foo = quick_gem 'foo' foo = quick_gem 'foo'
util_setup_source_info_cache foo @fetcher = Gem::FakeFetcher.new
Gem::RemoteFetcher.fetcher = @fetcher
util_setup_spec_fetcher foo
FileUtils.rm File.join(@gemhome, 'specifications', FileUtils.rm File.join(@gemhome, 'specifications',
"#{foo.full_name}.gemspec") "#{foo.full_name}.gemspec")

View File

@ -0,0 +1,39 @@
require 'test/unit'
require File.join(File.expand_path(File.dirname(__FILE__)), 'gemutilities')
require 'rubygems/commands/stale_command'
class TestGemCommandsStaleCommand < RubyGemTestCase
def setup
super
@cmd = Gem::Commands::StaleCommand.new
end
def test_execute_sorts
files = %w[lib/foo_bar.rb Rakefile]
foo_bar = quick_gem 'foo_bar' do |gem|
gem.files = files
end
bar_baz = quick_gem 'bar_baz' do |gem|
gem.files = files
end
files.each do |file|
filename = bar_baz.full_gem_path + "/#{file}"
FileUtils.mkdir_p(File.dirname(filename))
FileUtils.touch(filename, :mtime => Time.now)
filename = foo_bar.full_gem_path + "/#{file}"
FileUtils.mkdir_p(File.dirname(filename))
FileUtils.touch(filename, :mtime => Time.now - 86400)
end
use_ui @ui do
@cmd.execute
end
lines = @ui.output.split("\n")
assert_equal("#{foo_bar.name}-#{foo_bar.version}", lines[0].split.first)
assert_equal("#{bar_baz.name}-#{bar_baz.version}", lines[1].split.first)
end
end

View File

@ -14,14 +14,16 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
@a1_path = File.join @gemhome, 'cache', "#{@a1.full_name}.gem" @a1_path = File.join @gemhome, 'cache', "#{@a1.full_name}.gem"
@a2_path = File.join @gemhome, 'cache', "#{@a2.full_name}.gem" @a2_path = File.join @gemhome, 'cache', "#{@a2.full_name}.gem"
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = util_setup_spec_fetcher @a1, @a2
@source_index.dump
@fetcher.data["#{@gem_repo}/gems/#{@a1.full_name}.gem"] = read_binary @a1_path @fetcher.data["#{@gem_repo}gems/#{@a1.full_name}.gem"] =
@fetcher.data["#{@gem_repo}/gems/#{@a2.full_name}.gem"] = read_binary @a2_path read_binary @a1_path
@fetcher.data["#{@gem_repo}gems/#{@a2.full_name}.gem"] =
read_binary @a2_path
end end
def test_execute def test_execute
util_remove_gems util_clear_gems
Gem::Installer.new(@a1_path).install Gem::Installer.new(@a1_path).install
@ -33,7 +35,6 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
out = @ui.output.split "\n" out = @ui.output.split "\n"
assert_equal "Updating installed gems", out.shift assert_equal "Updating installed gems", out.shift
assert_match %r|Bulk updating|, out.shift
assert_equal "Updating #{@a2.name}", out.shift assert_equal "Updating #{@a2.name}", out.shift
assert_equal "Successfully installed #{@a2.full_name}", out.shift assert_equal "Successfully installed #{@a2.full_name}", out.shift
assert_equal "Gems updated: #{@a2.name}", out.shift assert_equal "Gems updated: #{@a2.name}", out.shift
@ -73,16 +74,15 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
util_build_gem @a2 util_build_gem @a2
util_build_gem @c2 util_build_gem @c2
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = @fetcher.data["#{@gem_repo}gems/#{@a1.full_name}.gem"] = read_binary @a1_path
@source_index.dump @fetcher.data["#{@gem_repo}gems/#{@a2.full_name}.gem"] = read_binary @a2_path
@fetcher.data["#{@gem_repo}/gems/#{@a1.full_name}.gem"] = read_binary @a1_path @fetcher.data["#{@gem_repo}gems/#{@b2.full_name}.gem"] = read_binary @b2_path
@fetcher.data["#{@gem_repo}/gems/#{@a2.full_name}.gem"] = read_binary @a2_path @fetcher.data["#{@gem_repo}gems/#{@c1_2.full_name}.gem"] =
@fetcher.data["#{@gem_repo}/gems/#{@b2.full_name}.gem"] = read_binary @b2_path
@fetcher.data["#{@gem_repo}/gems/#{@c1_2.full_name}.gem"] =
read_binary @c1_2_path read_binary @c1_2_path
@fetcher.data["#{@gem_repo}/gems/#{@c2.full_name}.gem"] = read_binary @c2_path @fetcher.data["#{@gem_repo}gems/#{@c2.full_name}.gem"] = read_binary @c2_path
util_remove_gems util_setup_spec_fetcher @a1, @a2, @b2, @c1_2, @c2
util_clear_gems
Gem::Installer.new(@c1_2_path).install Gem::Installer.new(@c1_2_path).install
Gem::Installer.new(@a1_path).install Gem::Installer.new(@a1_path).install
@ -95,9 +95,7 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
out = @ui.output.split "\n" out = @ui.output.split "\n"
assert_equal "Updating installed gems", out.shift assert_equal "Updating installed gems", out.shift
assert_match %r|Bulk updating|, out.shift
assert_equal "Updating #{@a2.name}", out.shift assert_equal "Updating #{@a2.name}", out.shift
assert_match %r|Bulk updating|, out.shift
assert_equal "Successfully installed #{@c2.full_name}", out.shift assert_equal "Successfully installed #{@c2.full_name}", out.shift
assert_equal "Successfully installed #{@b2.full_name}", out.shift assert_equal "Successfully installed #{@b2.full_name}", out.shift
assert_equal "Successfully installed #{@a2.full_name}", out.shift assert_equal "Successfully installed #{@a2.full_name}", out.shift
@ -108,7 +106,7 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
end end
def test_execute_named def test_execute_named
util_remove_gems util_clear_gems
Gem::Installer.new(@a1_path).install Gem::Installer.new(@a1_path).install
@ -120,7 +118,6 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
out = @ui.output.split "\n" out = @ui.output.split "\n"
assert_equal "Updating installed gems", out.shift assert_equal "Updating installed gems", out.shift
assert_match %r|Bulk updating|, out.shift
assert_equal "Updating #{@a2.name}", out.shift assert_equal "Updating #{@a2.name}", out.shift
assert_equal "Successfully installed #{@a2.full_name}", out.shift assert_equal "Successfully installed #{@a2.full_name}", out.shift
assert_equal "Gems updated: #{@a2.name}", out.shift assert_equal "Gems updated: #{@a2.name}", out.shift
@ -129,7 +126,7 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
end end
def test_execute_named_up_to_date def test_execute_named_up_to_date
util_remove_gems util_clear_gems
Gem::Installer.new(@a2_path).install Gem::Installer.new(@a2_path).install
@ -141,14 +138,13 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
out = @ui.output.split "\n" out = @ui.output.split "\n"
assert_equal "Updating installed gems", out.shift assert_equal "Updating installed gems", out.shift
assert_match %r|Bulk updating|, out.shift
assert_equal "Nothing to update", out.shift assert_equal "Nothing to update", out.shift
assert out.empty?, out.inspect assert out.empty?, out.inspect
end end
def test_execute_up_to_date def test_execute_up_to_date
util_remove_gems util_clear_gems
Gem::Installer.new(@a2_path).install Gem::Installer.new(@a2_path).install
@ -160,16 +156,10 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
out = @ui.output.split "\n" out = @ui.output.split "\n"
assert_equal "Updating installed gems", out.shift assert_equal "Updating installed gems", out.shift
assert_match %r|Bulk updating|, out.shift
assert_equal "Nothing to update", out.shift assert_equal "Nothing to update", out.shift
assert out.empty?, out.inspect assert out.empty?, out.inspect
end end
def util_remove_gems
FileUtils.rm_r File.join(@gemhome, 'gems')
FileUtils.rm_r File.join(@gemhome, 'specifications')
end
end end

View File

@ -17,9 +17,23 @@ class TestGemConfigFile < RubyGemTestCase
@temp_conf = File.join @tempdir, '.gemrc' @temp_conf = File.join @tempdir, '.gemrc'
@cfg_args = %W[--config-file #{@temp_conf}] @cfg_args = %W[--config-file #{@temp_conf}]
@orig_SYSTEM_WIDE_CONFIG_FILE = Gem::ConfigFile::SYSTEM_WIDE_CONFIG_FILE
Gem::ConfigFile.send :remove_const, :SYSTEM_WIDE_CONFIG_FILE
Gem::ConfigFile.send :const_set, :SYSTEM_WIDE_CONFIG_FILE,
File.join(@tempdir, 'system-gemrc')
util_config_file util_config_file
end end
def teardown
Gem::ConfigFile.send :remove_const, :SYSTEM_WIDE_CONFIG_FILE
Gem::ConfigFile.send :const_set, :SYSTEM_WIDE_CONFIG_FILE,
@orig_SYSTEM_WIDE_CONFIG_FILE
super
end
def test_initialize def test_initialize
assert_equal @temp_conf, @cfg.config_file_name assert_equal @temp_conf, @cfg.config_file_name
@ -28,7 +42,7 @@ class TestGemConfigFile < RubyGemTestCase
assert_equal false, @cfg.benchmark assert_equal false, @cfg.benchmark
assert_equal Gem::ConfigFile::DEFAULT_BULK_THRESHOLD, @cfg.bulk_threshold assert_equal Gem::ConfigFile::DEFAULT_BULK_THRESHOLD, @cfg.bulk_threshold
assert_equal true, @cfg.verbose assert_equal true, @cfg.verbose
assert_equal %w[http://gems.example.com], Gem.sources assert_equal [@gem_repo], Gem.sources
File.open @temp_conf, 'w' do |fp| File.open @temp_conf, 'w' do |fp|
fp.puts ":backtrace: true" fp.puts ":backtrace: true"
@ -202,6 +216,23 @@ class TestGemConfigFile < RubyGemTestCase
assert_equal %w[http://even-more-gems.example.com], Gem.sources assert_equal %w[http://even-more-gems.example.com], Gem.sources
end end
def test_global_config_file
File.open(@temp_conf, 'w') do |fp|
fp.puts ":backtrace: true"
end
File.open(File.join(Gem::ConfigFile::SYSTEM_WIDE_CONFIG_FILE),
'w') do |fp|
fp.puts ":backtrace: false"
fp.puts ":bulk_threshold: 2048"
end
util_config_file
assert_equal 2048, @cfg.bulk_threshold
assert @cfg.backtrace
end
def util_config_file(args = @cfg_args) def util_config_file(args = @cfg_args)
@cfg = Gem::ConfigFile.new args @cfg = Gem::ConfigFile.new args
end end

View File

@ -60,6 +60,21 @@ class TestGemDependency < RubyGemTestCase
assert_equal Gem::Requirement.new('= 2'), dep.version_requirements assert_equal Gem::Requirement.new('= 2'), dep.version_requirements
end end
def test_initialize_with_type
dep = Gem::Dependency.new("pkg", [], :development)
assert_equal(:development, dep.type)
end
def test_type_is_runtime_by_default
assert_equal(:runtime, Gem::Dependency.new("pkg", []).type)
end
def test_type_is_restricted
assert_raise(ArgumentError) do
Gem::Dependency.new("pkg", [:sometimes])
end
end
def test_equals2 def test_equals2
assert_equal @pkg1_0, @pkg1_0.dup assert_equal @pkg1_0, @pkg1_0.dup
assert_equal @pkg1_0.dup, @pkg1_0 assert_equal @pkg1_0.dup, @pkg1_0
@ -74,6 +89,36 @@ class TestGemDependency < RubyGemTestCase
assert_not_equal Object.new, @pkg1_0 assert_not_equal Object.new, @pkg1_0
end end
def test_equals2_type
runtime = Gem::Dependency.new("pkg", [])
development = Gem::Dependency.new("pkg", [], :development)
assert_not_equal(runtime, development)
end
def test_equals_tilde
def dep(name, version)
Gem::Dependency.new name, version
end
a0 = dep 'a', '0'
a1 = dep 'a', '1'
b0 = dep 'b', '0'
pa0 = dep 'a', '>= 0'
pa0r = dep(/a/, '>= 0')
pab0r = dep(/a|b/, '>= 0')
assert((a0 =~ a0), 'match self')
assert((pa0 =~ a0), 'match version exact')
assert((pa0 =~ a1), 'match version')
assert((pa0r =~ a0), 'match regex simple')
assert((pab0r =~ a0), 'match regex complex')
assert(!(pa0r =~ b0), 'fail match regex')
assert(!(pa0r =~ Object.new), 'fail match Object')
end
def test_hash def test_hash
assert_equal @pkg1_0.hash, @pkg1_0.dup.hash assert_equal @pkg1_0.hash, @pkg1_0.dup.hash
assert_equal @pkg1_0.dup.hash, @pkg1_0.hash assert_equal @pkg1_0.dup.hash, @pkg1_0.hash
@ -85,5 +130,11 @@ class TestGemDependency < RubyGemTestCase
assert_not_equal @oth1_0.hash, @pkg1_0.hash, "names different" assert_not_equal @oth1_0.hash, @pkg1_0.hash, "names different"
end end
def test_hash_type
runtime = Gem::Dependency.new("pkg", [])
development = Gem::Dependency.new("pkg", [], :development)
assert_not_equal(runtime.hash, development.hash)
end
end end

View File

@ -15,8 +15,12 @@ class TestGemDependencyInstaller < RubyGemTestCase
fp.puts "#!/usr/bin/ruby" fp.puts "#!/usr/bin/ruby"
end end
@a1, @a1_gem = util_gem 'a', '1' do |s| s.executables << 'a_bin' end @a1, @a1_gem = util_gem 'a', '1' do |s| s.executables << 'a_bin' end
@aa1, @aa1_gem = util_gem 'aa', '1'
@b1, @b1_gem = util_gem 'b', '1' do |s| s.add_dependency 'a' end @b1, @b1_gem = util_gem 'b', '1' do |s|
s.add_dependency 'a'
s.add_development_dependency 'aa'
end
@d1, @d1_gem = util_gem 'd', '1' @d1, @d1_gem = util_gem 'd', '1'
@d2, @d2_gem = util_gem 'd', '2' @d2, @d2_gem = util_gem 'd', '2'
@ -38,15 +42,13 @@ class TestGemDependencyInstaller < RubyGemTestCase
@z1, @z1_gem = util_gem 'z', '1' do |s| s.add_dependency 'y' end @z1, @z1_gem = util_gem 'z', '1' do |s| s.add_dependency 'y' end
si = util_setup_source_info_cache @a1, @b1, @d1, @d2, @x1_m, @x1_o, @w1, @fetcher = Gem::FakeFetcher.new
@y1, @y1_1_p, @z1 Gem::RemoteFetcher.fetcher = @fetcher
@fetcher = FakeFetcher.new si = util_setup_spec_fetcher @a1, @b1, @d1, @d2, @x1_m, @x1_o, @w1, @y1,
Gem::RemoteFetcher.instance_variable_set :@fetcher, @fetcher @y1_1_p, @z1
@fetcher.uri = URI.parse 'http://gems.example.com'
@fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml
FileUtils.rm_rf File.join(@gemhome, 'gems') util_clear_gems
end end
def test_install def test_install
@ -64,6 +66,52 @@ class TestGemDependencyInstaller < RubyGemTestCase
assert_equal [@a1], inst.installed_gems assert_equal [@a1], inst.installed_gems
end end
def test_install_cache_dir
FileUtils.mv @a1_gem, @tempdir
FileUtils.mv @b1_gem, @tempdir
inst = nil
Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new :cache_dir => @tempdir
inst.install 'b'
end
assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
assert File.exist?(File.join(@tempdir, 'cache', "#{@a1.full_name}.gem"))
assert File.exist?(File.join(@tempdir, 'cache', "#{@b1.full_name}.gem"))
end
def test_install_dependencies_satisfied
a2, a2_gem = util_gem 'a', '2'
FileUtils.rm_rf File.join(@gemhome, 'gems')
Gem.source_index.refresh!
FileUtils.mv @a1_gem, @tempdir
FileUtils.mv a2_gem, @tempdir # not in index
FileUtils.mv @b1_gem, @tempdir
inst = nil
Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new
inst.install 'a-2'
end
FileUtils.rm File.join(@tempdir, "#{a2.full_name}.gem")
Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new
inst.install 'b'
end
installed = Gem::SourceIndex.from_installed_gems.map { |n,s| s.full_name }
assert_equal %w[a-2 b-1], installed.sort
assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
end
def test_install_dependency def test_install_dependency
FileUtils.mv @a1_gem, @tempdir FileUtils.mv @a1_gem, @tempdir
FileUtils.mv @b1_gem, @tempdir FileUtils.mv @b1_gem, @tempdir
@ -77,6 +125,20 @@ class TestGemDependencyInstaller < RubyGemTestCase
assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name } assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
end end
def test_install_dependency_development
FileUtils.mv @a1_gem, @tempdir
FileUtils.mv @aa1_gem, @tempdir
FileUtils.mv @b1_gem, @tempdir
inst = nil
Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new(:development => true)
inst.install 'b'
end
assert_equal %w[a-1 aa-1 b-1], inst.installed_gems.map { |s| s.full_name }
end
def test_install_dependency_existing def test_install_dependency_existing
Gem::Installer.new(@a1_gem).install Gem::Installer.new(@a1_gem).install
FileUtils.mv @a1_gem, @tempdir FileUtils.mv @a1_gem, @tempdir
@ -177,7 +239,7 @@ class TestGemDependencyInstaller < RubyGemTestCase
def test_install_force def test_install_force
FileUtils.mv @b1_gem, @tempdir FileUtils.mv @b1_gem, @tempdir
si = util_setup_source_info_cache @b1 si = util_setup_spec_fetcher @b1
@fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml @fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml
inst = nil inst = nil
@ -249,8 +311,6 @@ class TestGemDependencyInstaller < RubyGemTestCase
end end
def test_install_domain_both_no_network def test_install_domain_both_no_network
Gem::SourceInfoCache.instance_variable_set :@cache, nil
@fetcher.data["http://gems.example.com/gems/Marshal.#{@marshal_version}"] = @fetcher.data["http://gems.example.com/gems/Marshal.#{@marshal_version}"] =
proc do proc do
raise Gem::RemoteFetcher::FetchError raise Gem::RemoteFetcher::FetchError
@ -272,12 +332,14 @@ class TestGemDependencyInstaller < RubyGemTestCase
FileUtils.mv @b1_gem, @tempdir FileUtils.mv @b1_gem, @tempdir
inst = nil inst = nil
Gem.source_index.gems.delete @a1.full_name
Dir.chdir @tempdir do Dir.chdir @tempdir do
e = assert_raise Gem::InstallError do e = assert_raise Gem::InstallError do
inst = Gem::DependencyInstaller.new :domain => :local inst = Gem::DependencyInstaller.new :domain => :local
inst.install 'b' inst.install 'b'
end end
assert_equal 'b requires a (>= 0)', e.message assert_equal 'b requires a (>= 0, runtime)', e.message
end end
assert_equal [], inst.installed_gems.map { |s| s.full_name } assert_equal [], inst.installed_gems.map { |s| s.full_name }
@ -297,6 +359,30 @@ class TestGemDependencyInstaller < RubyGemTestCase
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name } assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
end end
def test_install_dual_repository
FileUtils.mv @a1_gem, @tempdir
FileUtils.mv @b1_gem, @tempdir
inst = nil
gemhome2 = "#{@gemhome}2"
Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new :install_dir => gemhome2
inst.install 'a'
end
ENV['GEM_HOME'] = @gemhome
ENV['GEM_PATH'] = [@gemhome, gemhome2].join ':'
Gem.clear_paths
Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new
inst.install 'b'
end
assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
end
def test_install_remote def test_install_remote
a1_data = nil a1_data = nil
File.open @a1_gem, 'rb' do |fp| File.open @a1_gem, 'rb' do |fp|
@ -337,7 +423,9 @@ class TestGemDependencyInstaller < RubyGemTestCase
s.platform = Gem::Platform.new %w[cpu other_platform 1] s.platform = Gem::Platform.new %w[cpu other_platform 1]
end end
si = util_setup_source_info_cache @a1, a2_o util_clear_gems
si = util_setup_spec_fetcher @a1, a2_o
@fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml @fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml
@ -439,7 +527,7 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = Gem::DependencyInstaller.new inst = Gem::DependencyInstaller.new
dep = Gem::Dependency.new 'b', '>= 0' dep = Gem::Dependency.new 'b', '>= 0'
assert_equal [[@b1, 'http://gems.example.com']], assert_equal [[@b1, @gem_repo]],
inst.find_gems_with_sources(dep) inst.find_gems_with_sources(dep)
end end
@ -456,7 +544,7 @@ class TestGemDependencyInstaller < RubyGemTestCase
assert_equal 2, gems.length assert_equal 2, gems.length
remote = gems.first remote = gems.first
assert_equal 'a-1', remote.first.full_name, 'remote spec' assert_equal 'a-1', remote.first.full_name, 'remote spec'
assert_equal 'http://gems.example.com', remote.last, 'remote path' assert_equal @gem_repo, remote.last, 'remote path'
local = gems.last local = gems.last
assert_equal 'a-1', local.first.full_name, 'local spec' assert_equal 'a-1', local.first.full_name, 'local spec'
@ -476,12 +564,9 @@ class TestGemDependencyInstaller < RubyGemTestCase
b2, = util_gem 'b', '2' b2, = util_gem 'b', '2'
c1, = util_gem 'c', '1' do |s| s.add_dependency 'b' end c1, = util_gem 'c', '1' do |s| s.add_dependency 'b' end
si = util_setup_source_info_cache @a1, @b1, b2, c1 util_clear_gems
@fetcher = FakeFetcher.new si = util_setup_spec_fetcher @a1, @b1, b2, c1
Gem::RemoteFetcher.instance_variable_set :@fetcher, @fetcher
@fetcher.uri = URI.parse 'http://gems.example.com'
@fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml
inst = Gem::DependencyInstaller.new inst = Gem::DependencyInstaller.new
inst.find_spec_by_name_and_version 'c' inst.find_spec_by_name_and_version 'c'
@ -512,12 +597,9 @@ class TestGemDependencyInstaller < RubyGemTestCase
def test_gather_dependencies_old_required def test_gather_dependencies_old_required
e1, = util_gem 'e', '1' do |s| s.add_dependency 'd', '= 1' end e1, = util_gem 'e', '1' do |s| s.add_dependency 'd', '= 1' end
si = util_setup_source_info_cache @d1, @d2, e1 util_clear_gems
@fetcher = FakeFetcher.new si = util_setup_spec_fetcher @d1, @d2, e1
Gem::RemoteFetcher.instance_variable_set :@fetcher, @fetcher
@fetcher.uri = URI.parse 'http://gems.example.com'
@fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml
inst = Gem::DependencyInstaller.new inst = Gem::DependencyInstaller.new
inst.find_spec_by_name_and_version 'e' inst.find_spec_by_name_and_version 'e'
@ -525,5 +607,6 @@ class TestGemDependencyInstaller < RubyGemTestCase
assert_equal %w[d-1 e-1], inst.gems_to_install.map { |s| s.full_name } assert_equal %w[d-1 e-1], inst.gems_to_install.map { |s| s.full_name }
end end
end end

View File

@ -28,7 +28,10 @@ class TestGemGemPathSearcher < RubyGemTestCase
@bar1 = quick_gem 'bar', '0.1' @bar1 = quick_gem 'bar', '0.1'
@bar2 = quick_gem 'bar', '0.2' @bar2 = quick_gem 'bar', '0.2'
Gem.source_index = util_setup_source_info_cache @foo1, @foo2, @bar1, @bar2 @fetcher = Gem::FakeFetcher.new
Gem::RemoteFetcher.fetcher = @fetcher
Gem.source_index = util_setup_spec_fetcher @foo1, @foo2, @bar1, @bar2
@gps = Gem::GemPathSearcher.new @gps = Gem::GemPathSearcher.new
end end

View File

@ -20,6 +20,10 @@ class TestGemIndexer < RubyGemTestCase
util_make_gems util_make_gems
@d2_0 = quick_gem 'd', '2.0'
write_file File.join(*%W[gems #{@d2_0.original_name} lib code.rb]) do end
util_build_gem @d2_0
gems = File.join(@tempdir, 'gems') gems = File.join(@tempdir, 'gems')
FileUtils.mkdir_p gems FileUtils.mkdir_p gems
cache_gems = File.join @gemhome, 'cache', '*.gem' cache_gems = File.join @gemhome, 'cache', '*.gem'
@ -39,10 +43,10 @@ class TestGemIndexer < RubyGemTestCase
@indexer.generate_index @indexer.generate_index
end end
assert File.exist?(File.join(@tempdir, 'yaml')) assert_indexed @tempdir, 'yaml'
assert File.exist?(File.join(@tempdir, 'yaml.Z')) assert_indexed @tempdir, 'yaml.Z'
assert File.exist?(File.join(@tempdir, "Marshal.#{@marshal_version}")) assert_indexed @tempdir, "Marshal.#{@marshal_version}"
assert File.exist?(File.join(@tempdir, "Marshal.#{@marshal_version}.Z")) assert_indexed @tempdir, "Marshal.#{@marshal_version}.Z"
quickdir = File.join @tempdir, 'quick' quickdir = File.join @tempdir, 'quick'
marshal_quickdir = File.join quickdir, "Marshal.#{@marshal_version}" marshal_quickdir = File.join quickdir, "Marshal.#{@marshal_version}"
@ -53,10 +57,33 @@ class TestGemIndexer < RubyGemTestCase
assert_indexed quickdir, "index" assert_indexed quickdir, "index"
assert_indexed quickdir, "index.rz" assert_indexed quickdir, "index.rz"
quick_index = File.read File.join(quickdir, 'index')
expected = <<-EOF
a-1
a-2
a_evil-9
b-2
c-1.2
d-2.0
pl-1-i386-linux
EOF
assert_equal expected, quick_index
assert_indexed quickdir, "latest_index" assert_indexed quickdir, "latest_index"
assert_indexed quickdir, "latest_index.rz" assert_indexed quickdir, "latest_index.rz"
assert_no_match %r|a-1|, File.read(File.join(quickdir, 'latest_index')) latest_quick_index = File.read File.join(quickdir, 'latest_index')
expected = <<-EOF
a-2
a_evil-9
b-2
c-1.2
d-2.0
pl-1-i386-linux
EOF
assert_equal expected, latest_quick_index
assert_indexed quickdir, "#{@a1.full_name}.gemspec.rz" assert_indexed quickdir, "#{@a1.full_name}.gemspec.rz"
assert_indexed quickdir, "#{@a2.full_name}.gemspec.rz" assert_indexed quickdir, "#{@a2.full_name}.gemspec.rz"
@ -64,13 +91,19 @@ class TestGemIndexer < RubyGemTestCase
assert_indexed quickdir, "#{@c1_2.full_name}.gemspec.rz" assert_indexed quickdir, "#{@c1_2.full_name}.gemspec.rz"
assert_indexed quickdir, "#{@pl1.original_name}.gemspec.rz" assert_indexed quickdir, "#{@pl1.original_name}.gemspec.rz"
deny_indexed quickdir, "#{@pl1.full_name}.gemspec.rz" refute_indexed quickdir, "#{@pl1.full_name}.gemspec.rz"
assert_indexed marshal_quickdir, "#{@a1.full_name}.gemspec.rz" assert_indexed marshal_quickdir, "#{@a1.full_name}.gemspec.rz"
assert_indexed marshal_quickdir, "#{@a2.full_name}.gemspec.rz" assert_indexed marshal_quickdir, "#{@a2.full_name}.gemspec.rz"
deny_indexed quickdir, "#{@c1_2.full_name}.gemspec" refute_indexed quickdir, "#{@c1_2.full_name}.gemspec"
deny_indexed marshal_quickdir, "#{@c1_2.full_name}.gemspec" refute_indexed marshal_quickdir, "#{@c1_2.full_name}.gemspec"
assert_indexed @tempdir, "specs.#{@marshal_version}"
assert_indexed @tempdir, "specs.#{@marshal_version}.gz"
assert_indexed @tempdir, "latest_specs.#{@marshal_version}"
assert_indexed @tempdir, "latest_specs.#{@marshal_version}.gz"
end end
def test_generate_index_ui def test_generate_index_ui
@ -79,28 +112,37 @@ class TestGemIndexer < RubyGemTestCase
end end
expected = <<-EOF expected = <<-EOF
Generating index for 6 gems in #{@tempdir} Loading 7 gems from #{@tempdir}
...... .......
Loaded all gems Loaded all gems
Generating master indexes (this may take a while) Generating quick index gemspecs for 7 gems
.......
Complete
Generating specs index
Generating latest specs index
Generating quick index
Generating latest index
Generating Marshal master index
Generating YAML master index for 7 gems (this may take a while)
.......
Complete
Compressing indicies
EOF EOF
assert_equal expected, @ui.output assert_equal expected, @ui.output
assert_equal '', @ui.error assert_equal '', @ui.error
end end
def test_generate_index_contents def test_generate_index_master
use_ui @ui do use_ui @ui do
@indexer.generate_index @indexer.generate_index
end end
yaml_path = File.join(@tempdir, 'yaml') yaml_path = File.join @tempdir, 'yaml'
dump_path = File.join(@tempdir, "Marshal.#{@marshal_version}") dump_path = File.join @tempdir, "Marshal.#{@marshal_version}"
yaml_index = YAML.load_file(yaml_path) yaml_index = YAML.load_file yaml_path
dump_str = nil dump_index = Marshal.load Gem.read_binary(dump_path)
File.open dump_path, 'rb' do |fp| dump_str = fp.read end
dump_index = Marshal.load dump_str
dump_index.each do |_,gem| dump_index.each do |_,gem|
gem.send :remove_instance_variable, :@loaded gem.send :remove_instance_variable, :@loaded
@ -110,12 +152,75 @@ Generating master indexes (this may take a while)
"expected YAML and Marshal to produce identical results" "expected YAML and Marshal to produce identical results"
end end
def test_generate_index_specs
use_ui @ui do
@indexer.generate_index
end
specs_path = File.join @tempdir, "specs.#{@marshal_version}"
specs_dump = Gem.read_binary specs_path
specs = Marshal.load specs_dump
expected = [
['a', Gem::Version.new(1), 'ruby'],
['a', Gem::Version.new(2), 'ruby'],
['a_evil', Gem::Version.new(9), 'ruby'],
['b', Gem::Version.new(2), 'ruby'],
['c', Gem::Version.new('1.2'), 'ruby'],
['d', Gem::Version.new('2.0'), 'ruby'],
['pl', Gem::Version.new(1), 'i386-linux'],
]
assert_equal expected, specs
assert_same specs[0].first, specs[1].first,
'identical names not identical'
assert_same specs[0][1], specs[-1][1],
'identical versions not identical'
assert_same specs[0].last, specs[1].last,
'identical platforms not identical'
assert_not_same specs[1][1], specs[5][1],
'different versions not different'
end
def test_generate_index_latest_specs
use_ui @ui do
@indexer.generate_index
end
latest_specs_path = File.join @tempdir, "latest_specs.#{@marshal_version}"
latest_specs_dump = Gem.read_binary latest_specs_path
latest_specs = Marshal.load latest_specs_dump
expected = [
['a', Gem::Version.new(2), 'ruby'],
['a_evil', Gem::Version.new(9), 'ruby'],
['b', Gem::Version.new(2), 'ruby'],
['c', Gem::Version.new('1.2'), 'ruby'],
['d', Gem::Version.new('2.0'), 'ruby'],
['pl', Gem::Version.new(1), 'i386-linux'],
]
assert_equal expected, latest_specs
assert_same latest_specs[0][1], latest_specs[2][1],
'identical versions not identical'
assert_same latest_specs[0].last, latest_specs[1].last,
'identical platforms not identical'
end
def assert_indexed(dir, name) def assert_indexed(dir, name)
file = File.join dir, name file = File.join dir, name
assert File.exist?(file), "#{file} does not exist" assert File.exist?(file), "#{file} does not exist"
end end
def deny_indexed(dir, name) def refute_indexed(dir, name)
file = File.join dir, name file = File.join dir, name
assert !File.exist?(file), "#{file} exists" assert !File.exist?(file), "#{file} exists"
end end

View File

@ -102,7 +102,7 @@ load 'my_exec'
@installer.ensure_dependency @spec, dep @installer.ensure_dependency @spec, dep
end end
assert_equal 'a requires b (> 2)', e.message assert_equal 'a requires b (> 2, runtime)', e.message
end end
def test_expand_and_validate_gem_dir def test_expand_and_validate_gem_dir
@ -128,7 +128,12 @@ load 'my_exec'
@installer.extract_files @installer.extract_files
assert_equal 'thefile', File.read(File.join(util_gem_dir, 'thefile')) thefile_path = File.join(util_gem_dir, 'thefile')
assert_equal 'thefile', File.read(thefile_path)
unless Gem.win_platform? then
assert_equal 0400, File.stat(thefile_path).mode & 0777
end
end end
def test_extract_files_bad_dest def test_extract_files_bad_dest
@ -313,6 +318,29 @@ load 'my_exec'
#assert_no_match %r|generated by RubyGems|, wrapper #assert_no_match %r|generated by RubyGems|, wrapper
end end
def test_generate_bin_script_wrappers
@installer.wrappers = true
util_make_exec
@installer.gem_dir = util_gem_dir
installed_exec = File.join(util_inst_bindir, "my_exec")
real_exec = File.join util_gem_dir, 'bin', 'my_exec'
# fake --no-wrappers for previous install
FileUtils.mkdir_p File.dirname(installed_exec)
FileUtils.ln_s real_exec, installed_exec
@installer.generate_bin
assert_equal true, File.directory?(util_inst_bindir)
assert_equal true, File.exist?(installed_exec)
assert_equal(0100755, File.stat(installed_exec).mode) unless win_platform?
assert_match %r|generated by RubyGems|, File.read(installed_exec)
assert_no_match %r|generated by RubyGems|, File.read(real_exec),
'real executable overwritten'
end
def test_generate_bin_symlink def test_generate_bin_symlink
return if win_platform? #Windows FS do not support symlinks return if win_platform? #Windows FS do not support symlinks

View File

@ -46,12 +46,13 @@ class TestGemLocalRemoteOptions < RubyGemTestCase
def test_source_option def test_source_option
@cmd.add_source_option @cmd.add_source_option
s1 = URI.parse 'http://more-gems.example.com' s1 = URI.parse 'http://more-gems.example.com/'
s2 = URI.parse 'http://even-more-gems.example.com' s2 = URI.parse 'http://even-more-gems.example.com/'
s3 = URI.parse 'http://other-gems.example.com/some_subdir'
@cmd.handle_options %W[--source #{s1} --source #{s2}] @cmd.handle_options %W[--source #{s1} --source #{s2} --source #{s3}]
assert_equal [s1, s2], Gem.sources assert_equal [s1.to_s, s2.to_s, "#{s3}/"], Gem.sources
end end
def test_update_sources_option def test_update_sources_option
@ -77,7 +78,7 @@ class TestGemLocalRemoteOptions < RubyGemTestCase
@cmd.handle_options %W[--source #{s1}] @cmd.handle_options %W[--source #{s1}]
end end
assert_equal %w[http://gems.example.com], Gem.sources assert_equal [@gem_repo], Gem.sources
end end
end end

View File

@ -24,7 +24,7 @@ require 'rubygems/remote_fetcher'
# Note that the proxy server is not a *real* proxy server. But our # Note that the proxy server is not a *real* proxy server. But our
# software doesn't really care, as long as we hit the proxy URL when a # software doesn't really care, as long as we hit the proxy URL when a
# proxy is configured. # proxy is configured.
#
class TestGemRemoteFetcher < RubyGemTestCase class TestGemRemoteFetcher < RubyGemTestCase
include Gem::DefaultUserInteraction include Gem::DefaultUserInteraction
@ -105,7 +105,7 @@ gems:
@a1, @a1_gem = util_gem 'a', '1' do |s| s.executables << 'a_bin' end @a1, @a1_gem = util_gem 'a', '1' do |s| s.executables << 'a_bin' end
Gem::RemoteFetcher.instance_variable_set :@fetcher, nil Gem::RemoteFetcher.fetcher = nil
end end
def test_self_fetcher def test_self_fetcher
@ -144,7 +144,7 @@ gems:
def test_fetch_size_socket_error def test_fetch_size_socket_error
fetcher = Gem::RemoteFetcher.new nil fetcher = Gem::RemoteFetcher.new nil
def fetcher.connect_to(host, port) def fetcher.connection_for(uri)
raise SocketError raise SocketError
end end
@ -153,7 +153,8 @@ gems:
fetcher.fetch_size uri fetcher.fetch_size uri
end end
assert_equal "SocketError (SocketError)\n\tgetting size of #{uri}", e.message assert_equal "SocketError (SocketError)\n\tfetching size (#{uri})",
e.message
end end
def test_no_proxy def test_no_proxy
@ -182,7 +183,7 @@ gems:
@test_data @test_data
end end
raise Gem::RemoteFetcher::FetchError, "haha!" raise Gem::RemoteFetcher::FetchError.new("haha!", nil)
end end
end end
@ -371,7 +372,8 @@ gems:
fetcher.fetch_path 'uri' fetcher.fetch_path 'uri'
end end
assert_equal 'EOFError: EOFError reading uri', e.message assert_equal 'EOFError: EOFError (uri)', e.message
assert_equal 'uri', e.uri
end end
def test_fetch_path_socket_error def test_fetch_path_socket_error
@ -383,7 +385,8 @@ gems:
fetcher.fetch_path 'uri' fetcher.fetch_path 'uri'
end end
assert_equal 'SocketError: SocketError reading uri', e.message assert_equal 'SocketError: SocketError (uri)', e.message
assert_equal 'uri', e.uri
end end
def test_fetch_path_system_call_error def test_fetch_path_system_call_error
@ -397,8 +400,9 @@ gems:
fetcher.fetch_path 'uri' fetcher.fetch_path 'uri'
end end
assert_match %r|ECONNREFUSED:.*connect\(2\) reading uri\z|, assert_match %r|ECONNREFUSED:.*connect\(2\) \(uri\)\z|,
e.message e.message
assert_equal 'uri', e.uri
end end
def test_get_proxy_from_env_empty def test_get_proxy_from_env_empty
@ -494,7 +498,8 @@ gems:
fetcher.send :open_uri_or_path, 'http://gems.example.com/redirect' fetcher.send :open_uri_or_path, 'http://gems.example.com/redirect'
end end
assert_equal 'too many redirects', e.message assert_equal 'too many redirects (http://gems.example.com/redirect)',
e.message
end end
def test_zip def test_zip

View File

@ -14,12 +14,121 @@ class TestGemServer < RubyGemTestCase
super super
@a1 = quick_gem 'a', '1' @a1 = quick_gem 'a', '1'
@a2 = quick_gem 'a', '2'
@server = Gem::Server.new Gem.dir, process_based_port, false @server = Gem::Server.new Gem.dir, process_based_port, false
@req = WEBrick::HTTPRequest.new :Logger => nil @req = WEBrick::HTTPRequest.new :Logger => nil
@res = WEBrick::HTTPResponse.new :HTTPVersion => '1.0' @res = WEBrick::HTTPResponse.new :HTTPVersion => '1.0'
end end
def test_Marshal
data = StringIO.new "GET /Marshal.#{Gem.marshal_version} HTTP/1.0\r\n\r\n"
@req.parse data
@server.Marshal @req, @res
assert_equal 200, @res.status, @res.body
assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'application/octet-stream', @res['content-type']
si = Gem::SourceIndex.new
si.add_specs @a1, @a2
assert_equal si, Marshal.load(@res.body)
end
def test_Marshal_Z
data = StringIO.new "GET /Marshal.#{Gem.marshal_version}.Z HTTP/1.0\r\n\r\n"
@req.parse data
@server.Marshal @req, @res
assert_equal 200, @res.status, @res.body
assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'application/x-deflate', @res['content-type']
si = Gem::SourceIndex.new
si.add_specs @a1, @a2
assert_equal si, Marshal.load(Gem.inflate(@res.body))
end
def test_latest_specs
data = StringIO.new "GET /latest_specs.#{Gem.marshal_version} HTTP/1.0\r\n\r\n"
@req.parse data
@server.latest_specs @req, @res
assert_equal 200, @res.status, @res.body
assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'application/octet-stream', @res['content-type']
assert_equal [['a', Gem::Version.new(2), Gem::Platform::RUBY]],
Marshal.load(@res.body)
end
def test_latest_specs_gz
data = StringIO.new "GET /latest_specs.#{Gem.marshal_version}.gz HTTP/1.0\r\n\r\n"
@req.parse data
@server.latest_specs @req, @res
assert_equal 200, @res.status, @res.body
assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'application/x-gzip', @res['content-type']
assert_equal [['a', Gem::Version.new(2), Gem::Platform::RUBY]],
Marshal.load(Gem.gunzip(@res.body))
end
def test_quick_a_1_gemspec_rz
data = StringIO.new "GET /quick/a-1.gemspec.rz HTTP/1.0\r\n\r\n"
@req.parse data
@server.quick @req, @res
assert_equal 200, @res.status, @res.body
assert @res['date']
assert_equal 'application/x-deflate', @res['content-type']
spec = YAML.load Gem.inflate(@res.body)
assert_equal 'a', spec.name
assert_equal Gem::Version.new(1), spec.version
end
def test_quick_a_1_mswin32_gemspec_rz
a1_p = quick_gem 'a', '1' do |s| s.platform = Gem::Platform.local end
data = StringIO.new "GET /quick/a-1-#{Gem::Platform.local}.gemspec.rz HTTP/1.0\r\n\r\n"
@req.parse data
@server.quick @req, @res
assert_equal 200, @res.status, @res.body
assert @res['date']
assert_equal 'application/x-deflate', @res['content-type']
spec = YAML.load Gem.inflate(@res.body)
assert_equal 'a', spec.name
assert_equal Gem::Version.new(1), spec.version
assert_equal Gem::Platform.local, spec.platform
end
def test_quick_common_substrings
ab1 = quick_gem 'ab', '1'
data = StringIO.new "GET /quick/a-1.gemspec.rz HTTP/1.0\r\n\r\n"
@req.parse data
@server.quick @req, @res
assert_equal 200, @res.status, @res.body
assert @res['date']
assert_equal 'application/x-deflate', @res['content-type']
spec = YAML.load Gem.inflate(@res.body)
assert_equal 'a', spec.name
assert_equal Gem::Version.new(1), spec.version
end
def test_quick_index def test_quick_index
data = StringIO.new "GET /quick/index HTTP/1.0\r\n\r\n" data = StringIO.new "GET /quick/index HTTP/1.0\r\n\r\n"
@req.parse data @req.parse data
@ -29,7 +138,7 @@ class TestGemServer < RubyGemTestCase
assert_equal 200, @res.status, @res.body assert_equal 200, @res.status, @res.body
assert_match %r| \d\d:\d\d:\d\d |, @res['date'] assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'text/plain', @res['content-type'] assert_equal 'text/plain', @res['content-type']
assert_equal "a-1", @res.body assert_equal "a-1\na-2", @res.body
end end
def test_quick_index_rz def test_quick_index_rz
@ -40,65 +149,35 @@ class TestGemServer < RubyGemTestCase
assert_equal 200, @res.status, @res.body assert_equal 200, @res.status, @res.body
assert_match %r| \d\d:\d\d:\d\d |, @res['date'] assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'text/plain', @res['content-type'] assert_equal 'application/x-deflate', @res['content-type']
assert_equal "a-1", Zlib::Inflate.inflate(@res.body) assert_equal "a-1\na-2", Gem.inflate(@res.body)
end end
def test_quick_a_1_gemspec_rz def test_quick_latest_index
data = StringIO.new "GET /quick/a-1.gemspec.rz HTTP/1.0\r\n\r\n" data = StringIO.new "GET /quick/latest_index HTTP/1.0\r\n\r\n"
@req.parse data @req.parse data
@server.quick @req, @res @server.quick @req, @res
assert_equal 200, @res.status, @res.body assert_equal 200, @res.status, @res.body
assert @res['date'] assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'text/plain', @res['content-type'] assert_equal 'text/plain', @res['content-type']
assert_equal 'a-2', @res.body
spec = YAML.load Zlib::Inflate.inflate(@res.body)
assert_equal 'a', spec.name
assert_equal Gem::Version.new(1), spec.version
end end
def test_quick_a_1_mswin32_gemspec_rz def test_quick_latest_index_rz
a1_p = quick_gem 'a', '1' do |s| s.platform = Gem::Platform.local end data = StringIO.new "GET /quick/latest_index.rz HTTP/1.0\r\n\r\n"
si = Gem::SourceIndex.new @a1.full_name => @a1, a1_p.full_name => a1_p
@server.source_index = si
data = StringIO.new "GET /quick/a-1-#{Gem::Platform.local}.gemspec.rz HTTP/1.0\r\n\r\n"
@req.parse data @req.parse data
@server.quick @req, @res @server.quick @req, @res
assert_equal 200, @res.status, @res.body assert_equal 200, @res.status, @res.body
assert @res['date'] assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'text/plain', @res['content-type'] assert_equal 'application/x-deflate', @res['content-type']
assert_equal 'a-2', Gem.inflate(@res.body)
spec = YAML.load Zlib::Inflate.inflate(@res.body)
assert_equal 'a', spec.name
assert_equal Gem::Version.new(1), spec.version
assert_equal Gem::Platform.local, spec.platform
end end
def test_quick_common_substrings def test_quick_missing
ab1 = quick_gem 'ab', '1'
si = Gem::SourceIndex.new @a1.full_name => @a1, ab1.full_name => ab1
@server.source_index = si
data = StringIO.new "GET /quick/a-1.gemspec.rz HTTP/1.0\r\n\r\n"
@req.parse data
@server.quick @req, @res
assert_equal 200, @res.status, @res.body
assert @res['date']
assert_equal 'text/plain', @res['content-type']
spec = YAML.load Zlib::Inflate.inflate(@res.body)
assert_equal 'a', spec.name
assert_equal Gem::Version.new(1), spec.version
end
def test_quick_z_9_gemspec_rz
data = StringIO.new "GET /quick/z-9.gemspec.rz HTTP/1.0\r\n\r\n" data = StringIO.new "GET /quick/z-9.gemspec.rz HTTP/1.0\r\n\r\n"
@req.parse data @req.parse data
@ -111,5 +190,112 @@ class TestGemServer < RubyGemTestCase
assert_equal 404, @res.status assert_equal 404, @res.status
end end
def test_quick_marshal_a_1_gemspec_rz
data = StringIO.new "GET /quick/Marshal.#{Gem.marshal_version}/a-1.gemspec.rz HTTP/1.0\r\n\r\n"
@req.parse data
@server.quick @req, @res
assert_equal 200, @res.status, @res.body
assert @res['date']
assert_equal 'application/x-deflate', @res['content-type']
spec = Marshal.load Gem.inflate(@res.body)
assert_equal 'a', spec.name
assert_equal Gem::Version.new(1), spec.version
end
def test_quick_marshal_a_1_mswin32_gemspec_rz
a1_p = quick_gem 'a', '1' do |s| s.platform = Gem::Platform.local end
data = StringIO.new "GET /quick/Marshal.#{Gem.marshal_version}/a-1-#{Gem::Platform.local}.gemspec.rz HTTP/1.0\r\n\r\n"
@req.parse data
@server.quick @req, @res
assert_equal 200, @res.status, @res.body
assert @res['date']
assert_equal 'application/x-deflate', @res['content-type']
spec = Marshal.load Gem.inflate(@res.body)
assert_equal 'a', spec.name
assert_equal Gem::Version.new(1), spec.version
assert_equal Gem::Platform.local, spec.platform
end
def test_root
data = StringIO.new "GET / HTTP/1.0\r\n\r\n"
@req.parse data
@server.root @req, @res
assert_equal 200, @res.status, @res.body
assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'text/html', @res['content-type']
end
def test_specs
data = StringIO.new "GET /specs.#{Gem.marshal_version} HTTP/1.0\r\n\r\n"
@req.parse data
@server.specs @req, @res
assert_equal 200, @res.status, @res.body
assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'application/octet-stream', @res['content-type']
assert_equal [['a', Gem::Version.new(1), Gem::Platform::RUBY],
['a', Gem::Version.new(2), Gem::Platform::RUBY]],
Marshal.load(@res.body)
end
def test_specs_gz
data = StringIO.new "GET /specs.#{Gem.marshal_version}.gz HTTP/1.0\r\n\r\n"
@req.parse data
@server.specs @req, @res
assert_equal 200, @res.status, @res.body
assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'application/x-gzip', @res['content-type']
assert_equal [['a', Gem::Version.new(1), Gem::Platform::RUBY],
['a', Gem::Version.new(2), Gem::Platform::RUBY]],
Marshal.load(Gem.gunzip(@res.body))
end
def test_yaml
data = StringIO.new "GET /yaml.#{Gem.marshal_version} HTTP/1.0\r\n\r\n"
@req.parse data
@server.yaml @req, @res
assert_equal 200, @res.status, @res.body
assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'text/plain', @res['content-type']
si = Gem::SourceIndex.new
si.add_specs @a1, @a2
assert_equal si, YAML.load(@res.body)
end
def test_yaml_Z
data = StringIO.new "GET /yaml.#{Gem.marshal_version}.Z HTTP/1.0\r\n\r\n"
@req.parse data
@server.yaml @req, @res
assert_equal 200, @res.status, @res.body
assert_match %r| \d\d:\d\d:\d\d |, @res['date']
assert_equal 'application/x-deflate', @res['content-type']
si = Gem::SourceIndex.new
si.add_specs @a1, @a2
assert_equal si, YAML.load(Gem.inflate(@res.body))
end
end end

View File

@ -23,6 +23,141 @@ class TestGemSourceIndex < RubyGemTestCase
util_setup_fake_fetcher util_setup_fake_fetcher
end end
def test_self_from_gems_in
spec_dir = File.join @gemhome, 'specifications'
FileUtils.rm_r spec_dir
FileUtils.mkdir_p spec_dir
a1 = quick_gem 'a', '1' do |spec| spec.author = 'author 1' end
spec_file = File.join spec_dir, "#{a1.full_name}.gemspec"
File.open spec_file, 'w' do |fp|
fp.write a1.to_ruby
end
si = Gem::SourceIndex.from_gems_in spec_dir
assert_equal [spec_dir], si.spec_dirs
assert_equal [a1.full_name], si.gems.keys
end
def test_self_load_specification
spec_dir = File.join @gemhome, 'specifications'
FileUtils.rm_r spec_dir
FileUtils.mkdir_p spec_dir
a1 = quick_gem 'a', '1' do |spec| spec.author = 'author 1' end
spec_file = File.join spec_dir, "#{a1.full_name}.gemspec"
File.open spec_file, 'w' do |fp|
fp.write a1.to_ruby
end
spec = Gem::SourceIndex.load_specification spec_file
assert_equal a1.author, spec.author
end
def test_self_load_specification_exception
spec_dir = File.join @gemhome, 'specifications'
FileUtils.mkdir_p spec_dir
spec_file = File.join spec_dir, 'a-1.gemspec'
File.open spec_file, 'w' do |fp|
fp.write 'raise Exception, "epic fail"'
end
use_ui @ui do
assert_equal nil, Gem::SourceIndex.load_specification(spec_file)
end
assert_equal '', @ui.output
expected = <<-EOF
WARNING: #<Exception: epic fail>
raise Exception, "epic fail"
WARNING: Invalid .gemspec format in '#{spec_file}'
EOF
assert_equal expected, @ui.error
end
def test_self_load_specification_interrupt
spec_dir = File.join @gemhome, 'specifications'
FileUtils.mkdir_p spec_dir
spec_file = File.join spec_dir, 'a-1.gemspec'
File.open spec_file, 'w' do |fp|
fp.write 'raise Interrupt, "^C"'
end
use_ui @ui do
assert_raise Interrupt do
Gem::SourceIndex.load_specification(spec_file)
end
end
assert_equal '', @ui.output
assert_equal '', @ui.error
end
def test_self_load_specification_syntax_error
spec_dir = File.join @gemhome, 'specifications'
FileUtils.mkdir_p spec_dir
spec_file = File.join spec_dir, 'a-1.gemspec'
File.open spec_file, 'w' do |fp|
fp.write '1 +'
end
use_ui @ui do
assert_equal nil, Gem::SourceIndex.load_specification(spec_file)
end
assert_equal '', @ui.output
expected = <<-EOF
WARNING: compile error
#{spec_file}:1: syntax error, unexpected $end
WARNING: 1 +
EOF
assert_equal expected, @ui.error
end
def test_self_load_specification_system_exit
spec_dir = File.join @gemhome, 'specifications'
FileUtils.mkdir_p spec_dir
spec_file = File.join spec_dir, 'a-1.gemspec'
File.open spec_file, 'w' do |fp|
fp.write 'raise SystemExit, "bye-bye"'
end
use_ui @ui do
assert_raise SystemExit do
Gem::SourceIndex.load_specification(spec_file)
end
end
assert_equal '', @ui.output
assert_equal '', @ui.error
end
def test_create_from_directory def test_create_from_directory
# TODO # TODO
end end
@ -43,16 +178,16 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift assert_equal "#{@gem_repo}Marshal.#{@marshal_version}.Z", paths.shift
assert paths.empty?, paths.join(', ') assert paths.empty?, paths.join(', ')
end end
def test_fetch_bulk_index_error def test_fetch_bulk_index_error
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}.Z"] = proc { raise SocketError } @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}.Z"] = proc { raise SocketError }
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = proc { raise SocketError } @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}"] = proc { raise SocketError }
@fetcher.data["#{@gem_repo}/yaml.Z"] = proc { raise SocketError } @fetcher.data["#{@gem_repo}yaml.Z"] = proc { raise SocketError }
@fetcher.data["#{@gem_repo}/yaml"] = proc { raise SocketError } @fetcher.data["#{@gem_repo}yaml"] = proc { raise SocketError }
e = assert_raise Gem::RemoteSourceException do e = assert_raise Gem::RemoteSourceException do
use_ui @ui do use_ui @ui do
@ -62,10 +197,10 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift assert_equal "#{@gem_repo}Marshal.#{@marshal_version}.Z", paths.shift
assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}", paths.shift assert_equal "#{@gem_repo}Marshal.#{@marshal_version}", paths.shift
assert_equal "#{@gem_repo}/yaml.Z", paths.shift assert_equal "#{@gem_repo}yaml.Z", paths.shift
assert_equal "#{@gem_repo}/yaml", paths.shift assert_equal "#{@gem_repo}yaml", paths.shift
assert paths.empty?, paths.join(', ') assert paths.empty?, paths.join(', ')
@ -74,12 +209,12 @@ class TestGemSourceIndex < RubyGemTestCase
end end
def test_fetch_bulk_index_fallback def test_fetch_bulk_index_fallback
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}.Z"] = @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}.Z"] =
proc { raise SocketError } proc { raise SocketError }
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}"] =
proc { raise SocketError } proc { raise SocketError }
@fetcher.data["#{@gem_repo}/yaml.Z"] = proc { raise SocketError } @fetcher.data["#{@gem_repo}yaml.Z"] = proc { raise SocketError }
@fetcher.data["#{@gem_repo}/yaml"] = @source_index.to_yaml @fetcher.data["#{@gem_repo}yaml"] = @source_index.to_yaml
use_ui @ui do use_ui @ui do
fetched_index = @source_index.fetch_bulk_index @uri fetched_index = @source_index.fetch_bulk_index @uri
@ -90,10 +225,10 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift assert_equal "#{@gem_repo}Marshal.#{@marshal_version}.Z", paths.shift
assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}", paths.shift assert_equal "#{@gem_repo}Marshal.#{@marshal_version}", paths.shift
assert_equal "#{@gem_repo}/yaml.Z", paths.shift assert_equal "#{@gem_repo}yaml.Z", paths.shift
assert_equal "#{@gem_repo}/yaml", paths.shift assert_equal "#{@gem_repo}yaml", paths.shift
assert paths.empty?, paths.join(', ') assert paths.empty?, paths.join(', ')
end end
@ -102,8 +237,8 @@ class TestGemSourceIndex < RubyGemTestCase
marshal = @source_index.dump marshal = @source_index.dump
marshal[0] = (Marshal::MAJOR_VERSION - 1).chr marshal[0] = (Marshal::MAJOR_VERSION - 1).chr
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = marshal @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}"] = marshal
@fetcher.data["#{@gem_repo}/yaml"] = @source_index.to_yaml @fetcher.data["#{@gem_repo}yaml"] = @source_index.to_yaml
use_ui @ui do use_ui @ui do
fetched_index = @source_index.fetch_bulk_index @uri fetched_index = @source_index.fetch_bulk_index @uri
@ -114,10 +249,10 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift assert_equal "#{@gem_repo}Marshal.#{@marshal_version}.Z", paths.shift
assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}", paths.shift assert_equal "#{@gem_repo}Marshal.#{@marshal_version}", paths.shift
assert_equal "#{@gem_repo}/yaml.Z", paths.shift assert_equal "#{@gem_repo}yaml.Z", paths.shift
assert_equal "#{@gem_repo}/yaml", paths.shift assert_equal "#{@gem_repo}yaml", paths.shift
assert paths.empty?, paths.join(', ') assert paths.empty?, paths.join(', ')
end end
@ -133,8 +268,8 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift assert_equal "#{@gem_repo}Marshal.#{@marshal_version}.Z", paths.shift
assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}", paths.shift assert_equal "#{@gem_repo}Marshal.#{@marshal_version}", paths.shift
assert paths.empty?, paths.join(', ') assert paths.empty?, paths.join(', ')
end end
@ -143,8 +278,8 @@ class TestGemSourceIndex < RubyGemTestCase
index = util_zip @gem_names index = util_zip @gem_names
latest_index = util_zip [@a2.full_name, @b2.full_name].join("\n") latest_index = util_zip [@a2.full_name, @b2.full_name].join("\n")
@fetcher.data["#{@gem_repo}/quick/index.rz"] = index @fetcher.data["#{@gem_repo}quick/index.rz"] = index
@fetcher.data["#{@gem_repo}/quick/latest_index.rz"] = latest_index @fetcher.data["#{@gem_repo}quick/latest_index.rz"] = latest_index
quick_index = @source_index.fetch_quick_index @uri, false quick_index = @source_index.fetch_quick_index @uri, false
assert_equal [@a2.full_name, @b2.full_name].sort, assert_equal [@a2.full_name, @b2.full_name].sort,
@ -152,7 +287,7 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/quick/latest_index.rz", paths.shift assert_equal "#{@gem_repo}quick/latest_index.rz", paths.shift
assert paths.empty?, paths.join(', ') assert paths.empty?, paths.join(', ')
end end
@ -161,8 +296,8 @@ class TestGemSourceIndex < RubyGemTestCase
index = util_zip @gem_names index = util_zip @gem_names
latest_index = util_zip [@a2.full_name, @b2.full_name].join("\n") latest_index = util_zip [@a2.full_name, @b2.full_name].join("\n")
@fetcher.data["#{@gem_repo}/quick/index.rz"] = index @fetcher.data["#{@gem_repo}quick/index.rz"] = index
@fetcher.data["#{@gem_repo}/quick/latest_index.rz"] = latest_index @fetcher.data["#{@gem_repo}quick/latest_index.rz"] = latest_index
quick_index = @source_index.fetch_quick_index @uri, true quick_index = @source_index.fetch_quick_index @uri, true
assert_equal [@a1.full_name, @a2.full_name, @b2.full_name].sort, assert_equal [@a1.full_name, @a2.full_name, @b2.full_name].sort,
@ -170,13 +305,13 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/quick/index.rz", paths.shift assert_equal "#{@gem_repo}quick/index.rz", paths.shift
assert paths.empty?, paths.join(', ') assert paths.empty?, paths.join(', ')
end end
def test_fetch_quick_index_error def test_fetch_quick_index_error
@fetcher.data["#{@gem_repo}/quick/index.rz"] = @fetcher.data["#{@gem_repo}quick/index.rz"] =
proc { raise Exception } proc { raise Exception }
e = assert_raise Gem::OperationNotSupportedError do e = assert_raise Gem::OperationNotSupportedError do
@ -187,7 +322,7 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/quick/index.rz", paths.shift assert_equal "#{@gem_repo}quick/index.rz", paths.shift
assert paths.empty?, paths.join(', ') assert paths.empty?, paths.join(', ')
end end
@ -195,22 +330,22 @@ class TestGemSourceIndex < RubyGemTestCase
def test_fetch_quick_index_fallback def test_fetch_quick_index_fallback
index = util_zip @gem_names index = util_zip @gem_names
@fetcher.data["#{@gem_repo}/quick/index.rz"] = index @fetcher.data["#{@gem_repo}quick/index.rz"] = index
quick_index = @source_index.fetch_quick_index @uri, false quick_index = @source_index.fetch_quick_index @uri, false
assert_equal @gem_names.split, quick_index.sort assert_equal @gem_names.split, quick_index.sort
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/quick/latest_index.rz", paths.shift assert_equal "#{@gem_repo}quick/latest_index.rz", paths.shift
assert_equal "#{@gem_repo}/quick/index.rz", paths.shift assert_equal "#{@gem_repo}quick/index.rz", paths.shift
assert paths.empty?, paths.join(', ') assert paths.empty?, paths.join(', ')
end end
def test_fetch_quick_index_subdir def test_fetch_quick_index_subdir
latest_index = util_zip [@a2.full_name, @b2.full_name].join("\n") latest_index = util_zip [@a2.full_name, @b2.full_name].join("\n")
repo = URI.parse "#{@gem_repo}/~nobody/mirror/" repo = URI.parse "#{@gem_repo}~nobody/mirror/"
@fetcher.data["#{repo}quick/latest_index.rz"] = latest_index @fetcher.data["#{repo}quick/latest_index.rz"] = latest_index
@ -226,7 +361,7 @@ class TestGemSourceIndex < RubyGemTestCase
end end
def test_fetch_single_spec def test_fetch_single_spec
a1_spec_url = "#{@gem_repo}/quick/Marshal.#{Gem.marshal_version}/#{@a1.full_name}.gemspec.rz" a1_spec_url = "#{@gem_repo}quick/Marshal.#{Gem.marshal_version}/#{@a1.full_name}.gemspec.rz"
@fetcher.data[a1_spec_url] = util_zip Marshal.dump(@a1) @fetcher.data[a1_spec_url] = util_zip Marshal.dump(@a1)
spec = @source_index.send :fetch_single_spec, URI.parse(@gem_repo), spec = @source_index.send :fetch_single_spec, URI.parse(@gem_repo),
@ -242,7 +377,7 @@ class TestGemSourceIndex < RubyGemTestCase
end end
def test_fetch_single_spec_subdir def test_fetch_single_spec_subdir
repo = URI.parse "#{@gem_repo}/~nobody/mirror/" repo = URI.parse "#{@gem_repo}~nobody/mirror/"
a1_spec_url = "#{repo}quick/Marshal.#{Gem.marshal_version}/#{@a1.full_name}.gemspec.rz" a1_spec_url = "#{repo}quick/Marshal.#{Gem.marshal_version}/#{@a1.full_name}.gemspec.rz"
@fetcher.data[a1_spec_url] = util_zip Marshal.dump(@a1) @fetcher.data[a1_spec_url] = util_zip Marshal.dump(@a1)
@ -259,7 +394,7 @@ class TestGemSourceIndex < RubyGemTestCase
end end
def test_fetch_single_spec_yaml def test_fetch_single_spec_yaml
a1_spec_url = "#{@gem_repo}/quick/#{@a1.full_name}.gemspec.rz" a1_spec_url = "#{@gem_repo}quick/#{@a1.full_name}.gemspec.rz"
@fetcher.data[a1_spec_url] = util_zip @a1.to_yaml @fetcher.data[a1_spec_url] = util_zip @a1.to_yaml
repo = URI.parse @gem_repo repo = URI.parse @gem_repo
@ -270,14 +405,14 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/quick/Marshal.#{Gem.marshal_version}/#{@a1.full_name}.gemspec.rz", paths.shift assert_equal "#{@gem_repo}quick/Marshal.#{Gem.marshal_version}/#{@a1.full_name}.gemspec.rz", paths.shift
assert_equal a1_spec_url, paths.shift assert_equal a1_spec_url, paths.shift
assert paths.empty?, paths.join(', ') assert paths.empty?, paths.join(', ')
end end
def test_fetch_single_spec_yaml_subdir def test_fetch_single_spec_yaml_subdir
repo = URI.parse "#{@gem_repo}/~nobody/mirror/" repo = URI.parse "#{@gem_repo}~nobody/mirror/"
a1_spec_url = "#{repo}quick/#{@a1.full_name}.gemspec.rz" a1_spec_url = "#{repo}quick/#{@a1.full_name}.gemspec.rz"
@fetcher.data[a1_spec_url] = util_zip @a1.to_yaml @fetcher.data[a1_spec_url] = util_zip @a1.to_yaml
@ -377,12 +512,12 @@ class TestGemSourceIndex < RubyGemTestCase
end end
def test_outdated def test_outdated
util_setup_source_info_cache util_setup_spec_fetcher
assert_equal [], @source_index.outdated assert_equal [], @source_index.outdated
updated = quick_gem @a2.name, (@a2.version.bump) updated = quick_gem @a2.name, (@a2.version.bump)
util_setup_source_info_cache updated util_setup_spec_fetcher updated
assert_equal [updated.name], @source_index.outdated assert_equal [updated.name], @source_index.outdated
@ -390,7 +525,7 @@ class TestGemSourceIndex < RubyGemTestCase
s.platform = Gem::Platform.new 'x86-other_platform1' s.platform = Gem::Platform.new 'x86-other_platform1'
end end
util_setup_source_info_cache updated, updated_platform util_setup_spec_fetcher updated, updated_platform
assert_equal [updated_platform.name], @source_index.outdated assert_equal [updated_platform.name], @source_index.outdated
end end
@ -411,6 +546,16 @@ class TestGemSourceIndex < RubyGemTestCase
assert source_index.gems.include?(@a1.full_name) assert source_index.gems.include?(@a1.full_name)
end end
def test_refresh_bang_not_from_dir
source_index = Gem::SourceIndex.new
e = assert_raise RuntimeError do
source_index.refresh!
end
assert_equal 'source index not created from disk', e.message
end
def test_remove_extra def test_remove_extra
@source_index.add_spec @a1 @source_index.add_spec @a1
@source_index.add_spec @a2 @source_index.add_spec @a2
@ -516,8 +661,8 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/quick/index.rz", paths.shift assert_equal "#{@gem_repo}quick/index.rz", paths.shift
assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift assert_equal "#{@gem_repo}Marshal.#{@marshal_version}.Z", paths.shift
assert paths.empty?, paths.join(', ') assert paths.empty?, paths.join(', ')
end end
@ -528,7 +673,7 @@ class TestGemSourceIndex < RubyGemTestCase
latest_names = [@a2, @a_evil9, @b2, @c1_2].map { |s| s.full_name } latest_names = [@a2, @a_evil9, @b2, @c1_2].map { |s| s.full_name }
latest_index = util_zip latest_names.join("\n") latest_index = util_zip latest_names.join("\n")
@fetcher.data["#{@gem_repo}/quick/latest_index.rz"] = latest_index @fetcher.data["#{@gem_repo}quick/latest_index.rz"] = latest_index
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}", marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
"#{@b2.full_name}.gemspec.rz" "#{@b2.full_name}.gemspec.rz"
@ -541,7 +686,7 @@ class TestGemSourceIndex < RubyGemTestCase
end end
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/quick/latest_index.rz", paths.shift assert_equal "#{@gem_repo}quick/latest_index.rz", paths.shift
assert_equal marshal_uri, paths.shift assert_equal marshal_uri, paths.shift
assert paths.empty?, paths.join(', ') assert paths.empty?, paths.join(', ')
@ -554,7 +699,7 @@ class TestGemSourceIndex < RubyGemTestCase
Gem.configuration = Gem::ConfigFile.new([]) Gem.configuration = Gem::ConfigFile.new([])
quick_index = util_zip @all_gem_names.join("\n") quick_index = util_zip @all_gem_names.join("\n")
@fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index @fetcher.data["#{@gem_repo}quick/index.rz"] = quick_index
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}", marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
"#{@b2.full_name}.gemspec.rz" "#{@b2.full_name}.gemspec.rz"
@ -567,7 +712,7 @@ class TestGemSourceIndex < RubyGemTestCase
end end
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/quick/index.rz", paths.shift assert_equal "#{@gem_repo}quick/index.rz", paths.shift
assert_equal marshal_uri, paths.shift assert_equal marshal_uri, paths.shift
assert paths.empty?, paths.join(', ') assert paths.empty?, paths.join(', ')
@ -580,12 +725,12 @@ class TestGemSourceIndex < RubyGemTestCase
Gem.configuration = Gem::ConfigFile.new([]) Gem.configuration = Gem::ConfigFile.new([])
quick_index = util_zip @all_gem_names.join("\n") quick_index = util_zip @all_gem_names.join("\n")
@fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index @fetcher.data["#{@gem_repo}quick/index.rz"] = quick_index
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}", marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
"#{@b2.full_name}.gemspec.rz" "#{@b2.full_name}.gemspec.rz"
yaml_uri = "#{@gem_repo}/quick/#{@b2.full_name}.gemspec.rz" yaml_uri = "#{@gem_repo}quick/#{@b2.full_name}.gemspec.rz"
@fetcher.data[yaml_uri] = util_zip @b2.to_yaml @fetcher.data[yaml_uri] = util_zip @b2.to_yaml
use_ui @ui do use_ui @ui do
@ -595,7 +740,7 @@ class TestGemSourceIndex < RubyGemTestCase
end end
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/quick/index.rz", paths.shift assert_equal "#{@gem_repo}quick/index.rz", paths.shift
assert_equal marshal_uri, paths.shift assert_equal marshal_uri, paths.shift
assert_equal yaml_uri, paths.shift assert_equal yaml_uri, paths.shift
@ -609,7 +754,7 @@ class TestGemSourceIndex < RubyGemTestCase
Gem.configuration = Gem::ConfigFile.new([]) Gem.configuration = Gem::ConfigFile.new([])
quick_index = util_zip @all_gem_names.join("\n") quick_index = util_zip @all_gem_names.join("\n")
@fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index @fetcher.data["#{@gem_repo}quick/index.rz"] = quick_index
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}", marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
"#{@b2.full_name}.gemspec.rz" "#{@b2.full_name}.gemspec.rz"
@ -617,7 +762,7 @@ class TestGemSourceIndex < RubyGemTestCase
marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr
@fetcher.data[marshal_uri] = util_zip marshal_data @fetcher.data[marshal_uri] = util_zip marshal_data
yaml_uri = "#{@gem_repo}/quick/#{@b2.full_name}.gemspec.rz" yaml_uri = "#{@gem_repo}quick/#{@b2.full_name}.gemspec.rz"
@fetcher.data[yaml_uri] = util_zip @b2.to_yaml @fetcher.data[yaml_uri] = util_zip @b2.to_yaml
use_ui @ui do use_ui @ui do
@ -627,7 +772,7 @@ class TestGemSourceIndex < RubyGemTestCase
end end
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/quick/index.rz", paths.shift assert_equal "#{@gem_repo}quick/index.rz", paths.shift
assert_equal marshal_uri, paths.shift assert_equal marshal_uri, paths.shift
assert_equal yaml_uri, paths.shift assert_equal yaml_uri, paths.shift
@ -637,14 +782,14 @@ class TestGemSourceIndex < RubyGemTestCase
end end
def test_update_subdir def test_update_subdir
@gem_repo = @gem_repo + "/subdir" @gem_repo = @gem_repo + 'subdir/'
util_setup_bulk_fetch true util_setup_bulk_fetch true
@source_index.gems.replace({}) @source_index.gems.replace({})
assert_equal [], @source_index.gems.keys.sort assert_equal [], @source_index.gems.keys.sort
uri = @uri.to_s + "/subdir" uri = @uri.to_s + 'subdir/'
use_ui @ui do use_ui @ui do
@source_index.update uri, true @source_index.update uri, true
@ -656,8 +801,8 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths paths = @fetcher.paths
assert_equal "#{@gem_repo}/quick/index.rz", paths.shift assert_equal "#{@gem_repo}quick/index.rz", paths.shift
assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift assert_equal "#{@gem_repo}Marshal.#{@marshal_version}.Z", paths.shift
assert paths.empty?, paths.join(', ') assert paths.empty?, paths.join(', ')
end end
@ -684,9 +829,9 @@ class TestGemSourceIndex < RubyGemTestCase
source_index = @source_index.dump source_index = @source_index.dump
if compressed then if compressed then
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}.Z"] = util_zip source_index @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}.Z"] = util_zip source_index
else else
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = source_index @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}"] = source_index
end end
end end

View File

@ -36,6 +36,7 @@ class TestGemSourceInfoCache < RubyGemTestCase
def teardown def teardown
super super
Gem.sources.replace @original_sources Gem.sources.replace @original_sources
Gem::SourceInfoCache.instance_variable_set :@cache, nil
end end
def test_self_cache_refreshes def test_self_cache_refreshes
@ -43,7 +44,7 @@ class TestGemSourceInfoCache < RubyGemTestCase
si = Gem::SourceIndex.new si = Gem::SourceIndex.new
si.add_spec @a1 si.add_spec @a1
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}"] = si.dump
Gem.sources.replace %W[#{@gem_repo}] Gem.sources.replace %W[#{@gem_repo}]
@ -52,8 +53,9 @@ class TestGemSourceInfoCache < RubyGemTestCase
assert_kind_of Gem::SourceInfoCache, Gem::SourceInfoCache.cache assert_kind_of Gem::SourceInfoCache, Gem::SourceInfoCache.cache
assert_equal Gem::SourceInfoCache.cache.object_id, assert_equal Gem::SourceInfoCache.cache.object_id,
Gem::SourceInfoCache.cache.object_id Gem::SourceInfoCache.cache.object_id
assert_match %r|Bulk updating|, @ui.output
end end
assert_match %r|Bulk updating|, @ui.output
end end
def test_self_cache_skips_refresh_based_on_configuration def test_self_cache_skips_refresh_based_on_configuration
@ -61,7 +63,7 @@ class TestGemSourceInfoCache < RubyGemTestCase
si = Gem::SourceIndex.new si = Gem::SourceIndex.new
si.add_spec @a1 si.add_spec @a1
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}"] = si.dump
Gem.sources.replace %w[#{@gem_repo}] Gem.sources.replace %w[#{@gem_repo}]
@ -78,7 +80,7 @@ class TestGemSourceInfoCache < RubyGemTestCase
si = Gem::SourceIndex.new si = Gem::SourceIndex.new
si.add_spec @a1 si.add_spec @a1
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}"] = si.dump
Gem::SourceInfoCache.instance_variable_set :@cache, nil Gem::SourceInfoCache.instance_variable_set :@cache, nil
sice = Gem::SourceInfoCacheEntry.new si, 0 sice = Gem::SourceInfoCacheEntry.new si, 0
@ -106,7 +108,7 @@ class TestGemSourceInfoCache < RubyGemTestCase
end end
def test_cache_data_irreparable def test_cache_data_irreparable
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = @source_index.dump @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}"] = @source_index.dump
data = { @gem_repo => { 'totally' => 'borked' } } data = { @gem_repo => { 'totally' => 'borked' } }

View File

@ -15,9 +15,9 @@ class TestGemSourceInfoCacheEntry < RubyGemTestCase
end end
def test_refresh def test_refresh
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}.Z"] = @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}.Z"] =
proc { raise } proc { raise }
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = @si.dump @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}"] = @si.dump
use_ui @ui do use_ui @ui do
@sic_e.refresh @gem_repo, true @sic_e.refresh @gem_repo, true
@ -30,18 +30,20 @@ class TestGemSourceInfoCacheEntry < RubyGemTestCase
a1_name = @a1.full_name a1_name = @a1.full_name
a2_name = @a2.full_name a2_name = @a2.full_name
@fetcher.data["#{@gem_repo}/quick/index.rz"] = @fetcher.data["#{@gem_repo}quick/index.rz"] =
util_zip [a1_name, a2_name].join("\n") util_zip [a1_name, a2_name].join("\n")
@fetcher.data["#{@gem_repo}/quick/latest_index.rz"] = util_zip a2_name @fetcher.data["#{@gem_repo}quick/latest_index.rz"] = util_zip a2_name
@fetcher.data["#{@gem_repo}/quick/Marshal.#{Gem.marshal_version}/#{a1_name}.gemspec.rz"] = util_zip Marshal.dump(@a1) @fetcher.data["#{@gem_repo}quick/Marshal.#{Gem.marshal_version}/#{a1_name}.gemspec.rz"] = util_zip Marshal.dump(@a1)
@fetcher.data["#{@gem_repo}/quick/Marshal.#{Gem.marshal_version}/#{a2_name}.gemspec.rz"] = util_zip Marshal.dump(@a2) @fetcher.data["#{@gem_repo}quick/Marshal.#{Gem.marshal_version}/#{a2_name}.gemspec.rz"] = util_zip Marshal.dump(@a2)
@fetcher.data["#{@gem_repo}/Marshal.#{Gem.marshal_version}"] = @fetcher.data["#{@gem_repo}Marshal.#{Gem.marshal_version}"] =
Marshal.dump @si Marshal.dump @si
sic_e = Gem::SourceInfoCacheEntry.new Gem::SourceIndex.new, 0 sic_e = Gem::SourceInfoCacheEntry.new Gem::SourceIndex.new, 0
assert_equal [], sic_e.source_index.map { |n,| n }
use_ui @ui do use_ui @ui do
sic_e.refresh @gem_repo, false assert sic_e.refresh(@gem_repo, false)
end end
assert_equal [a2_name], sic_e.source_index.map { |n,| n }.sort assert_equal [a2_name], sic_e.source_index.map { |n,| n }.sort
@ -63,7 +65,7 @@ class TestGemSourceInfoCacheEntry < RubyGemTestCase
si = Gem::SourceIndex.new si = Gem::SourceIndex.new
si.add_spec @a1 si.add_spec @a1
si.add_spec @b2 si.add_spec @b2
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}"] = si.dump
use_ui @ui do use_ui @ui do
@sic_e.refresh @gem_repo, true @sic_e.refresh @gem_repo, true

View File

@ -0,0 +1,303 @@
require 'test/unit'
require File.join(File.expand_path(File.dirname(__FILE__)), 'gemutilities')
require 'rubygems/spec_fetcher'
class TestGemSpecFetcher < RubyGemTestCase
def setup
super
@uri = URI.parse @gem_repo
util_setup_fake_fetcher
@source_index.add_spec @pl1
@specs = @source_index.gems.sort.map do |name, spec|
[spec.name, spec.version, spec.original_platform]
end.sort
@fetcher.data["#{@gem_repo}specs.#{Gem.marshal_version}.gz"] =
util_gzip(Marshal.dump(@specs))
@latest_specs = @source_index.latest_specs.sort.map do |spec|
[spec.name, spec.version, spec.original_platform]
end
@fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz"] =
util_gzip(Marshal.dump(@latest_specs))
@sf = Gem::SpecFetcher.new
end
def test_fetch_all
@fetcher.data["#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}#{@a1.full_name}.gemspec.rz"] =
util_zip(Marshal.dump(@a1))
@fetcher.data["#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}#{@a2.full_name}.gemspec.rz"] =
util_zip(Marshal.dump(@a2))
dep = Gem::Dependency.new 'a', 1
specs_and_sources = @sf.fetch dep, true
spec_names = specs_and_sources.map do |spec, source_uri|
[spec.full_name, source_uri]
end
expected = [[@a1.full_name, @gem_repo], [@a2.full_name, @gem_repo]]
assert_equal expected, spec_names
assert_same specs_and_sources.first.last, specs_and_sources.last.last
end
def test_fetch_latest
@fetcher.data["#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}#{@a1.full_name}.gemspec.rz"] =
util_zip(Marshal.dump(@a1))
@fetcher.data["#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}#{@a2.full_name}.gemspec.rz"] =
util_zip(Marshal.dump(@a2))
dep = Gem::Dependency.new 'a', 1
specs_and_sources = @sf.fetch dep
spec_names = specs_and_sources.map do |spec, source_uri|
[spec.full_name, source_uri]
end
assert_equal [[@a2.full_name, @gem_repo]], spec_names
end
def test_fetch_legacy_repo
@fetcher.data["#{@gem_repo}specs.#{Gem.marshal_version}.gz"] = nil
@fetcher.data["#{@gem_repo}yaml"] = ''
util_setup_source_info_cache @a1, @a2
dep = Gem::Dependency.new 'a', 1
specs = nil
use_ui @ui do
specs = @sf.fetch dep, true
end
expected = <<-EOF
WARNING: RubyGems 1.2+ index not found for:
\thttp://gems.example.com/
RubyGems will revert to legacy indexes degrading performance.
EOF
assert_equal expected, @ui.error
specs = specs.map { |spec, source_uri| [spec.full_name, source_uri] }
expected = [
[@a1.full_name, @gem_repo],
[@a2.full_name, @gem_repo],
]
assert_equal expected, specs
end
def test_fetch_platform
util_set_arch 'i386-linux'
@fetcher.data["#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}#{@pl1.original_name}.gemspec.rz"] =
util_zip(Marshal.dump(@pl1))
dep = Gem::Dependency.new 'pl', 1
specs_and_sources = @sf.fetch dep
spec_names = specs_and_sources.map do |spec, source_uri|
[spec.full_name, source_uri]
end
assert_equal [[@pl1.full_name, @gem_repo]], spec_names
end
def test_fetch_spec
spec_uri = "#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}#{@a1.full_name}.gemspec"
@fetcher.data["#{spec_uri}.rz"] = util_zip(Marshal.dump(@a1))
spec = @sf.fetch_spec ['a', Gem::Version.new(1), 'ruby'], @uri
assert_equal @a1.full_name, spec.full_name
cache_dir = @sf.cache_dir URI.parse(spec_uri)
cache_file = File.join cache_dir, "#{@a1.full_name}.gemspec"
assert File.exist?(cache_file)
end
def test_fetch_spec_cached
spec_uri = "#{@gem_repo}/#{Gem::MARSHAL_SPEC_DIR}#{@a1.full_name}.gemspec"
@fetcher.data["#{spec_uri}.rz"] = nil
cache_dir = @sf.cache_dir URI.parse(spec_uri)
FileUtils.mkdir_p cache_dir
cache_file = File.join cache_dir, "#{@a1.full_name}.gemspec"
open cache_file, 'wb' do |io|
Marshal.dump @a1, io
end
spec = @sf.fetch_spec ['a', Gem::Version.new(1), 'ruby'], @uri
assert_equal @a1.full_name, spec.full_name
end
def test_fetch_spec_platform
@fetcher.data["#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}#{@pl1.original_name}.gemspec.rz"] =
util_zip(Marshal.dump(@pl1))
spec = @sf.fetch_spec ['pl', Gem::Version.new(1), 'i386-linux'], @uri
assert_equal @pl1.full_name, spec.full_name
end
def test_fetch_spec_platform_ruby
@fetcher.data["#{@gem_repo}#{Gem::MARSHAL_SPEC_DIR}#{@a1.full_name}.gemspec.rz"] =
util_zip(Marshal.dump(@a1))
spec = @sf.fetch_spec ['a', Gem::Version.new(1), nil], @uri
assert_equal @a1.full_name, spec.full_name
spec = @sf.fetch_spec ['a', Gem::Version.new(1), ''], @uri
assert_equal @a1.full_name, spec.full_name
end
def test_find_matching_all
dep = Gem::Dependency.new 'a', 1
specs = @sf.find_matching dep, true
expected = [
[['a', Gem::Version.new(1), Gem::Platform::RUBY], @gem_repo],
[['a', Gem::Version.new(2), Gem::Platform::RUBY], @gem_repo],
]
assert_equal expected, specs
end
def test_find_matching_latest
dep = Gem::Dependency.new 'a', 1
specs = @sf.find_matching dep
expected = [
[['a', Gem::Version.new(2), Gem::Platform::RUBY], @gem_repo],
]
assert_equal expected, specs
end
def test_find_matching_platform
util_set_arch 'i386-linux'
dep = Gem::Dependency.new 'pl', 1
specs = @sf.find_matching dep
expected = [
[['pl', Gem::Version.new(1), 'i386-linux'], @gem_repo],
]
assert_equal expected, specs
util_set_arch 'i386-freebsd6'
dep = Gem::Dependency.new 'pl', 1
specs = @sf.find_matching dep
assert_equal [], specs
end
def test_find_all_platforms
util_set_arch 'i386-freebsd6'
dep = Gem::Dependency.new 'pl', 1
specs = @sf.find_matching dep, false, false
expected = [
[['pl', Gem::Version.new(1), 'i386-linux'], @gem_repo],
]
assert_equal expected, specs
end
def test_list
specs = @sf.list
assert_equal [@uri], specs.keys
assert_equal @latest_specs, specs[@uri].sort
end
def test_list_all
specs = @sf.list true
assert_equal [@uri], specs.keys
assert_equal @specs, specs[@uri].sort
end
def test_list_cache
specs = @sf.list
assert !specs[@uri].empty?
@fetcher.data["#{@gem_repo}/latest_specs.#{Gem.marshal_version}.gz"] = nil
cached_specs = @sf.list
assert_equal specs, cached_specs
end
def test_list_cache_all
specs = @sf.list true
assert !specs[@uri].empty?
@fetcher.data["#{@gem_repo}/specs.#{Gem.marshal_version}.gz"] = nil
cached_specs = @sf.list true
assert_equal specs, cached_specs
end
def test_load_specs
specs = @sf.load_specs @uri, 'specs'
expected = [
['a', Gem::Version.new(1), Gem::Platform::RUBY],
['a', Gem::Version.new(2), Gem::Platform::RUBY],
['a_evil', Gem::Version.new(9), Gem::Platform::RUBY],
['c', Gem::Version.new('1.2'), Gem::Platform::RUBY],
['pl', Gem::Version.new(1), 'i386-linux'],
]
assert_equal expected, specs
cache_dir = File.join Gem.user_home, '.gem', 'specs', 'gems.example.com%80'
assert File.exist?(cache_dir), "#{cache_dir} does not exist"
cache_file = File.join cache_dir, "specs.#{Gem.marshal_version}"
assert File.exist?(cache_file)
end
def test_load_specs_cached
@fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz"] = nil
@fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}"] =
' ' * Marshal.dump(@latest_specs).length
cache_dir = File.join Gem.user_home, '.gem', 'specs', 'gems.example.com:80'
FileUtils.mkdir_p cache_dir
cache_file = File.join cache_dir, "latest_specs.#{Gem.marshal_version}"
open cache_file, 'wb' do |io|
Marshal.dump @latest_specs, io
end
specs = @sf.load_specs @uri, 'specs'
assert_equal @specs, specs
end
end

View File

@ -213,6 +213,15 @@ end
assert_equal 'old_platform', same_spec.original_platform assert_equal 'old_platform', same_spec.original_platform
end end
def test_add_dependency_with_explicit_type
gem = quick_gem "awesome", "1.0" do |awesome|
awesome.add_development_dependency "monkey"
end
monkey = gem.dependencies.detect { |d| d.name == "monkey" }
assert_equal(:development, monkey.type)
end
def test_author def test_author
assert_equal 'A User', @a1.author assert_equal 'A User', @a1.author
end end
@ -282,6 +291,20 @@ end
assert_equal [rake, jabber, pqa], @a1.dependencies assert_equal [rake, jabber, pqa], @a1.dependencies
end end
def test_dependencies_scoped_by_type
gem = quick_gem "awesome", "1.0" do |awesome|
awesome.add_runtime_dependency "bonobo", []
awesome.add_development_dependency "monkey", []
end
bonobo = Gem::Dependency.new("bonobo", [])
monkey = Gem::Dependency.new("monkey", [], :development)
assert_equal([bonobo, monkey], gem.dependencies)
assert_equal([bonobo], gem.runtime_dependencies)
assert_equal([monkey], gem.development_dependencies)
end
def test_description def test_description
assert_equal 'This is a test description', @a1.description assert_equal 'This is a test description', @a1.description
end end
@ -423,6 +446,15 @@ end
@a1.full_gem_path @a1.full_gem_path
end end
def test_full_gem_path_double_slash
gemhome = @gemhome.sub(/\w\//, '\&/')
@a1.loaded_from = File.join gemhome, 'specifications',
"#{@a1.full_name}.gemspec"
assert_equal File.join(@gemhome, 'gems', @a1.full_name),
@a1.full_gem_path
end
def test_full_name def test_full_name
assert_equal 'a-1', @a1.full_name assert_equal 'a-1', @a1.full_name
@ -531,6 +563,17 @@ end
assert_equal ['A working computer'], @a1.requirements assert_equal ['A working computer'], @a1.requirements
end end
def test_runtime_dependencies_legacy
# legacy gems don't have a type
@a1.runtime_dependencies.each do |dep|
dep.instance_variable_set :@type, nil
end
expected = %w[rake jabber4r pqa]
assert_equal expected, @a1.runtime_dependencies.map { |d| d.name }
end
def test_spaceship_name def test_spaceship_name
s1 = quick_gem 'a', '1' s1 = quick_gem 'a', '1'
s2 = quick_gem 'b', '1' s2 = quick_gem 'b', '1'
@ -570,6 +613,8 @@ end
end end
def test_to_ruby def test_to_ruby
@a2.add_runtime_dependency 'b', '1'
@a2.dependencies.first.instance_variable_set :@type, nil
@a2.required_rubygems_version = Gem::Requirement.new '> 0' @a2.required_rubygems_version = Gem::Requirement.new '> 0'
ruby_code = @a2.to_ruby ruby_code = @a2.to_ruby
@ -578,8 +623,6 @@ end
s.name = %q{a} s.name = %q{a}
s.version = \"2\" s.version = \"2\"
s.specification_version = #{Gem::Specification::CURRENT_SPECIFICATION_VERSION} if s.respond_to? :specification_version=
s.required_rubygems_version = Gem::Requirement.new(\"> 0\") if s.respond_to? :required_rubygems_version= s.required_rubygems_version = Gem::Requirement.new(\"> 0\") if s.respond_to? :required_rubygems_version=
s.authors = [\"A User\"] s.authors = [\"A User\"]
s.date = %q{#{Gem::Specification::TODAY.strftime "%Y-%m-%d"}} s.date = %q{#{Gem::Specification::TODAY.strftime "%Y-%m-%d"}}
@ -591,6 +634,19 @@ end
s.require_paths = [\"lib\"] s.require_paths = [\"lib\"]
s.rubygems_version = %q{#{Gem::RubyGemsVersion}} s.rubygems_version = %q{#{Gem::RubyGemsVersion}}
s.summary = %q{this is a summary} s.summary = %q{this is a summary}
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = #{Gem::Specification::CURRENT_SPECIFICATION_VERSION}
if current_version >= 3 then
s.add_runtime_dependency(%q<b>, [\"= 1\"])
else
s.add_dependency(%q<b>, [\"= 1\"])
end
else
s.add_dependency(%q<b>, [\"= 1\"])
end
end end
" "
@ -613,8 +669,6 @@ end
s.version = \"1\" s.version = \"1\"
s.platform = Gem::Platform.new(#{expected_platform}) s.platform = Gem::Platform.new(#{expected_platform})
s.specification_version = 2 if s.respond_to? :specification_version=
s.required_rubygems_version = Gem::Requirement.new(\">= 0\") if s.respond_to? :required_rubygems_version= s.required_rubygems_version = Gem::Requirement.new(\">= 0\") if s.respond_to? :required_rubygems_version=
s.authors = [\"A User\"] s.authors = [\"A User\"]
s.date = %q{#{Gem::Specification::TODAY.strftime "%Y-%m-%d"}} s.date = %q{#{Gem::Specification::TODAY.strftime "%Y-%m-%d"}}
@ -633,9 +687,24 @@ end
s.summary = %q{this is a summary} s.summary = %q{this is a summary}
s.test_files = [\"test/suite.rb\"] s.test_files = [\"test/suite.rb\"]
s.add_dependency(%q<rake>, [\"> 0.4\"]) if s.respond_to? :specification_version then
s.add_dependency(%q<jabber4r>, [\"> 0.0.0\"]) current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.add_dependency(%q<pqa>, [\"> 0.4\", \"<= 0.6\"]) s.specification_version = 3
if current_version >= 3 then
s.add_runtime_dependency(%q<rake>, [\"> 0.4\"])
s.add_runtime_dependency(%q<jabber4r>, [\"> 0.0.0\"])
s.add_runtime_dependency(%q<pqa>, [\"> 0.4\", \"<= 0.6\"])
else
s.add_dependency(%q<rake>, [\"> 0.4\"])
s.add_dependency(%q<jabber4r>, [\"> 0.0.0\"])
s.add_dependency(%q<pqa>, [\"> 0.4\", \"<= 0.6\"])
end
else
s.add_dependency(%q<rake>, [\"> 0.4\"])
s.add_dependency(%q<jabber4r>, [\"> 0.0.0\"])
s.add_dependency(%q<pqa>, [\"> 0.4\", \"<= 0.6\"])
end
end end
" "

View File

@ -15,6 +15,12 @@ class TestGemUninstaller < GemInstallerTestCase
end end
end end
def test_initialize_expand_path
uninstaller = Gem::Uninstaller.new nil, :install_dir => '/foo//bar'
assert_match %r|/foo/bar$|, uninstaller.instance_variable_get(:@gem_home)
end
def test_remove_executables_force_keep def test_remove_executables_force_keep
uninstaller = Gem::Uninstaller.new nil, :executables => false uninstaller = Gem::Uninstaller.new nil, :executables => false
@ -39,5 +45,20 @@ class TestGemUninstaller < GemInstallerTestCase
assert_equal false, File.exist?(File.join(@gemhome, 'bin', 'executable')) assert_equal false, File.exist?(File.join(@gemhome, 'bin', 'executable'))
end end
def test_path_ok_eh
uninstaller = Gem::Uninstaller.new nil
assert_equal true, uninstaller.path_ok?(@spec)
end
def test_path_ok_eh_legacy
uninstaller = Gem::Uninstaller.new nil
@spec.loaded_from.gsub! @spec.full_name, '\&-legacy'
@spec.platform = 'legacy'
assert_equal true, uninstaller.path_ok?(@spec)
end
end end

View File

@ -71,10 +71,14 @@ class TestGemVersion < RubyGemTestCase
end end
def test_eql_eh def test_eql_eh
v = Gem::Version.new("1.2") v1_2 = Gem::Version.new '1.2'
v1_2_0 = Gem::Version.new '1.2.0'
assert_equal true, v.eql?(@v1_2) assert_equal true, v1_2.eql?(@v1_2)
assert_equal true, @v1_2.eql?(v) assert_equal true, @v1_2.eql?(v1_2)
assert_equal false, v1_2_0.eql?(@v1_2)
assert_equal false, @v1_2.eql?(v1_2_0)
assert_equal false, @v1_2.eql?(@v1_3) assert_equal false, @v1_2.eql?(@v1_3)
assert_equal false, @v1_3.eql?(@v1_2) assert_equal false, @v1_3.eql?(@v1_2)
@ -91,8 +95,13 @@ class TestGemVersion < RubyGemTestCase
end end
def test_hash def test_hash
v = Gem::Version.new("1.2") v1_2 = Gem::Version.new "1.2"
assert_equal v.hash, @v1_2.hash v1_2_0 = Gem::Version.new "1.2.0"
assert_equal v1_2.hash, @v1_2.hash
assert_not_equal v1_2_0.hash, @v1_2.hash
assert_not_equal @v1_2.hash, @v1_3.hash assert_not_equal @v1_2.hash, @v1_3.hash
end end

View File

@ -52,7 +52,7 @@ class TestKernel < RubyGemTestCase
gem 'a', '= 2' gem 'a', '= 2'
end end
assert_match(/activate a \(= 2\)/, ex.message) assert_match(/activate a \(= 2, runtime\)/, ex.message)
assert_match(/activated a-1/, ex.message) assert_match(/activated a-1/, ex.message)
assert $:.any? { |p| %r{a-1/lib} =~ p } assert $:.any? { |p| %r{a-1/lib} =~ p }