summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--lib/rubygems.rb95
-rw-r--r--lib/rubygems/command_manager.rb1
-rw-r--r--lib/rubygems/commands/dependency_command.rb67
-rw-r--r--lib/rubygems/commands/environment_command.rb2
-rw-r--r--lib/rubygems/commands/fetch_command.rb4
-rw-r--r--lib/rubygems/commands/install_command.rb4
-rw-r--r--lib/rubygems/commands/list_command.rb58
-rw-r--r--lib/rubygems/commands/lock_command.rb2
-rw-r--r--lib/rubygems/commands/outdated_command.rb7
-rw-r--r--lib/rubygems/commands/pristine_command.rb47
-rw-r--r--lib/rubygems/commands/query_command.rb101
-rw-r--r--lib/rubygems/commands/sources_command.rb84
-rw-r--r--lib/rubygems/commands/specification_command.rb7
-rw-r--r--lib/rubygems/commands/stale_command.rb27
-rw-r--r--lib/rubygems/commands/update_command.rb57
-rw-r--r--lib/rubygems/config_file.rb40
-rwxr-xr-xlib/rubygems/custom_require.rb2
-rw-r--r--lib/rubygems/defaults.rb2
-rw-r--r--lib/rubygems/dependency.rb72
-rw-r--r--lib/rubygems/dependency_installer.rb80
-rw-r--r--lib/rubygems/dependency_list.rb2
-rw-r--r--lib/rubygems/doc_manager.rb10
-rw-r--r--lib/rubygems/indexer.rb352
-rw-r--r--lib/rubygems/install_update_options.rb6
-rw-r--r--lib/rubygems/installer.rb47
-rw-r--r--lib/rubygems/local_remote_options.rb31
-rw-r--r--lib/rubygems/platform.rb16
-rw-r--r--lib/rubygems/remote_fetcher.rb230
-rw-r--r--lib/rubygems/requirement.rb28
-rw-r--r--lib/rubygems/rubygems_version.rb2
-rw-r--r--lib/rubygems/server.rb347
-rw-r--r--lib/rubygems/source_index.rb62
-rw-r--r--lib/rubygems/spec_fetcher.rb251
-rw-r--r--lib/rubygems/specification.rb143
-rw-r--r--lib/rubygems/test_utilities.rb120
-rw-r--r--lib/rubygems/uninstaller.rb5
-rw-r--r--lib/rubygems/user_interaction.rb223
-rw-r--r--lib/rubygems/version.rb44
-rw-r--r--test/rubygems/gemutilities.rb149
-rw-r--r--test/rubygems/test_config.rb5
-rw-r--r--test/rubygems/test_gem.rb24
-rw-r--r--test/rubygems/test_gem_command_manager.rb2
-rw-r--r--test/rubygems/test_gem_commands_dependency_command.rb79
-rw-r--r--test/rubygems/test_gem_commands_environment_command.rb1
-rw-r--r--test/rubygems/test_gem_commands_fetch_command.rb28
-rw-r--r--test/rubygems/test_gem_commands_install_command.rb14
-rw-r--r--test/rubygems/test_gem_commands_outdated_command.rb43
-rw-r--r--test/rubygems/test_gem_commands_pristine_command.rb17
-rw-r--r--test/rubygems/test_gem_commands_query_command.rb150
-rw-r--r--test/rubygems/test_gem_commands_sources_command.rb132
-rw-r--r--test/rubygems/test_gem_commands_specification_command.rb5
-rw-r--r--test/rubygems/test_gem_commands_stale_command.rb39
-rw-r--r--test/rubygems/test_gem_commands_update_command.rb44
-rw-r--r--test/rubygems/test_gem_config_file.rb33
-rw-r--r--test/rubygems/test_gem_dependency.rb51
-rw-r--r--test/rubygems/test_gem_dependency_installer.rb133
-rw-r--r--test/rubygems/test_gem_gem_path_searcher.rb5
-rw-r--r--test/rubygems/test_gem_indexer.rb143
-rw-r--r--test/rubygems/test_gem_installer.rb32
-rw-r--r--test/rubygems/test_gem_local_remote_options.rb11
-rw-r--r--test/rubygems/test_gem_remote_fetcher.rb23
-rw-r--r--test/rubygems/test_gem_server.rb228
-rw-r--r--test/rubygems/test_gem_source_index.rb271
-rw-r--r--test/rubygems/test_gem_source_info_cache.rb12
-rw-r--r--test/rubygems/test_gem_source_info_cache_entry.rb20
-rw-r--r--test/rubygems/test_gem_spec_fetcher.rb303
-rw-r--r--test/rubygems/test_gem_specification.rb83
-rw-r--r--test/rubygems/test_gem_uninstaller.rb21
-rw-r--r--test/rubygems/test_gem_version.rb19
-rw-r--r--test/rubygems/test_kernel.rb2
71 files changed, 3721 insertions, 1083 deletions
diff --git a/ChangeLog b/ChangeLog
index 87c81cc835..1809b79552 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
* KNOWNBUGS.rb, bootstraptest/pending.rb: move pending bug.
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 1cb205cbc9..4f2cc94ae0 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -67,11 +67,14 @@ module Gem
:RUBY_SO_NAME => RbConfig::CONFIG["RUBY_SO_NAME"],
:arch => RbConfig::CONFIG["arch"],
:bindir => RbConfig::CONFIG["bindir"],
+ :datadir => RbConfig::CONFIG["datadir"],
:libdir => RbConfig::CONFIG["libdir"],
:ruby_install_name => RbConfig::CONFIG["ruby_install_name"],
:ruby_version => RbConfig::CONFIG["ruby_version"],
: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)
@@ -137,7 +140,7 @@ module Gem
unless matches.any? { |spec| spec.version == existing_spec.version } then
raise Gem::Exception,
- "can't activate #{gem}, already activated #{existing_spec.full_name}]"
+ "can't activate #{gem}, already activated #{existing_spec.full_name}"
end
return false
@@ -151,7 +154,7 @@ module Gem
@loaded_specs[spec.name] = spec
# Load dependent gems first
- spec.dependencies.each do |dep_gem|
+ spec.runtime_dependencies.each do |dep_gem|
activate dep_gem
end
@@ -204,6 +207,19 @@ module Gem
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.
def self.binary_mode
@@ -268,6 +284,13 @@ module Gem
end
##
+ # A Zlib::Deflate.deflate wrapper
+
+ def self.deflate(data)
+ Zlib::Deflate.deflate data
+ end
+
+ ##
# The path where gems are to be installed.
def self.dir
@@ -346,6 +369,33 @@ module Gem
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
# gems in the Gem installation.
@@ -438,7 +488,11 @@ module Gem
@gem_path ||= nil
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
paths << APPLE_GEM_HOME
@@ -459,7 +513,7 @@ module Gem
##
# Array of platforms this RubyGems supports.
-
+
def self.platforms
@platforms ||= []
if @platforms.empty?
@@ -586,13 +640,13 @@ module Gem
def self.set_paths(gpaths)
if gpaths
@gem_path = gpaths.split(File::PATH_SEPARATOR)
-
+
if File::ALT_SEPARATOR then
@gem_path.map! do |path|
path.gsub File::ALT_SEPARATOR, File::SEPARATOR
end
end
-
+
@gem_path << Gem.dir
else
@gem_path = [Gem.dir]
@@ -683,24 +737,25 @@ module Gem
end
-end
+ MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/"
-# Modify the non-gem version of datadir to handle gem package names.
+ YAML_SPEC_DIR = 'quick/'
-require 'rbconfig/datadir'
+end
-module Config # :nodoc:
+module Config
+ # :stopdoc:
class << self
- alias gem_original_datadir datadir
-
# Return the path to the data directory associated with the named
# package. If the package is loaded as a gem, return the gem
# specific data directory. Otherwise return a path to the share
# area as define by "#{ConfigMap[: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
+ # :startdoc:
end
require 'rubygems/exceptions'
@@ -712,6 +767,18 @@ require 'rubygems/source_index' # Needed for Kernel#gem
require 'rubygems/platform'
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
require 'rubygems/custom_require'
end
diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb
index ee55d5a200..dd9a1aee15 100644
--- a/lib/rubygems/command_manager.rb
+++ b/lib/rubygems/command_manager.rb
@@ -46,6 +46,7 @@ module Gem
register_command :server
register_command :sources
register_command :specification
+ register_command :stale
register_command :uninstall
register_command :unpack
register_command :update
diff --git a/lib/rubygems/commands/dependency_command.rb b/lib/rubygems/commands/dependency_command.rb
index 1a43505d7c..8fae87c90f 100644
--- a/lib/rubygems/commands/dependency_command.rb
+++ b/lib/rubygems/commands/dependency_command.rb
@@ -46,37 +46,67 @@ class Gem::Commands::DependencyCommand < Gem::Command
options[:args] << '.' if options[:args].empty?
specs = {}
- source_indexes = []
+ source_indexes = Hash.new do |h, source_uri|
+ h[source_uri] = Gem::SourceIndex.new
+ end
- if local? then
- source_indexes << Gem::SourceIndex.from_installed_gems
+ 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 remote? then
- Gem::SourceInfoCache.cache_data.map do |_, sice|
- source_indexes << sice.source_index
+ if local? then
+ Gem.source_index.search(dependency).each do |spec|
+ source_indexes[:local].add_spec spec
end
end
- options[:args].each do |name|
- new_specs = nil
- source_indexes.each do |source_index|
- new_specs = find_gems(name, source_index)
+ if remote? and not options[:reverse_dependencies] then
+ fetcher = Gem::SpecFetcher.fetcher
+
+ 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?
+ if source_indexes.empty? then
+ patterns = options[:args].join ','
+ say "No gems found matching #{patterns} (#{options[:version]})" if
+ Gem.configuration.verbose
- specs = specs.merge new_specs
+ terminate_interaction 1
end
- terminate_interaction 1 if specs.empty?
+ 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] = [] }
if options[:reverse_dependencies] then
- specs.values.each do |source_index, spec|
- reverse[spec.full_name] = find_reverse_dependencies spec, source_index
+ specs.values.each do |_, spec|
+ reverse[spec.full_name] = find_reverse_dependencies spec
end
end
@@ -118,10 +148,10 @@ class Gem::Commands::DependencyCommand < Gem::Command
end
# Retuns list of [specification, dep] that are satisfied by spec.
- def find_reverse_dependencies(spec, source_index)
+ def find_reverse_dependencies(spec)
result = []
- source_index.each do |name, sp|
+ Gem.source_index.each do |name, sp|
sp.dependencies.each do |dep|
dep = Gem::Dependency.new(*dep) unless Gem::Dependency === dep
@@ -146,5 +176,6 @@ class Gem::Commands::DependencyCommand < Gem::Command
specs
end
+
end
diff --git a/lib/rubygems/commands/environment_command.rb b/lib/rubygems/commands/environment_command.rb
index 342f93ca54..a67c00bfd6 100644
--- a/lib/rubygems/commands/environment_command.rb
+++ b/lib/rubygems/commands/environment_command.rb
@@ -51,6 +51,8 @@ class Gem::Commands::EnvironmentCommand < Gem::Command
out << " - RUBY EXECUTABLE: #{Gem.ruby}\n"
+ out << " - EXECUTABLE DIRECTORY: #{Gem.bindir}\n"
+
out << " - RUBYGEMS PLATFORMS:\n"
Gem.platforms.each do |platform|
out << " - #{platform}\n"
diff --git a/lib/rubygems/commands/fetch_command.rb b/lib/rubygems/commands/fetch_command.rb
index ccedc45401..76c9924e6b 100644
--- a/lib/rubygems/commands/fetch_command.rb
+++ b/lib/rubygems/commands/fetch_command.rb
@@ -33,12 +33,14 @@ class Gem::Commands::FetchCommand < Gem::Command
def execute
version = options[:version] || Gem::Requirement.default
+ all = Gem::Requirement.default
gem_names = get_all_gem_names
gem_names.each do |gem_name|
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 }
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
index ce0bc6ba04..48cd3869f9 100644
--- a/lib/rubygems/commands/install_command.rb
+++ b/lib/rubygems/commands/install_command.rb
@@ -16,7 +16,6 @@ class Gem::Commands::InstallCommand < Gem::Command
defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
:generate_rdoc => true,
:generate_ri => true,
- :install_dir => Gem.dir,
:format_executable => false,
:test => false,
:version => Gem::Requirement.default,
@@ -62,7 +61,8 @@ class Gem::Commands::InstallCommand < Gem::Command
:install_dir => options[:install_dir],
:security_policy => options[:security_policy],
:wrappers => options[:wrappers],
- :bin_dir => options[:bin_dir]
+ :bin_dir => options[:bin_dir],
+ :development => options[:development],
}
exit_code = 0
diff --git a/lib/rubygems/commands/list_command.rb b/lib/rubygems/commands/list_command.rb
index f8b377fcde..f3e5da9551 100644
--- a/lib/rubygems/commands/list_command.rb
+++ b/lib/rubygems/commands/list_command.rb
@@ -1,33 +1,35 @@
require 'rubygems/command'
require 'rubygems/commands/query_command'
-module Gem
- module Commands
- class ListCommand < QueryCommand
-
- def initialize
- super 'list', 'Display gems whose name starts with STRING'
-
- remove_option('--name-matches')
- 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
+##
+# An alternate to Gem::Commands::QueryCommand that searches for gems starting
+# with the the supplied argument.
+
+class Gem::Commands::ListCommand < Gem::Commands::QueryCommand
+
+ def initialize
+ super 'list', 'Display gems whose name starts with STRING'
+
+ remove_option('--name-matches')
+ 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
+
diff --git a/lib/rubygems/commands/lock_command.rb b/lib/rubygems/commands/lock_command.rb
index 3a3dcc0c6b..6be2774e92 100644
--- a/lib/rubygems/commands/lock_command.rb
+++ b/lib/rubygems/commands/lock_command.rb
@@ -80,7 +80,7 @@ lock it down to the exact version.
say "gem '#{spec.name}', '= #{spec.version}'" unless locked[spec.name]
locked[spec.name] = true
- spec.dependencies.each do |dep|
+ spec.runtime_dependencies.each do |dep|
next if locked[dep.name]
candidates = Gem.source_index.search dep.name, dep.requirement_list
diff --git a/lib/rubygems/commands/outdated_command.rb b/lib/rubygems/commands/outdated_command.rb
index 9c0062019b..1cd1087dd1 100644
--- a/lib/rubygems/commands/outdated_command.rb
+++ b/lib/rubygems/commands/outdated_command.rb
@@ -1,6 +1,6 @@
require 'rubygems/command'
require 'rubygems/local_remote_options'
-require 'rubygems/source_info_cache'
+require 'rubygems/spec_fetcher'
require 'rubygems/version_option'
class Gem::Commands::OutdatedCommand < Gem::Command
@@ -20,8 +20,11 @@ class Gem::Commands::OutdatedCommand < Gem::Command
locals.outdated.sort.each do |name|
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
+
say "#{local.name} (#{local.version} < #{remote.version})"
end
end
diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb
index bbea835133..3e55a1bb30 100644
--- a/lib/rubygems/commands/pristine_command.rb
+++ b/lib/rubygems/commands/pristine_command.rb
@@ -82,51 +82,10 @@ revert the gem.
end
# 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"
-
- 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
+ say "Restored #{spec.full_name}"
end
end
diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb
index ea83b93bbb..cc81f3f07e 100644
--- a/lib/rubygems/commands/query_command.rb
+++ b/lib/rubygems/commands/query_command.rb
@@ -1,6 +1,6 @@
require 'rubygems/command'
require 'rubygems/local_remote_options'
-require 'rubygems/source_info_cache'
+require 'rubygems/spec_fetcher'
require 'rubygems/version_option'
class Gem::Commands::QueryCommand < Gem::Command
@@ -74,7 +74,13 @@ class Gem::Commands::QueryCommand < Gem::Command
say "*** LOCAL GEMS ***"
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
if remote? then
@@ -84,13 +90,26 @@ class Gem::Commands::QueryCommand < Gem::Command
all = options[:all]
+ dep = Gem::Dependency.new name, Gem::Requirement.default
begin
- Gem::SourceInfoCache.cache all
- rescue Gem::RemoteFetcher::FetchError
- # no network
+ fetcher = Gem::SpecFetcher.fetcher
+ spec_tuples = fetcher.find_matching dep, all, false
+ 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
- output_query_results Gem::SourceInfoCache.search(name, false, all)
+ output_query_results spec_tuples
end
end
@@ -104,28 +123,30 @@ class Gem::Commands::QueryCommand < Gem::Command
!Gem.source_index.search(dep).empty?
end
- def output_query_results(gemspecs)
+ def output_query_results(spec_tuples)
output = []
- gem_list_with_version = {}
+ versions = Hash.new { |h,name| h[name] = [] }
- gemspecs.flatten.each do |gemspec|
- gem_list_with_version[gemspec.name] ||= []
- gem_list_with_version[gemspec.name] << gemspec
+ spec_tuples.each do |spec_tuple, source_uri|
+ versions[spec_tuple.first] << [spec_tuple, source_uri]
end
- gem_list_with_version = gem_list_with_version.sort_by do |name, spec|
+ versions = versions.sort_by do |(name,),|
name.downcase
end
- gem_list_with_version.each do |gem_name, list_of_matching|
- list_of_matching = list_of_matching.sort_by { |x| x.version.to_ints }.reverse
- seen_versions = {}
+ versions.each do |gem_name, matching_tuples|
+ matching_tuples = matching_tuples.sort_by do |(name, version,),|
+ version
+ end.reverse
- list_of_matching.delete_if do |item|
- if seen_versions[item.version] then
+ seen = {}
+
+ matching_tuples.delete_if do |(name, version,),|
+ if seen[version] then
true
else
- seen_versions[item.version] = true
+ seen[version] = true
false
end
end
@@ -133,12 +154,50 @@ class Gem::Commands::QueryCommand < Gem::Command
entry = gem_name.dup
if options[:versions] then
- versions = list_of_matching.map { |s| s.version }.uniq
+ versions = matching_tuples.map { |(name, version,),| version }.uniq
entry << " (#{versions.join ', '})"
end
- entry << "\n" << format_text(list_of_matching[0].summary, 68, 4) if
- options[:details]
+ if options[:details] then
+ 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
end
diff --git a/lib/rubygems/commands/sources_command.rb b/lib/rubygems/commands/sources_command.rb
index 1558d79b8b..f45438463c 100644
--- a/lib/rubygems/commands/sources_command.rb
+++ b/lib/rubygems/commands/sources_command.rb
@@ -1,7 +1,8 @@
+require 'fileutils'
require 'rubygems/command'
require 'rubygems/remote_fetcher'
require 'rubygems/source_info_cache'
-require 'rubygems/source_info_cache_entry'
+require 'rubygems/spec_fetcher'
class Gem::Commands::SourcesCommand < Gem::Command
@@ -21,14 +22,14 @@ class Gem::Commands::SourcesCommand < Gem::Command
options[:remove] = value
end
- add_option '-u', '--update', 'Update source cache' do |value, options|
- options[:update] = value
- end
-
add_option '-c', '--clear-all',
'Remove all sources (clear the cache)' do |value, options|
options[:clear_all] = value
end
+
+ add_option '-u', '--update', 'Update source cache' do |value, options|
+ options[:update] = value
+ end
end
def defaults_str
@@ -36,9 +37,23 @@ class Gem::Commands::SourcesCommand < Gem::Command
end
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
+ 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
remove_cache_file 'user', sic.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
source_uri = options[:add]
+ uri = URI.parse source_uri
- sice = Gem::SourceInfoCacheEntry.new nil, nil
begin
- sice.refresh source_uri, true
-
- Gem::SourceInfoCache.cache_data[source_uri] = sice
- Gem::SourceInfoCache.cache.update
- Gem::SourceInfoCache.cache.flush
-
+ Gem::SpecFetcher.fetcher.load_specs uri, 'specs'
Gem.sources << source_uri
Gem.configuration.write
@@ -64,15 +74,24 @@ class Gem::Commands::SourcesCommand < Gem::Command
rescue URI::Error, ArgumentError
say "#{source_uri} is not a URI"
rescue Gem::RemoteFetcher::FetchError => e
- say "Error fetching #{source_uri}:\n\t#{e.message}"
- end
- end
+ yaml_uri = uri + 'yaml'
+ gem_repo = Gem::RemoteFetcher.fetcher.fetch_size yaml_uri rescue false
- if options[:update] then
- Gem::SourceInfoCache.cache true
- Gem::SourceInfoCache.cache.flush
+ if e.uri =~ /specs\.#{Regexp.escape Gem.marshal_version}\.gz$/ and
+ gem_repo then
- say "source cache successfully updated"
+ 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
if options[:remove] then
@@ -81,14 +100,6 @@ class Gem::Commands::SourcesCommand < Gem::Command
unless Gem.sources.include? source_uri then
say "source #{source_uri} not present in cache"
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.configuration.write
@@ -96,6 +107,23 @@ class Gem::Commands::SourcesCommand < Gem::Command
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
say "*** CURRENT SOURCES ***"
say
diff --git a/lib/rubygems/commands/specification_command.rb b/lib/rubygems/commands/specification_command.rb
index 7c8598e53b..689f2560c9 100644
--- a/lib/rubygems/commands/specification_command.rb
+++ b/lib/rubygems/commands/specification_command.rb
@@ -52,9 +52,10 @@ class Gem::Commands::SpecificationCommand < Gem::Command
end
if remote? then
- Gem::SourceInfoCache.cache_data.each do |_,sice|
- specs.push(*sice.source_index.search(gem, options[:version]))
- end
+ dep = Gem::Dependency.new gem, options[:version]
+ found = Gem::SpecFetcher.fetcher.fetch dep
+
+ specs.push(*found.map { |spec,| spec })
end
if specs.empty? then
diff --git a/lib/rubygems/commands/stale_command.rb b/lib/rubygems/commands/stale_command.rb
new file mode 100644
index 0000000000..78cbdcc00a
--- /dev/null
+++ b/lib/rubygems/commands/stale_command.rb
@@ -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
diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb
index 31a97c4844..78baa8ba56 100644
--- a/lib/rubygems/commands/update_command.rb
+++ b/lib/rubygems/commands/update_command.rb
@@ -2,7 +2,7 @@ require 'rubygems/command'
require 'rubygems/command_manager'
require 'rubygems/install_update_options'
require 'rubygems/local_remote_options'
-require 'rubygems/source_info_cache'
+require 'rubygems/spec_fetcher'
require 'rubygems/version_option'
require 'rubygems/commands/install_command'
@@ -15,11 +15,10 @@ class Gem::Commands::UpdateCommand < Gem::Command
def initialize
super 'update',
'Update the named gems (or all installed gems) in the local repository',
- :generate_rdoc => true,
- :generate_ri => true,
- :force => false,
- :test => false,
- :install_dir => Gem.dir
+ :generate_rdoc => true,
+ :generate_ri => true,
+ :force => false,
+ :test => false
add_install_update_options
@@ -60,21 +59,13 @@ class Gem::Commands::UpdateCommand < Gem::Command
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
hig[spec.name] = spec
end
end
- pattern = if options[:args].empty? then
- //
- else
- Regexp.union(*options[:args])
- end
-
- remote_gemspecs = Gem::SourceInfoCache.search pattern
-
- gems_to_update = which_to_update hig, remote_gemspecs
+ gems_to_update = which_to_update hig, options[:args]
updated = []
@@ -135,20 +126,42 @@ class Gem::Commands::UpdateCommand < Gem::Command
end
end
- def which_to_update(highest_installed_gems, remote_gemspecs)
+ def which_to_update(highest_installed_gems, gem_names)
result = []
highest_installed_gems.each do |l_name, l_spec|
- matching_gems = remote_gemspecs.select do |spec|
- spec.name == l_name and Gem.platforms.any? do |platform|
- platform == spec.platform
+ next if not gem_names.empty? and
+ gem_names.all? { |name| /#{name}/ !~ l_spec.name }
+
+ 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
- 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
- l_spec.version < highest_remote_gem.version then
+ l_spec.version < highest_remote_gem.first[1] then
result << l_name
end
end
diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb
index 5bca0bd14e..c657bf7f01 100644
--- a/lib/rubygems/config_file.rb
+++ b/lib/rubygems/config_file.rb
@@ -18,6 +18,22 @@ class Gem::ConfigFile
DEFAULT_VERBOSITY = 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.
attr_reader :args
@@ -81,18 +97,8 @@ class Gem::ConfigFile
@verbose = DEFAULT_VERBOSITY
@update_sources = DEFAULT_UPDATE_SOURCES
- begin
- # HACK $SAFE ok?
- @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 ||= {}
+ @hash = load_file(SYSTEM_WIDE_CONFIG_FILE)
+ @hash.merge!(load_file(config_file_name.dup.untaint))
# HACK these override command-line args, which is bad
@backtrace = @hash[:backtrace] if @hash.key? :backtrace
@@ -105,6 +111,16 @@ class Gem::ConfigFile
handle_arguments arg_list
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.
def backtrace
@backtrace or $DEBUG
diff --git a/lib/rubygems/custom_require.rb b/lib/rubygems/custom_require.rb
index 5ff65afb14..90e6b53959 100755
--- a/lib/rubygems/custom_require.rb
+++ b/lib/rubygems/custom_require.rb
@@ -26,7 +26,7 @@ module Kernel
def require(path) # :nodoc:
gem_original_require path
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
Gem.activate(spec.name, "= #{spec.version}")
gem_original_require path
diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb
index 3864e5faca..914b9f777f 100644
--- a/lib/rubygems/defaults.rb
+++ b/lib/rubygems/defaults.rb
@@ -2,7 +2,7 @@ module Gem
# An Array of the default sources that come with RubyGems.
def self.default_sources
- %w[http://gems.rubyforge.org]
+ %w[http://gems.rubyforge.org/]
end
# Default home directory path to be used if an alternate value is not
diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb
index be731d564e..7b9904df55 100644
--- a/lib/rubygems/dependency.rb
+++ b/lib/rubygems/dependency.rb
@@ -8,24 +8,54 @@ require 'rubygems'
##
# The Dependency class holds a Gem name and a Gem::Requirement
+
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
+ ##
+ # Dependency type.
+
+ attr_reader :type
+
+ ##
+ # Dependent versions.
+
attr_writer :version_requirements
+ ##
+ # Orders dependencies by name only.
+
def <=>(other)
[@name] <=> [other.name]
end
##
- # Constructs the dependency
- #
- # name:: [String] name of the Gem
- # version_requirements:: [String Array] version requirement (e.g. ["> 1.2"])
- #
- def initialize(name, version_requirements)
+ # Constructs a dependency with +name+ and +requirements+.
+
+ def initialize(name, version_requirements, type=:runtime)
@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_requirement = nil # Avoid warnings.
end
@@ -48,17 +78,41 @@ class Gem::Dependency
end
def to_s # :nodoc:
- "#{name} (#{version_requirements})"
+ "#{name} (#{version_requirements}, #{@type || :runtime})"
end
def ==(other) # :nodoc:
self.class === other &&
self.name == other.name &&
+ self.type == other.type &&
self.version_requirements == other.version_requirements
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
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index 7ea2c0c317..b849d37245 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -1,9 +1,12 @@
require 'rubygems'
require 'rubygems/dependency_list'
require 'rubygems/installer'
-require 'rubygems/source_info_cache'
+require 'rubygems/spec_fetcher'
require 'rubygems/user_interaction'
+##
+# Installs a gem along with all its dependencies from local and remote gems.
+
class Gem::DependencyInstaller
include Gem::UserInteraction
@@ -25,36 +28,50 @@ class Gem::DependencyInstaller
# Creates a new installer instance.
#
# 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
# current directory. :remote searches only gems in Gem::sources.
# :both searches both.
+ # :env_shebang:: See Gem::Installer::new.
# :force:: See Gem::Installer#install.
# :format_executable:: See Gem::Installer#initialize.
- # :ignore_dependencies: Don't install any dependencies.
- # :install_dir: See Gem::Installer#install.
- # :security_policy: See Gem::Installer::new and Gem::Security.
- # :wrappers: See Gem::Installer::new
+ # :ignore_dependencies:: Don't install any dependencies.
+ # :install_dir:: See Gem::Installer#install.
+ # :security_policy:: See Gem::Installer::new and Gem::Security.
+ # :wrappers:: See Gem::Installer::new
+
def initialize(options = {})
options = DEFAULT_OPTIONS.merge options
- @env_shebang = options[:env_shebang]
+
+ @bin_dir = options[:bin_dir]
+ @development = options[:development]
@domain = options[:domain]
+ @env_shebang = options[:env_shebang]
@force = options[:force]
@format_executable = options[:format_executable]
@ignore_dependencies = options[:ignore_dependencies]
- @install_dir = options[:install_dir] || Gem.dir
@security_policy = options[:security_policy]
@wrappers = options[:wrappers]
- @bin_dir = options[:bin_dir]
@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
##
# Returns a list of pairs of gemspecs and source_uris that match
# 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.
+
def find_gems_with_sources(dep)
gems_and_sources = []
@@ -74,8 +91,7 @@ class Gem::DependencyInstaller
all = requirements.length > 1 ||
(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)
rescue Gem::RemoteFetcher::FetchError => e
@@ -95,6 +111,7 @@ class Gem::DependencyInstaller
##
# Gathers all dependencies necessary for the installation from local and
# remote sources unless the ignore_dependencies was given.
+
def gather_dependencies
specs = @specs_and_sources.map { |spec,_| spec }
@@ -110,8 +127,18 @@ class Gem::DependencyInstaller
next if spec.nil? or seen[spec.name]
seen[spec.name] = true
- spec.dependencies.each do |dep|
- results = find_gems_with_sources(dep).reverse # local gems first
+ deps = spec.runtime_dependencies
+ 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|
next if seen[dep_spec.name]
@@ -126,6 +153,11 @@ class Gem::DependencyInstaller
@gems_to_install = dependency_list.dependency_order.reverse
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
spec_and_source = nil
@@ -160,14 +192,16 @@ class Gem::DependencyInstaller
if spec_and_source.nil? then
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
@specs_and_sources = [spec_and_source]
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
if String === dep_or_name then
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]
end
- gather_dependencies
+ @installed_gems = []
- spec_dir = File.join @install_dir, 'specifications'
- source_index = Gem::SourceIndex.from_gems_in spec_dir
+ gather_dependencies
@gems_to_install.each do |spec|
last = spec == @gems_to_install.last
# 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
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
begin
local_gem_path = Gem::RemoteFetcher.fetcher.download spec, source_uri,
- @install_dir
+ @cache_dir
rescue Gem::RemoteFetcher::FetchError
next if @force
raise
@@ -205,12 +238,15 @@ class Gem::DependencyInstaller
:install_dir => @install_dir,
:security_policy => @security_policy,
:wrappers => @wrappers,
- :bin_dir => @bin_dir
+ :bin_dir => @bin_dir,
+ :development => @development
spec = inst.install
@installed_gems << spec
end
+
+ @installed_gems
end
end
diff --git a/lib/rubygems/dependency_list.rb b/lib/rubygems/dependency_list.rb
index 81aa65bfb2..a129743914 100644
--- a/lib/rubygems/dependency_list.rb
+++ b/lib/rubygems/dependency_list.rb
@@ -69,7 +69,7 @@ class Gem::DependencyList
# Are all the dependencies in the list satisfied?
def ok?
@specs.all? do |spec|
- spec.dependencies.all? do |dep|
+ spec.runtime_dependencies.all? do |dep|
@specs.find { |s| s.satisfies_requirement? dep }
end
end
diff --git a/lib/rubygems/doc_manager.rb b/lib/rubygems/doc_manager.rb
index f214269ab3..88d7964d85 100644
--- a/lib/rubygems/doc_manager.rb
+++ b/lib/rubygems/doc_manager.rb
@@ -9,9 +9,9 @@ require 'fileutils'
module Gem
class DocManager
-
+
include UserInteraction
-
+
# Create a document manager for the given gem spec.
#
# 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)
@rdoc_args = rdoc_args.nil? ? [] : rdoc_args.split
end
-
+
# Is the RDoc documentation installed?
def rdoc_installed?
return File.exist?(File.join(@doc_dir, "rdoc"))
end
-
+
# Generate the RI documents for this gem spec.
#
# Note that if both RI and RDoc documents are generated from the
@@ -102,7 +102,7 @@ module Gem
args << '--quiet'
args << @spec.require_paths.clone
args << @spec.extra_rdoc_files
- args.flatten!
+ args = args.flatten.map do |arg| arg.to_s end
r = RDoc::RDoc.new
diff --git a/lib/rubygems/indexer.rb b/lib/rubygems/indexer.rb
index 9f6c0a2fc9..b45931a91d 100644
--- a/lib/rubygems/indexer.rb
+++ b/lib/rubygems/indexer.rb
@@ -1,5 +1,6 @@
require 'fileutils'
require 'tmpdir'
+require 'zlib'
require 'rubygems'
require 'rubygems/format'
@@ -40,116 +41,303 @@ class Gem::Indexer
marshal_name = "Marshal.#{Gem.marshal_version}"
- @master_index = Gem::Indexer::MasterIndexBuilder.new "yaml", @directory
- @marshal_index = Gem::Indexer::MarshalIndexBuilder.new marshal_name, @directory
- @quick_index = Gem::Indexer::QuickIndexBuilder.new 'index', @directory
+ @master_index = File.join @directory, 'yaml'
+ @marshal_index = File.join @directory, marshal_name
- quick_dir = File.join @directory, 'quick'
- @latest_index = Gem::Indexer::LatestIndexBuilder.new 'latest_index', quick_dir
+ @quick_dir = File.join @directory, 'quick'
+
+ @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
+
+ ##
+ # 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)
+ spec.files = []
+ spec.test_files = []
+ spec.rdoc_options = []
+ spec.extra_rdoc_files = []
+ spec.cert_chain = []
+ spec
end
##
- # Build the index.
-
- 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
+ # 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
- def install_index
- verbose = Gem.configuration.really_verbose
+ ##
+ # Compacts Marshal output for the specs index data source by using identical
+ # objects as much as possible.
- say "Moving index into production dir #{@dest_directory}" if verbose
+ def compact_specs(specs)
+ names = {}
+ versions = {}
+ platforms = {}
- files = @master_index.files + @quick_index.files + @marshal_index.files +
- @latest_index.files
+ 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
- files.each do |file|
- src_name = File.join @directory, file
- dst_name = File.join @dest_directory, file
+ [names[name], versions[version], platforms[platform]]
+ end
+ end
- FileUtils.rm_rf dst_name, :verbose => verbose
- FileUtils.mv src_name, @dest_directory, :verbose => verbose
+ ##
+ # 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
- build_index
- install_index
+ index = collect_specs
+ build_indicies index
+ install_indicies
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"))
+ ##
+ # 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
- # 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)
- spec.files = []
- spec.test_files = []
- spec.rdoc_options = []
- spec.extra_rdoc_files = []
- spec.cert_chain = []
- spec
+ ##
+ # 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
# characters will garble the site index. Non-ASCII characters will
# be replaced by their XML entity equivalent.
+
def sanitize(spec)
spec.summary = sanitize_string(spec.summary)
spec.description = sanitize_string(spec.description)
@@ -158,7 +346,9 @@ class Gem::Indexer
spec
end
+ ##
# Sanitize a single string.
+
def sanitize_string(string)
# 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
@@ -168,9 +358,3 @@ class Gem::Indexer
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'
-
diff --git a/lib/rubygems/install_update_options.rb b/lib/rubygems/install_update_options.rb
index 58807be62a..5202c105db 100644
--- a/lib/rubygems/install_update_options.rb
+++ b/lib/rubygems/install_update_options.rb
@@ -89,6 +89,12 @@ module Gem::InstallUpdateOptions
'foo_exec18') do |value, options|
options[:format_executable] = value
end
+
+ add_option(:"Install/Update", "--development",
+ "Install any additional development",
+ "dependencies") do |value, options|
+ options[:development] = true
+ end
end
# Default options for the gem install command.
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index 9dbbca8d08..ae699a90a0 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -56,6 +56,7 @@ class Gem::Installer
# foo_exec18.
# :security_policy:: Use the specified security policy. See Gem::Security
# :wrappers:: Install wrappers if true, symlinks if false.
+
def initialize(gem, options={})
@gem = gem
@@ -76,6 +77,7 @@ class Gem::Installer
@security_policy = options[:security_policy]
@wrappers = options[:wrappers]
@bin_dir = options[:bin_dir]
+ @development = options[:development]
begin
@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
# gems/<gem-version>/... #=> extracted files
# specifications/<gem-version>.gemspec #=> the Gem::Specification
+
def install
# If we're forcing the install then disable security unless the security
# policy says that we only install singed gems.
@@ -119,7 +122,10 @@ class Gem::Installer
end
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
end
end
@@ -150,6 +156,8 @@ class Gem::Installer
@spec.loaded_from = File.join(@gem_home, 'specifications',
"#{@spec.full_name}.gemspec")
+ Gem.source_index.add_spec @spec
+
return @spec
rescue Zlib::GzipFile::Error
raise Gem::InstallError, "gzip error installing #{@gem}"
@@ -161,6 +169,7 @@ class Gem::Installer
#
# spec :: Gem::Specification
# dependency :: Gem::Dependency
+
def ensure_dependency(spec, dependency)
unless installation_satisfies_dependency? dependency then
raise Gem::InstallError, "#{spec.name} requires #{dependency}"
@@ -170,17 +179,15 @@ class Gem::Installer
end
##
- # True if the current installed gems satisfy the given dependency.
- #
- # dependency :: Gem::Dependency
+ # True if the gems in Gem.source_index satisfy +dependency+.
+
def installation_satisfies_dependency?(dependency)
- current_index = Gem::SourceIndex.from_installed_gems
- current_index.find_name(dependency.name, dependency.version_requirements).size > 0
+ Gem.source_index.find_name(dependency.name, dependency.version_requirements).size > 0
end
##
# Unpacks the gem into the given directory.
- #
+
def unpack(directory)
@gem_dir = directory
@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_path:: [String] The location (path) to write the gemspec to
- #
+
def write_spec
rubycode = @spec.to_ruby
@@ -208,7 +215,7 @@ class Gem::Installer
##
# Creates windows .bat files for easy running of commands
- #
+
def generate_windows_script(bindir, filename)
if Gem.win_platform? then
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
# the system gem directory, then use the system bin directory, else create
# (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
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
# bug or misfeature in the Windows shell's pipe. See
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/193379
- #
+
def generate_bin_script(filename, bindir)
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
#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.print app_script_text(filename)
end
@@ -277,7 +286,7 @@ class Gem::Installer
##
# Creates the symlinks to run the applications in the gem. Moves
# the symlink if the gem being installed has a newer version.
- #
+
def generate_bin_symlink(filename, bindir)
if Gem.win_platform? then
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
# necessary.
+
def shebang(bin_file_name)
if @env_shebang then
"#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name]
@@ -324,7 +334,9 @@ class Gem::Installer
end
end
+ ##
# Return the text for an application file.
+
def app_script_text(bin_file_name)
<<-TEXT
#{shebang bin_file_name}
@@ -349,7 +361,9 @@ load '#{bin_file_name}'
TEXT
end
+ ##
# return the stub script text used to launch the true ruby script
+
def windows_stub_script(bindir, bin_file_name)
<<-TEXT
@ECHO OFF
@@ -361,8 +375,10 @@ GOTO :EOF
TEXT
end
+ ##
# Builds extensions. Valid types of extensions are extconf.rb files,
# configure scripts and rakefiles or mkrf_conf files.
+
def build_extensions
return if @spec.extensions.empty?
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.
#
# Ensures that files can't be installed outside the gem directory.
+
def extract_files
expand_and_validate_gem_dir
@@ -445,11 +462,15 @@ Results logged to #{File.join(Dir.pwd, 'gem_make.out')}
out.write file_data
end
+ FileUtils.chmod entry['mode'], path
+
say path if Gem.configuration.really_verbose
end
end
+ ##
# Prefix and suffix the program filename the same as ruby.
+
def formatted_program_filename(filename)
if @format_executable then
self.class.exec_format % File.basename(filename)
@@ -460,7 +481,9 @@ Results logged to #{File.join(Dir.pwd, 'gem_make.out')}
private
+ ##
# HACK Pathname is broken on windows.
+
def absolute_path? pathname
pathname.absolute? or (Gem.win_platform? and pathname.to_s =~ /\A[a-z]:/i)
end
diff --git a/lib/rubygems/local_remote_options.rb b/lib/rubygems/local_remote_options.rb
index 1a5410bef7..799b9d5893 100644
--- a/lib/rubygems/local_remote_options.rb
+++ b/lib/rubygems/local_remote_options.rb
@@ -4,27 +4,34 @@
# See LICENSE.txt for permissions.
#++
+require 'uri'
require 'rubygems'
+##
# Mixin methods for local and remote Gem::Command options.
+
module Gem::LocalRemoteOptions
+ ##
# Allows OptionParser to handle HTTP URIs.
+
def accept_uri_http
OptionParser.accept URI::HTTP do |value|
begin
- value = URI.parse value
+ uri = URI.parse value
rescue URI::InvalidURIError
raise OptionParser::InvalidArgument, value
end
- raise OptionParser::InvalidArgument, value unless value.scheme == 'http'
+ raise OptionParser::InvalidArgument, value unless uri.scheme == 'http'
value
end
end
+ ##
# Add local/remote options to the command line parser.
+
def add_local_remote_options
add_option(:"Local/Remote", '-l', '--local',
'Restrict operations to the LOCAL domain') do |value, options|
@@ -47,7 +54,9 @@ module Gem::LocalRemoteOptions
add_update_sources_option
end
+ ##
# Add the --bulk-threshold option
+
def add_bulk_threshold_option
add_option(:"Local/Remote", '-B', '--bulk-threshold COUNT',
"Threshold for switching to bulk",
@@ -57,7 +66,9 @@ module Gem::LocalRemoteOptions
end
end
+ ##
# Add the --http-proxy option
+
def add_proxy_option
accept_uri_http
@@ -68,22 +79,28 @@ module Gem::LocalRemoteOptions
end
end
+ ##
# Add the --source option
+
def add_source_option
accept_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
- Gem.sources << value
+ Gem.sources << source
else
options[:added_source] = true
- Gem.sources.replace [value]
+ Gem.sources.replace [source]
end
end
end
+ ##
# Add the --source option
+
def add_update_sources_option
add_option(:"Local/Remote", '-u', '--[no-]update-sources',
@@ -92,12 +109,16 @@ module Gem::LocalRemoteOptions
end
end
+ ##
# Is local fetching enabled?
+
def local?
options[:domain] == :local || options[:domain] == :both
end
+ ##
# Is remote fetching enabled?
+
def remote?
options[:domain] == :remote || options[:domain] == :both
end
diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb
index 7abc15ef94..5e932cd592 100644
--- a/lib/rubygems/platform.rb
+++ b/lib/rubygems/platform.rb
@@ -1,7 +1,8 @@
require 'rubygems'
+##
# Available list of platforms for targeting Gem installations.
-#
+
class Gem::Platform
@local = nil
@@ -122,11 +123,20 @@ class Gem::Platform
to_a.compact.join '-'
end
+ ##
+ # Is +other+ equal to this platform? Two platforms are equal if they have
+ # the same CPU, OS and version.
+
def ==(other)
self.class === other and
@cpu == other.cpu and @os == other.os and @version == other.version
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)
return nil unless Gem::Platform === other
@@ -140,6 +150,10 @@ class Gem::Platform
(@version.nil? or other.version.nil? or @version == other.version)
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)
case other
when Gem::Platform then # nop
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index 96775c4d00..93252fe83a 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -1,4 +1,5 @@
require 'net/http'
+require 'stringio'
require 'uri'
require 'rubygems'
@@ -11,15 +12,38 @@ class Gem::RemoteFetcher
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
+ ##
# Cached RemoteFetcher instance.
+
def self.fetcher
@fetcher ||= self.new Gem.configuration[:http_proxy]
end
+ ##
# Initialize a remote fetcher using the source URI and possible proxy
# information.
#
@@ -29,6 +53,7 @@ class Gem::RemoteFetcher
# * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER,
# HTTP_PROXY_PASS)
# * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy
+
def initialize(proxy)
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
# already there. If the source_uri is local the gem cache dir copy is
# always replaced.
+
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 install_dir, 'cache', gem_file_name
+ local_gem_path = File.join cache_dir, gem_file_name
- Gem.ensure_gem_subdirectories install_dir
+ FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir
source_uri = URI.parse source_uri unless URI::Generic === source_uri
scheme = source_uri.scheme
@@ -102,21 +129,26 @@ class Gem::RemoteFetcher
local_gem_path
end
- # Downloads +uri+.
+ ##
+ # Downloads +uri+ and returns it as a String.
+
def fetch_path(uri)
open_uri_or_path(uri) do |input|
input.read
end
+ rescue FetchError
+ raise
rescue Timeout::Error
- raise FetchError, "timed out fetching #{uri}"
+ raise FetchError.new('timed out', uri)
rescue IOError, SocketError, SystemCallError => e
- raise FetchError, "#{e.class}: #{e} reading #{uri}"
+ raise FetchError.new("#{e.class}: #{e}", uri)
rescue => e
- message = "#{e.class}: #{e} reading #{uri}"
- raise FetchError, message
+ raise FetchError.new("#{e.class}: #{e}", uri)
end
+ ##
# Returns the size of +uri+ in bytes.
+
def fetch_size(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
- http = connect_to uri.host, uri.port
-
- request = Net::HTTP::Head.new uri.request_uri
-
- request.basic_auth unescape(uri.user), unescape(uri.password) unless
- uri.user.nil? or uri.user.empty?
+ response = request uri, Net::HTTP::Head
- resp = http.request request
-
- if resp.code !~ /^2/ then
- raise Gem::RemoteSourceException,
- "HTTP Response #{resp.code} fetching #{uri}"
+ if response.code !~ /^2/ then
+ raise FetchError.new("bad response #{response.message} #{response.code}", uri)
end
- if resp['content-length'] then
- return resp['content-length'].to_i
+ if response['content-length'] then
+ return response['content-length'].to_i
else
- resp = http.get uri.request_uri
- return resp.body.size
+ response = http.get uri.request_uri
+ return response.body.size
end
rescue SocketError, SystemCallError, Timeout::Error => e
- raise Gem::RemoteFetcher::FetchError,
- "#{e.message} (#{e.class})\n\tgetting size of #{uri}"
+ raise FetchError.new("#{e.message} (#{e.class})\n\tfetching size", uri)
end
private
@@ -162,7 +185,9 @@ class Gem::RemoteFetcher
URI.unescape(str)
end
+ ##
# Returns an HTTP proxy URI if one is set in the environment variables.
+
def get_proxy_from_env
env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
@@ -179,104 +204,129 @@ class Gem::RemoteFetcher
uri
end
+ ##
# Normalize the URI by adding "http://" if it is missing.
+
def normalize_uri(uri)
(uri =~ /^(https?|ftp|file):/) ? uri : "http://#{uri}"
end
- # Connect to the source host/port, using a proxy if needed.
- def connect_to(host, port)
- if @proxy_uri
- Net::HTTP::Proxy(@proxy_uri.host, @proxy_uri.port, unescape(@proxy_uri.user), unescape(@proxy_uri.password)).new(host, port)
- else
- Net::HTTP.new(host, port)
+ ##
+ # Creates or an HTTP connection based on +uri+, or retrieves an existing
+ # connection, using a proxy if needed.
+
+ def connection_for(uri)
+ 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
+
+ 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
+ ##
# Read the data from the (source based) URI, but if it is a file:// URI,
# read from the filesystem instead.
+
def open_uri_or_path(uri, depth = 0, &block)
if file_uri?(uri)
open(get_file_uri_path(uri), &block)
else
uri = URI.parse uri unless URI::Generic === uri
- 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
-
- 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
+ response = request uri
case response
when Net::HTTPOK then
block.call(StringIO.new(response.body)) if block
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)
else
- raise Gem::RemoteFetcher::FetchError,
- "bad response #{response.message} #{response.code}"
+ raise FetchError.new("bad response #{response.message} #{response.code}", uri)
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.
+
def file_uri?(uri)
uri =~ %r{\Afile://}
end
+ ##
# Given a file:// URI, returns its local path.
+
def get_file_uri_path(uri)
uri.sub(%r{\Afile://}, '')
end
diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb
index f1213152f2..c9128b5ebc 100644
--- a/lib/rubygems/requirement.rb
+++ b/lib/rubygems/requirement.rb
@@ -12,6 +12,7 @@ require 'rubygems/version'
#
# A Requirement object can actually contain multiple, er,
# requirements, as in (> 1.2, < 2.0).
+
class Gem::Requirement
include Comparable
@@ -35,7 +36,7 @@ class Gem::Requirement
# Version, a String, or nil. Intended to simplify client code.
#
# If the input is "weird", the default version requirement is returned.
- #
+
def self.create(input)
case input
when Gem::Requirement then
@@ -57,6 +58,7 @@ class Gem::Requirement
# This comment once said:
#
# "A default "version requirement" can surely _only_ be '> 0'."
+
def self.default
self.new ['>= 0']
end
@@ -65,6 +67,7 @@ class Gem::Requirement
# Constructs a Requirement from +requirements+ which can be a String, a
# Gem::Version, or an Array of those. See parse for details on the
# formatting of requirement strings.
+
def initialize(requirements)
@requirements = case requirements
when Array then
@@ -77,13 +80,17 @@ class Gem::Requirement
@version = nil # Avoid warnings.
end
+ ##
# Marshal raw requirements, rather than the full object
- def marshal_dump
+
+ def marshal_dump # :nodoc:
[@requirements]
end
+ ##
# Load custom marshal format
- def marshal_load(array)
+
+ def marshal_load(array) # :nodoc:
@requirements = array[0]
@version = nil
end
@@ -108,20 +115,16 @@ class Gem::Requirement
end
##
- # Is the requirement satisfied by +version+.
- #
- # version:: [Gem::Version] the version to compare against
- # return:: [Boolean] true if this requirement is satisfied by
- # the version, otherwise false
- #
+ # True if this requirement satisfied by the Gem::Version +version+.
+
def satisfied_by?(version)
normalize
@requirements.all? { |op, rv| satisfy?(op, version, rv) }
end
##
- # Is "version op required_version" satisfied?
- #
+ # Is "+version+ +op+ +required_version+" satisfied?
+
def satisfy?(op, version, required_version)
OPS[op].call(version, required_version)
end
@@ -132,6 +135,7 @@ class Gem::Requirement
# The requirement can be a String or a Gem::Version. A String can be an
# operator (<, <=, =, =>, >, !=, ~>), a version number, or both, operator
# first.
+
def parse(obj)
case obj
when /^\s*(#{OP_RE})\s*([0-9.]+)\s*$/o then
@@ -147,7 +151,7 @@ class Gem::Requirement
end
end
- def <=>(other)
+ def <=>(other) # :nodoc:
to_s <=> other.to_s
end
diff --git a/lib/rubygems/rubygems_version.rb b/lib/rubygems/rubygems_version.rb
index 453f9b57b6..d4d4af0558 100644
--- a/lib/rubygems/rubygems_version.rb
+++ b/lib/rubygems/rubygems_version.rb
@@ -2,5 +2,5 @@
# This file is auto-generated by build scripts.
# See: rake update_version
module Gem
- RubyGemsVersion = '1.1.1'
+ RubyGemsVersion = '1.1.1.1778'
end
diff --git a/lib/rubygems/server.rb b/lib/rubygems/server.rb
index b4f58f9706..2c617ff144 100644
--- a/lib/rubygems/server.rb
+++ b/lib/rubygems/server.rb
@@ -4,6 +4,7 @@ require 'zlib'
require 'erb'
require 'rubygems'
+require 'rubygems/doc_manager'
##
# 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:
# * "/" - Browsing of gem spec files for installed gems
-# * "/Marshal" - Full SourceIndex dump of metadata for installed gems
-# * "/yaml" - YAML dump of metadata for installed gems - deprecated
+# * "/specs.#{Gem.marshal_version}.gz" - specs name/version/platform index
+# * "/latest_specs.#{Gem.marshal_version}.gz" - latest specs
+# name/version/platform index
+# * "/quick/" - Individual gemspecs
# * "/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
#
-# 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::
-# Root gem directory containing both "cache" and "specifications"
-# subdirectories.
+#--
+# TODO Refactor into a real WEBrick servlet to remove code duplication.
+
class Gem::Server
include Gem::UserInteraction
@@ -36,7 +43,6 @@ class Gem::Server
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<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" />
</head>
<body>
@@ -325,32 +331,99 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
new(options[:gemdir], options[:port], options[:daemon]).run
end
- def initialize(gemdir, port, daemon)
+ def initialize(gem_dir, port, daemon)
Socket.do_not_reverse_lookup = true
- @gemdir = gemdir
+ @gem_dir = gem_dir
@port = port
@daemon = daemon
logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL
@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
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)
+ @source_index.refresh!
+
res['content-type'] = 'text/plain'
res['date'] = File.stat(@spec_dir).mtime
- case req.request_uri.request_uri
+ case req.request_uri.path
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
- index = @source_index.map { |name,_| name }.join("\n")
- res.body << Zlib::Deflate.deflate(index)
+ index = @source_index.map { |name,| name }.sort.join("\n")
+ 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
dep = Gem::Dependency.new $2, $3
specs = @source_index.search dep
+ marshal_format = $1
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
res.status = 500
res.body = "Multiple gems found matching #{selector}"
- elsif $1 then # marshal quickindex instead of YAML
- res.body << Zlib::Deflate.deflate(Marshal.dump(specs.first))
+ elsif marshal_format then
+ res['content-type'] = 'application/x-deflate'
+ res.body << Gem.deflate(Marshal.dump(specs.first))
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
else
- res.status = 404
- res.body = "#{req.request_uri} not found"
+ raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
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
@server.listen nil, @port
@@ -386,27 +540,21 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
WEBrick::Daemon.start if @daemon
- @server.mount_proc("/yaml") do |req, res|
- res['content-type'] = 'text/plain'
- 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 "/yaml", method(:yaml)
+ @server.mount_proc "/yaml.Z", method(:yaml)
- @server.mount_proc("/Marshal") do |req, res|
- res['content-type'] = 'text/plain'
- 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 "/Marshal.#{Gem.marshal_version}", method(:Marshal)
+ @server.mount_proc "/Marshal.#{Gem.marshal_version}.Z", method(:Marshal)
+
+ @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 "/quick/", method(:quick)
@server.mount_proc("/gem-server-rdoc-style.css") do |req, res|
res['content-type'] = 'text/css'
@@ -414,80 +562,12 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
res.body << RDOC_CSS
end
- @server.mount_proc("/") do |req, res|
- 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
+ @server.mount_proc "/", method(:root)
paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" }
paths.each do |mount_point, mount_dir|
@server.mount(mount_point, WEBrick::HTTPServlet::FileHandler,
- File.join(@gemdir, mount_dir), true)
+ File.join(@gem_dir, mount_dir), true)
end
trap("INT") { @server.shutdown; exit! }
@@ -496,5 +576,54 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
@server.start
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
diff --git a/lib/rubygems/source_index.rb b/lib/rubygems/source_index.rb
index 8057fd1d0f..b940b83cf8 100644
--- a/lib/rubygems/source_index.rb
+++ b/lib/rubygems/source_index.rb
@@ -7,6 +7,7 @@
require 'rubygems'
require 'rubygems/user_interaction'
require 'rubygems/specification'
+require 'rubygems/spec_fetcher'
##
# The SourceIndex object indexes all the gems available from a
@@ -27,6 +28,11 @@ class Gem::SourceIndex
attr_reader :gems # :nodoc:
+ ##
+ # Directories to use to refresh this SourceIndex when calling refresh!
+
+ attr_accessor :spec_dirs
+
class << self
include Gem::UserInteraction
@@ -39,7 +45,7 @@ class Gem::SourceIndex
# +from_gems_in+. This argument is deprecated and is provided
# just for backwards compatibility, and should not generally
# be used.
- #
+ #
# return::
# SourceIndex instance
@@ -63,7 +69,9 @@ class Gem::SourceIndex
# +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
##
@@ -79,6 +87,8 @@ class Gem::SourceIndex
return gemspec
end
alert_warning "File '#{file_name}' does not evaluate to a gem specification"
+ rescue SignalException, SystemExit
+ raise
rescue SyntaxError => e
alert_warning e
alert_warning spec_code
@@ -100,6 +110,7 @@ class Gem::SourceIndex
def initialize(specifications={})
@gems = specifications
+ @spec_dirs = nil
end
##
@@ -121,8 +132,8 @@ class Gem::SourceIndex
end
##
- # Returns a Hash of name => Specification of the latest versions of each
- # gem in this index.
+ # Returns an Array specifications for the latest versions of each gem in
+ # this index.
def latest_specs
result = Hash.new { |h,k| h[k] = [] }
@@ -241,7 +252,9 @@ class Gem::SourceIndex
when Gem::Dependency then
only_platform = platform_only
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
/^#{Regexp.escape gem_pattern.name}$/
@@ -271,29 +284,43 @@ class Gem::SourceIndex
##
# 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!
- 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
##
# Returns an Array of Gem::Specifications that are not up to date.
def outdated
- dep = Gem::Dependency.new '', Gem::Requirement.default
-
- remotes = Gem::SourceInfoCache.search dep, true
-
outdateds = []
latest_specs.each do |local|
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
outdateds
@@ -387,7 +414,8 @@ class Gem::SourceIndex
end
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)
if index.nil? then
@@ -447,7 +475,7 @@ class Gem::SourceIndex
def unzip(string)
require 'zlib'
- Zlib::Inflate.inflate(string)
+ Gem.inflate string
end
##
diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb
new file mode 100644
index 0000000000..29db889af5
--- /dev/null
+++ b/lib/rubygems/spec_fetcher.rb
@@ -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
+
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index c50910aeb4..0642a4f3e0 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -6,6 +6,7 @@
require 'rubygems'
require 'rubygems/version'
+require 'rubygems/requirement'
require 'rubygems/platform'
# :stopdoc:
@@ -16,6 +17,9 @@ if RUBY_VERSION < '1.9' then
t - ((t.to_f + t.gmt_offset) % 86400)
end unless defined? Time.today
end
+
+class Date; end # for ruby_code if date.rb wasn't required
+
# :startdoc:
module Gem
@@ -37,22 +41,32 @@ module Gem
#
class Specification
+ ##
# Allows deinstallation of gems with legacy platforms.
+
attr_accessor :original_platform # :nodoc:
# ------------------------- Specification version constants.
+ ##
# The the version number of a specification that does not specify one
# (i.e. RubyGems 0.7 or earlier).
+
NONEXISTENT_SPECIFICATION_VERSION = -1
+ ##
# The specification version applied to any new Specification instances
# created. This should be bumped whenever something in the spec format
# 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
# key should be equal to the CURRENT_SPECIFICATION_VERSION.
+
SPECIFICATION_VERSION_HISTORY = {
-1 => ['(RubyGems versions up to and including 0.7 did not have versioned specifications)'],
1 => [
@@ -63,10 +77,13 @@ module Gem
'Added "required_rubygems_version"',
'Now forward-compatible with future versions',
],
+ 3 => [
+ 'Added dependency types',
+ ],
}
# :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)
TODAY = now - ((now.to_i + now.gmt_offset) % 86400)
@@ -335,6 +352,14 @@ module Gem
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 -------------------------------------
attribute_alias_singular :executable, :executables
@@ -629,27 +654,31 @@ module Gem
end
end
- # Adds a dependency to this Gem. For example,
+ # Adds a development dependency to this Gem. For example,
+ #
+ # spec.add_development_dependency('jabber4r', '> 0.1', '<= 0.5')
#
- # spec.add_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.
# requirements:: [default=">= 0"] The version requirements.
+ def add_development_dependency(gem, *requirements)
+ add_dependency_with_type(gem, :development, *requirements)
+ end
+
+ # Adds a runtime dependency to this Gem. For example,
#
- def add_dependency(gem, *requirements)
- requirements = if requirements.empty? then
- Gem::Requirement.default
- else
- requirements.flatten
- end
+ # 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
- unless gem.respond_to?(:name) && gem.respond_to?(:version_requirements)
- gem = Dependency.new(gem, requirements)
- end
+ alias add_dependency add_runtime_dependency
- dependencies << gem
- end
-
# Returns the full name (name-version) of this Gem. Platform information
# is included (name-version-platform) if it is specified (and not the
# default Ruby platform).
@@ -673,30 +702,31 @@ module Gem
end
end
+ ##
# The full path to the gem (install path + full name).
- #
- # return:: [String] the full gem path
- #
+
def full_gem_path
path = File.join installation_path, 'gems', full_name
return path if File.directory? path
File.join installation_path, 'gems', original_name
end
-
+
+ ##
# The default (generated) file name of the gem.
+
def file_name
full_name + ".gem"
end
-
- # The root directory that the gem was installed into.
- #
- # return:: [String] the installation path
- #
+
+ ##
+ # The directory that this gem was installed into.
+
def installation_path
- (File.dirname(@loaded_from).split(File::SEPARATOR)[0..-2]).
- join(File::SEPARATOR)
+ path = File.dirname(@loaded_from).split(File::SEPARATOR)[0..-2]
+ path = path.join File::SEPARATOR
+ File.expand_path path
end
-
+
# Checks if this Specification meets the requirement of the supplied
# dependency.
#
@@ -778,9 +808,11 @@ module Gem
self.platform = Gem::Platform.new @platform
end
+ ##
# Returns a Ruby code representation of this specification, such that it
# can be eval'ed and reconstruct the same specification later. Attributes
# that still have their default values are omitted.
+
def to_ruby
mark_version
result = []
@@ -792,8 +824,6 @@ module Gem
result << " s.platform = #{ruby_code original_platform}"
end
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="
handled = [
@@ -816,15 +846,42 @@ module Gem
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
+
+ result << " if current_version >= 3 then"
+
+ 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
+
+ result << " else"
- dependencies.each do |dep|
- 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
+ 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 << ""
+ result << nil
result.join "\n"
end
@@ -940,6 +997,22 @@ module Gem
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)
Gem.source_index.each do |name,gem|
if(gem.satisfies_requirement?(dep)) then
diff --git a/lib/rubygems/test_utilities.rb b/lib/rubygems/test_utilities.rb
new file mode 100644
index 0000000000..0486db2b32
--- /dev/null
+++ b/lib/rubygems/test_utilities.rb
@@ -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
+
diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb
index e2b5e5372b..c5ae47b7eb 100644
--- a/lib/rubygems/uninstaller.rb
+++ b/lib/rubygems/uninstaller.rb
@@ -176,9 +176,10 @@ class Gem::Uninstaller
end
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
def dependencies_ok?(spec)
diff --git a/lib/rubygems/user_interaction.rb b/lib/rubygems/user_interaction.rb
index 8d27df8768..30a728c597 100644
--- a/lib/rubygems/user_interaction.rb
+++ b/lib/rubygems/user_interaction.rb
@@ -6,54 +6,71 @@
module Gem
- ####################################################################
- # Module that defines the default UserInteraction. Any class
- # including this module will have access to the +ui+ method that
- # returns the default UI.
+ ##
+ # Module that defines the default UserInteraction. Any class including this
+ # module will have access to the +ui+ method that returns the default UI.
+
module DefaultUserInteraction
+ ##
+ # The default UI is a class variable of the singleton class for this
+ # module.
+
+ @ui = nil
+
+ ##
# 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
DefaultUserInteraction.ui
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)
DefaultUserInteraction.ui = new_ui
end
+ ##
+ # See DefaultUserInteraction::use_ui
+
def use_ui(new_ui, &block)
DefaultUserInteraction.use_ui(new_ui, &block)
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
- ####################################################################
+ ##
# Make the default UI accessable without the "ui." prefix. Classes
- # including this module may use the interaction methods on the
- # default UI directly. Classes may also reference the +ui+ and
- # <tt>ui=</tt> methods.
+ # including this module may use the interaction methods on the default UI
+ # directly. Classes may also reference the ui and ui= methods.
#
# Example:
#
@@ -64,22 +81,30 @@ module Gem
# n = ask("What is the meaning of life?")
# end
# end
+
module UserInteraction
+
include DefaultUserInteraction
- [
- :choose_from_list, :ask, :ask_yes_no, :say, :alert, :alert_warning,
- :alert_error, :terminate_interaction
- ].each do |methname|
+
+ [:alert,
+ :alert_error,
+ :alert_warning,
+ :ask,
+ :ask_yes_no,
+ :choose_from_list,
+ :say,
+ :terminate_interaction ].each do |methname|
class_eval %{
def #{methname}(*args)
ui.#{methname}(*args)
end
- }
+ }, __FILE__, __LINE__
end
end
- ####################################################################
+ ##
# StreamUI implements a simple stream based user interface.
+
class StreamUI
attr_reader :ins, :outs, :errs
@@ -89,15 +114,19 @@ module Gem
@outs = out_stream
@errs = err_stream
end
-
- # Choose from a list of options. +question+ is a prompt displayed
- # above the list. +list+ is a list of option strings. Returns
- # the pair [option_name, option_index].
+
+ ##
+ # Choose from a list of options. +question+ is a prompt displayed above
+ # the list. +list+ is a list of option strings. Returns the pair
+ # [option_name, option_index].
+
def choose_from_list(question, list)
@outs.puts question
+
list.each_with_index do |item, index|
@outs.puts " #{index+1}. #{item}"
end
+
@outs.print "> "
@outs.flush
@@ -109,28 +138,32 @@ module Gem
return list[result], result
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,
- # otherwise returns default.
+ ##
+ # Ask a question. Returns a true for yes, false for no. If not connected
+ # to a tty, raises an exception if default is nil, otherwise returns
+ # default.
+
def ask_yes_no(question, default=nil)
- if not @ins.tty? then
+ unless @ins.tty? then
if default.nil? then
- raise(
- Gem::OperationNotSupportedError,
- "Not connected to a tty and no default specified")
+ raise Gem::OperationNotSupportedError,
+ "Not connected to a tty and no default specified"
else
return default
end
end
+
qstr = case default
- when nil
- 'yn'
- when true
- 'Yn'
- else
- 'yN'
- end
+ when nil
+ 'yn'
+ when true
+ 'Yn'
+ else
+ 'yN'
+ end
+
result = nil
+
while result.nil?
result = ask("#{question} [#{qstr}]")
result = case result
@@ -144,51 +177,68 @@ module Gem
nil
end
end
+
return result
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)
return nil if not @ins.tty?
+
@outs.print(question + " ")
@outs.flush
+
result = @ins.gets
result.chomp! if result
result
end
-
+
+ ##
# Display a statement.
+
def say(statement="")
@outs.puts statement
end
-
- # Display an informational alert.
+
+ ##
+ # Display an informational alert. Will ask +question+ if it is not nil.
+
def alert(statement, question=nil)
@outs.puts "INFO: #{statement}"
- return ask(question) if question
+ ask(question) if question
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)
@errs.puts "WARNING: #{statement}"
- ask(question) if question
+ ask(question) if question
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)
@errs.puts "ERROR: #{statement}"
ask(question) if question
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)
raise Gem::SystemExitException, status
end
- # Return a progress reporter object
+ ##
+ # Return a progress reporter object chosen from the current verbosity.
+
def progress_reporter(*args)
case Gem.configuration.verbose
when nil, false
@@ -200,6 +250,9 @@ module Gem
end
end
+ ##
+ # An absolutely silent progress reporter.
+
class SilentProgressReporter
attr_reader :count
@@ -213,6 +266,9 @@ module Gem
end
end
+ ##
+ # A basic dotted progress reporter.
+
class SimpleProgressReporter
include DefaultUserInteraction
@@ -228,17 +284,27 @@ module Gem
@out.puts initial_message
end
+ ##
+ # Prints out a dot and ignores +message+.
+
def updated(message)
@count += 1
@out.print "."
@out.flush
end
+ ##
+ # Prints out the terminal message.
+
def done
@out.puts "\n#{@terminal_message}"
end
+
end
+ ##
+ # A progress reporter that prints out messages about the current progress.
+
class VerboseProgressReporter
include DefaultUserInteraction
@@ -254,32 +320,41 @@ module Gem
@out.puts initial_message
end
+ ##
+ # Prints out the position relative to the total and the +message+.
+
def updated(message)
@count += 1
@out.puts "#{@count}/#{@total}: #{message}"
end
+ ##
+ # Prints out the terminal message.
+
def done
@out.puts @terminal_message
end
end
end
- ####################################################################
- # Subclass of StreamUI that instantiates the user interaction using
- # standard in, out and error.
+ ##
+ # Subclass of StreamUI that instantiates the user interaction using STDIN,
+ # STDOUT, and STDERR.
+
class ConsoleUI < StreamUI
def initialize
super(STDIN, STDOUT, STDERR)
end
end
- ####################################################################
+ ##
# SilentUI is a UI choice that is absolutely silent.
+
class SilentUI
def method_missing(sym, *args, &block)
self
end
end
+
end
diff --git a/lib/rubygems/version.rb b/lib/rubygems/version.rb
index 87a1bc72ed..ff4a7bf079 100644
--- a/lib/rubygems/version.rb
+++ b/lib/rubygems/version.rb
@@ -8,6 +8,7 @@ require 'rubygems'
##
# The Version class processes string versions into comparable values
+
class Gem::Version
include Comparable
@@ -17,11 +18,8 @@ class Gem::Version
attr_reader :version
##
- # Checks if version string is valid format
- #
- # str:: [String] the version string
- # return:: [Boolean] true if the string format is correct, otherwise false
- #
+ # Returns true if +version+ is a valid version string.
+
def self.correct?(version)
case version
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)
# ver2 = Version.create(ver1) # -> (ver1)
# ver3 = Version.create(nil) # -> nil
- #
+
def self.create(input)
if input.respond_to? :version then
input
@@ -48,10 +46,9 @@ class Gem::Version
end
##
- # Constructs a version from the supplied string
- #
- # version:: [String] The version string. Format is digit.digit...
- #
+ # Constructs a Version from the +version+ string. A version string is a
+ # series of digits separated by dots.
+
def initialize(version)
raise ArgumentError, "Malformed version number string #{version}" unless
self.class.correct?(version)
@@ -73,7 +70,9 @@ class Gem::Version
self.version = array[0]
end
+ ##
# Strip ignored trailing zeros.
+
def normalize
@ints = build_array_from_version_string
@@ -94,10 +93,8 @@ class Gem::Version
end
##
- # Convert version to integer array
- #
- # return:: [Array] list of integers
- #
+ # Returns an integer array representation of this Version.
+
def to_ints
normalize unless @ints
@ints
@@ -117,20 +114,25 @@ class Gem::Version
end
##
- # Compares two versions
- #
- # other:: [Version or .ints] other version to compare to
- # return:: [Fixnum] -1, 0, 1
- #
+ # Compares this version with +other+ returning -1, 0, or 1 if the other
+ # version is larger, the same, or smaller than this one.
+
def <=>(other)
+ return nil unless self.class === other
return 1 unless other
@ints <=> other.ints
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:
- to_ints.inject { |hash_code, n| hash_code + n }
+ @version.hash
end
# Return a new version object where the next to the last revision
diff --git a/test/rubygems/gemutilities.rb b/test/rubygems/gemutilities.rb
index 967e3dc34d..d8818d0b01 100644
--- a/test/rubygems/gemutilities.rb
+++ b/test/rubygems/gemutilities.rb
@@ -10,10 +10,9 @@ at_exit { $SAFE = 1 }
require 'fileutils'
require 'test/unit'
require 'tmpdir'
-require 'tempfile'
require 'uri'
-require 'rubygems/source_info_cache'
require 'rubygems/package'
+require 'rubygems/test_utilities'
require File.join(File.expand_path(File.dirname(__FILE__)), 'mockgemui')
@@ -27,54 +26,6 @@ module Gem
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
include Gem::DefaultUserInteraction
@@ -94,8 +45,13 @@ class RubyGemTestCase < Test::Unit::TestCase
@gemcache = File.join(@gemhome, "source_cache")
@usrcache = File.join(@gemhome, ".gem", "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 @userhome
ENV['GEMCACHE'] = @usrcache
Gem.use_paths(@gemhome)
@@ -104,9 +60,12 @@ class RubyGemTestCase < Test::Unit::TestCase
Gem.configuration.verbose = 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::SpecFetcher.fetcher = nil
+
@orig_BASERUBY = Gem::ConfigMap[:BASERUBY]
Gem::ConfigMap[:BASERUBY] = Gem::ConfigMap[:RUBY_INSTALL_NAME]
@@ -131,7 +90,7 @@ class RubyGemTestCase < Test::Unit::TestCase
Gem::ConfigMap[:arch] = @orig_arch
if defined? Gem::RemoteFetcher then
- Gem::RemoteFetcher.instance_variable_set :@fetcher, nil
+ Gem::RemoteFetcher.fetcher = nil
end
FileUtils.rm_rf @tempdir
@@ -141,7 +100,6 @@ class RubyGemTestCase < Test::Unit::TestCase
ENV.delete 'GEM_PATH'
Gem.clear_paths
- Gem::SourceInfoCache.instance_variable_set :@cache, nil
end
def install_gem gem
@@ -154,7 +112,7 @@ class RubyGemTestCase < Test::Unit::TestCase
end
gem = File.join(@tempdir, "#{gem.full_name}.gem").untaint
- Gem::Installer.new(gem).install
+ Gem::Installer.new(gem, :wrappers => true).install
end
def prep_cache_files(lc)
@@ -231,6 +189,8 @@ class RubyGemTestCase < Test::Unit::TestCase
spec.loaded_from = written_path
+ Gem.source_index.add_spec spec
+
return spec
end
@@ -254,6 +214,12 @@ class RubyGemTestCase < Test::Unit::TestCase
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)
spec = quick_gem(name, version, &block)
@@ -271,6 +237,16 @@ class RubyGemTestCase < Test::Unit::TestCase
[spec, cache_file]
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
init = proc do |s|
s.files = %w[lib/code.rb]
@@ -303,7 +279,7 @@ class RubyGemTestCase < Test::Unit::TestCase
end
##
- # Set the platform to +cpu+ and +os+
+ # Set the platform to +arch+
def util_set_arch(arch)
Gem::ConfigMap[:arch] = arch
@@ -320,9 +296,7 @@ class RubyGemTestCase < Test::Unit::TestCase
require 'socket'
require 'rubygems/remote_fetcher'
- @uri = URI.parse @gem_repo
- @fetcher = FakeFetcher.new
- @fetcher.uri = @uri
+ @fetcher = Gem::FakeFetcher.new
util_make_gems
@@ -338,10 +312,11 @@ class RubyGemTestCase < Test::Unit::TestCase
@source_index.add_spec @a_evil9
@source_index.add_spec @c1_2
- Gem::RemoteFetcher.instance_variable_set :@fetcher, @fetcher
+ Gem::RemoteFetcher.fetcher = @fetcher
end
def util_setup_source_info_cache(*specs)
+ require 'rubygems/source_info_cache'
require 'rubygems/source_info_cache_entry'
specs = Hash[*specs.map { |spec| [spec.full_name, spec] }.flatten]
@@ -356,6 +331,35 @@ class RubyGemTestCase < Test::Unit::TestCase
sic.reset_cache_data
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
end
@@ -384,30 +388,3 @@ class RubyGemTestCase < Test::Unit::TestCase
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
-
diff --git a/test/rubygems/test_config.rb b/test/rubygems/test_config.rb
index 89ac0e4462..0568996c4a 100644
--- a/test/rubygems/test_config.rb
+++ b/test/rubygems/test_config.rb
@@ -12,11 +12,6 @@ require 'rubygems'
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
datadir = Config::CONFIG['datadir']
assert_equal "#{datadir}/xyz", Config.datadir('xyz')
diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb
index ab147abe65..8a9e2c6e61 100644
--- a/test/rubygems/test_gem.rb
+++ b/test/rubygems/test_gem.rb
@@ -27,6 +27,14 @@ class TestGem < RubyGemTestCase
assert_equal expected, Gem.all_load_paths.sort
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
assert_equal File.join(@gemhome, 'bin'), Gem.bindir
@@ -129,7 +137,7 @@ class TestGem < RubyGemTestCase
end
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
def test_self_dir
@@ -237,6 +245,18 @@ class TestGem < RubyGemTestCase
assert_equal [Gem.dir], Gem.path
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?
def test_self_path_APPLE_GEM_HOME
Gem.clear_paths
@@ -382,7 +402,7 @@ class TestGem < RubyGemTestCase
end
def test_self_sources
- assert_equal %w[http://gems.example.com], Gem.sources
+ assert_equal %w[http://gems.example.com/], Gem.sources
end
def test_ssl_available_eh
diff --git a/test/rubygems/test_gem_command_manager.rb b/test/rubygems/test_gem_command_manager.rb
index b7767f421d..ee58e89844 100644
--- a/test/rubygems/test_gem_command_manager.rb
+++ b/test/rubygems/test_gem_command_manager.rb
@@ -66,7 +66,7 @@ class TestGemCommandManager < RubyGemTestCase
assert_equal :both, check_options[:domain]
assert_equal true, check_options[:wrappers]
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]
#check settings
diff --git a/test/rubygems/test_gem_commands_dependency_command.rb b/test/rubygems/test_gem_commands_dependency_command.rb
index 0f0d95695d..93e772c691 100644
--- a/test/rubygems/test_gem_commands_dependency_command.rb
+++ b/test/rubygems/test_gem_commands_dependency_command.rb
@@ -9,6 +9,8 @@ class TestGemCommandsDependencyCommand < RubyGemTestCase
@cmd = Gem::Commands::DependencyCommand.new
@cmd.options[:domain] = :local
+
+ util_setup_fake_fetcher
end
def test_execute
@@ -16,13 +18,15 @@ class TestGemCommandsDependencyCommand < RubyGemTestCase
gem.add_dependency 'bar', '> 1'
end
+ Gem.source_index = nil
+
@cmd.options[:args] = %w[foo]
use_ui @ui do
@cmd.execute
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
end
@@ -35,7 +39,7 @@ class TestGemCommandsDependencyCommand < RubyGemTestCase
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
end
@@ -64,6 +68,8 @@ class TestGemCommandsDependencyCommand < RubyGemTestCase
gem.add_dependency 'foo'
end
+ Gem.source_index = nil
+
@cmd.options[:args] = %w[foo]
@cmd.options[:reverse_dependencies] = true
@@ -73,9 +79,9 @@ class TestGemCommandsDependencyCommand < RubyGemTestCase
expected = <<-EOF
Gem foo-2
- bar (> 1)
+ bar (> 1, runtime)
Used by
- baz-2 (foo (>= 0))
+ baz-2 (foo (>= 0, runtime))
EOF
@@ -83,12 +89,34 @@ Gem foo-2
assert_equal '', @ui.error
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
foo = quick_gem 'foo' do |gem|
gem.add_dependency 'bar', '> 1'
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',
"#{foo.full_name}.gemspec")
@@ -100,9 +128,48 @@ Gem foo-2
@cmd.execute
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
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
diff --git a/test/rubygems/test_gem_commands_environment_command.rb b/test/rubygems/test_gem_commands_environment_command.rb
index 7cbb53bd88..78246b0301 100644
--- a/test/rubygems/test_gem_commands_environment_command.rb
+++ b/test/rubygems/test_gem_commands_environment_command.rb
@@ -27,6 +27,7 @@ class TestGemCommandsEnvironmentCommand < RubyGemTestCase
assert_match %r|RUBYGEMS PREFIX: |, @ui.output
assert_match %r|RUBY EXECUTABLE:.*#{Gem::ConfigMap[:RUBY_INSTALL_NAME]}|,
@ui.output
+ assert_match %r|EXECUTABLE DIRECTORY:|, @ui.output
assert_match %r|RUBYGEMS PLATFORMS:|, @ui.output
assert_match %r|- #{Gem::Platform.local}|, @ui.output
assert_match %r|GEM PATHS:|, @ui.output
diff --git a/test/rubygems/test_gem_commands_fetch_command.rb b/test/rubygems/test_gem_commands_fetch_command.rb
index 5a42e4e81e..eaa13595b7 100644
--- a/test/rubygems/test_gem_commands_fetch_command.rb
+++ b/test/rubygems/test_gem_commands_fetch_command.rb
@@ -14,10 +14,9 @@ class TestGemCommandsFetchCommand < RubyGemTestCase
def test_execute
util_setup_fake_fetcher
+ util_setup_spec_fetcher @a2
- @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] =
- @source_index.dump
- @fetcher.data["#{@gem_repo}/gems/#{@a2.full_name}.gem"] =
+ @fetcher.data["#{@gem_repo}gems/#{@a2.full_name}.gem"] =
File.read(File.join(@gemhome, 'cache', "#{@a2.full_name}.gem"))
@cmd.options[:args] = [@a2.name]
@@ -28,7 +27,28 @@ class TestGemCommandsFetchCommand < RubyGemTestCase
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
diff --git a/test/rubygems/test_gem_commands_install_command.rb b/test/rubygems/test_gem_commands_install_command.rb
index 101195a43e..ef04072b93 100644
--- a/test/rubygems/test_gem_commands_install_command.rb
+++ b/test/rubygems/test_gem_commands_install_command.rb
@@ -72,7 +72,7 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
end
# 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
end
@@ -86,8 +86,7 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
def test_execute_nonexistent
util_setup_fake_fetcher
- @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] =
- @source_index.dump
+ util_setup_spec_fetcher
@cmd.options[:args] = %w[nonexistent]
@@ -98,18 +97,18 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
assert_equal 2, e.exit_code
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
end
def test_execute_remote
@cmd.options[:generate_rdoc] = true
@cmd.options[:generate_ri] = true
+
util_setup_fake_fetcher
+ util_setup_spec_fetcher @a2
- @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] =
- @source_index.dump
- @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"))
@cmd.options[:args] = [@a2.name]
@@ -122,7 +121,6 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
end
out = @ui.output.split "\n"
- assert_match %r|Bulk updating|, out.shift
assert_equal "Successfully installed #{@a2.full_name}", out.shift
assert_equal "1 gem installed", out.shift
assert_equal "Installing ri documentation for #{@a2.full_name}...",
diff --git a/test/rubygems/test_gem_commands_outdated_command.rb b/test/rubygems/test_gem_commands_outdated_command.rb
new file mode 100644
index 0000000000..a6668c01fc
--- /dev/null
+++ b/test/rubygems/test_gem_commands_outdated_command.rb
@@ -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
+
diff --git a/test/rubygems/test_gem_commands_pristine_command.rb b/test/rubygems/test_gem_commands_pristine_command.rb
index cd1d3500ae..d5d2d7f339 100644
--- a/test/rubygems/test_gem_commands_pristine_command.rb
+++ b/test/rubygems/test_gem_commands_pristine_command.rb
@@ -18,16 +18,24 @@ class TestGemCommandsPristineCommand < RubyGemTestCase
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]
use_ui @ui do
@cmd.execute
end
+ assert_equal "#!/usr/bin/ruby\n", File.read(foo_path), foo_path
+
out = @ui.output.split "\n"
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
end
@@ -40,7 +48,7 @@ class TestGemCommandsPristineCommand < RubyGemTestCase
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
@@ -50,11 +58,12 @@ class TestGemCommandsPristineCommand < RubyGemTestCase
@cmd.execute
end
+ assert File.exist?(gem_bin)
+
out = @ui.output.split "\n"
assert_equal "Restoring gem(s) to pristine condition...", out.shift
- assert_equal "Restoring 1 file to #{a.full_name}...", out.shift
- assert_equal " #{gem_bin}", out.shift
+ assert_equal "Restored #{a.full_name}", out.shift
assert out.empty?, out.inspect
end
diff --git a/test/rubygems/test_gem_commands_query_command.rb b/test/rubygems/test_gem_commands_query_command.rb
index 3c86c69a41..1b65fc7633 100644
--- a/test/rubygems/test_gem_commands_query_command.rb
+++ b/test/rubygems/test_gem_commands_query_command.rb
@@ -7,33 +7,18 @@ class TestGemCommandsQueryCommand < RubyGemTestCase
def setup
super
- util_make_gems
-
- @a2.summary = 'This is a lot of text. ' * 4
-
@cmd = Gem::Commands::QueryCommand.new
- @si = util_setup_source_info_cache @a1, @a2, @pl1
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
end
end
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]
use_ui @ui do
@@ -44,10 +29,8 @@ class TestGemCommandsQueryCommand < RubyGemTestCase
*** REMOTE GEMS ***
-Updating metadata for 1 gems from http://gems.example.com/
-.
-complete
a (2)
+pl (1)
EOF
assert_equal expected, @ui.output
@@ -55,21 +38,8 @@ a (2)
end
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
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]
@@ -81,10 +51,8 @@ a (2)
*** REMOTE GEMS ***
-Updating metadata for 2 gems from http://gems.example.com/
-..
-complete
a (2, 1)
+pl (1)
EOF
assert_equal expected, @ui.output
@@ -92,6 +60,13 @@ a (2, 1)
end
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]
use_ui @ui do
@@ -103,10 +78,17 @@ a (2, 1)
*** REMOTE GEMS ***
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.
pl (1)
+ Author: A User
+ Homepage: http://example.com
+
this is a summary
EOF
@@ -126,6 +108,7 @@ pl (1)
assert_equal 0, e.exit_code
assert_equal "true\n", @ui.output
+
assert_equal '', @ui.error
end
@@ -189,6 +172,99 @@ pl (1)
assert_equal 1, e.exit_code
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
@cmd.handle_options %w[-r --no-versions]
diff --git a/test/rubygems/test_gem_commands_sources_command.rb b/test/rubygems/test_gem_commands_sources_command.rb
index f15d44dfe8..623c732e50 100644
--- a/test/rubygems/test_gem_commands_sources_command.rb
+++ b/test/rubygems/test_gem_commands_sources_command.rb
@@ -8,10 +8,12 @@ class TestGemCommandsSourcesCommand < RubyGemTestCase
super
@cmd = Gem::Commands::SourcesCommand.new
+
+ @new_repo = "http://beta-gems.example.com"
end
def test_execute
- util_setup_source_info_cache
+ util_setup_spec_fetcher
@cmd.handle_options []
use_ui @ui do
@@ -34,43 +36,49 @@ class TestGemCommandsSourcesCommand < RubyGemTestCase
si = Gem::SourceIndex.new
si.add_spec @a1
- @fetcher.data["http://beta-gems.example.com/Marshal.#{@marshal_version}"] =
- si.dump
+ specs = si.map do |_, spec|
+ [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
@cmd.execute
end
+ assert_equal [@gem_repo, @new_repo], Gem.sources
+
expected = <<-EOF
-Bulk updating Gem source index for: http://beta-gems.example.com/
-http://beta-gems.example.com added to sources
+#{@new_repo} added to sources
EOF
assert_equal expected, @ui.output
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
def test_execute_add_nonexistent_source
util_setup_fake_fetcher
- @fetcher.data["http://beta-gems.example.com/Marshal.#{@marshal_version}"] =
- proc do
- raise Gem::RemoteFetcher::FetchError, 'it died'
- end
+ uri = "http://beta-gems.example.com/specs.#{@marshal_version}.gz"
+ @fetcher.data[uri] = proc do
+ raise Gem::RemoteFetcher::FetchError.new('it died', uri)
+ end
- Gem::RemoteFetcher.instance_variable_set :@fetcher, @fetcher
+ Gem::RemoteFetcher.fetcher = @fetcher
@cmd.handle_options %w[--add http://beta-gems.example.com]
- util_setup_source_info_cache
+ util_setup_spec_fetcher
use_ui @ui do
@cmd.execute
@@ -78,7 +86,7 @@ http://beta-gems.example.com added to sources
expected = <<-EOF
Error fetching http://beta-gems.example.com:
-\tit died
+\tit died (#{uri})
EOF
assert_equal expected, @ui.output
@@ -88,12 +96,14 @@ Error fetching http://beta-gems.example.com:
def test_execute_add_bad_uri
@cmd.handle_options %w[--add beta-gems.example.com]
- util_setup_source_info_cache
+ util_setup_spec_fetcher
use_ui @ui do
@cmd.execute
end
+ assert_equal [@gem_repo], Gem.sources
+
expected = <<-EOF
beta-gems.example.com is not a URI
EOF
@@ -102,6 +112,34 @@ beta-gems.example.com is not a URI
assert_equal '', @ui.error
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
@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),
'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
@cmd.execute
end
expected = <<-EOF
+*** Removed specs cache ***
*** Removed user source cache ***
*** Removed latest user 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),
'latest system cache file'
+ assert !File.exist?(fetcher.dir), 'cache dir removed'
end
def test_execute_remove
@cmd.handle_options %W[--remove #{@gem_repo}]
- util_setup_source_info_cache
+ util_setup_spec_fetcher
use_ui @ui do
@cmd.execute
@@ -150,9 +197,6 @@ beta-gems.example.com is not a URI
assert_equal expected, @ui.output
assert_equal '', @ui.error
-
- Gem::SourceInfoCache.cache.flush
- assert_equal [], Gem::SourceInfoCache.cache_data.keys
end
def test_execute_remove_no_network
@@ -160,7 +204,7 @@ beta-gems.example.com is not a URI
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
end
@@ -172,34 +216,60 @@ beta-gems.example.com is not a URI
assert_equal expected, @ui.output
assert_equal '', @ui.error
-
- Gem::SourceInfoCache.cache.flush
- assert_equal [], Gem::SourceInfoCache.cache_data.keys
end
def test_execute_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
Gem::SourceInfoCache.reset
- util_setup_fake_fetcher
si = Gem::SourceIndex.new
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
@cmd.execute
end
expected = <<-EOF
-Bulk updating Gem source index for: #{@gem_repo}/
+Bulk updating Gem source index for: #{@gem_repo}
source cache successfully updated
EOF
assert_equal expected, @ui.output
assert_equal '', @ui.error
end
-
+
end
diff --git a/test/rubygems/test_gem_commands_specification_command.rb b/test/rubygems/test_gem_commands_specification_command.rb
index f66f0c0d49..7ac0429f32 100644
--- a/test/rubygems/test_gem_commands_specification_command.rb
+++ b/test/rubygems/test_gem_commands_specification_command.rb
@@ -74,7 +74,10 @@ class TestGemCommandsSpecificationCommand < RubyGemTestCase
def test_execute_remote
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',
"#{foo.full_name}.gemspec")
diff --git a/test/rubygems/test_gem_commands_stale_command.rb b/test/rubygems/test_gem_commands_stale_command.rb
new file mode 100644
index 0000000000..6f66c854a5
--- /dev/null
+++ b/test/rubygems/test_gem_commands_stale_command.rb
@@ -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
diff --git a/test/rubygems/test_gem_commands_update_command.rb b/test/rubygems/test_gem_commands_update_command.rb
index 6aeb53d25a..11da1f8a83 100644
--- a/test/rubygems/test_gem_commands_update_command.rb
+++ b/test/rubygems/test_gem_commands_update_command.rb
@@ -14,14 +14,16 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
@a1_path = File.join @gemhome, 'cache', "#{@a1.full_name}.gem"
@a2_path = File.join @gemhome, 'cache', "#{@a2.full_name}.gem"
- @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] =
- @source_index.dump
- @fetcher.data["#{@gem_repo}/gems/#{@a1.full_name}.gem"] = read_binary @a1_path
- @fetcher.data["#{@gem_repo}/gems/#{@a2.full_name}.gem"] = read_binary @a2_path
+ util_setup_spec_fetcher @a1, @a2
+
+ @fetcher.data["#{@gem_repo}gems/#{@a1.full_name}.gem"] =
+ read_binary @a1_path
+ @fetcher.data["#{@gem_repo}gems/#{@a2.full_name}.gem"] =
+ read_binary @a2_path
end
def test_execute
- util_remove_gems
+ util_clear_gems
Gem::Installer.new(@a1_path).install
@@ -33,7 +35,6 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
out = @ui.output.split "\n"
assert_equal "Updating installed gems", out.shift
- assert_match %r|Bulk updating|, out.shift
assert_equal "Updating #{@a2.name}", out.shift
assert_equal "Successfully installed #{@a2.full_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 @c2
- @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] =
- @source_index.dump
- @fetcher.data["#{@gem_repo}/gems/#{@a1.full_name}.gem"] = read_binary @a1_path
- @fetcher.data["#{@gem_repo}/gems/#{@a2.full_name}.gem"] = read_binary @a2_path
- @fetcher.data["#{@gem_repo}/gems/#{@b2.full_name}.gem"] = read_binary @b2_path
- @fetcher.data["#{@gem_repo}/gems/#{@c1_2.full_name}.gem"] =
+ @fetcher.data["#{@gem_repo}gems/#{@a1.full_name}.gem"] = read_binary @a1_path
+ @fetcher.data["#{@gem_repo}gems/#{@a2.full_name}.gem"] = read_binary @a2_path
+ @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
- @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(@a1_path).install
@@ -95,9 +95,7 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
out = @ui.output.split "\n"
assert_equal "Updating installed gems", out.shift
- assert_match %r|Bulk updating|, 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 #{@b2.full_name}", out.shift
assert_equal "Successfully installed #{@a2.full_name}", out.shift
@@ -108,7 +106,7 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
end
def test_execute_named
- util_remove_gems
+ util_clear_gems
Gem::Installer.new(@a1_path).install
@@ -120,7 +118,6 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
out = @ui.output.split "\n"
assert_equal "Updating installed gems", out.shift
- assert_match %r|Bulk updating|, out.shift
assert_equal "Updating #{@a2.name}", out.shift
assert_equal "Successfully installed #{@a2.full_name}", out.shift
assert_equal "Gems updated: #{@a2.name}", out.shift
@@ -129,7 +126,7 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
end
def test_execute_named_up_to_date
- util_remove_gems
+ util_clear_gems
Gem::Installer.new(@a2_path).install
@@ -141,14 +138,13 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
out = @ui.output.split "\n"
assert_equal "Updating installed gems", out.shift
- assert_match %r|Bulk updating|, out.shift
assert_equal "Nothing to update", out.shift
assert out.empty?, out.inspect
end
def test_execute_up_to_date
- util_remove_gems
+ util_clear_gems
Gem::Installer.new(@a2_path).install
@@ -160,16 +156,10 @@ class TestGemCommandsUpdateCommand < RubyGemTestCase
out = @ui.output.split "\n"
assert_equal "Updating installed gems", out.shift
- assert_match %r|Bulk updating|, out.shift
assert_equal "Nothing to update", out.shift
assert out.empty?, out.inspect
end
- def util_remove_gems
- FileUtils.rm_r File.join(@gemhome, 'gems')
- FileUtils.rm_r File.join(@gemhome, 'specifications')
- end
-
end
diff --git a/test/rubygems/test_gem_config_file.rb b/test/rubygems/test_gem_config_file.rb
index e0360b0d6b..c7f6b93c7f 100644
--- a/test/rubygems/test_gem_config_file.rb
+++ b/test/rubygems/test_gem_config_file.rb
@@ -17,9 +17,23 @@ class TestGemConfigFile < RubyGemTestCase
@temp_conf = File.join @tempdir, '.gemrc'
@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
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
assert_equal @temp_conf, @cfg.config_file_name
@@ -28,7 +42,7 @@ class TestGemConfigFile < RubyGemTestCase
assert_equal false, @cfg.benchmark
assert_equal Gem::ConfigFile::DEFAULT_BULK_THRESHOLD, @cfg.bulk_threshold
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|
fp.puts ":backtrace: true"
@@ -202,6 +216,23 @@ class TestGemConfigFile < RubyGemTestCase
assert_equal %w[http://even-more-gems.example.com], Gem.sources
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)
@cfg = Gem::ConfigFile.new args
end
diff --git a/test/rubygems/test_gem_dependency.rb b/test/rubygems/test_gem_dependency.rb
index f280221a00..315c49d6ce 100644
--- a/test/rubygems/test_gem_dependency.rb
+++ b/test/rubygems/test_gem_dependency.rb
@@ -60,6 +60,21 @@ class TestGemDependency < RubyGemTestCase
assert_equal Gem::Requirement.new('= 2'), dep.version_requirements
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
assert_equal @pkg1_0, @pkg1_0.dup
assert_equal @pkg1_0.dup, @pkg1_0
@@ -74,6 +89,36 @@ class TestGemDependency < RubyGemTestCase
assert_not_equal Object.new, @pkg1_0
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
assert_equal @pkg1_0.hash, @pkg1_0.dup.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"
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
diff --git a/test/rubygems/test_gem_dependency_installer.rb b/test/rubygems/test_gem_dependency_installer.rb
index 914d4aa216..80c446a2ad 100644
--- a/test/rubygems/test_gem_dependency_installer.rb
+++ b/test/rubygems/test_gem_dependency_installer.rb
@@ -15,8 +15,12 @@ class TestGemDependencyInstaller < RubyGemTestCase
fp.puts "#!/usr/bin/ruby"
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'
@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
- si = util_setup_source_info_cache @a1, @b1, @d1, @d2, @x1_m, @x1_o, @w1,
- @y1, @y1_1_p, @z1
+ @fetcher = Gem::FakeFetcher.new
+ Gem::RemoteFetcher.fetcher = @fetcher
- @fetcher = FakeFetcher.new
- 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
+ si = util_setup_spec_fetcher @a1, @b1, @d1, @d2, @x1_m, @x1_o, @w1, @y1,
+ @y1_1_p, @z1
- FileUtils.rm_rf File.join(@gemhome, 'gems')
+ util_clear_gems
end
def test_install
@@ -64,6 +66,52 @@ class TestGemDependencyInstaller < RubyGemTestCase
assert_equal [@a1], inst.installed_gems
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
FileUtils.mv @a1_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 }
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
Gem::Installer.new(@a1_gem).install
FileUtils.mv @a1_gem, @tempdir
@@ -177,7 +239,7 @@ class TestGemDependencyInstaller < RubyGemTestCase
def test_install_force
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
inst = nil
@@ -249,8 +311,6 @@ class TestGemDependencyInstaller < RubyGemTestCase
end
def test_install_domain_both_no_network
- Gem::SourceInfoCache.instance_variable_set :@cache, nil
-
@fetcher.data["http://gems.example.com/gems/Marshal.#{@marshal_version}"] =
proc do
raise Gem::RemoteFetcher::FetchError
@@ -272,12 +332,14 @@ class TestGemDependencyInstaller < RubyGemTestCase
FileUtils.mv @b1_gem, @tempdir
inst = nil
+ Gem.source_index.gems.delete @a1.full_name
+
Dir.chdir @tempdir do
e = assert_raise Gem::InstallError do
inst = Gem::DependencyInstaller.new :domain => :local
inst.install 'b'
end
- assert_equal 'b requires a (>= 0)', e.message
+ assert_equal 'b requires a (>= 0, runtime)', e.message
end
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 }
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
a1_data = nil
File.open @a1_gem, 'rb' do |fp|
@@ -337,7 +423,9 @@ class TestGemDependencyInstaller < RubyGemTestCase
s.platform = Gem::Platform.new %w[cpu other_platform 1]
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
@@ -439,7 +527,7 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = Gem::DependencyInstaller.new
dep = Gem::Dependency.new 'b', '>= 0'
- assert_equal [[@b1, 'http://gems.example.com']],
+ assert_equal [[@b1, @gem_repo]],
inst.find_gems_with_sources(dep)
end
@@ -456,7 +544,7 @@ class TestGemDependencyInstaller < RubyGemTestCase
assert_equal 2, gems.length
remote = gems.first
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
assert_equal 'a-1', local.first.full_name, 'local spec'
@@ -476,12 +564,9 @@ class TestGemDependencyInstaller < RubyGemTestCase
b2, = util_gem 'b', '2'
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
- 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
+ si = util_setup_spec_fetcher @a1, @b1, b2, c1
inst = Gem::DependencyInstaller.new
inst.find_spec_by_name_and_version 'c'
@@ -512,12 +597,9 @@ class TestGemDependencyInstaller < RubyGemTestCase
def test_gather_dependencies_old_required
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
- 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
+ si = util_setup_spec_fetcher @d1, @d2, e1
inst = Gem::DependencyInstaller.new
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 }
end
+
end
diff --git a/test/rubygems/test_gem_gem_path_searcher.rb b/test/rubygems/test_gem_gem_path_searcher.rb
index d35416e867..c9da4d2b05 100644
--- a/test/rubygems/test_gem_gem_path_searcher.rb
+++ b/test/rubygems/test_gem_gem_path_searcher.rb
@@ -28,7 +28,10 @@ class TestGemGemPathSearcher < RubyGemTestCase
@bar1 = quick_gem 'bar', '0.1'
@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
end
diff --git a/test/rubygems/test_gem_indexer.rb b/test/rubygems/test_gem_indexer.rb
index 12469b5d57..5ccaaff01f 100644
--- a/test/rubygems/test_gem_indexer.rb
+++ b/test/rubygems/test_gem_indexer.rb
@@ -20,6 +20,10 @@ class TestGemIndexer < RubyGemTestCase
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')
FileUtils.mkdir_p gems
cache_gems = File.join @gemhome, 'cache', '*.gem'
@@ -39,10 +43,10 @@ class TestGemIndexer < RubyGemTestCase
@indexer.generate_index
end
- assert File.exist?(File.join(@tempdir, 'yaml'))
- assert File.exist?(File.join(@tempdir, 'yaml.Z'))
- assert File.exist?(File.join(@tempdir, "Marshal.#{@marshal_version}"))
- assert File.exist?(File.join(@tempdir, "Marshal.#{@marshal_version}.Z"))
+ assert_indexed @tempdir, 'yaml'
+ assert_indexed @tempdir, 'yaml.Z'
+ assert_indexed @tempdir, "Marshal.#{@marshal_version}"
+ assert_indexed @tempdir, "Marshal.#{@marshal_version}.Z"
quickdir = File.join @tempdir, 'quick'
marshal_quickdir = File.join quickdir, "Marshal.#{@marshal_version}"
@@ -53,10 +57,33 @@ class TestGemIndexer < RubyGemTestCase
assert_indexed quickdir, "index"
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.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, "#{@a2.full_name}.gemspec.rz"
@@ -64,13 +91,19 @@ class TestGemIndexer < RubyGemTestCase
assert_indexed quickdir, "#{@c1_2.full_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, "#{@a2.full_name}.gemspec.rz"
- deny_indexed quickdir, "#{@c1_2.full_name}.gemspec"
- deny_indexed marshal_quickdir, "#{@c1_2.full_name}.gemspec"
+ refute_indexed 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
def test_generate_index_ui
@@ -79,28 +112,37 @@ class TestGemIndexer < RubyGemTestCase
end
expected = <<-EOF
-Generating index for 6 gems in #{@tempdir}
-......
+Loading 7 gems from #{@tempdir}
+.......
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
assert_equal expected, @ui.output
assert_equal '', @ui.error
end
- def test_generate_index_contents
+ def test_generate_index_master
use_ui @ui do
@indexer.generate_index
end
- yaml_path = File.join(@tempdir, 'yaml')
- dump_path = File.join(@tempdir, "Marshal.#{@marshal_version}")
+ yaml_path = File.join @tempdir, 'yaml'
+ dump_path = File.join @tempdir, "Marshal.#{@marshal_version}"
- yaml_index = YAML.load_file(yaml_path)
- dump_str = nil
- File.open dump_path, 'rb' do |fp| dump_str = fp.read end
- dump_index = Marshal.load dump_str
+ yaml_index = YAML.load_file yaml_path
+ dump_index = Marshal.load Gem.read_binary(dump_path)
dump_index.each do |_,gem|
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"
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)
file = File.join dir, name
assert File.exist?(file), "#{file} does not exist"
end
- def deny_indexed(dir, name)
+ def refute_indexed(dir, name)
file = File.join dir, name
assert !File.exist?(file), "#{file} exists"
end
diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb
index f7d36c66ed..edd8b472cd 100644
--- a/test/rubygems/test_gem_installer.rb
+++ b/test/rubygems/test_gem_installer.rb
@@ -102,7 +102,7 @@ load 'my_exec'
@installer.ensure_dependency @spec, dep
end
- assert_equal 'a requires b (> 2)', e.message
+ assert_equal 'a requires b (> 2, runtime)', e.message
end
def test_expand_and_validate_gem_dir
@@ -128,7 +128,12 @@ load 'my_exec'
@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
def test_extract_files_bad_dest
@@ -313,6 +318,29 @@ load 'my_exec'
#assert_no_match %r|generated by RubyGems|, wrapper
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
return if win_platform? #Windows FS do not support symlinks
diff --git a/test/rubygems/test_gem_local_remote_options.rb b/test/rubygems/test_gem_local_remote_options.rb
index d5a6651ade..e676c94f21 100644
--- a/test/rubygems/test_gem_local_remote_options.rb
+++ b/test/rubygems/test_gem_local_remote_options.rb
@@ -46,12 +46,13 @@ class TestGemLocalRemoteOptions < RubyGemTestCase
def test_source_option
@cmd.add_source_option
- s1 = URI.parse 'http://more-gems.example.com'
- s2 = URI.parse 'http://even-more-gems.example.com'
+ s1 = URI.parse 'http://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
def test_update_sources_option
@@ -77,7 +78,7 @@ class TestGemLocalRemoteOptions < RubyGemTestCase
@cmd.handle_options %W[--source #{s1}]
end
- assert_equal %w[http://gems.example.com], Gem.sources
+ assert_equal [@gem_repo], Gem.sources
end
end
diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb
index ddadeb9fcf..1d2103bd06 100644
--- a/test/rubygems/test_gem_remote_fetcher.rb
+++ b/test/rubygems/test_gem_remote_fetcher.rb
@@ -24,7 +24,7 @@ require 'rubygems/remote_fetcher'
# 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
# proxy is configured.
-#
+
class TestGemRemoteFetcher < RubyGemTestCase
include Gem::DefaultUserInteraction
@@ -105,7 +105,7 @@ gems:
@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
def test_self_fetcher
@@ -144,7 +144,7 @@ gems:
def test_fetch_size_socket_error
fetcher = Gem::RemoteFetcher.new nil
- def fetcher.connect_to(host, port)
+ def fetcher.connection_for(uri)
raise SocketError
end
@@ -153,7 +153,8 @@ gems:
fetcher.fetch_size uri
end
- assert_equal "SocketError (SocketError)\n\tgetting size of #{uri}", e.message
+ assert_equal "SocketError (SocketError)\n\tfetching size (#{uri})",
+ e.message
end
def test_no_proxy
@@ -182,7 +183,7 @@ gems:
@test_data
end
- raise Gem::RemoteFetcher::FetchError, "haha!"
+ raise Gem::RemoteFetcher::FetchError.new("haha!", nil)
end
end
@@ -371,7 +372,8 @@ gems:
fetcher.fetch_path 'uri'
end
- assert_equal 'EOFError: EOFError reading uri', e.message
+ assert_equal 'EOFError: EOFError (uri)', e.message
+ assert_equal 'uri', e.uri
end
def test_fetch_path_socket_error
@@ -383,7 +385,8 @@ gems:
fetcher.fetch_path 'uri'
end
- assert_equal 'SocketError: SocketError reading uri', e.message
+ assert_equal 'SocketError: SocketError (uri)', e.message
+ assert_equal 'uri', e.uri
end
def test_fetch_path_system_call_error
@@ -397,8 +400,9 @@ gems:
fetcher.fetch_path 'uri'
end
- assert_match %r|ECONNREFUSED:.*connect\(2\) reading uri\z|,
+ assert_match %r|ECONNREFUSED:.*connect\(2\) \(uri\)\z|,
e.message
+ assert_equal 'uri', e.uri
end
def test_get_proxy_from_env_empty
@@ -494,7 +498,8 @@ gems:
fetcher.send :open_uri_or_path, 'http://gems.example.com/redirect'
end
- assert_equal 'too many redirects', e.message
+ assert_equal 'too many redirects (http://gems.example.com/redirect)',
+ e.message
end
def test_zip
diff --git a/test/rubygems/test_gem_server.rb b/test/rubygems/test_gem_server.rb
index 24f88bf59e..dcdc766f0f 100644
--- a/test/rubygems/test_gem_server.rb
+++ b/test/rubygems/test_gem_server.rb
@@ -14,34 +14,69 @@ class TestGemServer < RubyGemTestCase
super
@a1 = quick_gem 'a', '1'
+ @a2 = quick_gem 'a', '2'
@server = Gem::Server.new Gem.dir, process_based_port, false
@req = WEBrick::HTTPRequest.new :Logger => nil
@res = WEBrick::HTTPResponse.new :HTTPVersion => '1.0'
end
- def test_quick_index
- data = StringIO.new "GET /quick/index HTTP/1.0\r\n\r\n"
+ def test_Marshal
+ data = StringIO.new "GET /Marshal.#{Gem.marshal_version} HTTP/1.0\r\n\r\n"
@req.parse data
- @server.quick @req, @res
+ @server.Marshal @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']
- assert_equal "a-1", @res.body
+ 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_quick_index_rz
- data = StringIO.new "GET /quick/index.rz HTTP/1.0\r\n\r\n"
+ def test_Marshal_Z
+ data = StringIO.new "GET /Marshal.#{Gem.marshal_version}.Z HTTP/1.0\r\n\r\n"
@req.parse data
- @server.quick @req, @res
+ @server.Marshal @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']
- assert_equal "a-1", Zlib::Inflate.inflate(@res.body)
+ 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
@@ -52,17 +87,15 @@ class TestGemServer < RubyGemTestCase
assert_equal 200, @res.status, @res.body
assert @res['date']
- assert_equal 'text/plain', @res['content-type']
+ assert_equal 'application/x-deflate', @res['content-type']
- spec = YAML.load Zlib::Inflate.inflate(@res.body)
+ 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
- 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
@@ -71,9 +104,9 @@ class TestGemServer < RubyGemTestCase
assert_equal 200, @res.status, @res.body
assert @res['date']
- assert_equal 'text/plain', @res['content-type']
+ assert_equal 'application/x-deflate', @res['content-type']
- spec = YAML.load Zlib::Inflate.inflate(@res.body)
+ 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
@@ -81,8 +114,6 @@ class TestGemServer < RubyGemTestCase
def test_quick_common_substrings
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
@@ -91,14 +122,62 @@ class TestGemServer < RubyGemTestCase
assert_equal 200, @res.status, @res.body
assert @res['date']
- assert_equal 'text/plain', @res['content-type']
+ assert_equal 'application/x-deflate', @res['content-type']
- spec = YAML.load Zlib::Inflate.inflate(@res.body)
+ spec = YAML.load Gem.inflate(@res.body)
assert_equal 'a', spec.name
assert_equal Gem::Version.new(1), spec.version
end
- def test_quick_z_9_gemspec_rz
+ def test_quick_index
+ data = StringIO.new "GET /quick/index HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.quick @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']
+ assert_equal "a-1\na-2", @res.body
+ end
+
+ def test_quick_index_rz
+ data = StringIO.new "GET /quick/index.rz HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.quick @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']
+ assert_equal "a-1\na-2", Gem.inflate(@res.body)
+ end
+
+ def test_quick_latest_index
+ data = StringIO.new "GET /quick/latest_index HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.quick @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']
+ assert_equal 'a-2', @res.body
+ end
+
+ def test_quick_latest_index_rz
+ data = StringIO.new "GET /quick/latest_index.rz HTTP/1.0\r\n\r\n"
+ @req.parse data
+
+ @server.quick @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']
+ assert_equal 'a-2', Gem.inflate(@res.body)
+ end
+
+ def test_quick_missing
data = StringIO.new "GET /quick/z-9.gemspec.rz HTTP/1.0\r\n\r\n"
@req.parse data
@@ -111,5 +190,112 @@ class TestGemServer < RubyGemTestCase
assert_equal 404, @res.status
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
diff --git a/test/rubygems/test_gem_source_index.rb b/test/rubygems/test_gem_source_index.rb
index 140f3ad067..adb9037caa 100644
--- a/test/rubygems/test_gem_source_index.rb
+++ b/test/rubygems/test_gem_source_index.rb
@@ -23,6 +23,141 @@ class TestGemSourceIndex < RubyGemTestCase
util_setup_fake_fetcher
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
# TODO
end
@@ -43,16 +178,16 @@ class TestGemSourceIndex < RubyGemTestCase
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(', ')
end
def test_fetch_bulk_index_error
- @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}/yaml.Z"] = proc { raise SocketError }
- @fetcher.data["#{@gem_repo}/yaml"] = 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}yaml.Z"] = proc { raise SocketError }
+ @fetcher.data["#{@gem_repo}yaml"] = proc { raise SocketError }
e = assert_raise Gem::RemoteSourceException do
use_ui @ui do
@@ -62,10 +197,10 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths
- assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift
- assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}", paths.shift
- assert_equal "#{@gem_repo}/yaml.Z", paths.shift
- assert_equal "#{@gem_repo}/yaml", 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}yaml.Z", paths.shift
+ assert_equal "#{@gem_repo}yaml", paths.shift
assert paths.empty?, paths.join(', ')
@@ -74,12 +209,12 @@ class TestGemSourceIndex < RubyGemTestCase
end
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 }
- @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] =
+ @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}"] =
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.Z"] = proc { raise SocketError }
+ @fetcher.data["#{@gem_repo}yaml"] = @source_index.to_yaml
use_ui @ui do
fetched_index = @source_index.fetch_bulk_index @uri
@@ -90,10 +225,10 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths
- assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift
- assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}", paths.shift
- assert_equal "#{@gem_repo}/yaml.Z", paths.shift
- assert_equal "#{@gem_repo}/yaml", 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}yaml.Z", paths.shift
+ assert_equal "#{@gem_repo}yaml", paths.shift
assert paths.empty?, paths.join(', ')
end
@@ -102,8 +237,8 @@ class TestGemSourceIndex < RubyGemTestCase
marshal = @source_index.dump
marshal[0] = (Marshal::MAJOR_VERSION - 1).chr
- @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = marshal
- @fetcher.data["#{@gem_repo}/yaml"] = @source_index.to_yaml
+ @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}"] = marshal
+ @fetcher.data["#{@gem_repo}yaml"] = @source_index.to_yaml
use_ui @ui do
fetched_index = @source_index.fetch_bulk_index @uri
@@ -114,10 +249,10 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths
- assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift
- assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}", paths.shift
- assert_equal "#{@gem_repo}/yaml.Z", paths.shift
- assert_equal "#{@gem_repo}/yaml", 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}yaml.Z", paths.shift
+ assert_equal "#{@gem_repo}yaml", paths.shift
assert paths.empty?, paths.join(', ')
end
@@ -133,8 +268,8 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths
- 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}.Z", paths.shift
+ assert_equal "#{@gem_repo}Marshal.#{@marshal_version}", paths.shift
assert paths.empty?, paths.join(', ')
end
@@ -143,8 +278,8 @@ class TestGemSourceIndex < RubyGemTestCase
index = util_zip @gem_names
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/latest_index.rz"] = latest_index
+ @fetcher.data["#{@gem_repo}quick/index.rz"] = index
+ @fetcher.data["#{@gem_repo}quick/latest_index.rz"] = latest_index
quick_index = @source_index.fetch_quick_index @uri, false
assert_equal [@a2.full_name, @b2.full_name].sort,
@@ -152,7 +287,7 @@ class TestGemSourceIndex < RubyGemTestCase
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(', ')
end
@@ -161,8 +296,8 @@ class TestGemSourceIndex < RubyGemTestCase
index = util_zip @gem_names
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/latest_index.rz"] = latest_index
+ @fetcher.data["#{@gem_repo}quick/index.rz"] = index
+ @fetcher.data["#{@gem_repo}quick/latest_index.rz"] = latest_index
quick_index = @source_index.fetch_quick_index @uri, true
assert_equal [@a1.full_name, @a2.full_name, @b2.full_name].sort,
@@ -170,13 +305,13 @@ class TestGemSourceIndex < RubyGemTestCase
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(', ')
end
def test_fetch_quick_index_error
- @fetcher.data["#{@gem_repo}/quick/index.rz"] =
+ @fetcher.data["#{@gem_repo}quick/index.rz"] =
proc { raise Exception }
e = assert_raise Gem::OperationNotSupportedError do
@@ -187,7 +322,7 @@ class TestGemSourceIndex < RubyGemTestCase
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(', ')
end
@@ -195,22 +330,22 @@ class TestGemSourceIndex < RubyGemTestCase
def test_fetch_quick_index_fallback
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
assert_equal @gem_names.split, quick_index.sort
paths = @fetcher.paths
- assert_equal "#{@gem_repo}/quick/latest_index.rz", paths.shift
- assert_equal "#{@gem_repo}/quick/index.rz", paths.shift
+ assert_equal "#{@gem_repo}quick/latest_index.rz", paths.shift
+ assert_equal "#{@gem_repo}quick/index.rz", paths.shift
assert paths.empty?, paths.join(', ')
end
def test_fetch_quick_index_subdir
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
@@ -226,7 +361,7 @@ class TestGemSourceIndex < RubyGemTestCase
end
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)
spec = @source_index.send :fetch_single_spec, URI.parse(@gem_repo),
@@ -242,7 +377,7 @@ class TestGemSourceIndex < RubyGemTestCase
end
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"
@fetcher.data[a1_spec_url] = util_zip Marshal.dump(@a1)
@@ -259,7 +394,7 @@ class TestGemSourceIndex < RubyGemTestCase
end
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
repo = URI.parse @gem_repo
@@ -270,14 +405,14 @@ class TestGemSourceIndex < RubyGemTestCase
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 paths.empty?, paths.join(', ')
end
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"
@fetcher.data[a1_spec_url] = util_zip @a1.to_yaml
@@ -377,12 +512,12 @@ class TestGemSourceIndex < RubyGemTestCase
end
def test_outdated
- util_setup_source_info_cache
+ util_setup_spec_fetcher
assert_equal [], @source_index.outdated
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
@@ -390,7 +525,7 @@ class TestGemSourceIndex < RubyGemTestCase
s.platform = Gem::Platform.new 'x86-other_platform1'
end
- util_setup_source_info_cache updated, updated_platform
+ util_setup_spec_fetcher updated, updated_platform
assert_equal [updated_platform.name], @source_index.outdated
end
@@ -411,6 +546,16 @@ class TestGemSourceIndex < RubyGemTestCase
assert source_index.gems.include?(@a1.full_name)
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
@source_index.add_spec @a1
@source_index.add_spec @a2
@@ -516,8 +661,8 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths
- assert_equal "#{@gem_repo}/quick/index.rz", paths.shift
- assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift
+ assert_equal "#{@gem_repo}quick/index.rz", paths.shift
+ assert_equal "#{@gem_repo}Marshal.#{@marshal_version}.Z", paths.shift
assert paths.empty?, paths.join(', ')
end
@@ -528,7 +673,7 @@ class TestGemSourceIndex < RubyGemTestCase
latest_names = [@a2, @a_evil9, @b2, @c1_2].map { |s| s.full_name }
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}",
"#{@b2.full_name}.gemspec.rz"
@@ -541,7 +686,7 @@ class TestGemSourceIndex < RubyGemTestCase
end
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 paths.empty?, paths.join(', ')
@@ -554,7 +699,7 @@ class TestGemSourceIndex < RubyGemTestCase
Gem.configuration = Gem::ConfigFile.new([])
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}",
"#{@b2.full_name}.gemspec.rz"
@@ -567,7 +712,7 @@ class TestGemSourceIndex < RubyGemTestCase
end
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 paths.empty?, paths.join(', ')
@@ -580,12 +725,12 @@ class TestGemSourceIndex < RubyGemTestCase
Gem.configuration = Gem::ConfigFile.new([])
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}",
"#{@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
use_ui @ui do
@@ -595,7 +740,7 @@ class TestGemSourceIndex < RubyGemTestCase
end
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 yaml_uri, paths.shift
@@ -609,7 +754,7 @@ class TestGemSourceIndex < RubyGemTestCase
Gem.configuration = Gem::ConfigFile.new([])
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}",
"#{@b2.full_name}.gemspec.rz"
@@ -617,7 +762,7 @@ class TestGemSourceIndex < RubyGemTestCase
marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr
@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
use_ui @ui do
@@ -627,7 +772,7 @@ class TestGemSourceIndex < RubyGemTestCase
end
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 yaml_uri, paths.shift
@@ -637,14 +782,14 @@ class TestGemSourceIndex < RubyGemTestCase
end
def test_update_subdir
- @gem_repo = @gem_repo + "/subdir"
+ @gem_repo = @gem_repo + 'subdir/'
util_setup_bulk_fetch true
@source_index.gems.replace({})
assert_equal [], @source_index.gems.keys.sort
- uri = @uri.to_s + "/subdir"
+ uri = @uri.to_s + 'subdir/'
use_ui @ui do
@source_index.update uri, true
@@ -656,8 +801,8 @@ class TestGemSourceIndex < RubyGemTestCase
paths = @fetcher.paths
- assert_equal "#{@gem_repo}/quick/index.rz", paths.shift
- assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift
+ assert_equal "#{@gem_repo}quick/index.rz", paths.shift
+ assert_equal "#{@gem_repo}Marshal.#{@marshal_version}.Z", paths.shift
assert paths.empty?, paths.join(', ')
end
@@ -684,9 +829,9 @@ class TestGemSourceIndex < RubyGemTestCase
source_index = @source_index.dump
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
- @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = source_index
+ @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}"] = source_index
end
end
diff --git a/test/rubygems/test_gem_source_info_cache.rb b/test/rubygems/test_gem_source_info_cache.rb
index 523b404280..83cd100a9d 100644
--- a/test/rubygems/test_gem_source_info_cache.rb
+++ b/test/rubygems/test_gem_source_info_cache.rb
@@ -36,6 +36,7 @@ class TestGemSourceInfoCache < RubyGemTestCase
def teardown
super
Gem.sources.replace @original_sources
+ Gem::SourceInfoCache.instance_variable_set :@cache, nil
end
def test_self_cache_refreshes
@@ -43,7 +44,7 @@ class TestGemSourceInfoCache < RubyGemTestCase
si = Gem::SourceIndex.new
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}]
@@ -52,8 +53,9 @@ class TestGemSourceInfoCache < RubyGemTestCase
assert_kind_of Gem::SourceInfoCache, Gem::SourceInfoCache.cache
assert_equal Gem::SourceInfoCache.cache.object_id,
Gem::SourceInfoCache.cache.object_id
- assert_match %r|Bulk updating|, @ui.output
end
+
+ assert_match %r|Bulk updating|, @ui.output
end
def test_self_cache_skips_refresh_based_on_configuration
@@ -61,7 +63,7 @@ class TestGemSourceInfoCache < RubyGemTestCase
si = Gem::SourceIndex.new
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}]
@@ -78,7 +80,7 @@ class TestGemSourceInfoCache < RubyGemTestCase
si = Gem::SourceIndex.new
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
sice = Gem::SourceInfoCacheEntry.new si, 0
@@ -106,7 +108,7 @@ class TestGemSourceInfoCache < RubyGemTestCase
end
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' } }
diff --git a/test/rubygems/test_gem_source_info_cache_entry.rb b/test/rubygems/test_gem_source_info_cache_entry.rb
index c1194e34bc..6986c9cd7f 100644
--- a/test/rubygems/test_gem_source_info_cache_entry.rb
+++ b/test/rubygems/test_gem_source_info_cache_entry.rb
@@ -15,9 +15,9 @@ class TestGemSourceInfoCacheEntry < RubyGemTestCase
end
def test_refresh
- @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}.Z"] =
+ @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}.Z"] =
proc { raise }
- @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = @si.dump
+ @fetcher.data["#{@gem_repo}Marshal.#{@marshal_version}"] = @si.dump
use_ui @ui do
@sic_e.refresh @gem_repo, true
@@ -30,18 +30,20 @@ class TestGemSourceInfoCacheEntry < RubyGemTestCase
a1_name = @a1.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")
- @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}"] =
+ @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
sic_e = Gem::SourceInfoCacheEntry.new Gem::SourceIndex.new, 0
+ assert_equal [], sic_e.source_index.map { |n,| n }
+
use_ui @ui do
- sic_e.refresh @gem_repo, false
+ assert sic_e.refresh(@gem_repo, false)
end
assert_equal [a2_name], sic_e.source_index.map { |n,| n }.sort
@@ -63,7 +65,7 @@ class TestGemSourceInfoCacheEntry < RubyGemTestCase
si = Gem::SourceIndex.new
si.add_spec @a1
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
@sic_e.refresh @gem_repo, true
diff --git a/test/rubygems/test_gem_spec_fetcher.rb b/test/rubygems/test_gem_spec_fetcher.rb
new file mode 100644
index 0000000000..7539d0ff4c
--- /dev/null
+++ b/test/rubygems/test_gem_spec_fetcher.rb
@@ -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
+
diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb
index 20eb12479f..003ded7bc0 100644
--- a/test/rubygems/test_gem_specification.rb
+++ b/test/rubygems/test_gem_specification.rb
@@ -213,6 +213,15 @@ end
assert_equal 'old_platform', same_spec.original_platform
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
assert_equal 'A User', @a1.author
end
@@ -282,6 +291,20 @@ end
assert_equal [rake, jabber, pqa], @a1.dependencies
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
assert_equal 'This is a test description', @a1.description
end
@@ -423,6 +446,15 @@ end
@a1.full_gem_path
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
assert_equal 'a-1', @a1.full_name
@@ -531,6 +563,17 @@ end
assert_equal ['A working computer'], @a1.requirements
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
s1 = quick_gem 'a', '1'
s2 = quick_gem 'b', '1'
@@ -570,6 +613,8 @@ end
end
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'
ruby_code = @a2.to_ruby
@@ -578,8 +623,6 @@ end
s.name = %q{a}
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.authors = [\"A User\"]
s.date = %q{#{Gem::Specification::TODAY.strftime "%Y-%m-%d"}}
@@ -591,6 +634,19 @@ end
s.require_paths = [\"lib\"]
s.rubygems_version = %q{#{Gem::RubyGemsVersion}}
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
"
@@ -613,8 +669,6 @@ end
s.version = \"1\"
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.authors = [\"A User\"]
s.date = %q{#{Gem::Specification::TODAY.strftime "%Y-%m-%d"}}
@@ -633,9 +687,24 @@ end
s.summary = %q{this is a summary}
s.test_files = [\"test/suite.rb\"]
- s.add_dependency(%q<rake>, [\"> 0.4\"])
- s.add_dependency(%q<jabber4r>, [\"> 0.0.0\"])
- s.add_dependency(%q<pqa>, [\"> 0.4\", \"<= 0.6\"])
+ if s.respond_to? :specification_version then
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+ 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
"
diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb
index d6e41814ed..aadf0a39c8 100644
--- a/test/rubygems/test_gem_uninstaller.rb
+++ b/test/rubygems/test_gem_uninstaller.rb
@@ -15,6 +15,12 @@ class TestGemUninstaller < GemInstallerTestCase
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
uninstaller = Gem::Uninstaller.new nil, :executables => false
@@ -39,5 +45,20 @@ class TestGemUninstaller < GemInstallerTestCase
assert_equal false, File.exist?(File.join(@gemhome, 'bin', 'executable'))
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
diff --git a/test/rubygems/test_gem_version.rb b/test/rubygems/test_gem_version.rb
index 27c522c3d7..8d10700490 100644
--- a/test/rubygems/test_gem_version.rb
+++ b/test/rubygems/test_gem_version.rb
@@ -71,10 +71,14 @@ class TestGemVersion < RubyGemTestCase
end
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, v1_2.eql?(@v1_2)
+ assert_equal true, @v1_2.eql?(v1_2)
- assert_equal true, v.eql?(@v1_2)
- assert_equal true, @v1_2.eql?(v)
+ 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_3.eql?(@v1_2)
@@ -91,8 +95,13 @@ class TestGemVersion < RubyGemTestCase
end
def test_hash
- v = Gem::Version.new("1.2")
- assert_equal v.hash, @v1_2.hash
+ v1_2 = Gem::Version.new "1.2"
+ 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
end
diff --git a/test/rubygems/test_kernel.rb b/test/rubygems/test_kernel.rb
index 3c6448e470..da31d772eb 100644
--- a/test/rubygems/test_kernel.rb
+++ b/test/rubygems/test_kernel.rb
@@ -52,7 +52,7 @@ class TestKernel < RubyGemTestCase
gem 'a', '= 2'
end
- assert_match(/activate a \(= 2\)/, ex.message)
+ assert_match(/activate a \(= 2, runtime\)/, ex.message)
assert_match(/activated a-1/, ex.message)
assert $:.any? { |p| %r{a-1/lib} =~ p }