summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rwxr-xr-xbin/gem13
-rw-r--r--gem_prelude.rb329
-rw-r--r--lib/rubygems.rb901
-rw-r--r--lib/rubygems/builder.rb21
-rw-r--r--lib/rubygems/command_manager.rb1
-rw-r--r--lib/rubygems/commands/cleanup_command.rb136
-rw-r--r--lib/rubygems/commands/environment_command.rb20
-rw-r--r--lib/rubygems/commands/fetch_command.rb16
-rw-r--r--lib/rubygems/commands/install_command.rb12
-rw-r--r--lib/rubygems/commands/list_command.rb6
-rw-r--r--lib/rubygems/commands/mirror_command.rb2
-rw-r--r--lib/rubygems/commands/query_command.rb57
-rw-r--r--lib/rubygems/commands/sources_command.rb25
-rw-r--r--lib/rubygems/commands/specification_command.rb12
-rw-r--r--lib/rubygems/commands/uninstall_command.rb13
-rw-r--r--lib/rubygems/commands/unpack_command.rb18
-rw-r--r--lib/rubygems/commands/update_command.rb43
-rwxr-xr-xlib/rubygems/custom_require.rb2
-rw-r--r--lib/rubygems/defaults.rb9
-rw-r--r--lib/rubygems/dependency_installer.rb176
-rw-r--r--lib/rubygems/exceptions.rb19
-rw-r--r--lib/rubygems/format.rb26
-rw-r--r--lib/rubygems/indexer.rb79
-rw-r--r--lib/rubygems/indexer/abstract_index_builder.rb12
-rw-r--r--lib/rubygems/indexer/latest_index_builder.rb35
-rw-r--r--lib/rubygems/indexer/master_index_builder.rb17
-rw-r--r--lib/rubygems/indexer/quick_index_builder.rb8
-rw-r--r--lib/rubygems/install_update_options.rb6
-rw-r--r--lib/rubygems/installer.rb14
-rw-r--r--lib/rubygems/package.rb793
-rw-r--r--lib/rubygems/package/f_sync_dir.rb24
-rw-r--r--lib/rubygems/package/tar_header.rb245
-rw-r--r--lib/rubygems/package/tar_input.rb219
-rw-r--r--lib/rubygems/package/tar_output.rb143
-rw-r--r--lib/rubygems/package/tar_reader.rb86
-rw-r--r--lib/rubygems/package/tar_reader/entry.rb99
-rw-r--r--lib/rubygems/package/tar_writer.rb180
-rw-r--r--lib/rubygems/remote_fetcher.rb147
-rw-r--r--lib/rubygems/requirement.rb2
-rw-r--r--lib/rubygems/rubygems_version.rb2
-rw-r--r--lib/rubygems/security.rb1
-rw-r--r--lib/rubygems/server.rb189
-rw-r--r--lib/rubygems/source_index.rb739
-rw-r--r--lib/rubygems/source_info_cache.rb312
-rw-r--r--lib/rubygems/source_info_cache_entry.rb18
-rw-r--r--lib/rubygems/specification.rb2
-rw-r--r--lib/rubygems/uninstaller.rb60
-rw-r--r--lib/rubygems/user_interaction.rb12
-rw-r--r--test/rubygems/gem_installer_test_case.rb86
-rw-r--r--test/rubygems/gem_package_tar_test_case.rb146
-rw-r--r--test/rubygems/gemutilities.rb151
-rw-r--r--test/rubygems/mockgemui.rb18
-rw-r--r--test/rubygems/private_key.pem27
-rw-r--r--test/rubygems/public_cert.pem20
-rw-r--r--test/rubygems/test_gem.rb47
-rw-r--r--test/rubygems/test_gem_command_manager.rb6
-rw-r--r--test/rubygems/test_gem_commands_environment_command.rb15
-rw-r--r--test/rubygems/test_gem_commands_fetch_command.rb9
-rw-r--r--test/rubygems/test_gem_commands_install_command.rb64
-rw-r--r--test/rubygems/test_gem_commands_query_command.rb159
-rw-r--r--test/rubygems/test_gem_commands_server_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_sources_command.rb74
-rw-r--r--test/rubygems/test_gem_commands_specification_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_unpack_command.rb50
-rw-r--r--test/rubygems/test_gem_commands_update_command.rb174
-rw-r--r--test/rubygems/test_gem_dependency_installer.rb351
-rw-r--r--test/rubygems/test_gem_ext_configure_builder.rb15
-rw-r--r--test/rubygems/test_gem_format.rb2
-rw-r--r--test/rubygems/test_gem_indexer.rb9
-rw-r--r--test/rubygems/test_gem_installer.rb89
-rw-r--r--test/rubygems/test_gem_package_tar_header.rb137
-rw-r--r--test/rubygems/test_gem_package_tar_input.rb119
-rw-r--r--test/rubygems/test_gem_package_tar_output.rb104
-rw-r--r--test/rubygems/test_gem_package_tar_reader.rb53
-rw-r--r--test/rubygems/test_gem_package_tar_reader_entry.rb116
-rw-r--r--test/rubygems/test_gem_package_tar_writer.rb151
-rw-r--r--test/rubygems/test_gem_remote_fetcher.rb204
-rw-r--r--test/rubygems/test_gem_source_index.rb359
-rw-r--r--test/rubygems/test_gem_source_info_cache.rb232
-rw-r--r--test/rubygems/test_gem_source_info_cache_entry.rb51
-rw-r--r--test/rubygems/test_gem_uninstaller.rb43
82 files changed, 5619 insertions, 2771 deletions
diff --git a/ChangeLog b/ChangeLog
index 53d889796a..16f5894653 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+Tue Apr 1 07:31:58 2008 Eric Hodel <drbrain@segment7.net>
+
+ * lib/rubygems* test/rubygems*: Import RubyGems 1.1.0.
+
Tue Apr 1 03:20:40 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
* configure.in (RUBY_SETJMP, RUBY_LONGJMP, RUBY_JMP_BUF): prefers
diff --git a/bin/gem b/bin/gem
index aa8db57b51..b5b2b63809 100755
--- a/bin/gem
+++ b/bin/gem
@@ -7,11 +7,12 @@
require 'rubygems'
require 'rubygems/gem_runner'
+require 'rubygems/exceptions'
-required_version = Gem::Requirement.new ">= 1.8.3"
+required_version = Gem::Requirement.new "> 1.8.3"
-unless required_version.satisfied_by? Gem::Version.new(RUBY_VERSION) then
- abort "Expected Ruby Version #{required_version}, was #{RUBY_VERSION}"
+unless required_version.satisfied_by? Gem.ruby_version then
+ abort "Expected Ruby Version #{required_version}, was #{Gem.ruby_version}"
end
# We need to preserve the original ARGV to use for passing gem options
@@ -19,5 +20,9 @@ end
# it...its for the source building process.
args = !ARGV.include?("--") ? ARGV.clone : ARGV[0...ARGV.index("--")]
-Gem::GemRunner.new.run args
+begin
+ Gem::GemRunner.new.run args
+rescue Gem::SystemExitException => e
+ exit e.exit_code
+end
diff --git a/gem_prelude.rb b/gem_prelude.rb
index 1b4ef4409e..e727bee876 100644
--- a/gem_prelude.rb
+++ b/gem_prelude.rb
@@ -1,207 +1,214 @@
+# depends on: array.rb dir.rb env.rb file.rb hash.rb module.rb regexp.rb
+
# empty gem_prelude.rb
#
# p Gem::Enable
-if defined?(Gem::Enable) && Gem::Enable
-#t = Time.now
-
-module Kernel
+if defined?(Gem::Enable) && Gem::Enable then
+
+ module Kernel
+
+ def gem(gem_name, *version_requirements)
+ Gem.push_gem_version_on_load_path(gem_name, *version_requirements)
+ end
- def gem(gem_name, *version_requirements)
- Gem.push_gem_version_on_load_path(gem_name, *version_requirements)
end
-end
+ module Gem
+
+ ConfigMap = {
+ :sitedir => RbConfig::CONFIG["sitedir"],
+ :ruby_version => RbConfig::CONFIG["ruby_version"],
+ :libdir => RbConfig::CONFIG["libdir"],
+ :sitelibdir => RbConfig::CONFIG["sitelibdir"],
+ :arch => RbConfig::CONFIG["arch"],
+ :bindir => RbConfig::CONFIG["bindir"],
+ :EXEEXT => RbConfig::CONFIG["EXEEXT"],
+ :RUBY_SO_NAME => RbConfig::CONFIG["RUBY_SO_NAME"],
+ :ruby_install_name => RbConfig::CONFIG["ruby_install_name"]
+ }
+
+ class << self
-module Gem
-
- ConfigMap = {
- :sitedir => RbConfig::CONFIG["sitedir"],
- :ruby_version => RbConfig::CONFIG["ruby_version"],
- :libdir => RbConfig::CONFIG["libdir"],
- :sitelibdir => RbConfig::CONFIG["sitelibdir"],
- :arch => RbConfig::CONFIG["arch"],
- :bindir => RbConfig::CONFIG["bindir"],
- :EXEEXT => RbConfig::CONFIG["EXEEXT"],
- :RUBY_SO_NAME => RbConfig::CONFIG["RUBY_SO_NAME"],
- :ruby_install_name => RbConfig::CONFIG["ruby_install_name"]
- }
-
- class << self
-
- def default_dir
- if defined? RUBY_FRAMEWORK_VERSION
- return File.join(File.dirname(ConfigMap[:sitedir]), "Gems")
- else
- File.join(ConfigMap[:libdir], 'ruby', 'gems', ConfigMap[:ruby_version])
+ def default_dir
+ if defined? RUBY_FRAMEWORK_VERSION
+ return File.join(File.dirname(ConfigMap[:sitedir]), "Gems")
+ else
+ File.join(ConfigMap[:libdir], 'ruby', 'gems', ConfigMap[:ruby_version])
+ end
end
- end
- def dir
- @gem_home ||= nil
- set_home(ENV['GEM_HOME'] || default_dir) unless @gem_home
- @gem_home
- end
+ def dir
+ @gem_home ||= nil
+ set_home(ENV['GEM_HOME'] || default_dir) unless @gem_home
+ @gem_home
+ end
- def path
- @gem_path ||= nil
- unless @gem_path
- paths = [ENV['GEM_PATH']]
- paths << APPLE_GEM_HOME if defined? APPLE_GEM_HOME
- set_paths(paths.compact.join(File::PATH_SEPARATOR))
+ def path
+ @gem_path ||= nil
+ unless @gem_path
+ paths = [ENV['GEM_PATH']]
+ paths << APPLE_GEM_HOME if defined? APPLE_GEM_HOME
+ set_paths(paths.compact.join(File::PATH_SEPARATOR))
+ end
+ @gem_path
end
- @gem_path
- end
- # Set the Gem home directory (as reported by +dir+).
- def set_home(home)
- @gem_home = home
- ensure_gem_subdirectories(@gem_home)
- end
+ # Set the Gem home directory (as reported by +dir+).
+ def set_home(home)
+ @gem_home = home
+ ensure_gem_subdirectories(@gem_home)
+ end
- def set_paths(gpaths)
- if gpaths
- @gem_path = gpaths.split(File::PATH_SEPARATOR)
- @gem_path << Gem.dir
- else
- @gem_path = [Gem.dir]
+ def set_paths(gpaths)
+ if gpaths
+ @gem_path = gpaths.split(File::PATH_SEPARATOR)
+ @gem_path << Gem.dir
+ else
+ @gem_path = [Gem.dir]
+ end
+ @gem_path.uniq!
+ @gem_path.each do |gp| ensure_gem_subdirectories(gp) end
end
- @gem_path.uniq!
- @gem_path.each do |gp| ensure_gem_subdirectories(gp) end
- end
-
- def ensure_gem_subdirectories(path)
- end
- end
+ def ensure_gem_subdirectories(path)
+ end
+
+ end
- module QuickLoader
+ module QuickLoader
- class << self
- def load_full_rubygems_library
- class << Gem
- Gem.methods(false).each do |method_name|
- undef_method method_name
+ class << self
+ def load_full_rubygems_library
+ class << Gem
+ Gem.methods(false).each do |method_name|
+ undef_method method_name
+ end
end
- end
- Kernel.send :undef_method, :gem
+ Kernel.send :undef_method, :gem
- $".delete File.join(Gem::ConfigMap[:libdir], 'ruby',
- Gem::ConfigMap[:ruby_version], 'rubygems.rb')
+ $".delete File.join(Gem::ConfigMap[:libdir], 'ruby',
+ Gem::ConfigMap[:ruby_version], 'rubygems.rb')
- require 'rubygems'
+ require 'rubygems'
+ end
end
- end
- GemPaths = {}
- GemVersions = {}
-
- def push_gem_version_on_load_path(gem_name, *version_requirements)
- if version_requirements.empty?
- unless GemPaths.has_key?(gem_name)
- raise LoadError.new("Could not find RubyGem #{gem_name} (>= 0)\n")
- end
- # highest version gems already active
- return false
- else
- if version_requirements.length > 1
- QuickLoader.load_full_rubygems_library
- return gem(gem_name, *version_requirements)
- end
- requirement, version = version_requirements[0].split
- requirement.strip!
- if requirement == ">" || requirement == ">="
- if (GemVersions[gem_name] <=> Gem.calculate_integers_for_gem_version(version)) >= 0
- return false
+ GemPaths = {}
+ GemVersions = {}
+
+ def push_gem_version_on_load_path(gem_name, *version_requirements)
+ if version_requirements.empty?
+ unless GemPaths.has_key?(gem_name)
+ raise LoadError.new("Could not find RubyGem #{gem_name} (>= 0)\n")
+ end
+
+ # highest version gems already active
+ return false
+ else
+ if version_requirements.length > 1
+ QuickLoader.load_full_rubygems_library
+ return gem(gem_name, *version_requirements)
end
- elsif requirement == "~>"
- loaded_version = GemVersions[gem_name]
- required_version = Gem.calculate_integers_for_gem_version(version)
- if loaded_version && (loaded_version[0] == required_version[0])
- return false
+
+ requirement, version = version_requirements[0].split
+ requirement.strip!
+
+ if requirement == ">" || requirement == ">="
+ if (GemVersions[gem_name] <=> Gem.calculate_integers_for_gem_version(version)) >= 0
+ return false
+ end
+ elsif requirement == "~>"
+ loaded_version = GemVersions[gem_name]
+ required_version = Gem.calculate_integers_for_gem_version(version)
+ if loaded_version && (loaded_version[0] == required_version[0])
+ return false
+ end
end
+
+ QuickLoader.load_full_rubygems_library
+ gem(gem_name, *version_requirements)
end
- QuickLoader.load_full_rubygems_library
- gem(gem_name, *version_requirements)
end
- end
- def calculate_integers_for_gem_version(gem_version)
- numbers = gem_version.split(".").collect {|n| n.to_i}
- numbers.pop while numbers.last == 0
- numbers << 0 if numbers.empty?
- numbers
- end
-
- def push_all_highest_version_gems_on_load_path
- Gem.path.each do |path|
- gems_directory = File.join(path, "gems")
- if File.exist?(gems_directory)
- Dir.entries(gems_directory).each do |gem_directory_name|
- next if gem_directory_name == "." || gem_directory_name == ".."
- dash = gem_directory_name.rindex("-")
- next if dash.nil?
- gem_name = gem_directory_name[0...dash]
- current_version = GemVersions[gem_name]
- new_version = calculate_integers_for_gem_version(gem_directory_name[dash+1..-1])
- if current_version
- if (current_version <=> new_version) == -1
+ def calculate_integers_for_gem_version(gem_version)
+ numbers = gem_version.split(".").collect {|n| n.to_i}
+ numbers.pop while numbers.last == 0
+ numbers << 0 if numbers.empty?
+ numbers
+ end
+
+ def push_all_highest_version_gems_on_load_path
+ Gem.path.each do |path|
+ gems_directory = File.join(path, "gems")
+ if File.exist?(gems_directory)
+ Dir.entries(gems_directory).each do |gem_directory_name|
+ next if gem_directory_name == "." || gem_directory_name == ".."
+ dash = gem_directory_name.rindex("-")
+ next if dash.nil?
+ gem_name = gem_directory_name[0...dash]
+ current_version = GemVersions[gem_name]
+ new_version = calculate_integers_for_gem_version(gem_directory_name[dash+1..-1])
+ if current_version
+ if (current_version <=> new_version) == -1
+ GemVersions[gem_name] = new_version
+ GemPaths[gem_name] = File.join(gems_directory, gem_directory_name)
+ end
+ else
GemVersions[gem_name] = new_version
GemPaths[gem_name] = File.join(gems_directory, gem_directory_name)
end
- else
- GemVersions[gem_name] = new_version
- GemPaths[gem_name] = File.join(gems_directory, gem_directory_name)
end
end
end
- end
- require_paths = []
- GemPaths.values.each do |path|
- if File.exist?(File.join(path, ".require_paths"))
- require_paths.concat(File.read(File.join(path, ".require_paths")).split.map {|require_path| File.join(path, require_path)})
- else
- require_paths << File.join(path, "bin") if File.exist?(File.join(path, "bin"))
- require_paths << File.join(path, "lib") if File.exist?(File.join(path, "lib"))
+
+ require_paths = []
+
+ GemPaths.values.each do |path|
+ if File.exist?(File.join(path, ".require_paths"))
+ require_paths.concat(File.read(File.join(path, ".require_paths")).split.map {|require_path| File.join(path, require_path)})
+ else
+ require_paths << File.join(path, "bin") if File.exist?(File.join(path, "bin"))
+ require_paths << File.join(path, "lib") if File.exist?(File.join(path, "lib"))
+ end
+ end
+
+ # "tag" the first require_path inserted into the $LOAD_PATH to enable
+ # indexing correctly with rubygems proper when it inserts an explicitly
+ # gem version
+ unless require_paths.empty?
+ require_paths.first.instance_variable_set(:@gem_prelude_index, true)
end
+ # gem directories must come after -I and ENV['RUBYLIB']
+ $:[$:.index(ConfigMap[:sitelibdir]),0] = require_paths
+ end
+
+ def const_missing(constant)
+ QuickLoader.load_full_rubygems_library
+ Gem.const_get(constant)
end
-
- # "tag" the first require_path inserted into the $LOAD_PATH to enable
- # indexing correctly with rubygems proper when it inserts an explicitly
- # gem version
- unless require_paths.empty?
- require_paths.first.instance_variable_set(:@gem_prelude_index, true)
+
+ def method_missing(method, *args, &block)
+ QuickLoader.load_full_rubygems_library
+ super unless Gem.respond_to?(method)
+ Gem.send(method, *args, &block)
end
- # gem directories must come after -I and ENV['RUBYLIB']
- $:[$:.index(ConfigMap[:sitelibdir]),0] = require_paths
end
- def const_missing(constant)
- QuickLoader.load_full_rubygems_library
- Gem.const_get(constant)
- end
+ extend QuickLoader
- def method_missing(method, *args, &block)
- QuickLoader.load_full_rubygems_library
- super unless Gem.respond_to?(method)
- Gem.send(method, *args, &block)
- end
end
-
- extend QuickLoader
-end
+ begin
+ Gem.push_all_highest_version_gems_on_load_path
+ $" << File.join(Gem::ConfigMap[:libdir], "ruby",
+ Gem::ConfigMap[:ruby_version], "rubygems.rb")
+ rescue Exception => e
+ puts "Error loading gem paths on load path in gem_prelude"
+ puts e
+ puts e.backtrace.join("\n")
+ end
-begin
- Gem.push_all_highest_version_gems_on_load_path
- $" << File.join(Gem::ConfigMap[:libdir], "ruby",
- Gem::ConfigMap[:ruby_version], "rubygems.rb")
-rescue Exception => e
- puts "Error loading gem paths on load path in gem_prelude"
- puts e
- puts e.backtrace.join("\n")
end
-#puts "Gem load in #{Time.now - t} seconds"
-end # Gem::Enable
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 9549b9bdca..3f9657ac1e 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -17,82 +17,80 @@ end
module Kernel
- # Adds a Ruby Gem to the $LOAD_PATH. Before a Gem is loaded, its
- # required Gems are loaded. If the version information is omitted,
- # the highest version Gem of the supplied name is loaded. If a Gem
- # is not found that meets the version requirement and/or a required
- # Gem is not found, a Gem::LoadError is raised. More information on
- # version requirements can be found in the Gem::Version
- # documentation.
- #
- # The +gem+ directive should be executed *before* any require
- # statements (otherwise rubygems might select a conflicting library
- # version).
+ ##
+ # Use Kernel#gem to activate a specific version of +gem_name+.
#
- # You can define the environment variable GEM_SKIP as a way to not
- # load specified gems. You might do this to test out changes that
- # haven't been installed yet. Example:
+ # +version_requirements+ is a list of version requirements that the
+ # specified gem must match, most commonly "= example.version.number". See
+ # Gem::Requirement for how to specify a version requirement.
#
- # GEM_SKIP=libA:libB ruby-I../libA -I../libB ./mycode.rb
+ # If you will be activating the latest version of a gem, there is no need to
+ # call Kernel#gem, Kernel#require will do the right thing for you.
#
- # gem:: [String or Gem::Dependency] The gem name or dependency
- # instance.
+ # Kernel#gem returns true if the gem was activated, otherwise false. If the
+ # gem could not be found, didn't match the version requirements, or a
+ # different version was already activated, an exception will be raised.
#
- # version_requirement:: [default=">= 0"] The version
- # requirement.
+ # Kernel#gem should be called *before* any require statements (otherwise
+ # RubyGems may load a conflicting library version).
#
- # return:: [Boolean] true if the Gem is loaded, otherwise false.
+ # In older RubyGems versions, the environment variable GEM_SKIP could be
+ # used to skip activation of specified gems, for example to test out changes
+ # that haven't been installed yet. Now RubyGems defers to -I and the
+ # RUBYLIB environment variable to skip activation of a gem.
#
- # raises:: [Gem::LoadError] if Gem cannot be found, is listed in
- # GEM_SKIP, or version requirement not met.
+ # Example:
#
- def gem(gem_name, *version_requirements)
- active_gem_with_options(gem_name, version_requirements)
- end
-
- # Return the file name (string) and line number (integer) of the caller of
- # the caller of this method.
- def location_of_caller
- file, lineno = caller[1].split(':')
- lineno = lineno.to_i
- [file, lineno]
- end
- private :location_of_caller
+ # GEM_SKIP=libA:libB ruby -I../libA -I../libB ./mycode.rb
- def active_gem_with_options(gem_name, version_requirements, options={})
+ def gem(gem_name, *version_requirements)
skip_list = (ENV['GEM_SKIP'] || "").split(/:/)
raise Gem::LoadError, "skipping #{gem_name}" if skip_list.include? gem_name
- Gem.activate(gem_name, options[:auto_require], *version_requirements)
+ Gem.activate(gem_name, *version_requirements)
end
- private :active_gem_with_options
+
end
+##
# Main module to hold all RubyGem classes/modules.
-#
+
module Gem
ConfigMap = {} unless defined?(ConfigMap)
require 'rbconfig'
RbConfig = Config unless defined? ::RbConfig
+
ConfigMap.merge!(
- :BASERUBY => RbConfig::CONFIG["BASERUBY"],
- :EXEEXT => RbConfig::CONFIG["EXEEXT"],
- :RUBY_INSTALL_NAME => RbConfig::CONFIG["RUBY_INSTALL_NAME"],
- :RUBY_SO_NAME => RbConfig::CONFIG["RUBY_SO_NAME"],
- :arch => RbConfig::CONFIG["arch"],
- :bindir => RbConfig::CONFIG["bindir"],
- :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"]
+ :BASERUBY => RbConfig::CONFIG["BASERUBY"],
+ :EXEEXT => RbConfig::CONFIG["EXEEXT"],
+ :RUBY_INSTALL_NAME => RbConfig::CONFIG["RUBY_INSTALL_NAME"],
+ :RUBY_SO_NAME => RbConfig::CONFIG["RUBY_SO_NAME"],
+ :arch => RbConfig::CONFIG["arch"],
+ :bindir => RbConfig::CONFIG["bindir"],
+ :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"]
)
+ DIRECTORIES = %w[cache doc gems specifications] unless defined?(DIRECTORIES)
+
MUTEX = Mutex.new
RubyGemsPackageVersion = RubyGemsVersion
- DIRECTORIES = %w[cache doc gems specifications] unless defined?(DIRECTORIES)
+ ##
+ # An Array of Regexps that match windows ruby platforms.
+
+ WIN_PATTERNS = [
+ /bccwin/i,
+ /cygwin/i,
+ /djgpp/i,
+ /mingw/i,
+ /mswin/i,
+ /wince/i,
+ ]
@@source_index = nil
@@win_platform = nil
@@ -103,10 +101,129 @@ module Gem
@ruby = nil
@sources = []
+ ##
+ # Activates an installed gem matching +gem+. The gem must satisfy
+ # +version_requirements+.
+ #
+ # Returns true if the gem is activated, false if it is already
+ # loaded, or an exception otherwise.
+ #
+ # Gem#activate adds the library paths in +gem+ to $LOAD_PATH. Before a Gem
+ # is activated its required Gems are activated. If the version information
+ # is omitted, the highest version Gem of the supplied name is loaded. If a
+ # Gem is not found that meets the version requirements or a required Gem is
+ # not found, a Gem::LoadError is raised.
+ #
+ # More information on version requirements can be found in the
+ # Gem::Requirement and Gem::Version documentation.
+
+ def self.activate(gem, *version_requirements)
+ if version_requirements.empty? then
+ version_requirements = Gem::Requirement.default
+ end
+
+ unless gem.respond_to?(:name) and
+ gem.respond_to?(:version_requirements) then
+ gem = Gem::Dependency.new(gem, version_requirements)
+ end
+
+ matches = Gem.source_index.find_name(gem.name, gem.version_requirements)
+ report_activate_error(gem) if matches.empty?
+
+ if @loaded_specs[gem.name] then
+ # This gem is already loaded. If the currently loaded gem is not in the
+ # list of candidate gems, then we have a version conflict.
+ existing_spec = @loaded_specs[gem.name]
+
+ unless matches.any? { |spec| spec.version == existing_spec.version } then
+ raise Gem::Exception,
+ "can't activate #{gem}, already activated #{existing_spec.full_name}]"
+ end
+
+ return false
+ end
+
+ # new load
+ spec = matches.last
+ return false if spec.loaded?
+
+ spec.loaded = true
+ @loaded_specs[spec.name] = spec
+
+ # Load dependent gems first
+ spec.dependencies.each do |dep_gem|
+ activate dep_gem
+ end
+
+ # bin directory must come before library directories
+ spec.require_paths.unshift spec.bindir if spec.bindir
+
+ require_paths = spec.require_paths.map do |path|
+ File.join spec.full_gem_path, path
+ end
+
+ sitelibdir = ConfigMap[:sitelibdir]
+
+ # gem directories must come after -I and ENV['RUBYLIB']
+ insert_index = load_path_insert_index
+
+ if insert_index then
+ # gem directories must come after -I and ENV['RUBYLIB']
+ $LOAD_PATH.insert(insert_index, *require_paths)
+ else
+ # we are probably testing in core, -I and RUBYLIB don't apply
+ $LOAD_PATH.unshift(*require_paths)
+ end
+
+ return true
+ end
+
+ ##
+ # An Array of all possible load paths for all versions of all gems in the
+ # Gem installation.
+
+ def self.all_load_paths
+ result = []
+
+ Gem.path.each do |gemdir|
+ each_load_path all_partials(gemdir) do |load_path|
+ result << load_path
+ end
+ end
+
+ result
+ end
+
+ ##
+ # Return all the partial paths in +gemdir+.
+
+ def self.all_partials(gemdir)
+ Dir[File.join(gemdir, 'gems/*')]
+ end
+
+ private_class_method :all_partials
+
+ ##
+ # The mode needed to read a file as straight binary.
+
+ def self.binary_mode
+ @binary_mode ||= RUBY_VERSION > '1.9' ? 'rb:ascii-8bit' : 'rb'
+ end
+
+ ##
+ # The path where gem executables are to be installed.
+
+ def self.bindir(install_dir=Gem.dir)
+ return File.join(install_dir, 'bin') unless
+ install_dir.to_s == Gem.default_dir
+ Gem.default_bindir
+ end
+
+ ##
# Reset the +dir+ and +path+ values. The next time +dir+ or +path+
# is requested, the values will be calculated from scratch. This is
# mainly used by the unit tests to provide test isolation.
- #
+
def self.clear_paths
@gem_home = nil
@gem_path = nil
@@ -116,441 +233,430 @@ module Gem
end
end
- # The version of the Marshal format for your Ruby.
- def self.marshal_version
- "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
+ ##
+ # The path to standard location of the user's .gemrc file.
+
+ def self.config_file
+ File.join Gem.user_home, '.gemrc'
end
##
- # The directory prefix this RubyGems was installed at.
+ # The standard configuration object for gems.
- def self.prefix
- prefix = File.dirname File.expand_path(__FILE__)
- if prefix == ConfigMap[:sitelibdir] then
- nil
- else
- File.dirname prefix
- end
+ def self.configuration
+ return @configuration if @configuration
+ require 'rubygems/config_file'
+ @configuration = Gem::ConfigFile.new []
end
- # Returns an Cache of specifications that are in the Gem.path
- #
- # return:: [Gem::SourceIndex] Index of installed Gem::Specifications
- #
- def self.source_index
- @@source_index ||= SourceIndex.from_installed_gems
+ ##
+ # Use the given configuration object (which implements the ConfigFile
+ # protocol) as the standard configuration object.
+
+ def self.configuration=(config)
+ @configuration = config
end
##
- # An Array of Regexps that match windows ruby platforms.
+ # The path the the data directory specified by the gem name. If the
+ # package is not available as a gem, return nil.
- WIN_PATTERNS = [
- /bccwin/i,
- /cygwin/i,
- /djgpp/i,
- /mingw/i,
- /mswin/i,
- /wince/i,
- ]
+ def self.datadir(gem_name)
+ spec = @loaded_specs[gem_name]
+ return nil if spec.nil?
+ File.join(spec.full_gem_path, 'data', gem_name)
+ end
##
- # Is this a windows platform?
+ # The path where gems are to be installed.
- def self.win_platform?
- if @@win_platform.nil? then
- @@win_platform = !!WIN_PATTERNS.find { |r| RUBY_PLATFORM =~ r }
- end
+ def self.dir
+ @gem_home ||= nil
+ set_home(ENV['GEM_HOME'] || default_dir) unless @gem_home
+ @gem_home
+ end
- @@win_platform
+ ##
+ # Expand each partial gem path with each of the required paths specified
+ # in the Gem spec. Each expanded path is yielded.
+
+ def self.each_load_path(partials)
+ partials.each do |gp|
+ base = File.basename(gp)
+ specfn = File.join(dir, "specifications", base + ".gemspec")
+ if File.exist?(specfn)
+ spec = eval(File.read(specfn))
+ spec.require_paths.each do |rp|
+ yield(File.join(gp, rp))
+ end
+ else
+ filename = File.join(gp, 'lib')
+ yield(filename) if File.exist?(filename)
+ end
+ end
end
- class << self
+ private_class_method :each_load_path
- attr_reader :loaded_specs
+ ##
+ # Quietly ensure the named Gem directory contains all the proper
+ # subdirectories. If we can't create a directory due to a permission
+ # problem, then we will silently continue.
- # Quietly ensure the named Gem directory contains all the proper
- # subdirectories. If we can't create a directory due to a permission
- # problem, then we will silently continue.
- def ensure_gem_subdirectories(gemdir)
- require 'fileutils'
+ def self.ensure_gem_subdirectories(gemdir)
+ require 'fileutils'
- Gem::DIRECTORIES.each do |filename|
- fn = File.join gemdir, filename
- FileUtils.mkdir_p fn rescue nil unless File.exist? fn
- end
+ Gem::DIRECTORIES.each do |filename|
+ fn = File.join gemdir, filename
+ FileUtils.mkdir_p fn rescue nil unless File.exist? fn
end
+ end
- def platforms
- @platforms ||= [Gem::Platform::RUBY, Gem::Platform.local]
- end
+ ##
+ # Finds the user's home directory.
+ #--
+ # Some comments from the ruby-talk list regarding finding the home
+ # directory:
+ #
+ # I have HOME, USERPROFILE and HOMEDRIVE + HOMEPATH. Ruby seems
+ # to be depending on HOME in those code samples. I propose that
+ # it should fallback to USERPROFILE and HOMEDRIVE + HOMEPATH (at
+ # least on Win32).
- # Returns an Array of sources to fetch remote gems from. If the sources
- # list is empty, attempts to load the "sources" gem, then uses
- # default_sources if it is not installed.
- def sources
- if @sources.empty? then
- begin
- gem 'sources', '> 0.0.1'
- require 'sources'
- rescue LoadError
- @sources = default_sources
- end
- end
+ def self.find_home
+ ['HOME', 'USERPROFILE'].each do |homekey|
+ return ENV[homekey] if ENV[homekey]
+ end
- @sources
+ if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
+ return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
end
+ begin
+ File.expand_path("~")
+ rescue
+ if File::ALT_SEPARATOR then
+ "C:/"
+ else
+ "/"
+ end
+ end
+ end
- # Provide an alias for the old name.
- alias cache source_index
+ private_class_method :find_home
- # The directory path where Gems are to be installed.
- #
- # return:: [String] The directory path
- #
- def dir
- @gem_home ||= nil
- set_home(ENV['GEM_HOME'] || default_dir) unless @gem_home
- @gem_home
- end
+ ##
+ # Return a list of all possible load paths for the latest version for all
+ # gems in the Gem installation.
- # The directory path where executables are to be installed.
- #
- def bindir(install_dir=Gem.dir)
- return File.join(install_dir, 'bin') unless
- install_dir.to_s == Gem.default_dir
+ def self.latest_load_paths
+ result = []
- if defined? RUBY_FRAMEWORK_VERSION then # mac framework support
- '/usr/bin'
- else # generic install
- ConfigMap[:bindir]
+ Gem.path.each do |gemdir|
+ each_load_path(latest_partials(gemdir)) do |load_path|
+ result << load_path
end
end
- # List of directory paths to search for Gems.
- #
- # return:: [List<String>] List of directory paths.
- #
- def path
- @gem_path ||= nil
- unless @gem_path
- paths = [ENV['GEM_PATH']]
- paths << APPLE_GEM_HOME if defined? APPLE_GEM_HOME
- set_paths(paths.compact.join(File::PATH_SEPARATOR))
+ result
+ end
+
+ ##
+ # Return only the latest partial paths in the given +gemdir+.
+
+ def self.latest_partials(gemdir)
+ latest = {}
+ all_partials(gemdir).each do |gp|
+ base = File.basename(gp)
+ if base =~ /(.*)-((\d+\.)*\d+)/ then
+ name, version = $1, $2
+ ver = Gem::Version.new(version)
+ if latest[name].nil? || ver > latest[name][0]
+ latest[name] = [ver, gp]
+ end
end
- @gem_path
end
+ latest.collect { |k,v| v[1] }
+ end
- # The home directory for the user.
- def user_home
- @user_home ||= find_home
- end
+ private_class_method :latest_partials
- # Return the path to standard location of the users .gemrc file.
- def config_file
- File.join(Gem.user_home, '.gemrc')
+ ##
+ # The index to insert activated gem paths into the $LOAD_PATH.
+ #
+ # Defaults to the site lib directory unless gem_prelude.rb has loaded paths,
+ # then it inserts the activated gem's paths before the gem_prelude.rb paths
+ # so you can override the gem_prelude.rb default $LOAD_PATH paths.
+
+ def self.load_path_insert_index
+ index = $LOAD_PATH.index ConfigMap[:sitelibdir]
+
+ $LOAD_PATH.each_with_index do |path, i|
+ if path.instance_variables.include?(:@gem_prelude_index) or
+ path.instance_variables.include?('@gem_prelude_index') then
+ index = i
+ break
+ end
end
- # The standard configuration object for gems.
- def configuration
- return @configuration if @configuration
- require 'rubygems/config_file'
- @configuration = Gem::ConfigFile.new []
- end
+ index
+ end
- # Use the given configuration object (which implements the
- # ConfigFile protocol) as the standard configuration object.
- def configuration=(config)
- @configuration = config
- end
+ ##
+ # The file name and line number of the caller of the caller of this method.
- # Return the path the the data directory specified by the gem
- # name. If the package is not available as a gem, return nil.
- def datadir(gem_name)
- spec = @loaded_specs[gem_name]
- return nil if spec.nil?
- File.join(spec.full_gem_path, 'data', gem_name)
- end
+ def self.location_of_caller
+ file, lineno = caller[1].split(':')
+ lineno = lineno.to_i
+ [file, lineno]
+ end
- # Return the searcher object to search for matching gems.
- def searcher
- MUTEX.synchronize do
- @searcher ||= Gem::GemPathSearcher.new
- end
- end
+ private_class_method :location_of_caller
- # Return the Ruby command to use to execute the Ruby interpreter.
- def ruby
- if @ruby.nil? then
- @ruby = File.join(ConfigMap[:bindir],
- ConfigMap[:ruby_install_name])
- @ruby << ConfigMap[:EXEEXT]
- end
+ ##
+ # manage_gems is useless and deprecated. Don't call it anymore.
+ #--
+ # TODO warn w/ RubyGems 1.2.x release.
- @ruby
- end
+ def self.manage_gems
+ #file, lineno = location_of_caller
- # Return the index to insert activated gem paths into the $LOAD_PATH
- # Defaults to the site lib directory unless gem_prelude.rb has loaded
- # paths then it inserts the path before those paths so you can override
- # the gem_prelude.rb default $LOAD_PATH paths.
- def load_path_insert_index
- index = $LOAD_PATH.index ConfigMap[:sitelibdir]
-
- $LOAD_PATH.each_with_index do |path, i|
- if path.instance_variables.include?(:@gem_prelude_index) or
- path.instance_variables.include?('@gem_prelude_index') then
- index = i
- break
- end
- end
+ #warn "#{file}:#{lineno}:Warning: Gem#manage_gems is deprecated and will be removed on or after September 2008."
+ end
- index
- end
+ ##
+ # The version of the Marshal format for your Ruby.
- # Activate a gem (i.e. add it to the Ruby load path). The gem
- # must satisfy all the specified version constraints. If
- # +autorequire+ is true, then automatically require the specified
- # autorequire file in the gem spec.
- #
- # Returns true if the gem is loaded by this call, false if it is
- # already loaded, or an exception otherwise.
- #
- def activate(gem, autorequire, *version_requirements)
- if version_requirements.empty? then
- version_requirements = Gem::Requirement.default
- end
+ def self.marshal_version
+ "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
+ end
- unless gem.respond_to?(:name) && gem.respond_to?(:version_requirements)
- gem = Gem::Dependency.new(gem, version_requirements)
- end
+ ##
+ # Array of paths to search for Gems.
- matches = Gem.source_index.find_name(gem.name, gem.version_requirements)
- report_activate_error(gem) if matches.empty?
+ def self.path
+ @gem_path ||= nil
- if @loaded_specs[gem.name]
- # This gem is already loaded. If the currently loaded gem is
- # not in the list of candidate gems, then we have a version
- # conflict.
- existing_spec = @loaded_specs[gem.name]
- if ! matches.any? { |spec| spec.version == existing_spec.version }
- fail Gem::Exception, "can't activate #{gem}, already activated #{existing_spec.full_name}]"
- end
- return false
- end
+ unless @gem_path then
+ paths = [ENV['GEM_PATH']] || [default_path]
- # new load
- spec = matches.last
- if spec.loaded?
- return false unless autorequire
- result = spec.autorequire ? require(spec.autorequire) : false
- return result || false
+ if defined?(APPLE_GEM_HOME) and not ENV['GEM_PATH'] then
+ paths << APPLE_GEM_HOME
end
- spec.loaded = true
- @loaded_specs[spec.name] = spec
+ set_paths paths.compact.join(File::PATH_SEPARATOR)
+ end
- # Load dependent gems first
- spec.dependencies.each do |dep_gem|
- activate(dep_gem, autorequire)
- end
+ @gem_path
+ end
- # bin directory must come before library directories
- spec.require_paths.unshift spec.bindir if spec.bindir
+ ##
+ # Array of platforms this RubyGems supports.
- require_paths = spec.require_paths.map do |path|
- File.join spec.full_gem_path, path
- end
+ def self.platforms
+ @platforms ||= [Gem::Platform::RUBY, Gem::Platform.local]
+ end
- sitelibdir = ConfigMap[:sitelibdir]
+ ##
+ # The directory prefix this RubyGems was installed at.
- # gem directories must come after -I and ENV['RUBYLIB']
- insert_index = load_path_insert_index
+ def self.prefix
+ prefix = File.dirname File.expand_path(__FILE__)
- if insert_index then
- # gem directories must come after -I and ENV['RUBYLIB']
- $LOAD_PATH.insert(insert_index, *require_paths)
- else
- # we are probably testing in core, -I and RUBYLIB don't apply
- $LOAD_PATH.unshift(*require_paths)
- end
+ if prefix == File.expand_path(ConfigMap[:sitelibdir]) then
+ nil
+ else
+ File.dirname prefix
+ end
+ end
- # Now autorequire
- if autorequire && spec.autorequire then # DEPRECATED
- Array(spec.autorequire).each do |a_lib|
- require a_lib
- end
- end
+ ##
+ # Safely read a file in binary mode on all platforms.
- return true
- end
+ def self.read_binary(path)
+ File.open path, binary_mode do |f| f.read end
+ end
+
+ ##
+ # Report a load error during activation. The message of load error
+ # depends on whether it was a version mismatch or if there are not gems of
+ # any version by the requested name.
- # Report a load error during activation. The message of load
- # error depends on whether it was a version mismatch or if there
- # are not gems of any version by the requested name.
- def report_activate_error(gem)
- matches = Gem.source_index.find_name(gem.name)
+ def self.report_activate_error(gem)
+ matches = Gem.source_index.find_name(gem.name)
- if matches.empty? then
- error = Gem::LoadError.new(
+ if matches.empty? then
+ error = Gem::LoadError.new(
"Could not find RubyGem #{gem.name} (#{gem.version_requirements})\n")
- else
- error = Gem::LoadError.new(
+ else
+ error = Gem::LoadError.new(
"RubyGem version error: " +
"#{gem.name}(#{matches.first.version} not #{gem.version_requirements})\n")
- end
-
- error.name = gem.name
- error.version_requirement = gem.version_requirements
- raise error
- end
- private :report_activate_error
-
- # Use the +home+ and (optional) +paths+ values for +dir+ and +path+.
- # Used mainly by the unit tests to provide environment isolation.
- #
- def use_paths(home, paths=[])
- clear_paths
- set_home(home) if home
- set_paths(paths.join(File::PATH_SEPARATOR)) if paths
end
- # Return a list of all possible load paths for all versions for
- # all gems in the Gem installation.
- #
- def all_load_paths
- result = []
- Gem.path.each do |gemdir|
- each_load_path(all_partials(gemdir)) do |load_path|
- result << load_path
- end
- end
- result
- end
+ error.name = gem.name
+ error.version_requirement = gem.version_requirements
+ raise error
+ end
- # Return a list of all possible load paths for the latest version
- # for all gems in the Gem installation.
- def latest_load_paths
- result = []
- Gem.path.each do |gemdir|
- each_load_path(latest_partials(gemdir)) do |load_path|
- result << load_path
- end
- end
- result
- end
+ private_class_method :report_activate_error
- def required_location(gemname, libfile, *version_constraints)
- version_constraints = Gem::Requirement.default if version_constraints.empty?
- matches = Gem.source_index.find_name(gemname, version_constraints)
- return nil if matches.empty?
- spec = matches.last
- spec.require_paths.each do |path|
- result = File.join(spec.full_gem_path, path, libfile)
- return result if File.exist?(result)
- end
- nil
+ def self.required_location(gemname, libfile, *version_constraints)
+ version_constraints = Gem::Requirement.default if version_constraints.empty?
+ matches = Gem.source_index.find_name(gemname, version_constraints)
+ return nil if matches.empty?
+ spec = matches.last
+ spec.require_paths.each do |path|
+ result = File.join(spec.full_gem_path, path, libfile)
+ return result if File.exist?(result)
end
+ nil
+ end
- def suffixes
- ['', '.rb', '.rbw', '.so', '.bundle', '.dll', '.sl', '.jar']
- end
+ ##
+ # The path to the running Ruby interpreter.
- def suffix_pattern
- @suffix_pattern ||= "{#{suffixes.join(',')}}"
+ def self.ruby
+ if @ruby.nil? then
+ @ruby = File.join(ConfigMap[:bindir],
+ ConfigMap[:ruby_install_name])
+ @ruby << ConfigMap[:EXEEXT]
end
- # manage_gems is useless and deprecated. Don't call it anymore. This
- # will warn in two releases.
- def manage_gems
- # do nothing
- end
+ @ruby
+ end
- private
+ ##
+ # A Gem::Version for the currently running ruby.
- # Return all the partial paths in the given +gemdir+.
- def all_partials(gemdir)
- Dir[File.join(gemdir, 'gems/*')]
- end
+ def self.ruby_version
+ return @ruby_version if defined? @ruby_version
+ version = RUBY_VERSION.dup
+ version << ".#{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
+ @ruby_version = Gem::Version.new version
+ end
- # Return only the latest partial paths in the given +gemdir+.
- def latest_partials(gemdir)
- latest = {}
- all_partials(gemdir).each do |gp|
- base = File.basename(gp)
- if base =~ /(.*)-((\d+\.)*\d+)/ then
- name, version = $1, $2
- ver = Gem::Version.new(version)
- if latest[name].nil? || ver > latest[name][0]
- latest[name] = [ver, gp]
- end
- end
- end
- latest.collect { |k,v| v[1] }
+ ##
+ # The GemPathSearcher object used to search for matching installed gems.
+
+ def self.searcher
+ MUTEX.synchronize do
+ @searcher ||= Gem::GemPathSearcher.new
end
+ end
+
+ ##
+ # Set the Gem home directory (as reported by Gem.dir).
+
+ def self.set_home(home)
+ home = home.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
+ @gem_home = home
+ ensure_gem_subdirectories(@gem_home)
+ end
- # Expand each partial gem path with each of the required paths
- # specified in the Gem spec. Each expanded path is yielded.
- def each_load_path(partials)
- partials.each do |gp|
- base = File.basename(gp)
- specfn = File.join(dir, "specifications", base + ".gemspec")
- if File.exist?(specfn)
- spec = eval(File.read(specfn))
- spec.require_paths.each do |rp|
- yield(File.join(gp, rp))
- end
- else
- filename = File.join(gp, 'lib')
- yield(filename) if File.exist?(filename)
+ private_class_method :set_home
+
+ ##
+ # Set the Gem search path (as reported by Gem.path).
+
+ 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]
end
- # Set the Gem home directory (as reported by +dir+).
- def set_home(home)
- @gem_home = home
- ensure_gem_subdirectories(@gem_home)
- end
+ @gem_path.uniq!
+ @gem_path.each do |gp| ensure_gem_subdirectories(gp) end
+ end
- # Set the Gem search path (as reported by +path+).
- def set_paths(gpaths)
- if gpaths
- @gem_path = gpaths.split(File::PATH_SEPARATOR)
- @gem_path << Gem.dir
- else
- @gem_path = [Gem.dir]
- end
- @gem_path.uniq!
- @gem_path.each do |gp| ensure_gem_subdirectories(gp) end
- end
+ private_class_method :set_paths
- # Some comments from the ruby-talk list regarding finding the home
- # directory:
- #
- # I have HOME, USERPROFILE and HOMEDRIVE + HOMEPATH. Ruby seems
- # to be depending on HOME in those code samples. I propose that
- # it should fallback to USERPROFILE and HOMEDRIVE + HOMEPATH (at
- # least on Win32).
- #
- def find_home
- ['HOME', 'USERPROFILE'].each do |homekey|
- return ENV[homekey] if ENV[homekey]
- end
- if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
- return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
- end
+ ##
+ # Returns the Gem::SourceIndex of specifications that are in the Gem.path
+
+ def self.source_index
+ @@source_index ||= SourceIndex.from_installed_gems
+ end
+
+ ##
+ # Returns an Array of sources to fetch remote gems from. If the sources
+ # list is empty, attempts to load the "sources" gem, then uses
+ # default_sources if it is not installed.
+
+ def self.sources
+ if @sources.empty? then
begin
- File.expand_path("~")
- rescue StandardError => ex
- if File::ALT_SEPARATOR
- "C:/"
- else
- "/"
- end
+ gem 'sources', '> 0.0.1'
+ require 'sources'
+ rescue LoadError
+ @sources = default_sources
end
end
+ @sources
+ end
+
+ ##
+ # Glob pattern for require-able path suffixes.
+
+ def self.suffix_pattern
+ @suffix_pattern ||= "{#{suffixes.join(',')}}"
+ end
+
+ ##
+ # Suffixes for require-able paths.
+
+ def self.suffixes
+ ['', '.rb', '.rbw', '.so', '.bundle', '.dll', '.sl', '.jar']
+ end
+
+ ##
+ # Use the +home+ and +paths+ values for Gem.dir and Gem.path. Used mainly
+ # by the unit tests to provide environment isolation.
+
+ def self.use_paths(home, paths=[])
+ clear_paths
+ set_home(home) if home
+ set_paths(paths.join(File::PATH_SEPARATOR)) if paths
+ end
+
+ ##
+ # The home directory for the user.
+
+ def self.user_home
+ @user_home ||= find_home
+ end
+
+ ##
+ # Is this a windows platform?
+
+ def self.win_platform?
+ if @@win_platform.nil? then
+ @@win_platform = !!WIN_PATTERNS.find { |r| RUBY_PLATFORM =~ r }
+ end
+
+ @@win_platform
+ end
+
+ class << self
+
+ attr_reader :loaded_specs
+
+ # :stopdoc:
+
+ alias cache source_index # an alias for the old name
+
+ # :startdoc:
+
end
end
@@ -558,6 +664,7 @@ end
# Modify the non-gem version of datadir to handle gem package names.
require 'rbconfig/datadir'
+
module Config # :nodoc:
class << self
alias gem_original_datadir datadir
diff --git a/lib/rubygems/builder.rb b/lib/rubygems/builder.rb
index f7f07e86bf..6fd8528f56 100644
--- a/lib/rubygems/builder.rb
+++ b/lib/rubygems/builder.rb
@@ -65,13 +65,20 @@ EOM
end
def write_package
- Package.open(@spec.file_name, "w", @signer) do |pkg|
- pkg.metadata = @spec.to_yaml
- @spec.files.each do |file|
- next if File.directory? file
- pkg.add_file_simple(file, File.stat(@spec.file_name).mode & 0777,
- File.size(file)) do |os|
- os.write File.open(file, "rb"){|f|f.read}
+ open @spec.file_name, 'wb' do |gem_io|
+ Gem::Package.open gem_io, 'w', @signer do |pkg|
+ pkg.metadata = @spec.to_yaml
+
+ @spec.files.each do |file|
+ next if File.directory? file
+
+ stat = File.stat file
+ mode = stat.mode & 0777
+ size = stat.size
+
+ pkg.add_file_simple file, mode, size do |tar_io|
+ tar_io.write open(file, "rb") { |f| f.read }
+ end
end
end
end
diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb
index a80c821c5c..b8aa651f56 100644
--- a/lib/rubygems/command_manager.rb
+++ b/lib/rubygems/command_manager.rb
@@ -123,6 +123,7 @@ module Gem
end
private
+
def load_and_instantiate(command_name)
command_name = command_name.to_s
retried = false
diff --git a/lib/rubygems/commands/cleanup_command.rb b/lib/rubygems/commands/cleanup_command.rb
index f6deac9829..40dcb9db34 100644
--- a/lib/rubygems/commands/cleanup_command.rb
+++ b/lib/rubygems/commands/cleanup_command.rb
@@ -2,92 +2,90 @@ require 'rubygems/command'
require 'rubygems/source_index'
require 'rubygems/dependency_list'
-module Gem
- module Commands
- class CleanupCommand < Command
- def initialize
- super(
- 'cleanup',
+class Gem::Commands::CleanupCommand < Gem::Command
+
+ def initialize
+ super 'cleanup',
'Clean up old versions of installed gems in the local repository',
- {
- :force => false,
- :test => false,
- :install_dir => Gem.dir
- })
- add_option('-d', '--dryrun', "") do |value, options|
- options[:dryrun] = true
- end
- end
+ :force => false, :test => false, :install_dir => Gem.dir
- def arguments # :nodoc:
- "GEMNAME name of gem to cleanup"
- end
+ add_option('-d', '--dryrun', "") do |value, options|
+ options[:dryrun] = true
+ end
+ end
- def defaults_str # :nodoc:
- "--no-dryrun"
- end
+ def arguments # :nodoc:
+ "GEMNAME name of gem to cleanup"
+ end
+
+ def defaults_str # :nodoc:
+ "--no-dryrun"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [GEMNAME ...]"
+ end
+
+ def execute
+ say "Cleaning up installed gems..."
+ primary_gems = {}
- def usage # :nodoc:
- "#{program_name} [GEMNAME ...]"
+ Gem.source_index.each do |name, spec|
+ if primary_gems[spec.name].nil? or
+ primary_gems[spec.name].version < spec.version then
+ primary_gems[spec.name] = spec
end
+ end
- def execute
- say "Cleaning up installed gems..."
- srcindex = Gem::SourceIndex.from_installed_gems
- primary_gems = {}
+ gems_to_cleanup = []
- srcindex.each do |name, spec|
- if primary_gems[spec.name].nil? or primary_gems[spec.name].version < spec.version
- primary_gems[spec.name] = spec
- end
+ unless options[:args].empty? then
+ options[:args].each do |gem_name|
+ specs = Gem.cache.search(/^#{gem_name}$/i)
+ specs.each do |spec|
+ gems_to_cleanup << spec
end
+ end
+ else
+ Gem.source_index.each do |name, spec|
+ gems_to_cleanup << spec
+ end
+ end
- gems_to_cleanup = []
-
- unless options[:args].empty? then
- options[:args].each do |gem_name|
- specs = Gem.cache.search(/^#{gem_name}$/i)
- specs.each do |spec|
- gems_to_cleanup << spec
- end
- end
- else
- srcindex.each do |name, spec|
- gems_to_cleanup << spec
- end
- end
+ gems_to_cleanup = gems_to_cleanup.select { |spec|
+ primary_gems[spec.name].version != spec.version
+ }
- gems_to_cleanup = gems_to_cleanup.select { |spec|
- primary_gems[spec.name].version != spec.version
- }
+ uninstall_command = Gem::CommandManager.instance['uninstall']
+ deplist = Gem::DependencyList.new
+ gems_to_cleanup.uniq.each do |spec| deplist.add spec end
- uninstall_command = Gem::CommandManager.instance['uninstall']
- deplist = DependencyList.new
- gems_to_cleanup.uniq.each do |spec| deplist.add(spec) end
+ deps = deplist.strongly_connected_components.flatten.reverse
- deplist.dependency_order.each do |spec|
- if options[:dryrun] then
- say "Dry Run Mode: Would uninstall #{spec.full_name}"
- else
- say "Attempting uninstall on #{spec.full_name}"
+ deps.each do |spec|
+ if options[:dryrun] then
+ say "Dry Run Mode: Would uninstall #{spec.full_name}"
+ else
+ say "Attempting to uninstall #{spec.full_name}"
- options[:args] = [spec.name]
- options[:version] = "= #{spec.version}"
- options[:executables] = true
+ options[:args] = [spec.name]
+ options[:version] = "= #{spec.version}"
+ options[:executables] = false
- uninstall_command.merge_options(options)
+ uninstaller = Gem::Uninstaller.new spec.name, options
- begin
- uninstall_command.execute
- rescue Gem::DependencyRemovalException => ex
- say "Unable to uninstall #{spec.full_name} ... continuing with remaining gems"
- end
- end
+ begin
+ uninstaller.uninstall
+ rescue Gem::DependencyRemovalException,
+ Gem::GemNotInHomeException => e
+ say "Unable to uninstall #{spec.full_name}:"
+ say "\t#{e.class}: #{e.message}"
end
-
- say "Clean Up Complete"
end
end
-
+
+ say "Clean Up Complete"
end
+
end
+
diff --git a/lib/rubygems/commands/environment_command.rb b/lib/rubygems/commands/environment_command.rb
index ab85361753..56b373cfbe 100644
--- a/lib/rubygems/commands/environment_command.rb
+++ b/lib/rubygems/commands/environment_command.rb
@@ -25,19 +25,18 @@ class Gem::Commands::EnvironmentCommand < Gem::Command
def execute
out = ''
arg = options[:args][0]
- if begins?("packageversion", arg) then
+ case arg
+ when /^packageversion/ then
out << Gem::RubyGemsPackageVersion
- elsif begins?("version", arg) then
+ when /^version/ then
out << Gem::RubyGemsVersion
- elsif begins?("gemdir", arg) then
+ when /^gemdir/, /^gemhome/, /^home/, /^GEM_HOME/ then
out << Gem.dir
- elsif begins?("gempath", arg) then
- out << Gem.path.join("\n")
- elsif begins?("remotesources", arg) then
+ when /^gempath/, /^path/, /^GEM_PATH/ then
+ out << Gem.path.join(File::PATH_SEPARATOR)
+ when /^remotesources/ then
out << Gem.sources.join("\n")
- elsif arg then
- fail Gem::CommandLineError, "Unknown enviroment option [#{arg}]"
- else
+ when nil then
out = "RubyGems Environment:\n"
out << " - RUBYGEMS VERSION: #{Gem::RubyGemsVersion} (#{Gem::RubyGemsPackageVersion})\n"
@@ -75,6 +74,9 @@ class Gem::Commands::EnvironmentCommand < Gem::Command
Gem.sources.each do |s|
out << " - #{s}\n"
end
+
+ else
+ fail Gem::CommandLineError, "Unknown enviroment option [#{arg}]"
end
say out
true
diff --git a/lib/rubygems/commands/fetch_command.rb b/lib/rubygems/commands/fetch_command.rb
index 7db365eba0..ccedc45401 100644
--- a/lib/rubygems/commands/fetch_command.rb
+++ b/lib/rubygems/commands/fetch_command.rb
@@ -44,17 +44,15 @@ class Gem::Commands::FetchCommand < Gem::Command
spec, source_uri = specs_and_sources.last
- gem_file = "#{spec.full_name}.gem"
-
- gem_path = File.join source_uri, 'gems', gem_file
-
- gem = Gem::RemoteFetcher.fetcher.fetch_path gem_path
-
- File.open gem_file, 'wb' do |fp|
- fp.write gem
+ if spec.nil? then
+ alert_error "Could not find #{gem_name} in any repository"
+ next
end
- say "Downloaded #{gem_file}"
+ path = Gem::RemoteFetcher.fetcher.download spec, source_uri
+ FileUtils.mv path, "#{spec.full_name}.gem"
+
+ say "Downloaded #{spec.full_name}"
end
end
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
index aa9f480c2a..ce0bc6ba04 100644
--- a/lib/rubygems/commands/install_command.rb
+++ b/lib/rubygems/commands/install_command.rb
@@ -62,13 +62,15 @@ class Gem::Commands::InstallCommand < Gem::Command
:install_dir => options[:install_dir],
:security_policy => options[:security_policy],
:wrappers => options[:wrappers],
+ :bin_dir => options[:bin_dir]
}
+ exit_code = 0
+
get_all_gem_names.each do |gem_name|
begin
- inst = Gem::DependencyInstaller.new gem_name, options[:version],
- install_options
- inst.install
+ inst = Gem::DependencyInstaller.new install_options
+ inst.install gem_name, options[:version]
inst.installed_gems.each do |spec|
say "Successfully installed #{spec.full_name}"
@@ -77,8 +79,10 @@ class Gem::Commands::InstallCommand < Gem::Command
installed_gems.push(*inst.installed_gems)
rescue Gem::InstallError => e
alert_error "Error installing #{gem_name}:\n\t#{e.message}"
+ exit_code |= 1
rescue Gem::GemNotFoundException => e
alert_error e.message
+ exit_code |= 2
# rescue => e
# # TODO: Fix this handle to allow the error to propagate to
# # the top level handler. Examine the other errors as
@@ -121,6 +125,8 @@ class Gem::Commands::InstallCommand < Gem::Command
end
end
end
+
+ raise Gem::SystemExitException, exit_code
end
end
diff --git a/lib/rubygems/commands/list_command.rb b/lib/rubygems/commands/list_command.rb
index e179ff57ee..f8b377fcde 100644
--- a/lib/rubygems/commands/list_command.rb
+++ b/lib/rubygems/commands/list_command.rb
@@ -6,10 +6,8 @@ module Gem
class ListCommand < QueryCommand
def initialize
- super(
- 'list',
- 'Display all gems whose name starts with STRING'
- )
+ super 'list', 'Display gems whose name starts with STRING'
+
remove_option('--name-matches')
end
diff --git a/lib/rubygems/commands/mirror_command.rb b/lib/rubygems/commands/mirror_command.rb
index fc4f086ad3..959b8eaec3 100644
--- a/lib/rubygems/commands/mirror_command.rb
+++ b/lib/rubygems/commands/mirror_command.rb
@@ -2,7 +2,7 @@ require 'yaml'
require 'zlib'
require 'rubygems/command'
-require 'rubygems/gem_open_uri'
+require 'open-uri'
class Gem::Commands::MirrorCommand < Gem::Command
diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb
index 4f957625ee..fdc5a6a4ea 100644
--- a/lib/rubygems/commands/query_command.rb
+++ b/lib/rubygems/commands/query_command.rb
@@ -1,15 +1,25 @@
require 'rubygems/command'
require 'rubygems/local_remote_options'
require 'rubygems/source_info_cache'
+require 'rubygems/version_option'
class Gem::Commands::QueryCommand < Gem::Command
include Gem::LocalRemoteOptions
+ include Gem::VersionOption
def initialize(name = 'query',
summary = 'Query gem information in local or remote repositories')
super name, summary,
- :name => /.*/, :domain => :local, :details => false, :versions => true
+ :name => //, :domain => :local, :details => false, :versions => true,
+ :installed => false, :version => Gem::Requirement.default
+
+ add_option('-i', '--[no-]installed',
+ 'Check for installed gem') do |value, options|
+ options[:installed] = value
+ end
+
+ add_version_option
add_option('-n', '--name-matches REGEXP',
'Name of gem(s) to query on matches the',
@@ -28,33 +38,70 @@ class Gem::Commands::QueryCommand < Gem::Command
options[:details] = false unless value
end
+ add_option('-a', '--all',
+ 'Display all gem versions') do |value, options|
+ options[:all] = value
+ end
+
add_local_remote_options
end
def defaults_str # :nodoc:
- "--local --name-matches '.*' --no-details --versions"
+ "--local --name-matches // --no-details --versions --no-installed"
end
def execute
+ exit_code = 0
+
name = options[:name]
+ if options[:installed] then
+ if name.source.empty? then
+ alert_error "You must specify a gem name"
+ exit_code |= 4
+ elsif installed? name.source, options[:version] then
+ say "true"
+ else
+ say "false"
+ exit_code |= 1
+ end
+
+ raise Gem::SystemExitException, exit_code
+ end
+
if local? then
say
say "*** LOCAL GEMS ***"
say
- output_query_results Gem.cache.search(name)
+
+ output_query_results Gem.source_index.search(name)
end
if remote? then
say
say "*** REMOTE GEMS ***"
say
- output_query_results Gem::SourceInfoCache.search(name)
+
+ begin
+ Gem::SourceInfoCache.cache.refresh options[:all]
+ rescue Gem::RemoteFetcher::FetchError
+ # no network
+ end
+
+ output_query_results Gem::SourceInfoCache.search(name, false, true)
end
end
private
+ ##
+ # Check if gem +name+ version +version+ is installed.
+
+ def installed?(name, version = Gem::Requirement.default)
+ dep = Gem::Dependency.new name, version
+ !Gem.source_index.search(dep).empty?
+ end
+
def output_query_results(gemspecs)
output = []
gem_list_with_version = {}
@@ -98,7 +145,7 @@ class Gem::Commands::QueryCommand < Gem::Command
##
# Used for wrapping and indenting text
- #
+
def format_text(text, wrap, indent=0)
result = []
work = text.dup
diff --git a/lib/rubygems/commands/sources_command.rb b/lib/rubygems/commands/sources_command.rb
index a0977f90dc..6d9d5b5b90 100644
--- a/lib/rubygems/commands/sources_command.rb
+++ b/lib/rubygems/commands/sources_command.rb
@@ -39,8 +39,11 @@ class Gem::Commands::SourcesCommand < Gem::Command
options[:list] = !(options[:add] || options[:remove] || options[:clear_all] || options[:update])
if options[:clear_all] then
- remove_cache_file("user", Gem::SourceInfoCache.user_cache_file)
- remove_cache_file("system", Gem::SourceInfoCache.system_cache_file)
+ sic = Gem::SourceInfoCache
+ remove_cache_file 'user', sic.user_cache_file
+ remove_cache_file 'latest user', sic.latest_user_cache_file
+ remove_cache_file 'system', sic.system_cache_file
+ remove_cache_file 'latest system', sic.latest_system_cache_file
end
if options[:add] then
@@ -48,7 +51,7 @@ class Gem::Commands::SourcesCommand < Gem::Command
sice = Gem::SourceInfoCacheEntry.new nil, nil
begin
- sice.refresh source_uri
+ sice.refresh source_uri, true
Gem::SourceInfoCache.cache_data[source_uri] = sice
Gem::SourceInfoCache.cache.update
@@ -66,7 +69,7 @@ class Gem::Commands::SourcesCommand < Gem::Command
end
if options[:update] then
- Gem::SourceInfoCache.cache.refresh
+ Gem::SourceInfoCache.cache.refresh true
Gem::SourceInfoCache.cache.flush
say "source cache successfully updated"
@@ -78,6 +81,11 @@ 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
@@ -100,11 +108,12 @@ class Gem::Commands::SourcesCommand < Gem::Command
private
- def remove_cache_file(desc, fn)
- FileUtils.rm_rf fn rescue nil
- if ! File.exist?(fn)
+ def remove_cache_file(desc, path)
+ FileUtils.rm_rf path
+
+ if not File.exist?(path) then
say "*** Removed #{desc} source cache ***"
- elsif ! File.writable?(fn)
+ elsif not File.writable?(path) then
say "*** Unable to remove #{desc} source cache (write protected) ***"
else
say "*** Unable to remove #{desc} source cache ***"
diff --git a/lib/rubygems/commands/specification_command.rb b/lib/rubygems/commands/specification_command.rb
index 1ab2ad9260..7c8598e53b 100644
--- a/lib/rubygems/commands/specification_command.rb
+++ b/lib/rubygems/commands/specification_command.rb
@@ -3,6 +3,7 @@ require 'rubygems/command'
require 'rubygems/local_remote_options'
require 'rubygems/version_option'
require 'rubygems/source_info_cache'
+require 'rubygems/format'
class Gem::Commands::SpecificationCommand < Gem::Command
@@ -41,13 +42,16 @@ class Gem::Commands::SpecificationCommand < Gem::Command
gem = get_one_gem_name
if local? then
- source_index = Gem::SourceIndex.from_installed_gems
- specs.push(*source_index.search(/\A#{gem}\z/, options[:version]))
+ if File.exist? gem then
+ specs << Gem::Format.from_file_by_path(gem).spec rescue nil
+ end
+
+ if specs.empty? then
+ specs.push(*Gem.source_index.search(/\A#{gem}\z/, options[:version]))
+ end
end
if remote? then
- alert_warning "Remote information is not complete\n\n"
-
Gem::SourceInfoCache.cache_data.each do |_,sice|
specs.push(*sice.source_index.search(gem, options[:version]))
end
diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb
index 2d9c46ee52..3d6e2383bc 100644
--- a/lib/rubygems/commands/uninstall_command.rb
+++ b/lib/rubygems/commands/uninstall_command.rb
@@ -35,6 +35,11 @@ module Gem
options[:install_dir] = File.expand_path(value)
end
+ add_option('-n', '--bindir DIR',
+ 'Directory to remove binaries from') do |value, options|
+ options[:bin_dir] = File.expand_path(value)
+ end
+
add_version_option
add_platform_option
end
@@ -54,7 +59,13 @@ module Gem
def execute
get_all_gem_names.each do |gem_name|
- Gem::Uninstaller.new(gem_name, options).uninstall
+ begin
+ Gem::Uninstaller.new(gem_name, options).uninstall
+ rescue Gem::GemNotInHomeException => e
+ spec = e.spec
+ alert("In order to remove #{spec.name}, please execute:\n" \
+ "\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}")
+ end
end
end
end
diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb
index 23ebabc21a..d187f8a9ea 100644
--- a/lib/rubygems/commands/unpack_command.rb
+++ b/lib/rubygems/commands/unpack_command.rb
@@ -38,6 +38,7 @@ class Gem::Commands::UnpackCommand < Gem::Command
def execute
gemname = get_one_gem_name
path = get_path(gemname, options[:version])
+
if path then
basename = File.basename(path).sub(/\.gem$/, '')
target_dir = File.expand_path File.join(options[:target], basename)
@@ -66,16 +67,27 @@ class Gem::Commands::UnpackCommand < Gem::Command
# source directories?
def get_path(gemname, version_req)
return gemname if gemname =~ /\.gem$/i
- specs = Gem::SourceIndex.from_installed_gems.search(/\A#{gemname}\z/, version_req)
+
+ specs = Gem::source_index.search(/\A#{gemname}\z/, version_req)
+
selected = specs.sort_by { |s| s.version }.last
+
return nil if selected.nil?
+
# We expect to find (basename).gem in the 'cache' directory.
# Furthermore, the name match must be exact (ignoring case).
if gemname =~ /^#{selected.name}$/i
filename = selected.full_name + '.gem'
- return File.join(Gem.dir, 'cache', filename)
+ path = nil
+
+ Gem.path.find do |gem_dir|
+ path = File.join gem_dir, 'cache', filename
+ File.exist? path
+ end
+
+ path
else
- return nil
+ nil
end
end
diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb
index 88d48d705e..b8de911e20 100644
--- a/lib/rubygems/commands/update_command.rb
+++ b/lib/rubygems/commands/update_command.rb
@@ -1,8 +1,10 @@
require 'rubygems/command'
+require 'rubygems/command_manager'
require 'rubygems/install_update_options'
require 'rubygems/local_remote_options'
require 'rubygems/source_info_cache'
require 'rubygems/version_option'
+require 'rubygems/commands/install_command'
class Gem::Commands::UpdateCommand < Gem::Command
@@ -45,7 +47,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
def execute
if options[:system] then
- say "Updating RubyGems..."
+ say "Updating RubyGems"
unless options[:args].empty? then
fail "No gem names are allowed with the --system option"
@@ -53,10 +55,10 @@ class Gem::Commands::UpdateCommand < Gem::Command
options[:args] = ["rubygems-update"]
else
- say "Updating installed gems..."
+ say "Updating installed gems"
end
- hig = highest_installed_gems = {}
+ hig = {}
Gem::SourceIndex.from_installed_gems.each do |name, spec|
if hig[spec.name].nil? or hig[spec.name].version < spec.version then
@@ -64,25 +66,28 @@ class Gem::Commands::UpdateCommand < Gem::Command
end
end
- remote_gemspecs = Gem::SourceInfoCache.search(//)
+ pattern = if options[:args].empty? then
+ //
+ else
+ Regexp.union(*options[:args])
+ end
- gems_to_update = if options[:args].empty? then
- which_to_update(highest_installed_gems, remote_gemspecs)
- else
- options[:args]
- end
+ remote_gemspecs = Gem::SourceInfoCache.search pattern
- options[:domain] = :remote # install from remote source
+ gems_to_update = which_to_update hig, remote_gemspecs
- # HACK use the real API
- install_command = Gem::CommandManager.instance['install']
+ updated = []
+ # HACK use the real API
gems_to_update.uniq.sort.each do |name|
- say "Attempting remote update of #{name}"
- options[:args] = [name]
- options[:ignore_dependencies] = true # HACK skip seen gems instead
- install_command.merge_options(options)
- install_command.execute
+ next if updated.any? { |spec| spec.name == name }
+ say "Updating #{name}"
+ installer = Gem::DependencyInstaller.new options
+ installer.install name
+ installer.installed_gems.each do |spec|
+ updated << spec
+ say "Successfully installed #{spec.full_name}"
+ end
end
if gems_to_update.include? "rubygems-update" then
@@ -97,12 +102,10 @@ class Gem::Commands::UpdateCommand < Gem::Command
say "RubyGems system software updated" if installed
else
- updated = gems_to_update.uniq.sort.collect { |g| g.to_s }
-
if updated.empty? then
say "Nothing to update"
else
- say "Gems updated: #{updated.join ', '}"
+ say "Gems updated: #{updated.map { |spec| spec.name }.join ', '}"
end
end
end
diff --git a/lib/rubygems/custom_require.rb b/lib/rubygems/custom_require.rb
index 598ec3ef98..5ff65afb14 100755
--- a/lib/rubygems/custom_require.rb
+++ b/lib/rubygems/custom_require.rb
@@ -28,7 +28,7 @@ module Kernel
rescue LoadError => load_error
if load_error.message =~ /\A[Nn]o such file to load -- #{Regexp.escape path}\z/ and
spec = Gem.searcher.find(path) then
- Gem.activate(spec.name, false, "= #{spec.version}")
+ Gem.activate(spec.name, "= #{spec.version}")
gem_original_require path
else
raise load_error
diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb
index 3a6229511b..3864e5faca 100644
--- a/lib/rubygems/defaults.rb
+++ b/lib/rubygems/defaults.rb
@@ -11,6 +11,9 @@ module Gem
if defined? RUBY_FRAMEWORK_VERSION then
File.join File.dirname(ConfigMap[:sitedir]), 'Gems',
ConfigMap[:ruby_version]
+ elsif defined? RUBY_ENGINE then
+ File.join ConfigMap[:libdir], RUBY_ENGINE, 'gems',
+ ConfigMap[:ruby_version]
else
File.join ConfigMap[:libdir], 'ruby', 'gems', ConfigMap[:ruby_version]
end
@@ -29,7 +32,11 @@ module Gem
# The default directory for binaries
def self.default_bindir
- Config::CONFIG['bindir']
+ if defined? RUBY_FRAMEWORK_VERSION then # mac framework support
+ '/usr/bin'
+ else # generic install
+ ConfigMap[:bindir]
+ end
end
# The default system-wide source info cache directory.
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index ec8a50d912..26ef41b2f1 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -22,8 +22,7 @@ class Gem::DependencyInstaller
}
##
- # Creates a new installer instance that will install +gem_name+ using
- # version requirement +version+ and +options+.
+ # Creates a new installer instance.
#
# Options are:
# :env_shebang:: See Gem::Installer::new.
@@ -36,7 +35,7 @@ class Gem::DependencyInstaller
# :install_dir: See Gem::Installer#install.
# :security_policy: See Gem::Installer::new and Gem::Security.
# :wrappers: See Gem::Installer::new
- def initialize(gem_name, version = nil, options = {})
+ def initialize(options = {})
options = DEFAULT_OPTIONS.merge options
@env_shebang = options[:env_shebang]
@domain = options[:domain]
@@ -46,49 +45,9 @@ class Gem::DependencyInstaller
@install_dir = options[:install_dir] || Gem.dir
@security_policy = options[:security_policy]
@wrappers = options[:wrappers]
+ @bin_dir = options[:bin_dir]
@installed_gems = []
-
- spec_and_source = nil
-
- glob = if File::ALT_SEPARATOR then
- gem_name.gsub File::ALT_SEPARATOR, File::SEPARATOR
- else
- gem_name
- end
-
- local_gems = Dir["#{glob}*"].sort.reverse
-
- unless local_gems.empty? then
- local_gems.each do |gem_file|
- next unless gem_file =~ /gem$/
- begin
- spec = Gem::Format.from_file_by_path(gem_file).spec
- spec_and_source = [spec, gem_file]
- break
- rescue SystemCallError, Gem::Package::FormatError
- end
- end
- end
-
- if spec_and_source.nil? then
- version ||= Gem::Requirement.default
- @dep = Gem::Dependency.new gem_name, version
- spec_and_sources = find_gems_with_sources(@dep).reverse
-
- spec_and_source = spec_and_sources.find do |spec, source|
- Gem::Platform.match spec.platform
- end
- end
-
- if spec_and_source.nil? then
- raise Gem::GemNotFoundException,
- "could not find #{gem_name} locally or in a repository"
- end
-
- @specs_and_sources = [spec_and_source]
-
- gather_dependencies
end
##
@@ -107,71 +66,30 @@ class Gem::DependencyInstaller
end
if @domain == :both or @domain == :remote then
- gems_and_sources.push(*Gem::SourceInfoCache.search_with_source(dep, true))
- end
-
- gems_and_sources.sort_by do |gem, source|
- [gem, source !~ /^http:\/\// ? 1 : 0] # local gems win
- end
- end
-
- ##
- # 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)
- gem_file_name = "#{spec.full_name}.gem"
- local_gem_path = File.join @install_dir, 'cache', gem_file_name
-
- Gem.ensure_gem_subdirectories @install_dir
-
- source_uri = URI.parse source_uri unless URI::Generic === source_uri
- scheme = source_uri.scheme
-
- # URI.parse gets confused by MS Windows paths with forward slashes.
- scheme = nil if scheme =~ /^[a-z]$/i
-
- case scheme
- when 'http' then
- unless File.exist? local_gem_path then
- begin
- say "Downloading gem #{gem_file_name}" if
- Gem.configuration.really_verbose
-
- remote_gem_path = source_uri + "gems/#{gem_file_name}"
-
- gem = Gem::RemoteFetcher.fetcher.fetch_path remote_gem_path
- rescue Gem::RemoteFetcher::FetchError
- raise if spec.original_platform == spec.platform
-
- alternate_name = "#{spec.name}-#{spec.version}-#{spec.original_platform}.gem"
+ begin
+ requirements = dep.version_requirements.requirements.map do |req, ver|
+ req
+ end
- say "Failed, downloading gem #{alternate_name}" if
- Gem.configuration.really_verbose
+ all = requirements.length > 1 ||
+ requirements.first != ">=" || requirements.first != ">"
- remote_gem_path = source_uri + "gems/#{alternate_name}"
+ found = Gem::SourceInfoCache.search_with_source dep, true, all
- gem = Gem::RemoteFetcher.fetcher.fetch_path remote_gem_path
- end
+ gems_and_sources.push(*found)
- File.open local_gem_path, 'wb' do |fp|
- fp.write gem
+ rescue Gem::RemoteFetcher::FetchError => e
+ if Gem.configuration.really_verbose then
+ say "Error fetching remote data:\t\t#{e.message}"
+ say "Falling back to local-only install"
end
+ @domain = :local
end
- when nil, 'file' then # TODO test for local overriding cache
- begin
- FileUtils.cp source_uri.to_s, local_gem_path
- rescue Errno::EACCES
- local_gem_path = source_uri.to_s
- end
-
- say "Using local gem #{local_gem_path}" if
- Gem.configuration.really_verbose
- else
- raise Gem::InstallError, "unsupported URI scheme #{source_uri.scheme}"
end
- local_gem_path
+ gems_and_sources.sort_by do |gem, source|
+ [gem, source =~ /^http:\/\// ? 0 : 1] # local gems win
+ end
end
##
@@ -208,9 +126,57 @@ class Gem::DependencyInstaller
@gems_to_install = dependency_list.dependency_order.reverse
end
+ def find_spec_by_name_and_version gem_name, version = Gem::Requirement.default
+ spec_and_source = nil
+
+ glob = if File::ALT_SEPARATOR then
+ gem_name.gsub File::ALT_SEPARATOR, File::SEPARATOR
+ else
+ gem_name
+ end
+
+ local_gems = Dir["#{glob}*"].sort.reverse
+
+ unless local_gems.empty? then
+ local_gems.each do |gem_file|
+ next unless gem_file =~ /gem$/
+ begin
+ spec = Gem::Format.from_file_by_path(gem_file).spec
+ spec_and_source = [spec, gem_file]
+ break
+ rescue SystemCallError, Gem::Package::FormatError
+ end
+ end
+ end
+
+ if spec_and_source.nil? then
+ dep = Gem::Dependency.new gem_name, version
+ spec_and_sources = find_gems_with_sources(dep).reverse
+
+ spec_and_source = spec_and_sources.find { |spec, source|
+ Gem::Platform.match spec.platform
+ }
+ end
+
+ if spec_and_source.nil? then
+ raise Gem::GemNotFoundException,
+ "could not find #{gem_name} locally or in a repository"
+ end
+
+ @specs_and_sources = [spec_and_source]
+ end
+
##
# Installs the gem and all its dependencies.
- def install
+ 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
+ else
+ @specs_and_sources = [find_gems_with_sources(dep_or_name).last]
+ end
+
+ gather_dependencies
+
spec_dir = File.join @install_dir, 'specifications'
source_index = Gem::SourceIndex.from_gems_in spec_dir
@@ -219,10 +185,11 @@ class Gem::DependencyInstaller
# HACK is this test for full_name acceptable?
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
_, source_uri = @specs_and_sources.assoc spec
- local_gem_path = download spec, source_uri
+ local_gem_path = Gem::RemoteFetcher.fetcher.download spec, source_uri
inst = Gem::Installer.new local_gem_path,
:env_shebang => @env_shebang,
@@ -231,7 +198,8 @@ class Gem::DependencyInstaller
:ignore_dependencies => @ignore_dependencies,
:install_dir => @install_dir,
:security_policy => @security_policy,
- :wrappers => @wrappers
+ :wrappers => @wrappers,
+ :bin_dir => @bin_dir
spec = inst.install
diff --git a/lib/rubygems/exceptions.rb b/lib/rubygems/exceptions.rb
index b34bc718ff..c37507c62a 100644
--- a/lib/rubygems/exceptions.rb
+++ b/lib/rubygems/exceptions.rb
@@ -13,7 +13,10 @@ class Gem::DependencyRemovalException < Gem::Exception; end
##
# Raised when attempting to uninstall a gem that isn't in GEM_HOME.
-class Gem::GemNotInHomeException < Gem::Exception; end
+
+class Gem::GemNotInHomeException < Gem::Exception
+ attr_accessor :spec
+end
class Gem::DocumentError < Gem::Exception; end
@@ -65,3 +68,17 @@ class Gem::RemoteSourceException < Gem::Exception; end
class Gem::VerificationError < Gem::Exception; end
+##
+# Raised to indicate that a system exit should occur with the specified
+# exit_code
+
+class Gem::SystemExitException < SystemExit
+ attr_accessor :exit_code
+
+ def initialize(exit_code)
+ @exit_code = exit_code
+
+ super "Exiting RubyGems with exit_code #{exit_code}"
+ end
+
+end
diff --git a/lib/rubygems/format.rb b/lib/rubygems/format.rb
index 378a93018c..7dc127d5f4 100644
--- a/lib/rubygems/format.rb
+++ b/lib/rubygems/format.rb
@@ -43,15 +43,12 @@ module Gem
# check for old version gem
if File.read(file_path, 20).include?("MD5SUM =")
- #alert_warning "Gem #{file_path} is in old format."
require 'rubygems/old_format'
+
format = OldFormat.from_file_by_path(file_path)
else
- begin
- f = File.open(file_path, 'rb')
- format = from_io(f, file_path, security_policy)
- ensure
- f.close unless f.closed?
+ open file_path, Gem.binary_mode do |io|
+ format = from_io io, file_path, security_policy
end
end
@@ -65,15 +62,24 @@ module Gem
# io:: [IO] Stream from which to read the gem
#
def self.from_io(io, gem_path="(io)", security_policy = nil)
- format = self.new(gem_path)
- Package.open_from_io(io, 'r', security_policy) do |pkg|
+ format = new gem_path
+
+ Package.open io, 'r', security_policy do |pkg|
format.spec = pkg.metadata
format.file_entries = []
+
pkg.each do |entry|
- format.file_entries << [{"size" => entry.size, "mode" => entry.mode,
- "path" => entry.full_name}, entry.read]
+ size = entry.header.size
+ mode = entry.header.mode
+
+ format.file_entries << [{
+ "size" => size, "mode" => mode, "path" => entry.full_name,
+ },
+ entry.read
+ ]
end
end
+
format
end
diff --git a/lib/rubygems/indexer.rb b/lib/rubygems/indexer.rb
index 272cee3fd3..5496e452cc 100644
--- a/lib/rubygems/indexer.rb
+++ b/lib/rubygems/indexer.rb
@@ -11,6 +11,7 @@ end
##
# Top level class for building the gem repository index.
+
class Gem::Indexer
include Gem::UserInteraction
@@ -25,7 +26,9 @@ class Gem::Indexer
attr_reader :directory
+ ##
# Create an indexer that will index the gems in +directory+.
+
def initialize(directory)
unless ''.respond_to? :to_xs then
fail "Gem::Indexer requires that the XML Builder library be installed:" \
@@ -39,52 +42,60 @@ class Gem::Indexer
@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
+ @quick_index = Gem::Indexer::QuickIndexBuilder.new 'index', @directory
+
+ quick_dir = File.join @directory, 'quick'
+ @latest_index = Gem::Indexer::LatestIndexBuilder.new 'latest_index', quick_dir
end
+ ##
# Build the index.
+
def build_index
@master_index.build do
@quick_index.build do
@marshal_index.build do
- progress = ui.progress_reporter gem_file_list.size,
+ @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})"
+ gem_file_list.each do |gemfile|
+ if File.size(gemfile.to_s) == 0 then
+ alert_warning "Skipping zero-length gem: #{gemfile}"
next
end
- abbreviate spec
- sanitize spec
+ begin
+ spec = Gem::Format.from_file_by_path(gemfile).spec
- @master_index.add spec
- @quick_index.add spec
- @marshal_index.add 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
- progress.updated spec.original_name
+ abbreviate spec
+ sanitize spec
- rescue SignalException => e
- alert_error "Recieved 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
+ @master_index.add spec
+ @quick_index.add spec
+ @marshal_index.add spec
+ @latest_index.add spec
+
+ progress.updated spec.original_name
- progress.done
+ rescue SignalException => e
+ alert_error "Recieved 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)"
+ say "Generating master indexes (this may take a while)"
+ end
end
end
end
@@ -95,14 +106,15 @@ class Gem::Indexer
say "Moving index into production dir #{@dest_directory}" if verbose
- files = @master_index.files + @quick_index.files + @marshal_index.files
+ files = @master_index.files + @quick_index.files + @marshal_index.files +
+ @latest_index.files
files.each do |file|
- relative_name = file[/\A#{Regexp.escape @directory}.(.*)/, 1]
- dest_name = File.join @dest_directory, relative_name
+ src_name = File.join @directory, file
+ dst_name = File.join @dest_directory, file
- FileUtils.rm_rf dest_name, :verbose => verbose
- FileUtils.mv file, @dest_directory, :verbose => verbose
+ FileUtils.rm_rf dst_name, :verbose => verbose
+ FileUtils.mv src_name, @dest_directory, :verbose => verbose
end
end
@@ -160,4 +172,5 @@ 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/indexer/abstract_index_builder.rb b/lib/rubygems/indexer/abstract_index_builder.rb
index f25f21707b..5815dcda87 100644
--- a/lib/rubygems/indexer/abstract_index_builder.rb
+++ b/lib/rubygems/indexer/abstract_index_builder.rb
@@ -22,16 +22,18 @@ class Gem::Indexer::AbstractIndexBuilder
@files = []
end
+ ##
# Build a Gem index. Yields to block to handle the details of the
# actual building. Calls +begin_index+, +end_index+ and +cleanup+ at
# appropriate times to customize basic operations.
+
def build
FileUtils.mkdir_p @directory unless File.exist? @directory
raise "not a directory: #{@directory}" unless File.directory? @directory
file_path = File.join @directory, @filename
- @files << file_path
+ @files << @filename
File.open file_path, "wb" do |file|
@file = file
@@ -39,14 +41,20 @@ class Gem::Indexer::AbstractIndexBuilder
yield
end_index
end
+
cleanup
ensure
@file = nil
end
+ ##
# Compress the given file.
+
def compress(filename, ext="rz")
- zipped = zip(File.open(filename, 'rb'){ |fp| fp.read })
+ data = open filename, 'rb' do |fp| fp.read end
+
+ zipped = zip data
+
File.open "#{filename}.#{ext}", "wb" do |file|
file.write zipped
end
diff --git a/lib/rubygems/indexer/latest_index_builder.rb b/lib/rubygems/indexer/latest_index_builder.rb
new file mode 100644
index 0000000000..a5798580a6
--- /dev/null
+++ b/lib/rubygems/indexer/latest_index_builder.rb
@@ -0,0 +1,35 @@
+require 'rubygems/indexer'
+
+##
+# Construct the latest Gem index file.
+
+class Gem::Indexer::LatestIndexBuilder < Gem::Indexer::AbstractIndexBuilder
+
+ def start_index
+ super
+
+ @index = Gem::SourceIndex.new
+ end
+
+ def end_index
+ super
+
+ latest = @index.latest_specs.sort.map { |spec| spec.original_name }
+
+ @file.write latest.join("\n")
+ end
+
+ def cleanup
+ super
+
+ compress @file.path
+
+ @files.delete 'latest_index' # HACK installed via QuickIndexBuilder :/
+ end
+
+ def add(spec)
+ @index.add_spec(spec)
+ end
+
+end
+
diff --git a/lib/rubygems/indexer/master_index_builder.rb b/lib/rubygems/indexer/master_index_builder.rb
index dbe02370a9..669ea5a1df 100644
--- a/lib/rubygems/indexer/master_index_builder.rb
+++ b/lib/rubygems/indexer/master_index_builder.rb
@@ -1,6 +1,8 @@
require 'rubygems/indexer'
+##
# Construct the master Gem index file.
+
class Gem::Indexer::MasterIndexBuilder < Gem::Indexer::AbstractIndexBuilder
def start_index
@@ -10,6 +12,7 @@ class Gem::Indexer::MasterIndexBuilder < Gem::Indexer::AbstractIndexBuilder
def end_index
super
+
@file.puts "--- !ruby/object:#{@index.class}"
@file.puts "gems:"
@@ -28,11 +31,9 @@ class Gem::Indexer::MasterIndexBuilder < Gem::Indexer::AbstractIndexBuilder
index_file_name = File.join @directory, @filename
compress index_file_name, "Z"
- compressed_file_name = "#{index_file_name}.Z"
-
- paranoid index_file_name, compressed_file_name
+ paranoid index_file_name, "#{index_file_name}.Z"
- @files << compressed_file_name
+ @files << "#{@filename}.Z"
end
def add(spec)
@@ -41,12 +42,12 @@ class Gem::Indexer::MasterIndexBuilder < Gem::Indexer::AbstractIndexBuilder
private
- def paranoid(fn, compressed_fn)
- data = File.open(fn, 'rb') do |fp| fp.read end
- compressed_data = File.open(compressed_fn, 'rb') do |fp| fp.read end
+ def paranoid(path, compressed_path)
+ data = Gem.read_binary path
+ compressed_data = Gem.read_binary compressed_path
if data != unzip(compressed_data) then
- fail "Compressed file #{compressed_fn} does not match uncompressed file #{fn}"
+ raise "Compressed file #{compressed_path} does not match uncompressed file #{path}"
end
end
diff --git a/lib/rubygems/indexer/quick_index_builder.rb b/lib/rubygems/indexer/quick_index_builder.rb
index 23c7ca696b..dc36179dc5 100644
--- a/lib/rubygems/indexer/quick_index_builder.rb
+++ b/lib/rubygems/indexer/quick_index_builder.rb
@@ -1,7 +1,9 @@
require 'rubygems/indexer'
+##
# Construct a quick index file and all of the individual specs to support
# incremental loading.
+
class Gem::Indexer::QuickIndexBuilder < Gem::Indexer::AbstractIndexBuilder
def initialize(filename, directory)
@@ -13,12 +15,12 @@ class Gem::Indexer::QuickIndexBuilder < Gem::Indexer::AbstractIndexBuilder
def cleanup
super
- quick_index_file = File.join(@directory, @filename)
+ quick_index_file = File.join @directory, @filename
compress quick_index_file
# the complete quick index is in a directory, so move it as a whole
- @files.delete quick_index_file
- @files << @directory
+ @files.delete 'index'
+ @files << 'quick'
end
def add(spec)
diff --git a/lib/rubygems/install_update_options.rb b/lib/rubygems/install_update_options.rb
index af6be423f6..58807be62a 100644
--- a/lib/rubygems/install_update_options.rb
+++ b/lib/rubygems/install_update_options.rb
@@ -25,6 +25,12 @@ module Gem::InstallUpdateOptions
options[:install_dir] = File.expand_path(value)
end
+ add_option(:"Install/Update", '-n', '--bindir DIR',
+ 'Directory where binary files are',
+ 'located') do |value, options|
+ options[:bin_dir] = File.expand_path(value)
+ end
+
add_option(:"Install/Update", '-d', '--[no-]rdoc',
'Generate RDoc documentation for the gem on',
'install') do |value, options|
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index 552a803c12..9dbbca8d08 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -63,7 +63,8 @@ class Gem::Installer
:force => false,
:install_dir => Gem.dir,
:exec_format => false,
- :env_shebang => false
+ :env_shebang => false,
+ :bin_dir => nil
}.merge options
@env_shebang = options[:env_shebang]
@@ -74,6 +75,7 @@ class Gem::Installer
@format_executable = options[:format_executable]
@security_policy = options[:security_policy]
@wrappers = options[:wrappers]
+ @bin_dir = options[:bin_dir]
begin
@format = Gem::Format.from_file_by_path @gem, @security_policy
@@ -104,7 +106,7 @@ class Gem::Installer
unless @force then
if rrv = @spec.required_ruby_version then
- unless rrv.satisfied_by? Gem::Version.new(RUBY_VERSION) then
+ unless rrv.satisfied_by? Gem.ruby_version then
raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}"
end
end
@@ -225,7 +227,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 = 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
@@ -303,7 +305,7 @@ class Gem::Installer
# necessary.
def shebang(bin_file_name)
if @env_shebang then
- "#!/usr/bin/env ruby"
+ "#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name]
else
path = File.join @gem_dir, @spec.bindir, bin_file_name
@@ -352,10 +354,10 @@ TEXT
<<-TEXT
@ECHO OFF
IF NOT "%~f0" == "~f0" GOTO :WinNT
-@"#{Gem.ruby}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
+@"#{File.basename(Gem.ruby)}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
GOTO :EOF
:WinNT
-"%~dp0ruby.exe" "%~dpn0" %*
+@"#{File.basename(Gem.ruby)}" "%~dpn0" %*
TEXT
end
diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb
index f15e4feecb..9cb393b0c7 100644
--- a/lib/rubygems/package.rb
+++ b/lib/rubygems/package.rb
@@ -45,768 +45,15 @@ module Gem::Package
class TooLongFileName < Error; end
class FormatError < Error; end
- module FSyncDir
- private
- def fsync_dir(dirname)
- # make sure this hits the disc
- begin
- dir = open(dirname, "r")
- dir.fsync
- rescue # ignore IOError if it's an unpatched (old) Ruby
- ensure
- dir.close if dir rescue nil
- end
- end
- end
-
- class TarHeader
- FIELDS = [:name, :mode, :uid, :gid, :size, :mtime, :checksum, :typeflag,
- :linkname, :magic, :version, :uname, :gname, :devmajor,
- :devminor, :prefix]
- FIELDS.each {|x| attr_reader x}
-
- def self.new_from_stream(stream)
- data = stream.read(512)
- fields = data.unpack("A100" + # record name
- "A8A8A8" + # mode, uid, gid
- "A12A12" + # size, mtime
- "A8A" + # checksum, typeflag
- "A100" + # linkname
- "A6A2" + # magic, version
- "A32" + # uname
- "A32" + # gname
- "A8A8" + # devmajor, devminor
- "A155") # prefix
- name = fields.shift
- mode = fields.shift.oct
- uid = fields.shift.oct
- gid = fields.shift.oct
- size = fields.shift.oct
- mtime = fields.shift.oct
- checksum = fields.shift.oct
- typeflag = fields.shift
- linkname = fields.shift
- magic = fields.shift
- version = fields.shift.oct
- uname = fields.shift
- gname = fields.shift
- devmajor = fields.shift.oct
- devminor = fields.shift.oct
- prefix = fields.shift
-
- empty = (data == "\0" * 512)
-
- new(:name=>name, :mode=>mode, :uid=>uid, :gid=>gid, :size=>size,
- :mtime=>mtime, :checksum=>checksum, :typeflag=>typeflag,
- :magic=>magic, :version=>version, :uname=>uname, :gname=>gname,
- :devmajor=>devmajor, :devminor=>devminor, :prefix=>prefix,
- :empty => empty )
- end
-
- def initialize(vals)
- unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode]
- raise ArgumentError, ":name, :size, :prefix and :mode required"
- end
- vals[:uid] ||= 0
- vals[:gid] ||= 0
- vals[:mtime] ||= 0
- vals[:checksum] ||= ""
- vals[:typeflag] ||= "0"
- vals[:magic] ||= "ustar"
- vals[:version] ||= "00"
- vals[:uname] ||= "wheel"
- vals[:gname] ||= "wheel"
- vals[:devmajor] ||= 0
- vals[:devminor] ||= 0
- FIELDS.each {|x| instance_variable_set "@#{x.to_s}", vals[x]}
- @empty = vals[:empty]
- end
-
- def empty?
- @empty
- end
-
- def to_s
- update_checksum
- header(checksum)
- end
-
- def update_checksum
- h = header(" " * 8)
- @checksum = oct(calculate_checksum(h), 6)
- end
-
- private
- def oct(num, len)
- "%0#{len}o" % num
- end
-
- def calculate_checksum(hdr)
- #hdr.split('').map { |c| c[0] }.inject { |a, b| a + b } # HACK rubinius
- hdr.unpack("C*").inject{|a,b| a+b}
- end
-
- def header(chksum)
- # struct tarfile_entry_posix {
- # char name[100]; # ASCII + (Z unless filled)
- # char mode[8]; # 0 padded, octal, null
- # char uid[8]; # ditto
- # char gid[8]; # ditto
- # char size[12]; # 0 padded, octal, null
- # char mtime[12]; # 0 padded, octal, null
- # char checksum[8]; # 0 padded, octal, null, space
- # char typeflag[1]; # file: "0" dir: "5"
- # char linkname[100]; # ASCII + (Z unless filled)
- # char magic[6]; # "ustar\0"
- # char version[2]; # "00"
- # char uname[32]; # ASCIIZ
- # char gname[32]; # ASCIIZ
- # char devmajor[8]; # 0 padded, octal, null
- # char devminor[8]; # o padded, octal, null
- # char prefix[155]; # ASCII + (Z unless filled)
- # };
- arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11),
- oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version,
- uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix]
- str = arr.pack("a100a8a8a8a12a12" + # name, mode, uid, gid, size, mtime
- "a7aaa100a6a2" + # chksum, typeflag, linkname, magic, version
- "a32a32a8a8a155") # uname, gname, devmajor, devminor, prefix
- str + "\0" * ((512 - str.size) % 512)
- end
- end
-
- class TarWriter
- class FileOverflow < StandardError; end
- class BlockNeeded < StandardError; end
-
- class BoundedStream
- attr_reader :limit, :written
- def initialize(io, limit)
- @io = io
- @limit = limit
- @written = 0
- end
-
- def write(data)
- if data.size + @written > @limit
- raise FileOverflow,
- "You tried to feed more data than fits in the file."
- end
- @io.write data
- @written += data.size
- data.size
- end
- end
-
- class RestrictedStream
- def initialize(anIO)
- @io = anIO
- end
-
- def write(data)
- @io.write data
- end
- end
-
- def self.new(anIO)
- writer = super(anIO)
- return writer unless block_given?
- begin
- yield writer
- ensure
- writer.close
- end
- nil
- end
-
- def initialize(anIO)
- @io = anIO
- @closed = false
- end
-
- def add_file_simple(name, mode, size)
- raise BlockNeeded unless block_given?
- raise ClosedIO if @closed
- name, prefix = split_name(name)
- header = TarHeader.new(:name => name, :mode => mode,
- :size => size, :prefix => prefix).to_s
- @io.write header
- os = BoundedStream.new(@io, size)
- yield os
- #FIXME: what if an exception is raised in the block?
- min_padding = size - os.written
- @io.write("\0" * min_padding)
- remainder = (512 - (size % 512)) % 512
- @io.write("\0" * remainder)
- end
-
- def add_file(name, mode)
- raise BlockNeeded unless block_given?
- raise ClosedIO if @closed
- raise NonSeekableIO unless @io.respond_to? :pos=
- name, prefix = split_name(name)
- init_pos = @io.pos
- @io.write "\0" * 512 # placeholder for the header
- yield RestrictedStream.new(@io)
- #FIXME: what if an exception is raised in the block?
- #FIXME: what if an exception is raised in the block?
- size = @io.pos - init_pos - 512
- remainder = (512 - (size % 512)) % 512
- @io.write("\0" * remainder)
- final_pos = @io.pos
- @io.pos = init_pos
- header = TarHeader.new(:name => name, :mode => mode,
- :size => size, :prefix => prefix).to_s
- @io.write header
- @io.pos = final_pos
- end
-
- def mkdir(name, mode)
- raise ClosedIO if @closed
- name, prefix = split_name(name)
- header = TarHeader.new(:name => name, :mode => mode, :typeflag => "5",
- :size => 0, :prefix => prefix).to_s
- @io.write header
- nil
- end
-
- def flush
- raise ClosedIO if @closed
- @io.flush if @io.respond_to? :flush
- end
-
- def close
- #raise ClosedIO if @closed
- return if @closed
- @io.write "\0" * 1024
- @closed = true
- end
-
- private
- def split_name name
- raise TooLongFileName if name.size > 256
- if name.size <= 100
- prefix = ""
- else
- parts = name.split(/\//)
- newname = parts.pop
- nxt = ""
- loop do
- nxt = parts.pop
- break if newname.size + 1 + nxt.size > 100
- newname = nxt + "/" + newname
- end
- prefix = (parts + [nxt]).join "/"
- name = newname
- raise TooLongFileName if name.size > 100 || prefix.size > 155
- end
- return name, prefix
- end
- end
-
- class TarReader
-
- include Gem::Package
-
- class UnexpectedEOF < StandardError; end
-
- module InvalidEntry
- def read(len=nil); raise ClosedIO; end
- def getc; raise ClosedIO; end
- def rewind; raise ClosedIO; end
- end
-
- class Entry
- TarHeader::FIELDS.each{|x| attr_reader x}
-
- def initialize(header, anIO)
- @io = anIO
- @name = header.name
- @mode = header.mode
- @uid = header.uid
- @gid = header.gid
- @size = header.size
- @mtime = header.mtime
- @checksum = header.checksum
- @typeflag = header.typeflag
- @linkname = header.linkname
- @magic = header.magic
- @version = header.version
- @uname = header.uname
- @gname = header.gname
- @devmajor = header.devmajor
- @devminor = header.devminor
- @prefix = header.prefix
- @read = 0
- @orig_pos = @io.pos
- end
-
- def read(len = nil)
- return nil if @read >= @size
- len ||= @size - @read
- max_read = [len, @size - @read].min
- ret = @io.read(max_read)
- @read += ret.size
- ret
- end
-
- def getc
- return nil if @read >= @size
- ret = @io.getc
- @read += 1 if ret
- ret
- end
-
- def is_directory?
- @typeflag == "5"
- end
-
- def is_file?
- @typeflag == "0"
- end
-
- def eof?
- @read >= @size
- end
-
- def pos
- @read
- end
-
- def rewind
- raise NonSeekableIO unless @io.respond_to? :pos=
- @io.pos = @orig_pos
- @read = 0
- end
-
- alias_method :is_directory, :is_directory?
- alias_method :is_file, :is_file?
-
- def bytes_read
- @read
- end
-
- def full_name
- if @prefix != ""
- File.join(@prefix, @name)
- else
- @name
- end
- end
-
- def close
- invalidate
- end
-
- private
- def invalidate
- extend InvalidEntry
- end
- end
-
- def self.new(anIO)
- reader = super(anIO)
- return reader unless block_given?
- begin
- yield reader
- ensure
- reader.close
- end
- nil
- end
-
- def initialize(anIO)
- @io = anIO
- @init_pos = anIO.pos
- end
-
- def each(&block)
- each_entry(&block)
- end
-
- # do not call this during a #each or #each_entry iteration
- def rewind
- if @init_pos == 0
- raise NonSeekableIO unless @io.respond_to? :rewind
- @io.rewind
- else
- raise NonSeekableIO unless @io.respond_to? :pos=
- @io.pos = @init_pos
- end
- end
-
- def each_entry
- loop do
- return if @io.eof?
- header = TarHeader.new_from_stream(@io)
- return if header.empty?
- entry = Entry.new header, @io
- size = entry.size
- yield entry
- skip = (512 - (size % 512)) % 512
- if @io.respond_to? :seek
- # avoid reading...
- @io.seek(size - entry.bytes_read, IO::SEEK_CUR)
- else
- pending = size - entry.bytes_read
- while pending > 0
- bread = @io.read([pending, 4096].min).size
- raise UnexpectedEOF if @io.eof?
- pending -= bread
- end
- end
- @io.read(skip) # discard trailing zeros
- # make sure nobody can use #read, #getc or #rewind anymore
- entry.close
- end
- end
-
- def close
- end
-
- end
-
- class TarInput
-
- include FSyncDir
- include Enumerable
-
- attr_reader :metadata
-
- class << self; private :new end
-
- def initialize(io, security_policy = nil)
- @io = io
- @tarreader = TarReader.new(@io)
- has_meta = false
- data_sig, meta_sig, data_dgst, meta_dgst = nil, nil, nil, nil
- dgst_algo = security_policy ? Gem::Security::OPT[:dgst_algo] : nil
-
- @tarreader.each do |entry|
- case entry.full_name
- when "metadata"
- @metadata = load_gemspec(entry.read)
- has_meta = true
- break
- when "metadata.gz"
- begin
- # if we have a security_policy, then pre-read the metadata file
- # and calculate it's digest
- sio = nil
- if security_policy
- Gem.ensure_ssl_available
- sio = StringIO.new(entry.read)
- meta_dgst = dgst_algo.digest(sio.string)
- sio.rewind
- end
-
- gzis = Zlib::GzipReader.new(sio || entry)
- # YAML wants an instance of IO
- @metadata = load_gemspec(gzis)
- has_meta = true
- ensure
- gzis.close unless gzis.nil?
- end
- when 'metadata.gz.sig'
- meta_sig = entry.read
- when 'data.tar.gz.sig'
- data_sig = entry.read
- when 'data.tar.gz'
- if security_policy
- Gem.ensure_ssl_available
- data_dgst = dgst_algo.digest(entry.read)
- end
- end
- end
-
- if security_policy then
- Gem.ensure_ssl_available
-
- # map trust policy from string to actual class (or a serialized YAML
- # file, if that exists)
- if String === security_policy then
- if Gem::Security::Policy.key? security_policy then
- # load one of the pre-defined security policies
- security_policy = Gem::Security::Policy[security_policy]
- elsif File.exist? security_policy then
- # FIXME: this doesn't work yet
- security_policy = YAML.load File.read(security_policy)
- else
- raise Gem::Exception, "Unknown trust policy '#{security_policy}'"
- end
- end
-
- if data_sig && data_dgst && meta_sig && meta_dgst then
- # the user has a trust policy, and we have a signed gem
- # file, so use the trust policy to verify the gem signature
-
- begin
- security_policy.verify_gem(data_sig, data_dgst, @metadata.cert_chain)
- rescue Exception => e
- raise "Couldn't verify data signature: #{e}"
- end
-
- begin
- security_policy.verify_gem(meta_sig, meta_dgst, @metadata.cert_chain)
- rescue Exception => e
- raise "Couldn't verify metadata signature: #{e}"
- end
- elsif security_policy.only_signed
- raise Gem::Exception, "Unsigned gem"
- else
- # FIXME: should display warning here (trust policy, but
- # either unsigned or badly signed gem file)
- end
- end
-
- @tarreader.rewind
- @fileops = Gem::FileOperations.new
- raise FormatError, "No metadata found!" unless has_meta
- end
-
- # Attempt to YAML-load a gemspec from the given _io_ parameter. Return
- # nil if it fails.
- def load_gemspec(io)
- Gem::Specification.from_yaml(io)
- rescue Gem::Exception
- nil
- end
-
- def self.open(filename, security_policy = nil, &block)
- open_from_io(File.open(filename, "rb"), security_policy, &block)
- end
-
- def self.open_from_io(io, security_policy = nil, &block)
- raise "Want a block" unless block_given?
- begin
- is = new(io, security_policy)
- yield is
- ensure
- is.close if is
- end
- end
-
- def each(&block)
- @tarreader.each do |entry|
- next unless entry.full_name == "data.tar.gz"
- is = zipped_stream(entry)
- begin
- TarReader.new(is) do |inner|
- inner.each(&block)
- end
- ensure
- is.close if is
- end
- end
- @tarreader.rewind
- end
-
- # Return an IO stream for the zipped entry.
- #
- # NOTE: Originally this method used two approaches, Return a GZipReader
- # directly, or read the GZipReader into a string and return a StringIO on
- # the string. The string IO approach was used for versions of ZLib before
- # 1.2.1 to avoid buffer errors on windows machines. Then we found that
- # errors happened with 1.2.1 as well, so we changed the condition. Then
- # we discovered errors occurred with versions as late as 1.2.3. At this
- # point (after some benchmarking to show we weren't seriously crippling
- # the unpacking speed) we threw our hands in the air and declared that
- # this method would use the String IO approach on all platforms at all
- # times. And that's the way it is.
- def zipped_stream(entry)
- if defined? Rubinius then
- zis = Zlib::GzipReader.new entry
- dis = zis.read
- is = StringIO.new(dis)
- else
- # This is Jamis Buck's ZLib workaround for some unknown issue
- entry.read(10) # skip the gzip header
- zis = Zlib::Inflate.new(-Zlib::MAX_WBITS)
- is = StringIO.new(zis.inflate(entry.read))
- end
- ensure
- zis.finish if zis
- end
-
- def extract_entry(destdir, entry, expected_md5sum = nil)
- if entry.is_directory?
- dest = File.join(destdir, entry.full_name)
- if file_class.dir? dest
- @fileops.chmod entry.mode, dest, :verbose=>false
- else
- @fileops.mkdir_p(dest, :mode => entry.mode, :verbose=>false)
- end
- fsync_dir dest
- fsync_dir File.join(dest, "..")
- return
- end
- # it's a file
- md5 = Digest::MD5.new if expected_md5sum
- destdir = File.join(destdir, File.dirname(entry.full_name))
- @fileops.mkdir_p(destdir, :mode => 0755, :verbose=>false)
- destfile = File.join(destdir, File.basename(entry.full_name))
- @fileops.chmod(0600, destfile, :verbose=>false) rescue nil # Errno::ENOENT
- file_class.open(destfile, "wb", entry.mode) do |os|
- loop do
- data = entry.read(4096)
- break unless data
- md5 << data if expected_md5sum
- os.write(data)
- end
- os.fsync
- end
- @fileops.chmod(entry.mode, destfile, :verbose=>false)
- fsync_dir File.dirname(destfile)
- fsync_dir File.join(File.dirname(destfile), "..")
- if expected_md5sum && expected_md5sum != md5.hexdigest
- raise BadCheckSum
- end
- end
-
- def close
- @io.close
- @tarreader.close
- end
-
- private
-
- def file_class
- File
- end
- end
-
- class TarOutput
-
- class << self; private :new end
-
- def initialize(io)
- @io = io
- @external = TarWriter.new @io
- end
-
- def external_handle
- @external
- end
-
- def self.open(filename, signer = nil, &block)
- io = File.open(filename, "wb")
- open_from_io(io, signer, &block)
- nil
- end
-
- def self.open_from_io(io, signer = nil, &block)
- outputter = new(io)
- metadata = nil
- set_meta = lambda{|x| metadata = x}
- raise "Want a block" unless block_given?
- begin
- data_sig, meta_sig = nil, nil
-
- outputter.external_handle.add_file("data.tar.gz", 0644) do |inner|
- begin
- sio = signer ? StringIO.new : nil
- os = Zlib::GzipWriter.new(sio || inner)
-
- TarWriter.new(os) do |inner_tar_stream|
- klass = class << inner_tar_stream; self end
- klass.send(:define_method, :metadata=, &set_meta)
- block.call inner_tar_stream
- end
- ensure
- os.flush
- os.finish
- #os.close
-
- # if we have a signing key, then sign the data
- # digest and return the signature
- data_sig = nil
- if signer
- dgst_algo = Gem::Security::OPT[:dgst_algo]
- dig = dgst_algo.digest(sio.string)
- data_sig = signer.sign(dig)
- inner.write(sio.string)
- end
- end
- end
-
- # if we have a data signature, then write it to the gem too
- if data_sig
- sig_file = 'data.tar.gz.sig'
- outputter.external_handle.add_file(sig_file, 0644) do |os|
- os.write(data_sig)
- end
- end
-
- outputter.external_handle.add_file("metadata.gz", 0644) do |os|
- begin
- sio = signer ? StringIO.new : nil
- gzos = Zlib::GzipWriter.new(sio || os)
- gzos.write metadata
- ensure
- gzos.flush
- gzos.finish
-
- # if we have a signing key, then sign the metadata
- # digest and return the signature
- if signer
- dgst_algo = Gem::Security::OPT[:dgst_algo]
- dig = dgst_algo.digest(sio.string)
- meta_sig = signer.sign(dig)
- os.write(sio.string)
- end
- end
- end
-
- # if we have a metadata signature, then write to the gem as
- # well
- if meta_sig
- sig_file = 'metadata.gz.sig'
- outputter.external_handle.add_file(sig_file, 0644) do |os|
- os.write(meta_sig)
- end
- end
-
- ensure
- outputter.close
- end
- nil
- end
-
- def close
- @external.close
- @io.close
- end
-
- end
-
- #FIXME: refactor the following 2 methods
-
- def self.open(dest, mode = "r", signer = nil, &block)
- raise "Block needed" unless block_given?
-
- case mode
- when "r"
- security_policy = signer
- TarInput.open(dest, security_policy, &block)
- when "w"
- TarOutput.open(dest, signer, &block)
- else
- raise "Unknown Package open mode"
- end
- end
-
- def self.open_from_io(io, mode = "r", signer = nil, &block)
- raise "Block needed" unless block_given?
-
- case mode
- when "r"
- security_policy = signer
- TarInput.open_from_io(io, security_policy, &block)
- when "w"
- TarOutput.open_from_io(io, signer, &block)
- else
- raise "Unknown Package open mode"
- end
+ def self.open(io, mode = "r", signer = nil, &block)
+ tar_type = case mode
+ when 'r' then TarInput
+ when 'w' then TarOutput
+ else
+ raise "Unknown Package open mode"
+ end
+
+ tar_type.open(io, signer, &block)
end
def self.pack(src, destname, signer = nil)
@@ -836,19 +83,13 @@ module Gem::Package
end
end
- class << self
- def file_class
- File
- end
-
- def dir_class
- Dir
- end
-
- def find_class # HACK kill me
- Find
- end
- end
-
end
+require 'rubygems/package/f_sync_dir'
+require 'rubygems/package/tar_header'
+require 'rubygems/package/tar_input'
+require 'rubygems/package/tar_output'
+require 'rubygems/package/tar_reader'
+require 'rubygems/package/tar_reader/entry'
+require 'rubygems/package/tar_writer'
+
diff --git a/lib/rubygems/package/f_sync_dir.rb b/lib/rubygems/package/f_sync_dir.rb
new file mode 100644
index 0000000000..3e2e4a59a8
--- /dev/null
+++ b/lib/rubygems/package/f_sync_dir.rb
@@ -0,0 +1,24 @@
+#++
+# Copyright (C) 2004 Mauricio Julio Fernández Pradier
+# See LICENSE.txt for additional licensing information.
+#--
+
+require 'rubygems/package'
+
+module Gem::Package::FSyncDir
+
+ private
+
+ ##
+ # make sure this hits the disc
+
+ def fsync_dir(dirname)
+ dir = open dirname, 'r'
+ dir.fsync
+ rescue # ignore IOError if it's an unpatched (old) Ruby
+ ensure
+ dir.close if dir rescue nil
+ end
+
+end
+
diff --git a/lib/rubygems/package/tar_header.rb b/lib/rubygems/package/tar_header.rb
new file mode 100644
index 0000000000..c194cc0530
--- /dev/null
+++ b/lib/rubygems/package/tar_header.rb
@@ -0,0 +1,245 @@
+#++
+# Copyright (C) 2004 Mauricio Julio Fernández Pradier
+# See LICENSE.txt for additional licensing information.
+#--
+
+require 'rubygems/package'
+
+##
+#--
+# struct tarfile_entry_posix {
+# char name[100]; # ASCII + (Z unless filled)
+# char mode[8]; # 0 padded, octal, null
+# char uid[8]; # ditto
+# char gid[8]; # ditto
+# char size[12]; # 0 padded, octal, null
+# char mtime[12]; # 0 padded, octal, null
+# char checksum[8]; # 0 padded, octal, null, space
+# char typeflag[1]; # file: "0" dir: "5"
+# char linkname[100]; # ASCII + (Z unless filled)
+# char magic[6]; # "ustar\0"
+# char version[2]; # "00"
+# char uname[32]; # ASCIIZ
+# char gname[32]; # ASCIIZ
+# char devmajor[8]; # 0 padded, octal, null
+# char devminor[8]; # o padded, octal, null
+# char prefix[155]; # ASCII + (Z unless filled)
+# };
+#++
+
+class Gem::Package::TarHeader
+
+ FIELDS = [
+ :checksum,
+ :devmajor,
+ :devminor,
+ :gid,
+ :gname,
+ :linkname,
+ :magic,
+ :mode,
+ :mtime,
+ :name,
+ :prefix,
+ :size,
+ :typeflag,
+ :uid,
+ :uname,
+ :version,
+ ]
+
+ PACK_FORMAT = 'a100' + # name
+ 'a8' + # mode
+ 'a8' + # uid
+ 'a8' + # gid
+ 'a12' + # size
+ 'a12' + # mtime
+ 'a7a' + # chksum
+ 'a' + # typeflag
+ 'a100' + # linkname
+ 'a6' + # magic
+ 'a2' + # version
+ 'a32' + # uname
+ 'a32' + # gname
+ 'a8' + # devmajor
+ 'a8' + # devminor
+ 'a155' # prefix
+
+ UNPACK_FORMAT = 'A100' + # name
+ 'A8' + # mode
+ 'A8' + # uid
+ 'A8' + # gid
+ 'A12' + # size
+ 'A12' + # mtime
+ 'A8' + # checksum
+ 'A' + # typeflag
+ 'A100' + # linkname
+ 'A6' + # magic
+ 'A2' + # version
+ 'A32' + # uname
+ 'A32' + # gname
+ 'A8' + # devmajor
+ 'A8' + # devminor
+ 'A155' # prefix
+
+ attr_reader(*FIELDS)
+
+ def self.from(stream)
+ header = stream.read 512
+ empty = (header == "\0" * 512)
+
+ fields = header.unpack UNPACK_FORMAT
+
+ name = fields.shift
+ mode = fields.shift.oct
+ uid = fields.shift.oct
+ gid = fields.shift.oct
+ size = fields.shift.oct
+ mtime = fields.shift.oct
+ checksum = fields.shift.oct
+ typeflag = fields.shift
+ linkname = fields.shift
+ magic = fields.shift
+ version = fields.shift.oct
+ uname = fields.shift
+ gname = fields.shift
+ devmajor = fields.shift.oct
+ devminor = fields.shift.oct
+ prefix = fields.shift
+
+ new :name => name,
+ :mode => mode,
+ :uid => uid,
+ :gid => gid,
+ :size => size,
+ :mtime => mtime,
+ :checksum => checksum,
+ :typeflag => typeflag,
+ :linkname => linkname,
+ :magic => magic,
+ :version => version,
+ :uname => uname,
+ :gname => gname,
+ :devmajor => devmajor,
+ :devminor => devminor,
+ :prefix => prefix,
+
+ :empty => empty
+
+ # HACK unfactor for Rubinius
+ #new :name => fields.shift,
+ # :mode => fields.shift.oct,
+ # :uid => fields.shift.oct,
+ # :gid => fields.shift.oct,
+ # :size => fields.shift.oct,
+ # :mtime => fields.shift.oct,
+ # :checksum => fields.shift.oct,
+ # :typeflag => fields.shift,
+ # :linkname => fields.shift,
+ # :magic => fields.shift,
+ # :version => fields.shift.oct,
+ # :uname => fields.shift,
+ # :gname => fields.shift,
+ # :devmajor => fields.shift.oct,
+ # :devminor => fields.shift.oct,
+ # :prefix => fields.shift,
+
+ # :empty => empty
+ end
+
+ def initialize(vals)
+ unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode] then
+ raise ArgumentError, ":name, :size, :prefix and :mode required"
+ end
+
+ vals[:uid] ||= 0
+ vals[:gid] ||= 0
+ vals[:mtime] ||= 0
+ vals[:checksum] ||= ""
+ vals[:typeflag] ||= "0"
+ vals[:magic] ||= "ustar"
+ vals[:version] ||= "00"
+ vals[:uname] ||= "wheel"
+ vals[:gname] ||= "wheel"
+ vals[:devmajor] ||= 0
+ vals[:devminor] ||= 0
+
+ FIELDS.each do |name|
+ instance_variable_set "@#{name}", vals[name]
+ end
+
+ @empty = vals[:empty]
+ end
+
+ def empty?
+ @empty
+ end
+
+ def ==(other)
+ self.class === other and
+ @checksum == other.checksum and
+ @devmajor == other.devmajor and
+ @devminor == other.devminor and
+ @gid == other.gid and
+ @gname == other.gname and
+ @linkname == other.linkname and
+ @magic == other.magic and
+ @mode == other.mode and
+ @mtime == other.mtime and
+ @name == other.name and
+ @prefix == other.prefix and
+ @size == other.size and
+ @typeflag == other.typeflag and
+ @uid == other.uid and
+ @uname == other.uname and
+ @version == other.version
+ end
+
+ def to_s
+ update_checksum
+ header
+ end
+
+ def update_checksum
+ header = header " " * 8
+ @checksum = oct calculate_checksum(header), 6
+ end
+
+ private
+
+ def calculate_checksum(header)
+ header.unpack("C*").inject { |a, b| a + b }
+ end
+
+ def header(checksum = @checksum)
+ header = [
+ name,
+ oct(mode, 7),
+ oct(uid, 7),
+ oct(gid, 7),
+ oct(size, 11),
+ oct(mtime, 11),
+ checksum,
+ " ",
+ typeflag,
+ linkname,
+ magic,
+ oct(version, 2),
+ uname,
+ gname,
+ oct(devmajor, 7),
+ oct(devminor, 7),
+ prefix
+ ]
+
+ header = header.pack PACK_FORMAT
+
+ header << ("\0" * ((512 - header.size) % 512))
+ end
+
+ def oct(num, len)
+ "%0#{len}o" % num
+ end
+
+end
+
diff --git a/lib/rubygems/package/tar_input.rb b/lib/rubygems/package/tar_input.rb
new file mode 100644
index 0000000000..2ed3d6b772
--- /dev/null
+++ b/lib/rubygems/package/tar_input.rb
@@ -0,0 +1,219 @@
+#++
+# Copyright (C) 2004 Mauricio Julio Fernández Pradier
+# See LICENSE.txt for additional licensing information.
+#--
+
+require 'rubygems/package'
+
+class Gem::Package::TarInput
+
+ include Gem::Package::FSyncDir
+ include Enumerable
+
+ attr_reader :metadata
+
+ private_class_method :new
+
+ def self.open(io, security_policy = nil, &block)
+ is = new io, security_policy
+
+ yield is
+ ensure
+ is.close if is
+ end
+
+ def initialize(io, security_policy = nil)
+ @io = io
+ @tarreader = Gem::Package::TarReader.new @io
+ has_meta = false
+
+ data_sig, meta_sig, data_dgst, meta_dgst = nil, nil, nil, nil
+ dgst_algo = security_policy ? Gem::Security::OPT[:dgst_algo] : nil
+
+ @tarreader.each do |entry|
+ case entry.full_name
+ when "metadata"
+ @metadata = load_gemspec entry.read
+ has_meta = true
+ when "metadata.gz"
+ begin
+ # if we have a security_policy, then pre-read the metadata file
+ # and calculate it's digest
+ sio = nil
+ if security_policy
+ Gem.ensure_ssl_available
+ sio = StringIO.new(entry.read)
+ meta_dgst = dgst_algo.digest(sio.string)
+ sio.rewind
+ end
+
+ gzis = Zlib::GzipReader.new(sio || entry)
+ # YAML wants an instance of IO
+ @metadata = load_gemspec(gzis)
+ has_meta = true
+ ensure
+ gzis.close unless gzis.nil?
+ end
+ when 'metadata.gz.sig'
+ meta_sig = entry.read
+ when 'data.tar.gz.sig'
+ data_sig = entry.read
+ when 'data.tar.gz'
+ if security_policy
+ Gem.ensure_ssl_available
+ data_dgst = dgst_algo.digest(entry.read)
+ end
+ end
+ end
+
+ if security_policy then
+ Gem.ensure_ssl_available
+
+ # map trust policy from string to actual class (or a serialized YAML
+ # file, if that exists)
+ if String === security_policy then
+ if Gem::Security::Policy.key? security_policy then
+ # load one of the pre-defined security policies
+ security_policy = Gem::Security::Policy[security_policy]
+ elsif File.exist? security_policy then
+ # FIXME: this doesn't work yet
+ security_policy = YAML.load File.read(security_policy)
+ else
+ raise Gem::Exception, "Unknown trust policy '#{security_policy}'"
+ end
+ end
+
+ if data_sig && data_dgst && meta_sig && meta_dgst then
+ # the user has a trust policy, and we have a signed gem
+ # file, so use the trust policy to verify the gem signature
+
+ begin
+ security_policy.verify_gem(data_sig, data_dgst, @metadata.cert_chain)
+ rescue Exception => e
+ raise "Couldn't verify data signature: #{e}"
+ end
+
+ begin
+ security_policy.verify_gem(meta_sig, meta_dgst, @metadata.cert_chain)
+ rescue Exception => e
+ raise "Couldn't verify metadata signature: #{e}"
+ end
+ elsif security_policy.only_signed
+ raise Gem::Exception, "Unsigned gem"
+ else
+ # FIXME: should display warning here (trust policy, but
+ # either unsigned or badly signed gem file)
+ end
+ end
+
+ @tarreader.rewind
+ @fileops = Gem::FileOperations.new
+
+ raise Gem::Package::FormatError, "No metadata found!" unless has_meta
+ end
+
+ def close
+ @io.close
+ @tarreader.close
+ end
+
+ def each(&block)
+ @tarreader.each do |entry|
+ next unless entry.full_name == "data.tar.gz"
+ is = zipped_stream entry
+
+ begin
+ Gem::Package::TarReader.new is do |inner|
+ inner.each(&block)
+ end
+ ensure
+ is.close if is
+ end
+ end
+
+ @tarreader.rewind
+ end
+
+ def extract_entry(destdir, entry, expected_md5sum = nil)
+ if entry.directory? then
+ dest = File.join(destdir, entry.full_name)
+
+ if File.dir? dest then
+ @fileops.chmod entry.header.mode, dest, :verbose=>false
+ else
+ @fileops.mkdir_p dest, :mode => entry.header.mode, :verbose => false
+ end
+
+ fsync_dir dest
+ fsync_dir File.join(dest, "..")
+
+ return
+ end
+
+ # it's a file
+ md5 = Digest::MD5.new if expected_md5sum
+ destdir = File.join destdir, File.dirname(entry.full_name)
+ @fileops.mkdir_p destdir, :mode => 0755, :verbose => false
+ destfile = File.join destdir, File.basename(entry.full_name)
+ @fileops.chmod 0600, destfile, :verbose => false rescue nil # Errno::ENOENT
+
+ open destfile, "wb", entry.header.mode do |os|
+ loop do
+ data = entry.read 4096
+ break unless data
+ # HACK shouldn't we check the MD5 before writing to disk?
+ md5 << data if expected_md5sum
+ os.write(data)
+ end
+
+ os.fsync
+ end
+
+ @fileops.chmod entry.header.mode, destfile, :verbose => false
+ fsync_dir File.dirname(destfile)
+ fsync_dir File.join(File.dirname(destfile), "..")
+
+ if expected_md5sum && expected_md5sum != md5.hexdigest then
+ raise Gem::Package::BadCheckSum
+ end
+ end
+
+ # Attempt to YAML-load a gemspec from the given _io_ parameter. Return
+ # nil if it fails.
+ def load_gemspec(io)
+ Gem::Specification.from_yaml io
+ rescue Gem::Exception
+ nil
+ end
+
+ ##
+ # Return an IO stream for the zipped entry.
+ #
+ # NOTE: Originally this method used two approaches, Return a GZipReader
+ # directly, or read the GZipReader into a string and return a StringIO on
+ # the string. The string IO approach was used for versions of ZLib before
+ # 1.2.1 to avoid buffer errors on windows machines. Then we found that
+ # errors happened with 1.2.1 as well, so we changed the condition. Then
+ # we discovered errors occurred with versions as late as 1.2.3. At this
+ # point (after some benchmarking to show we weren't seriously crippling
+ # the unpacking speed) we threw our hands in the air and declared that
+ # this method would use the String IO approach on all platforms at all
+ # times. And that's the way it is.
+
+ def zipped_stream(entry)
+ if defined? Rubinius then
+ zis = Zlib::GzipReader.new entry
+ dis = zis.read
+ is = StringIO.new(dis)
+ else
+ # This is Jamis Buck's Zlib workaround for some unknown issue
+ entry.read(10) # skip the gzip header
+ zis = Zlib::Inflate.new(-Zlib::MAX_WBITS)
+ is = StringIO.new(zis.inflate(entry.read))
+ end
+ ensure
+ zis.finish if zis
+ end
+
+end
+
diff --git a/lib/rubygems/package/tar_output.rb b/lib/rubygems/package/tar_output.rb
new file mode 100644
index 0000000000..b22f7dd86b
--- /dev/null
+++ b/lib/rubygems/package/tar_output.rb
@@ -0,0 +1,143 @@
+#++
+# Copyright (C) 2004 Mauricio Julio Fernández Pradier
+# See LICENSE.txt for additional licensing information.
+#--
+
+require 'rubygems/package'
+
+##
+# TarOutput is a wrapper to TarWriter that builds gem-format tar file.
+#
+# Gem-format tar files contain the following files:
+# [data.tar.gz] A gzipped tar file containing the files that compose the gem
+# which will be extracted into the gem/ dir on installation.
+# [metadata.gz] A YAML format Gem::Specification.
+# [data.tar.gz.sig] A signature for the gem's data.tar.gz.
+# [metadata.gz.sig] A signature for the gem's metadata.gz.
+#
+# See TarOutput::open for usage details.
+
+class Gem::Package::TarOutput
+
+ ##
+ # Creates a new TarOutput which will yield a TarWriter object for the
+ # data.tar.gz portion of a gem-format tar file.
+ #
+ # See #initialize for details on +io+ and +signer+.
+ #
+ # See #add_gem_contents for details on adding metadata to the tar file.
+
+ def self.open(io, signer = nil, &block) # :yield: data_tar_writer
+ tar_outputter = new io, signer
+ tar_outputter.add_gem_contents(&block)
+ tar_outputter.add_metadata
+ tar_outputter.add_signatures
+
+ ensure
+ tar_outputter.close
+ end
+
+ ##
+ # Creates a new TarOutput that will write a gem-format tar file to +io+. If
+ # +signer+ is given, the data.tar.gz and metadata.gz will be signed and
+ # the signatures will be added to the tar file.
+
+ def initialize(io, signer)
+ @io = io
+ @signer = signer
+
+ @tar_writer = Gem::Package::TarWriter.new @io
+
+ @metadata = nil
+
+ @data_signature = nil
+ @meta_signature = nil
+ end
+
+ ##
+ # Yields a TarWriter for the data.tar.gz inside a gem-format tar file.
+ # The yielded TarWriter has been extended with a #metadata= method for
+ # attaching a YAML format Gem::Specification which will be written by
+ # add_metadata.
+
+ def add_gem_contents
+ @tar_writer.add_file "data.tar.gz", 0644 do |inner|
+ sio = @signer ? StringIO.new : nil
+ Zlib::GzipWriter.wrap(sio || inner) do |os|
+
+ Gem::Package::TarWriter.new os do |data_tar_writer|
+ def data_tar_writer.metadata() @metadata end
+ def data_tar_writer.metadata=(metadata) @metadata = metadata end
+
+ yield data_tar_writer
+
+ @metadata = data_tar_writer.metadata
+ end
+ end
+
+ # if we have a signing key, then sign the data
+ # digest and return the signature
+ if @signer then
+ digest = Gem::Security::OPT[:dgst_algo].digest sio.string
+ @data_signature = @signer.sign digest
+ inner.write sio.string
+ end
+ end
+
+ self
+ end
+
+ ##
+ # Adds metadata.gz to the gem-format tar file which was saved from a
+ # previous #add_gem_contents call.
+
+ def add_metadata
+ return if @metadata.nil?
+
+ @tar_writer.add_file "metadata.gz", 0644 do |io|
+ begin
+ sio = @signer ? StringIO.new : nil
+ gzos = Zlib::GzipWriter.new(sio || io)
+ gzos.write @metadata
+ ensure
+ gzos.flush
+ gzos.finish
+
+ # if we have a signing key, then sign the metadata digest and return
+ # the signature
+ if @signer then
+ digest = Gem::Security::OPT[:dgst_algo].digest sio.string
+ @meta_signature = @signer.sign digest
+ io.write sio.string
+ end
+ end
+ end
+ end
+
+ ##
+ # Adds data.tar.gz.sig and metadata.gz.sig to the gem-format tar files if
+ # a Gem::Security::Signer was sent to initialize.
+
+ def add_signatures
+ if @data_signature then
+ @tar_writer.add_file 'data.tar.gz.sig', 0644 do |io|
+ io.write @data_signature
+ end
+ end
+
+ if @meta_signature then
+ @tar_writer.add_file 'metadata.gz.sig', 0644 do |io|
+ io.write @meta_signature
+ end
+ end
+ end
+
+ ##
+ # Closes the TarOutput.
+
+ def close
+ @tar_writer.close
+ end
+
+end
+
diff --git a/lib/rubygems/package/tar_reader.rb b/lib/rubygems/package/tar_reader.rb
new file mode 100644
index 0000000000..8359399207
--- /dev/null
+++ b/lib/rubygems/package/tar_reader.rb
@@ -0,0 +1,86 @@
+#++
+# Copyright (C) 2004 Mauricio Julio Fernández Pradier
+# See LICENSE.txt for additional licensing information.
+#--
+
+require 'rubygems/package'
+
+class Gem::Package::TarReader
+
+ include Gem::Package
+
+ class UnexpectedEOF < StandardError; end
+
+ def self.new(io)
+ reader = super
+
+ return reader unless block_given?
+
+ begin
+ yield reader
+ ensure
+ reader.close
+ end
+
+ nil
+ end
+
+ def initialize(io)
+ @io = io
+ @init_pos = io.pos
+ end
+
+ def close
+ end
+
+ def each
+ loop do
+ return if @io.eof?
+
+ header = Gem::Package::TarHeader.from @io
+ return if header.empty?
+
+ entry = Gem::Package::TarReader::Entry.new header, @io
+ size = entry.header.size
+
+ yield entry
+
+ skip = (512 - (size % 512)) % 512
+
+ if @io.respond_to? :seek then
+ # avoid reading...
+ @io.seek(size - entry.bytes_read, IO::SEEK_CUR)
+ else
+ pending = size - entry.bytes_read
+
+ while pending > 0 do
+ bread = @io.read([pending, 4096].min).size
+ raise UnexpectedEOF if @io.eof?
+ pending -= bread
+ end
+ end
+
+ @io.read skip # discard trailing zeros
+
+ # make sure nobody can use #read, #getc or #rewind anymore
+ entry.close
+ end
+ end
+
+ alias each_entry each
+
+ ##
+ # NOTE: Do not call #rewind during #each
+
+ def rewind
+ if @init_pos == 0 then
+ raise Gem::Package::NonSeekableIO unless @io.respond_to? :rewind
+ @io.rewind
+ else
+ raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos=
+ @io.pos = @init_pos
+ end
+ end
+
+end
+
diff --git a/lib/rubygems/package/tar_reader/entry.rb b/lib/rubygems/package/tar_reader/entry.rb
new file mode 100644
index 0000000000..dcc66153d8
--- /dev/null
+++ b/lib/rubygems/package/tar_reader/entry.rb
@@ -0,0 +1,99 @@
+#++
+# Copyright (C) 2004 Mauricio Julio Fernández Pradier
+# See LICENSE.txt for additional licensing information.
+#--
+
+require 'rubygems/package'
+
+class Gem::Package::TarReader::Entry
+
+ attr_reader :header
+
+ def initialize(header, io)
+ @closed = false
+ @header = header
+ @io = io
+ @orig_pos = @io.pos
+ @read = 0
+ end
+
+ def check_closed # :nodoc:
+ raise IOError, "closed #{self.class}" if closed?
+ end
+
+ def bytes_read
+ @read
+ end
+
+ def close
+ @closed = true
+ end
+
+ def closed?
+ @closed
+ end
+
+ def eof?
+ check_closed
+
+ @read >= @header.size
+ end
+
+ def full_name
+ if @header.prefix != "" then
+ File.join @header.prefix, @header.name
+ else
+ @header.name
+ end
+ end
+
+ def getc
+ check_closed
+
+ return nil if @read >= @header.size
+
+ ret = @io.getc
+ @read += 1 if ret
+
+ ret
+ end
+
+ def directory?
+ @header.typeflag == "5"
+ end
+
+ def file?
+ @header.typeflag == "0"
+ end
+
+ def pos
+ check_closed
+
+ bytes_read
+ end
+
+ def read(len = nil)
+ check_closed
+
+ return nil if @read >= @header.size
+
+ len ||= @header.size - @read
+ max_read = [len, @header.size - @read].min
+
+ ret = @io.read max_read
+ @read += ret.size
+
+ ret
+ end
+
+ def rewind
+ check_closed
+
+ raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos=
+
+ @io.pos = @orig_pos
+ @read = 0
+ end
+
+end
+
diff --git a/lib/rubygems/package/tar_writer.rb b/lib/rubygems/package/tar_writer.rb
new file mode 100644
index 0000000000..6e11440e22
--- /dev/null
+++ b/lib/rubygems/package/tar_writer.rb
@@ -0,0 +1,180 @@
+#++
+# Copyright (C) 2004 Mauricio Julio Fernández Pradier
+# See LICENSE.txt for additional licensing information.
+#--
+
+require 'rubygems/package'
+
+class Gem::Package::TarWriter
+
+ class FileOverflow < StandardError; end
+
+ class BoundedStream
+
+ attr_reader :limit, :written
+
+ def initialize(io, limit)
+ @io = io
+ @limit = limit
+ @written = 0
+ end
+
+ def write(data)
+ if data.size + @written > @limit
+ raise FileOverflow, "You tried to feed more data than fits in the file."
+ end
+ @io.write data
+ @written += data.size
+ data.size
+ end
+
+ end
+
+ class RestrictedStream
+
+ def initialize(io)
+ @io = io
+ end
+
+ def write(data)
+ @io.write data
+ end
+
+ end
+
+ def self.new(io)
+ writer = super
+
+ return writer unless block_given?
+
+ begin
+ yield writer
+ ensure
+ writer.close
+ end
+
+ nil
+ end
+
+ def initialize(io)
+ @io = io
+ @closed = false
+ end
+
+ def add_file(name, mode)
+ check_closed
+
+ raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos=
+
+ name, prefix = split_name name
+
+ init_pos = @io.pos
+ @io.write "\0" * 512 # placeholder for the header
+
+ yield RestrictedStream.new(@io) if block_given?
+
+ size = @io.pos - init_pos - 512
+
+ remainder = (512 - (size % 512)) % 512
+ @io.write "\0" * remainder
+
+ final_pos = @io.pos
+ @io.pos = init_pos
+
+ header = Gem::Package::TarHeader.new :name => name, :mode => mode,
+ :size => size, :prefix => prefix
+
+ @io.write header
+ @io.pos = final_pos
+
+ self
+ end
+
+ def add_file_simple(name, mode, size)
+ check_closed
+
+ name, prefix = split_name name
+
+ header = Gem::Package::TarHeader.new(:name => name, :mode => mode,
+ :size => size, :prefix => prefix).to_s
+
+ @io.write header
+ os = BoundedStream.new @io, size
+
+ yield os if block_given?
+
+ min_padding = size - os.written
+ @io.write("\0" * min_padding)
+
+ remainder = (512 - (size % 512)) % 512
+ @io.write("\0" * remainder)
+
+ self
+ end
+
+ def check_closed
+ raise IOError, "closed #{self.class}" if closed?
+ end
+
+ def close
+ check_closed
+
+ @io.write "\0" * 1024
+ flush
+
+ @closed = true
+ end
+
+ def closed?
+ @closed
+ end
+
+ def flush
+ check_closed
+
+ @io.flush if @io.respond_to? :flush
+ end
+
+ def mkdir(name, mode)
+ check_closed
+
+ name, prefix = split_name(name)
+
+ header = Gem::Package::TarHeader.new :name => name, :mode => mode,
+ :typeflag => "5", :size => 0,
+ :prefix => prefix
+
+ @io.write header
+
+ self
+ end
+
+ def split_name(name) # :nodoc:
+ raise Gem::Package::TooLongFileName if name.size > 256
+
+ if name.size <= 100 then
+ prefix = ""
+ else
+ parts = name.split(/\//)
+ newname = parts.pop
+ nxt = ""
+
+ loop do
+ nxt = parts.pop
+ break if newname.size + 1 + nxt.size > 100
+ newname = nxt + "/" + newname
+ end
+
+ prefix = (parts + [nxt]).join "/"
+ name = newname
+
+ if name.size > 100 or prefix.size > 155 then
+ raise Gem::Package::TooLongFileName
+ end
+ end
+
+ return name, prefix
+ end
+
+end
+
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index cb22e1f1b1..f49ee2f4a1 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -2,7 +2,6 @@ require 'net/http'
require 'uri'
require 'rubygems'
-require 'rubygems/gem_open_uri'
##
# RemoteFetcher handles the details of fetching gems and gem information from
@@ -10,6 +9,8 @@ require 'rubygems/gem_open_uri'
class Gem::RemoteFetcher
+ include Gem::UserInteraction
+
class FetchError < Gem::Exception; end
@fetcher = nil
@@ -29,6 +30,10 @@ class Gem::RemoteFetcher
# 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
+
+ @connections = {}
+ @requests = Hash.new 0
@proxy_uri =
case proxy
when :no_proxy then nil
@@ -38,6 +43,65 @@ class Gem::RemoteFetcher
end
end
+ ##
+ # 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)
+ gem_file_name = "#{spec.full_name}.gem"
+ local_gem_path = File.join install_dir, 'cache', gem_file_name
+
+ Gem.ensure_gem_subdirectories install_dir
+
+ source_uri = URI.parse source_uri unless URI::Generic === source_uri
+ scheme = source_uri.scheme
+
+ # URI.parse gets confused by MS Windows paths with forward slashes.
+ scheme = nil if scheme =~ /^[a-z]$/i
+
+ case scheme
+ when 'http' then
+ unless File.exist? local_gem_path then
+ begin
+ say "Downloading gem #{gem_file_name}" if
+ Gem.configuration.really_verbose
+
+ remote_gem_path = source_uri + "gems/#{gem_file_name}"
+
+ gem = Gem::RemoteFetcher.fetcher.fetch_path remote_gem_path
+ rescue Gem::RemoteFetcher::FetchError
+ raise if spec.original_platform == spec.platform
+
+ alternate_name = "#{spec.original_name}.gem"
+
+ say "Failed, downloading gem #{alternate_name}" if
+ Gem.configuration.really_verbose
+
+ remote_gem_path = source_uri + "gems/#{alternate_name}"
+
+ gem = Gem::RemoteFetcher.fetcher.fetch_path remote_gem_path
+ end
+
+ File.open local_gem_path, 'wb' do |fp|
+ fp.write gem
+ end
+ end
+ when nil, 'file' then # TODO test for local overriding cache
+ begin
+ FileUtils.cp source_uri.to_s, local_gem_path
+ rescue Errno::EACCES
+ local_gem_path = source_uri.to_s
+ end
+
+ say "Using local gem #{local_gem_path}" if
+ Gem.configuration.really_verbose
+ else
+ raise Gem::InstallError, "unsupported URI scheme #{source_uri.scheme}"
+ end
+
+ local_gem_path
+ end
+
# Downloads +uri+.
def fetch_path(uri)
open_uri_or_path(uri) do |input|
@@ -47,9 +111,8 @@ class Gem::RemoteFetcher
raise FetchError, "timed out fetching #{uri}"
rescue IOError, SocketError, SystemCallError => e
raise FetchError, "#{e.class}: #{e} reading #{uri}"
- rescue OpenURI::HTTPError => e
- body = e.io.readlines.join "\n\t"
- message = "#{e.class}: #{e} reading #{uri}\n\t#{body}"
+ rescue => e
+ message = "#{e.class}: #{e} reading #{uri}"
raise FetchError, message
end
@@ -83,7 +146,8 @@ class Gem::RemoteFetcher
end
rescue SocketError, SystemCallError, Timeout::Error => e
- raise FetchError, "#{e.message} (#{e.class})\n\tgetting size of #{uri}"
+ raise Gem::RemoteFetcher::FetchError,
+ "#{e.message} (#{e.class})\n\tgetting size of #{uri}"
end
private
@@ -131,26 +195,77 @@ class Gem::RemoteFetcher
# 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, &block)
+ def open_uri_or_path(uri, depth = 0, &block)
if file_uri?(uri)
open(get_file_uri_path(uri), &block)
else
- connection_options = {
- "User-Agent" => "RubyGems/#{Gem::RubyGemsVersion} #{Gem::Platform.local}"
- }
+ 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 @proxy_uri
- http_proxy_url = "#{@proxy_uri.scheme}://#{@proxy_uri.host}:#{@proxy_uri.port}"
- connection_options[:proxy_http_basic_authentication] = [http_proxy_url, unescape(@proxy_uri.user)||'', unescape(@proxy_uri.password)||'']
+ if uri.scheme == 'https' && ! connection.started?
+ http_obj.use_ssl = true
+ http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
- uri = URI.parse uri unless URI::Generic === uri
+ connection.start unless connection.started?
+
+ request = Net::HTTP::Get.new(uri.request_uri)
unless uri.nil? || uri.user.nil? || uri.user.empty? then
- connection_options[:http_basic_authentication] =
- [unescape(uri.user), unescape(uri.password)]
+ request.basic_auth(uri.user, uri.password)
end
- open(uri, connection_options, &block)
+ 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
+ retried = false
+ begin
+ @requests[connection_id] += 1
+ response = connection.request(request)
+ rescue EOFError
+ requests = @requests[connection_id]
+ say "connection reset after #{requests} requests, retrying" if
+ Gem.configuration.really_verbose
+
+ raise Gem::RemoteFetcher::FetchError, 'too many connection resets' if
+ retried
+
+ @requests[connection_id] = 0
+
+ connection.finish
+ connection.start
+ retried = true
+ retry
+ end
+
+ case response
+ 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
+ open_uri_or_path(response['Location'], depth + 1, &block)
+ else
+ raise Gem::RemoteFetcher::FetchError,
+ "bad response #{response.message} #{response.code}"
+ end
end
end
diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb
index 4dfba4fa61..209bd432f0 100644
--- a/lib/rubygems/requirement.rb
+++ b/lib/rubygems/requirement.rb
@@ -16,6 +16,8 @@ class Gem::Requirement
include Comparable
+ attr_reader :requirements
+
OPS = {
"=" => lambda { |v, r| v == r },
"!=" => lambda { |v, r| v != r },
diff --git a/lib/rubygems/rubygems_version.rb b/lib/rubygems/rubygems_version.rb
index 146e2cfee4..cc7269d622 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.0.1'
+ RubyGemsVersion = '1.1.0'
end
diff --git a/lib/rubygems/security.rb b/lib/rubygems/security.rb
index 415e98ffc0..345d36bbd3 100644
--- a/lib/rubygems/security.rb
+++ b/lib/rubygems/security.rb
@@ -4,6 +4,7 @@
# See LICENSE.txt for permissions.
#++
+require 'rubygems'
require 'rubygems/gem_openssl'
# = Signed Gems README
diff --git a/lib/rubygems/server.rb b/lib/rubygems/server.rb
index ed957dd38a..dab449894f 100644
--- a/lib/rubygems/server.rb
+++ b/lib/rubygems/server.rb
@@ -1,7 +1,7 @@
require 'webrick'
-require 'rdoc/template'
require 'yaml'
require 'zlib'
+require 'erb'
require 'rubygems'
@@ -27,107 +27,87 @@ class Gem::Server
include Gem::UserInteraction
- DOC_TEMPLATE = <<-WEBPAGE
-<?xml version="1.0" encoding="iso-8859-1"?>
-<!DOCTYPE html
- PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<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>
- <div id="fileHeader">
- <h1>RubyGems Documentation Index</h1>
- </div>
- <!-- banner header -->
-
-<div id="bodyContent">
- <div id="contextContent">
- <div id="description">
- <h1>Summary</h1>
-<p>There are %gem_count% gems installed:</p>
-<p>
-START:specs
-IFNOT:is_last
-<a href="#%name%">%name%</a>,
-ENDIF:is_last
-IF:is_last
-<a href="#%name%">%name%</a>.
-ENDIF:is_last
-END:specs
-<h1>Gems</h1>
-
-<dl>
-START:specs
-<dt>
-IF:first_name_entry
- <a name="%name%"></a>
-ENDIF:first_name_entry
-<b>%name% %version%</b>
-IF:rdoc_installed
- <a href="%doc_path%">[rdoc]</a>
-ENDIF:rdoc_installed
-IFNOT:rdoc_installed
- <span title="rdoc not installed">[rdoc]</span>
-ENDIF:rdoc_installed
-IF:homepage
-<a href="%homepage%" title="%homepage%">[www]</a>
-ENDIF:homepage
-IFNOT:homepage
-<span title="no homepage available">[www]</span>
-ENDIF:homepage
-IF:has_deps
- - depends on
-START:dependencies
-IFNOT:is_last
-<a href="#%name%" title="%version%">%name%</a>,
-ENDIF:is_last
-IF:is_last
-<a href="#%name%" title="%version%">%name%</a>.
-ENDIF:is_last
-END:dependencies
-ENDIF:has_deps
-</dt>
-<dd>
-%summary%
-IF:executables
- <br/>
-
-IF:only_one_executable
- Executable is
-ENDIF:only_one_executable
-
-IFNOT:only_one_executable
- Executables are
-ENDIF:only_one_executable
-
-START:executables
-IFNOT:is_last
- <span class="context-item-name">%executable%</span>,
-ENDIF:is_last
-IF:is_last
- <span class="context-item-name">%executable%</span>.
-ENDIF:is_last
-END:executables
-ENDIF:executables
-<br/>
-<br/>
-</dd>
-END:specs
-</dl>
-
+ DOC_TEMPLATE = <<-'WEBPAGE'
+ <?xml version="1.0" encoding="iso-8859-1"?>
+ <!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+ <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>
+ <div id="fileHeader">
+ <h1>RubyGems Documentation Index</h1>
+ </div>
+ <!-- banner header -->
+
+ <div id="bodyContent">
+ <div id="contextContent">
+ <div id="description">
+ <h1>Summary</h1>
+ <p>There are <%=values["gem_count"]%> gems installed:</p>
+ <p>
+ <%= values["specs"].map { |v| "<a href=\"#{v["name"]}\">#{v["name"]}</a>" }.join ', ' %>.
+ <h1>Gems</h1>
+
+ <dl>
+ <% values["specs"].each do |spec| %>
+ <dt>
+ <% if spec["first_name_entry"] then %>
+ <a name="<%=spec["name"]%>"></a>
+ <% end %>
+
+ <b><%=spec["name"]%> <%=spec["version"]%></b>
+
+ <% if spec["rdoc_installed"] then %>
+ <a href="<%=spec["doc_path"]%>">[rdoc]</a>
+ <% else %>
+ <span title="rdoc not installed">[rdoc]</span>
+ <% end %>
+
+ <% if spec["homepage"] then %>
+ <a href="<%=spec["homepage"]%>" title="<%=spec["homepage"]%>">[www]</a>
+ <% else %>
+ <span title="no homepage available">[www]</span>
+ <% end %>
+
+ <% if spec["has_deps"] then %>
+ - depends on
+ <%= spec["dependencies"].map { |v| "<a href=\"#{v["name"]}\">#{v["name"]}</a>" }.join ', ' %>.
+ <% end %>
+ </dt>
+ <dd>
+ <%=spec["summary"]%>
+ <% if spec["executables"] then %>
+ <br/>
+
+ <% if spec["only_one_executable"] then %>
+ Executable is
+ <% else %>
+ Executables are
+ <%end%>
+
+ <%= spec["executables"].map { |v| "<span class=\"context-item-name\">#{v["executable"]}</span>"}.join ', ' %>.
+
+ <%end%>
+ <br/>
+ <br/>
+ </dd>
+ <% end %>
+ </dl>
+
+ </div>
+ </div>
</div>
- </div>
+ <div id="validator-badges">
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
</div>
-<div id="validator-badges">
- <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
-</div>
-</body>
-</html>
+ </body>
+ </html>
WEBPAGE
# CSS is copy & paste from rdoc-style.css, RDoc V1.0.1 - 20041108
@@ -496,11 +476,12 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
end
# create page from template
- template = TemplatePage.new(DOC_TEMPLATE)
+ template = ERB.new(DOC_TEMPLATE)
res['content-type'] = 'text/html'
- template.write_html_on res.body,
- "gem_count" => specs.size.to_s, "specs" => specs,
- "total_file_count" => total_file_count.to_s
+ values = { "gem_count" => specs.size.to_s, "specs" => specs,
+ "total_file_count" => total_file_count.to_s }
+ result = template.result binding
+ res.body = result
end
paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" }
diff --git a/lib/rubygems/source_index.rb b/lib/rubygems/source_index.rb
index 1283f8f904..61f5324a95 100644
--- a/lib/rubygems/source_index.rb
+++ b/lib/rubygems/source_index.rb
@@ -8,437 +8,512 @@ require 'rubygems'
require 'rubygems/user_interaction'
require 'rubygems/specification'
-module Gem
+##
+# The SourceIndex object indexes all the gems available from a
+# particular source (e.g. a list of gem directories, or a remote
+# source). A SourceIndex maps a gem full name to a gem
+# specification.
+#
+# NOTE:: The class used to be named Cache, but that became
+# confusing when cached source fetchers where introduced. The
+# constant Gem::Cache is an alias for this class to allow old
+# YAMLized source index objects to load properly.
- # The SourceIndex object indexes all the gems available from a
- # particular source (e.g. a list of gem directories, or a remote
- # source). A SourceIndex maps a gem full name to a gem
- # specification.
- #
- # NOTE:: The class used to be named Cache, but that became
- # confusing when cached source fetchers where introduced. The
- # constant Gem::Cache is an alias for this class to allow old
- # YAMLized source index objects to load properly.
- #
- class SourceIndex
+class Gem::SourceIndex
- include Enumerable
+ include Enumerable
+ include Gem::UserInteraction
+
+ class << self
include Gem::UserInteraction
- # Class Methods. -------------------------------------------------
- class << self
- include Gem::UserInteraction
-
- # Factory method to construct a source index instance for a given
- # path.
- #
- # deprecated::
- # If supplied, from_installed_gems will act just like
- # +from_gems_in+. This argument is deprecated and is provided
- # just for backwards compatibility, and should not generally
- # be used.
- #
- # return::
- # SourceIndex instance
- #
- def from_installed_gems(*deprecated)
- if deprecated.empty?
- from_gems_in(*installed_spec_directories)
- else
- from_gems_in(*deprecated) # HACK warn
- end
- end
-
- # Return a list of directories in the current gem path that
- # contain specifications.
- #
- # return::
- # List of directory paths (all ending in "../specifications").
- #
- def installed_spec_directories
- Gem.path.collect { |dir| File.join(dir, "specifications") }
+ ##
+ # Factory method to construct a source index instance for a given
+ # path.
+ #
+ # deprecated::
+ # If supplied, from_installed_gems will act just like
+ # +from_gems_in+. This argument is deprecated and is provided
+ # just for backwards compatibility, and should not generally
+ # be used.
+ #
+ # return::
+ # SourceIndex instance
+
+ def from_installed_gems(*deprecated)
+ if deprecated.empty?
+ from_gems_in(*installed_spec_directories)
+ else
+ from_gems_in(*deprecated) # HACK warn
end
+ end
- # Factory method to construct a source index instance for a
- # given path.
- #
- # spec_dirs::
- # List of directories to search for specifications. Each
- # directory should have a "specifications" subdirectory
- # containing the gem specifications.
- #
- # return::
- # SourceIndex instance
- #
- def from_gems_in(*spec_dirs)
- self.new.load_gems_in(*spec_dirs)
- end
-
- # Load a specification from a file (eval'd Ruby code)
- #
- # file_name:: [String] The .gemspec file
- # return:: Specification instance or nil if an error occurs
- #
- def load_specification(file_name)
- begin
- spec_code = File.read(file_name).untaint
- gemspec = eval spec_code, binding, file_name
- if gemspec.is_a?(Gem::Specification)
- gemspec.loaded_from = file_name
- return gemspec
- end
- alert_warning "File '#{file_name}' does not evaluate to a gem specification"
- rescue SyntaxError => e
- alert_warning e
- alert_warning spec_code
- rescue Exception => e
- alert_warning(e.inspect.to_s + "\n" + spec_code)
- alert_warning "Invalid .gemspec format in '#{file_name}'"
+ ##
+ # Return a list of directories in the current gem path that
+ # contain specifications.
+ #
+ # return::
+ # List of directory paths (all ending in "../specifications").
+
+ def installed_spec_directories
+ Gem.path.collect { |dir| File.join(dir, "specifications") }
+ end
+
+ ##
+ # Creates a new SourceIndex from the ruby format gem specifications in
+ # +spec_dirs+.
+
+ def from_gems_in(*spec_dirs)
+ self.new.load_gems_in(*spec_dirs)
+ end
+
+ ##
+ # Loads a ruby-format specification from +file_name+ and returns the
+ # loaded spec.
+
+ def load_specification(file_name)
+ begin
+ spec_code = File.read(file_name).untaint
+ gemspec = eval spec_code, binding, file_name
+ if gemspec.is_a?(Gem::Specification)
+ gemspec.loaded_from = file_name
+ return gemspec
end
- return nil
+ alert_warning "File '#{file_name}' does not evaluate to a gem specification"
+ rescue SyntaxError => e
+ alert_warning e
+ alert_warning spec_code
+ rescue Exception => e
+ alert_warning(e.inspect.to_s + "\n" + spec_code)
+ alert_warning "Invalid .gemspec format in '#{file_name}'"
end
-
+ return nil
end
- # Instance Methods -----------------------------------------------
+ end
- # Constructs a source index instance from the provided
- # specifications
- #
- # specifications::
- # [Hash] hash of [Gem name, Gem::Specification] pairs
- #
- def initialize(specifications={})
- @gems = specifications
- end
-
- # Reconstruct the source index from the list of source
- # directories.
- def load_gems_in(*spec_dirs)
- @gems.clear
- specs = Dir.glob File.join("{#{spec_dirs.join(',')}}", "*.gemspec")
-
- specs.each do |file_name|
- gemspec = self.class.load_specification(file_name.untaint)
- add_spec(gemspec) if gemspec
+ ##
+ # Constructs a source index instance from the provided
+ # specifications
+ #
+ # specifications::
+ # [Hash] hash of [Gem name, Gem::Specification] pairs
+
+ def initialize(specifications={})
+ @gems = specifications
+ end
+
+ ##
+ # Reconstruct the source index from the specifications in +spec_dirs+.
+
+ def load_gems_in(*spec_dirs)
+ @gems.clear
+
+ spec_dirs.reverse_each do |spec_dir|
+ spec_files = Dir.glob File.join(spec_dir, '*.gemspec')
+
+ spec_files.each do |spec_file|
+ gemspec = self.class.load_specification spec_file.untaint
+ add_spec gemspec if gemspec
end
- self
end
- # Returns a Hash of name => Specification of the latest versions of each
- # gem in this index.
- def latest_specs
- result, latest = Hash.new { |h,k| h[k] = [] }, {}
+ self
+ end
- self.each do |_, spec| # SourceIndex is not a hash, so we're stuck with each
- name = spec.name
- curr_ver = spec.version
- prev_ver = latest[name]
+ ##
+ # Returns a Hash of name => Specification of the latest versions of each
+ # gem in this index.
- next unless prev_ver.nil? or curr_ver >= prev_ver
+ def latest_specs
+ result = Hash.new { |h,k| h[k] = [] }
+ latest = {}
- if prev_ver.nil? or curr_ver > prev_ver then
- result[name].clear
- latest[name] = curr_ver
- end
+ sort.each do |_, spec|
+ name = spec.name
+ curr_ver = spec.version
+ prev_ver = latest.key?(name) ? latest[name].version : nil
+
+ next unless prev_ver.nil? or curr_ver >= prev_ver or
+ latest[name].platform != Gem::Platform::RUBY
- result[name] << spec
+ if prev_ver.nil? or
+ (curr_ver > prev_ver and spec.platform == Gem::Platform::RUBY) then
+ result[name].clear
+ latest[name] = spec
end
- result.values.flatten
- end
+ if spec.platform != Gem::Platform::RUBY then
+ result[name].delete_if do |result_spec|
+ result_spec.platform == spec.platform
+ end
+ end
- # Add a gem specification to the source index.
- def add_spec(gem_spec)
- @gems[gem_spec.full_name] = gem_spec
+ result[name] << spec
end
- # Remove a gem specification named +full_name+.
- def remove_spec(full_name)
- @gems.delete(full_name)
- end
+ result.values.flatten
+ end
- # Iterate over the specifications in the source index.
- def each(&block) # :yields: gem.full_name, gem
- @gems.each(&block)
- end
+ ##
+ # Add a gem specification to the source index.
- # The gem specification given a full gem spec name.
- def specification(full_name)
- @gems[full_name]
- end
+ def add_spec(gem_spec)
+ @gems[gem_spec.full_name] = gem_spec
+ end
- # The signature for the source index. Changes in the signature
- # indicate a change in the index.
- def index_signature
- require 'rubygems/digest/sha2'
+ ##
+ # Add gem specifications to the source index.
- Gem::SHA256.new.hexdigest(@gems.keys.sort.join(',')).to_s
+ def add_specs(*gem_specs)
+ gem_specs.each do |spec|
+ add_spec spec
end
+ end
- # The signature for the given gem specification.
- def gem_signature(gem_full_name)
- require 'rubygems/digest/sha2'
+ ##
+ # Remove a gem specification named +full_name+.
- Gem::SHA256.new.hexdigest(@gems[gem_full_name].to_yaml).to_s
- end
+ def remove_spec(full_name)
+ @gems.delete(full_name)
+ end
+
+ ##
+ # Iterate over the specifications in the source index.
+
+ def each(&block) # :yields: gem.full_name, gem
+ @gems.each(&block)
+ end
+
+ ##
+ # The gem specification given a full gem spec name.
+
+ def specification(full_name)
+ @gems[full_name]
+ end
+
+ ##
+ # The signature for the source index. Changes in the signature indicate a
+ # change in the index.
+
+ def index_signature
+ require 'rubygems/digest/sha2'
+
+ Gem::SHA256.new.hexdigest(@gems.keys.sort.join(',')).to_s
+ end
+
+ ##
+ # The signature for the given gem specification.
+
+ def gem_signature(gem_full_name)
+ require 'rubygems/digest/sha2'
- def size
- @gems.size
+ Gem::SHA256.new.hexdigest(@gems[gem_full_name].to_yaml).to_s
+ end
+
+ def size
+ @gems.size
+ end
+ alias length size
+
+ ##
+ # Find a gem by an exact match on the short name.
+
+ def find_name(gem_name, version_requirement = Gem::Requirement.default)
+ search(/^#{gem_name}$/, version_requirement)
+ end
+
+ ##
+ # Search for a gem by Gem::Dependency +gem_pattern+. If +only_platform+
+ # is true, only gems matching Gem::Platform.local will be returned. An
+ # Array of matching Gem::Specification objects is returned.
+ #
+ # For backwards compatibility, a String or Regexp pattern may be passed as
+ # +gem_pattern+, and a Gem::Requirement for +platform_only+. This
+ # behavior is deprecated and will be removed.
+
+ def search(gem_pattern, platform_only = false)
+ version_requirement = nil
+ only_platform = false
+
+ case gem_pattern # TODO warn after 2008/03, remove three months after
+ when Regexp then
+ version_requirement = platform_only || Gem::Requirement.default
+ when Gem::Dependency then
+ only_platform = platform_only
+ version_requirement = gem_pattern.version_requirements
+ gem_pattern = if gem_pattern.name.empty? then
+ //
+ else
+ /^#{Regexp.escape gem_pattern.name}$/
+ end
+ else
+ version_requirement = platform_only || Gem::Requirement.default
+ gem_pattern = /#{gem_pattern}/i
end
- alias length size
- # Find a gem by an exact match on the short name.
- def find_name(gem_name, version_requirement = Gem::Requirement.default)
- search(/^#{gem_name}$/, version_requirement)
+ unless Gem::Requirement === version_requirement then
+ version_requirement = Gem::Requirement.create version_requirement
end
- # Search for a gem by Gem::Dependency +gem_pattern+. If +only_platform+
- # is true, only gems matching Gem::Platform.local will be returned. An
- # Array of matching Gem::Specification objects is returned.
- #
- # For backwards compatibility, a String or Regexp pattern may be passed as
- # +gem_pattern+, and a Gem::Requirement for +platform_only+. This
- # behavior is deprecated and will be removed.
- def search(gem_pattern, platform_only = false)
- version_requirement = nil
- only_platform = false
-
- case gem_pattern # TODO warn after 2008/03, remove three months after
- when Regexp then
- version_requirement = platform_only || Gem::Requirement.default
- when Gem::Dependency then
- only_platform = platform_only
- version_requirement = gem_pattern.version_requirements
- gem_pattern = if gem_pattern.name.empty? then
- //
- else
- /^#{Regexp.escape gem_pattern.name}$/
- end
- else
- version_requirement = platform_only || Gem::Requirement.default
- gem_pattern = /#{gem_pattern}/i
- end
+ specs = @gems.values.select do |spec|
+ spec.name =~ gem_pattern and
+ version_requirement.satisfied_by? spec.version
+ end
- unless Gem::Requirement === version_requirement then
- version_requirement = Gem::Requirement.create version_requirement
+ if only_platform then
+ specs = specs.select do |spec|
+ Gem::Platform.match spec.platform
end
+ end
- specs = @gems.values.select do |spec|
- spec.name =~ gem_pattern and
- version_requirement.satisfied_by? spec.version
- end
+ specs.sort_by { |s| s.sort_obj }
+ end
- if only_platform then
- specs = specs.select do |spec|
- Gem::Platform.match spec.platform
- end
- end
+ ##
+ # Refresh the source index from the local file system.
+ #
+ # return:: Returns a pointer to itself.
- specs.sort_by { |s| s.sort_obj }
- end
+ def refresh!
+ load_gems_in(self.class.installed_spec_directories)
+ end
- # Refresh the source index from the local file system.
- #
- # return:: Returns a pointer to itself.
- #
- def refresh!
- load_gems_in(self.class.installed_spec_directories)
- end
+ ##
+ # Returns an Array of Gem::Specifications that are not up to date.
- # Returns an Array of Gem::Specifications that are not up to date.
- #
- def outdated
- dep = Gem::Dependency.new '', Gem::Requirement.default
+ def outdated
+ dep = Gem::Dependency.new '', Gem::Requirement.default
- remotes = Gem::SourceInfoCache.search dep, true
+ remotes = Gem::SourceInfoCache.search dep, true
- outdateds = []
+ 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
- end
+ latest_specs.each do |local|
+ name = local.name
+ remote = remotes.select { |spec| spec.name == name }.
+ sort_by { |spec| spec.version.to_ints }.
+ last
- outdateds
+ outdateds << name if remote and local.version < remote.version
end
- def update(source_uri)
- use_incremental = false
+ outdateds
+ end
- begin
- gem_names = fetch_quick_index source_uri
- remove_extra gem_names
- missing_gems = find_missing gem_names
+ ##
+ # Updates this SourceIndex from +source_uri+. If +all+ is false, only the
+ # latest gems are fetched.
- return false if missing_gems.size.zero?
+ def update(source_uri, all)
+ source_uri = URI.parse source_uri unless URI::Generic === source_uri
+ source_uri.path += '/' unless source_uri.path =~ /\/$/
- say "missing #{missing_gems.size} gems" if
- missing_gems.size > 0 and Gem.configuration.really_verbose
+ use_incremental = false
- use_incremental = missing_gems.size <= Gem.configuration.bulk_threshold
- rescue Gem::OperationNotSupportedError => ex
- alert_error "Falling back to bulk fetch: #{ex.message}" if
- Gem.configuration.really_verbose
- use_incremental = false
- end
+ begin
+ gem_names = fetch_quick_index source_uri, all
+ remove_extra gem_names
+ missing_gems = find_missing gem_names
- if use_incremental then
- update_with_missing(source_uri, missing_gems)
- else
- new_index = fetch_bulk_index(source_uri)
- @gems.replace(new_index.gems)
- end
+ return false if missing_gems.size.zero?
- true
- end
+ say "Missing metadata for #{missing_gems.size} gems" if
+ missing_gems.size > 0 and Gem.configuration.really_verbose
- def ==(other) # :nodoc:
- self.class === other and @gems == other.gems
+ use_incremental = missing_gems.size <= Gem.configuration.bulk_threshold
+ rescue Gem::OperationNotSupportedError => ex
+ alert_error "Falling back to bulk fetch: #{ex.message}" if
+ Gem.configuration.really_verbose
+ use_incremental = false
end
- def dump
- Marshal.dump(self)
+ if use_incremental then
+ update_with_missing(source_uri, missing_gems)
+ else
+ new_index = fetch_bulk_index(source_uri)
+ @gems.replace(new_index.gems)
end
- protected
+ true
+ end
- attr_reader :gems
+ def ==(other) # :nodoc:
+ self.class === other and @gems == other.gems
+ end
- private
+ def dump
+ Marshal.dump(self)
+ end
- def fetcher
- require 'rubygems/remote_fetcher'
+ protected
- Gem::RemoteFetcher.fetcher
- end
+ attr_reader :gems
- def fetch_index_from(source_uri)
- @fetch_error = nil
+ private
+
+ def fetcher
+ require 'rubygems/remote_fetcher'
+
+ Gem::RemoteFetcher.fetcher
+ end
+
+ def fetch_index_from(source_uri)
+ @fetch_error = nil
- indexes = %W[
+ indexes = %W[
Marshal.#{Gem.marshal_version}.Z
Marshal.#{Gem.marshal_version}
yaml.Z
yaml
]
- indexes.each do |name|
- spec_data = nil
- begin
- spec_data = fetcher.fetch_path("#{source_uri}/#{name}")
- spec_data = unzip(spec_data) if name =~ /\.Z$/
- if name =~ /Marshal/ then
- return Marshal.load(spec_data)
- else
- return YAML.load(spec_data)
- end
- rescue => e
- if Gem.configuration.really_verbose then
- alert_error "Unable to fetch #{name}: #{e.message}"
- end
- @fetch_error = e
+ indexes.each do |name|
+ spec_data = nil
+ index = source_uri + name
+ begin
+ spec_data = fetcher.fetch_path index
+ spec_data = unzip(spec_data) if name =~ /\.Z$/
+
+ if name =~ /Marshal/ then
+ return Marshal.load(spec_data)
+ else
+ return YAML.load(spec_data)
+ end
+ rescue => e
+ if Gem.configuration.really_verbose then
+ alert_error "Unable to fetch #{name}: #{e.message}"
end
+
+ @fetch_error = e
end
- nil
end
- def fetch_bulk_index(source_uri)
- say "Bulk updating Gem source index for: #{source_uri}"
+ nil
+ end
- index = fetch_index_from(source_uri)
- if index.nil? then
- raise Gem::RemoteSourceException,
+ def fetch_bulk_index(source_uri)
+ say "Bulk updating Gem source index for: #{source_uri}"
+
+ index = fetch_index_from(source_uri)
+ if index.nil? then
+ raise Gem::RemoteSourceException,
"Error fetching remote gem cache: #{@fetch_error}"
- end
- @fetch_error = nil
- index
end
+ @fetch_error = nil
+ index
+ end
+
+ ##
+ # Get the quick index needed for incremental updates.
+
+ def fetch_quick_index(source_uri, all)
+ index = all ? 'index' : 'latest_index'
- # Get the quick index needed for incremental updates.
- def fetch_quick_index(source_uri)
- zipped_index = fetcher.fetch_path source_uri + '/quick/index.rz'
- unzip(zipped_index).split("\n")
- rescue ::Exception => ex
+ zipped_index = fetcher.fetch_path source_uri + "quick/#{index}.rz"
+
+ unzip(zipped_index).split("\n")
+ rescue ::Exception => e
+ unless all then
+ say "Latest index not found, using quick index" if
+ Gem.configuration.really_verbose
+
+ fetch_quick_index source_uri, true
+ else
raise Gem::OperationNotSupportedError,
- "No quick index found: " + ex.message
+ "No quick index found: #{e.message}"
end
+ end
- # Make a list of full names for all the missing gemspecs.
- def find_missing(spec_names)
- spec_names.find_all { |full_name|
- specification(full_name).nil?
- }
- end
+ ##
+ # Make a list of full names for all the missing gemspecs.
- def remove_extra(spec_names)
- dictionary = spec_names.inject({}) { |h, k| h[k] = true; h }
- each do |name, spec|
- remove_spec name unless dictionary.include? name
- end
- end
+ def find_missing(spec_names)
+ spec_names.find_all { |full_name|
+ specification(full_name).nil?
+ }
+ end
- # Unzip the given string.
- def unzip(string)
- require 'zlib'
- Zlib::Inflate.inflate(string)
+ def remove_extra(spec_names)
+ dictionary = spec_names.inject({}) { |h, k| h[k] = true; h }
+ each do |name, spec|
+ remove_spec name unless dictionary.include? name
end
+ end
- # Tries to fetch Marshal representation first, then YAML
- def fetch_single_spec(source_uri, spec_name)
- @fetch_error = nil
- begin
- marshal_uri = source_uri + "/quick/Marshal.#{Gem.marshal_version}/#{spec_name}.gemspec.rz"
- zipped = fetcher.fetch_path marshal_uri
- return Marshal.load(unzip(zipped))
- rescue => ex
- @fetch_error = ex
- if Gem.configuration.really_verbose then
- say "unable to fetch marshal gemspec #{marshal_uri}: #{ex.class} - #{ex}"
- end
+ ##
+ # Unzip the given string.
+
+ def unzip(string)
+ require 'zlib'
+ Zlib::Inflate.inflate(string)
+ end
+
+ ##
+ # Tries to fetch Marshal representation first, then YAML
+
+ def fetch_single_spec(source_uri, spec_name)
+ @fetch_error = nil
+
+ begin
+ marshal_uri = source_uri + "quick/Marshal.#{Gem.marshal_version}/#{spec_name}.gemspec.rz"
+ zipped = fetcher.fetch_path marshal_uri
+ return Marshal.load(unzip(zipped))
+ rescue => ex
+ @fetch_error = ex
+
+ if Gem.configuration.really_verbose then
+ say "unable to fetch marshal gemspec #{marshal_uri}: #{ex.class} - #{ex}"
end
+ end
- begin
- yaml_uri = source_uri + "/quick/#{spec_name}.gemspec.rz"
- zipped = fetcher.fetch_path yaml_uri
- return YAML.load(unzip(zipped))
- rescue => ex
- @fetch_error = ex
- if Gem.configuration.really_verbose then
- say "unable to fetch YAML gemspec #{yaml_uri}: #{ex.class} - #{ex}"
- end
+ begin
+ yaml_uri = source_uri + "quick/#{spec_name}.gemspec.rz"
+ zipped = fetcher.fetch_path yaml_uri
+ return YAML.load(unzip(zipped))
+ rescue => ex
+ @fetch_error = ex
+ if Gem.configuration.really_verbose then
+ say "unable to fetch YAML gemspec #{yaml_uri}: #{ex.class} - #{ex}"
end
- nil
end
- # Update the cached source index with the missing names.
- def update_with_missing(source_uri, missing_names)
- progress = ui.progress_reporter(missing_names.size,
+ nil
+ end
+
+ ##
+ # Update the cached source index with the missing names.
+
+ def update_with_missing(source_uri, missing_names)
+ progress = ui.progress_reporter(missing_names.size,
"Updating metadata for #{missing_names.size} gems from #{source_uri}")
- missing_names.each do |spec_name|
- gemspec = fetch_single_spec(source_uri, spec_name)
- if gemspec.nil? then
- ui.say "Failed to download spec #{spec_name} from #{source_uri}:\n" \
+ missing_names.each do |spec_name|
+ gemspec = fetch_single_spec(source_uri, spec_name)
+ if gemspec.nil? then
+ ui.say "Failed to download spec #{spec_name} from #{source_uri}:\n" \
"\t#{@fetch_error.message}"
- else
- add_spec gemspec
- progress.updated spec_name
- end
- @fetch_error = nil
+ else
+ add_spec gemspec
+ progress.updated spec_name
end
- progress.done
- progress.count
+ @fetch_error = nil
end
-
+ progress.done
+ progress.count
end
- # Cache is an alias for SourceIndex to allow older YAMLized source
- # index objects to load properly.
+end
+
+module Gem
+
+ # :stopdoc:
+
+ # Cache is an alias for SourceIndex to allow older YAMLized source index
+ # objects to load properly.
Cache = SourceIndex
+ # :starddoc:
+
end
diff --git a/lib/rubygems/source_info_cache.rb b/lib/rubygems/source_info_cache.rb
index c84868a5f5..9383f6362e 100644
--- a/lib/rubygems/source_info_cache.rb
+++ b/lib/rubygems/source_info_cache.rb
@@ -4,6 +4,7 @@ require 'rubygems'
require 'rubygems/source_info_cache_entry'
require 'rubygems/user_interaction'
+##
# SourceInfoCache stores a copy of the gem index for each gem source.
#
# There are two possible cache locations, the system cache and the user cache:
@@ -25,7 +26,7 @@ require 'rubygems/user_interaction'
# @source_index => Gem::SourceIndex
# ...
# }
-#
+
class Gem::SourceInfoCache
include Gem::UserInteraction
@@ -37,7 +38,7 @@ class Gem::SourceInfoCache
def self.cache
return @cache if @cache
@cache = new
- @cache.refresh if Gem.configuration.update_sources
+ @cache.refresh false if Gem.configuration.update_sources
@cache
end
@@ -45,86 +46,178 @@ class Gem::SourceInfoCache
cache.cache_data
end
- # Search all source indexes for +pattern+.
- def self.search(pattern, platform_only = false)
- cache.search pattern, platform_only
+ ##
+ # The name of the system cache file.
+
+ def self.latest_system_cache_file
+ File.join File.dirname(system_cache_file),
+ "latest_#{File.basename system_cache_file}"
+ end
+
+ ##
+ # The name of the latest user cache file.
+
+ def self.latest_user_cache_file
+ File.join File.dirname(user_cache_file),
+ "latest_#{File.basename user_cache_file}"
+ end
+
+ ##
+ # Search all source indexes. See Gem::SourceInfoCache#search.
+
+ def self.search(*args)
+ cache.search(*args)
end
- # Search all source indexes for +pattern+. Only returns gems matching
- # Gem.platforms when +only_platform+ is true. See #search_with_source.
- def self.search_with_source(pattern, only_platform = false)
- cache.search_with_source(pattern, only_platform)
+ ##
+ # Search all source indexes returning the source_uri. See
+ # Gem::SourceInfoCache#search_with_source.
+
+ def self.search_with_source(*args)
+ cache.search_with_source(*args)
+ end
+
+ ##
+ # The name of the system cache file. (class method)
+
+ def self.system_cache_file
+ @system_cache_file ||= Gem.default_system_source_cache_dir
+ end
+
+ ##
+ # The name of the user cache file.
+
+ def self.user_cache_file
+ @user_cache_file ||=
+ ENV['GEMCACHE'] || Gem.default_user_source_cache_dir
end
def initialize # :nodoc:
@cache_data = nil
@cache_file = nil
@dirty = false
+ @only_latest = true
end
+ ##
# The most recent cache data.
+
def cache_data
return @cache_data if @cache_data
cache_file # HACK writable check
- begin
- # Marshal loads 30-40% faster from a String, and 2MB on 20061116 is small
- data = File.open cache_file, 'rb' do |fp| fp.read end
- @cache_data = Marshal.load data
-
- @cache_data.each do |url, sice|
- next unless sice.is_a?(Hash)
- update
- cache = sice['cache']
- size = sice['size']
- if cache.is_a?(Gem::SourceIndex) and size.is_a?(Numeric) then
- new_sice = Gem::SourceInfoCacheEntry.new cache, size
- @cache_data[url] = new_sice
- else # irreperable, force refetch.
- reset_cache_for(url)
- end
- end
- @cache_data
- rescue => e
- if Gem.configuration.really_verbose then
- say "Exception during cache_data handling: #{ex.class} - #{ex}"
- say "Cache file was: #{cache_file}"
- say "\t#{e.backtrace.join "\n\t"}"
- end
- reset_cache_data
- end
- end
-
- def reset_cache_for(url)
- say "Reseting cache for #{url}" if Gem.configuration.really_verbose
+ @only_latest = true
- sice = Gem::SourceInfoCacheEntry.new Gem::SourceIndex.new, 0
- sice.refresh url # HACK may be unnecessary, see ::cache and #refresh
+ @cache_data = read_cache_data latest_cache_file
- @cache_data[url] = sice
@cache_data
end
- def reset_cache_data
- @cache_data = {}
- end
+ ##
+ # The name of the cache file.
- # The name of the cache file to be read
def cache_file
return @cache_file if @cache_file
@cache_file = (try_file(system_cache_file) or
try_file(user_cache_file) or
raise "unable to locate a writable cache file")
end
+
+ ##
+ # Force cache file to be reset, useful for integration testing of rubygems
+
+ def reset_cache_file
+ @cache_file = nil
+ end
+ ##
# Write the cache to a local file (if it is dirty).
+
def flush
write_cache if @dirty
@dirty = false
end
- # Refreshes each source in the cache from its repository.
- def refresh
+ def latest_cache_data
+ latest_cache_data = {}
+
+ cache_data.each do |repo, sice|
+ latest = sice.source_index.latest_specs
+
+ new_si = Gem::SourceIndex.new
+ new_si.add_specs(*latest)
+
+ latest_sice = Gem::SourceInfoCacheEntry.new new_si, sice.size
+ latest_cache_data[repo] = latest_sice
+ end
+
+ latest_cache_data
+ end
+
+ ##
+ # The name of the latest cache file.
+
+ def latest_cache_file
+ File.join File.dirname(cache_file), "latest_#{File.basename cache_file}"
+ end
+
+ ##
+ # The name of the latest system cache file.
+
+ def latest_system_cache_file
+ self.class.latest_system_cache_file
+ end
+
+ ##
+ # The name of the latest user cache file.
+
+ def latest_user_cache_file
+ self.class.latest_user_cache_file
+ end
+
+ def read_all_cache_data
+ if @only_latest then
+ @only_latest = false
+ @cache_data = read_cache_data cache_file
+ end
+ end
+
+ def read_cache_data(file)
+ # Marshal loads 30-40% faster from a String, and 2MB on 20061116 is small
+ data = open file, 'rb' do |fp| fp.read end
+ cache_data = Marshal.load data
+
+ cache_data.each do |url, sice|
+ next unless sice.is_a?(Hash)
+ update
+
+ cache = sice['cache']
+ size = sice['size']
+
+ if cache.is_a?(Gem::SourceIndex) and size.is_a?(Numeric) then
+ new_sice = Gem::SourceInfoCacheEntry.new cache, size
+ cache_data[url] = new_sice
+ else # irreperable, force refetch.
+ reset_cache_for url, cache_data
+ end
+ end
+
+ cache_data
+ rescue => e
+ if Gem.configuration.really_verbose then
+ say "Exception during cache_data handling: #{e.class} - #{e}"
+ say "Cache file was: #{file}"
+ say "\t#{e.backtrace.join "\n\t"}"
+ end
+
+ {}
+ end
+
+ ##
+ # Refreshes each source in the cache from its repository. If +all+ is
+ # false, only latest gems are updated.
+
+ def refresh(all)
Gem.sources.each do |source_uri|
cache_entry = cache_data[source_uri]
if cache_entry.nil? then
@@ -132,14 +225,34 @@ class Gem::SourceInfoCache
cache_data[source_uri] = cache_entry
end
- update if cache_entry.refresh source_uri
+ update if cache_entry.refresh source_uri, all
end
flush
end
- # Searches all source indexes for +pattern+.
- def search(pattern, platform_only = false)
+ def reset_cache_for(url, cache_data)
+ say "Reseting cache for #{url}" if Gem.configuration.really_verbose
+
+ sice = Gem::SourceInfoCacheEntry.new Gem::SourceIndex.new, 0
+ sice.refresh url, false # HACK may be unnecessary, see ::cache and #refresh
+
+ cache_data[url] = sice
+ cache_data
+ end
+
+ def reset_cache_data
+ @cache_data = nil
+ end
+
+ ##
+ # Searches all source indexes. See Gem::SourceIndex#search for details on
+ # +pattern+ and +platform_only+. If +all+ is set to true, the full index
+ # will be loaded before searching.
+
+ def search(pattern, platform_only = false, all = false)
+ read_all_cache_data if all
+
cache_data.map do |source_uri, sic_entry|
next unless Gem.sources.include? source_uri
sic_entry.source_index.search pattern, platform_only
@@ -150,7 +263,9 @@ class Gem::SourceInfoCache
# only gems matching Gem.platforms will be selected. Returns an Array of
# pairs containing the Gem::Specification found and the source_uri it was
# found at.
- def search_with_source(pattern, only_platform = false)
+ def search_with_source(pattern, only_platform = false, all = false)
+ read_all_cache_data if all
+
results = []
cache_data.map do |source_uri, sic_entry|
@@ -164,68 +279,75 @@ class Gem::SourceInfoCache
results
end
- # Mark the cache as updated (i.e. dirty).
- def update
- @dirty = true
+ ##
+ # Set the source info cache data directly. This is mainly used for unit
+ # testing when we don't want to read a file system to grab the cached source
+ # index information. The +hash+ should map a source URL into a
+ # SourceInfoCacheEntry.
+
+ def set_cache_data(hash)
+ @cache_data = hash
+ update
end
+ ##
# The name of the system cache file.
+
def system_cache_file
self.class.system_cache_file
end
- # The name of the system cache file. (class method)
- def self.system_cache_file
- @system_cache_file ||= Gem.default_system_source_cache_dir
+ ##
+ # Determine if +path+ is a candidate for a cache file. Returns +path+ if
+ # it is, nil if not.
+
+ def try_file(path)
+ return path if File.writable? path
+ return nil if File.exist? path
+
+ dir = File.dirname path
+
+ unless File.exist? dir then
+ begin
+ FileUtils.mkdir_p dir
+ rescue RuntimeError, SystemCallError
+ return nil
+ end
+ end
+
+ if File.writable? dir then
+ open path, "wb" do |io| io.write Marshal.dump({}) end
+ return path
+ end
+
+ nil
+ end
+
+ ##
+ # Mark the cache as updated (i.e. dirty).
+
+ def update
+ @dirty = true
end
+ ##
# The name of the user cache file.
+
def user_cache_file
self.class.user_cache_file
end
- # The name of the user cache file. (class method)
- def self.user_cache_file
- @user_cache_file ||=
- ENV['GEMCACHE'] || Gem.default_user_source_cache_dir
- end
-
+ ##
# Write data to the proper cache.
+
def write_cache
- open cache_file, "wb" do |f|
- f.write Marshal.dump(cache_data)
+ open cache_file, 'wb' do |io|
+ io.write Marshal.dump(cache_data)
end
- end
- # Set the source info cache data directly. This is mainly used for unit
- # testing when we don't want to read a file system to grab the cached source
- # index information. The +hash+ should map a source URL into a
- # SourceInfoCacheEntry.
- def set_cache_data(hash)
- @cache_data = hash
- update
- end
-
- private
-
- # Determine if +fn+ is a candidate for a cache file. Return fn if
- # it is. Return nil if it is not.
- def try_file(fn)
- return fn if File.writable?(fn)
- return nil if File.exist?(fn)
- dir = File.dirname(fn)
- unless File.exist? dir then
- begin
- FileUtils.mkdir_p(dir)
- rescue RuntimeError, SystemCallError
- return nil
- end
+ open latest_cache_file, 'wb' do |io|
+ io.write Marshal.dump(latest_cache_data)
end
- if File.writable?(dir)
- File.open(fn, "wb") { |f| f << Marshal.dump({}) }
- return fn
- end
- nil
end
end
diff --git a/lib/rubygems/source_info_cache_entry.rb b/lib/rubygems/source_info_cache_entry.rb
index 02e03ca9db..c3f75e5b99 100644
--- a/lib/rubygems/source_info_cache_entry.rb
+++ b/lib/rubygems/source_info_cache_entry.rb
@@ -3,24 +3,31 @@ require 'rubygems/source_index'
require 'rubygems/remote_fetcher'
##
-# Entrys held by a SourceInfoCache.
+# Entries held by a SourceInfoCache.
class Gem::SourceInfoCacheEntry
+ ##
# The source index for this cache entry.
+
attr_reader :source_index
+ ##
# The size of the of the source entry. Used to determine if the
# source index has changed.
+
attr_reader :size
+ ##
# Create a cache entry.
+
def initialize(si, size)
@source_index = si || Gem::SourceIndex.new({})
@size = size
+ @all = false
end
- def refresh(source_uri)
+ def refresh(source_uri, all)
begin
marshal_uri = URI.join source_uri.to_s, "Marshal.#{Gem.marshal_version}"
remote_size = Gem::RemoteFetcher.fetcher.fetch_size marshal_uri
@@ -29,9 +36,12 @@ class Gem::SourceInfoCacheEntry
remote_size = Gem::RemoteFetcher.fetcher.fetch_size yaml_uri
end
- return false if @size == remote_size # TODO Use index_signature instead of size?
- updated = @source_index.update source_uri
+ # TODO Use index_signature instead of size?
+ return false if @size == remote_size and @all
+
+ updated = @source_index.update source_uri, all
@size = remote_size
+ @all = all
updated
end
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index be03150c96..de37a08b60 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -13,7 +13,7 @@ require 'rubygems/platform'
if RUBY_VERSION < '1.9' then
def Time.today
t = Time.now
- t - ((t.to_i + t.gmt_offset) % 86400)
+ t - ((t.to_f + t.gmt_offset) % 86400)
end unless defined? Time.today
end
# :startdoc:
diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb
index 1c979d8573..e2b5e5372b 100644
--- a/lib/rubygems/uninstaller.rb
+++ b/lib/rubygems/uninstaller.rb
@@ -12,7 +12,7 @@ require 'rubygems/user_interaction'
##
# An Uninstaller.
-#
+
class Gem::Uninstaller
include Gem::UserInteraction
@@ -21,8 +21,8 @@ class Gem::Uninstaller
# Constructs an Uninstaller instance
#
# gem:: [String] The Gem name to uninstall
- #
- def initialize(gem, options)
+
+ def initialize(gem, options = {})
@gem = gem
@version = options[:version] || Gem::Requirement.default
gem_home = options[:install_dir] || Gem.dir
@@ -30,12 +30,13 @@ class Gem::Uninstaller
@force_executables = options[:executables]
@force_all = options[:all]
@force_ignore = options[:ignore]
+ @bin_dir = options[:bin_dir]
end
##
# Performs the uninstall of the Gem. This removes the spec, the
# Gem directory, and the cached .gem file,
- #
+
def uninstall
list = Gem.source_index.search(/^#{@gem}$/, @version)
@@ -66,18 +67,14 @@ class Gem::Uninstaller
end
##
- # Remove executables and batch files (windows only) for the gem as
- # it is being installed
- #
- # gemspec::[Specification] the gem whose executables need to be removed.
- #
+ # Removes installed executables and batch files (windows only) for
+ # +gemspec+.
+
def remove_executables(gemspec)
return if gemspec.nil?
if gemspec.executables.size > 0 then
- bindir = Gem.bindir @gem_home
-
- raise Gem::FilePermissionError, bindir unless File.writable? bindir
+ bindir = @bin_dir ? @bin_dir : (Gem.bindir @gem_home)
list = Gem.source_index.search(gemspec.name).delete_if { |spec|
spec.version == gemspec.version
@@ -93,14 +90,19 @@ class Gem::Uninstaller
return if executables.size == 0
- answer = @force_executables || ask_yes_no(
- "Remove executables:\n" \
- "\t#{gemspec.executables.join(", ")}\n\nin addition to the gem?",
- true) # " # appease ruby-mode - don't ask
+ answer = if @force_executables.nil? then
+ ask_yes_no("Remove executables:\n" \
+ "\t#{gemspec.executables.join(", ")}\n\nin addition to the gem?",
+ true) # " # appease ruby-mode - don't ask
+ else
+ @force_executables
+ end
unless answer then
say "Executables and scripts will remain installed."
else
+ raise Gem::FilePermissionError, bindir unless File.writable? bindir
+
gemspec.executables.each do |exe_name|
say "Removing #{exe_name}"
FileUtils.rm_f File.join(bindir, exe_name)
@@ -110,23 +112,22 @@ class Gem::Uninstaller
end
end
+ ##
+ # Removes all gems in +list+.
#
- # list:: the list of all gems to remove
- #
- # Warning: this method modifies the +list+ parameter. Once it has
- # uninstalled a gem, it is removed from that list.
- #
+ # NOTE: removes uninstalled gems from +list+.
+
def remove_all(list)
- list.dup.each { |gem| remove(gem, list) }
+ list.dup.each { |spec| remove spec, list }
end
- #
+ ##
# spec:: the spec of the gem to be uninstalled
# list:: the list of all such gems
#
# Warning: this method modifies the +list+ parameter. Once it has
# uninstalled a gem, it is removed from that list.
- #
+
def remove(spec, list)
unless dependencies_ok? spec then
raise Gem::DependencyRemovalException,
@@ -134,10 +135,11 @@ class Gem::Uninstaller
end
unless path_ok? spec then
- alert("In order to remove #{spec.name}, please execute:\n" \
- "\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}")
- raise Gem::GemNotInHomeException,
+ e = Gem::GemNotInHomeException.new \
"Gem is not installed in directory #{@gem_home}"
+ e.spec = spec
+
+ raise e
end
raise Gem::FilePermissionError, spec.installation_path unless
@@ -182,8 +184,8 @@ class Gem::Uninstaller
def dependencies_ok?(spec)
return true if @force_ignore
- srcindex = Gem::SourceIndex.from_installed_gems
- deplist = Gem::DependencyList.from_source_index srcindex
+ source_index = Gem::SourceIndex.from_installed_gems
+ deplist = Gem::DependencyList.from_source_index source_index
deplist.ok_to_remove?(spec.full_name) || ask_if_ok(spec)
end
diff --git a/lib/rubygems/user_interaction.rb b/lib/rubygems/user_interaction.rb
index 4970f33c00..ffccb60314 100644
--- a/lib/rubygems/user_interaction.rb
+++ b/lib/rubygems/user_interaction.rb
@@ -68,7 +68,7 @@ module Gem
include DefaultUserInteraction
[
:choose_from_list, :ask, :ask_yes_no, :say, :alert, :alert_warning,
- :alert_error, :terminate_interaction!, :terminate_interaction
+ :alert_error, :terminate_interaction
].each do |methname|
class_eval %{
def #{methname}(*args)
@@ -182,16 +182,10 @@ module Gem
ask(question) if question
end
- # Terminate the application immediately without running any exit
- # handlers.
- def terminate_interaction!(status=-1)
- exit!(status)
- end
-
# Terminate the appliation normally, running any exit handlers
# that might have been defined.
- def terminate_interaction(status=0)
- exit(status)
+ def terminate_interaction(status = 0)
+ raise Gem::SystemExitException, status
end
# Return a progress reporter object
diff --git a/test/rubygems/gem_installer_test_case.rb b/test/rubygems/gem_installer_test_case.rb
new file mode 100644
index 0000000000..0d684eb1eb
--- /dev/null
+++ b/test/rubygems/gem_installer_test_case.rb
@@ -0,0 +1,86 @@
+require 'test/unit'
+require File.join(File.expand_path(File.dirname(__FILE__)), 'gemutilities')
+require 'rubygems/installer'
+
+class Gem::Installer
+ attr_accessor :gem_dir
+
+ attr_writer :format
+ attr_writer :gem_home
+ attr_writer :env_shebang
+ attr_writer :ignore_dependencies
+ attr_writer :format_executable
+ attr_writer :security_policy
+ attr_writer :spec
+ attr_writer :wrappers
+end
+
+class GemInstallerTestCase < RubyGemTestCase
+
+ def setup
+ super
+
+ @spec = quick_gem "a"
+ @gem = File.join @tempdir, "#{@spec.full_name}.gem"
+
+ util_build_gem @spec
+ FileUtils.mv File.join(@gemhome, 'cache', "#{@spec.full_name}.gem"),
+ @tempdir
+
+ @installer = Gem::Installer.new @gem
+ @installer.gem_dir = util_gem_dir
+ @installer.gem_home = @gemhome
+ @installer.spec = @spec
+ end
+
+ def util_gem_bindir(version = '2')
+ File.join util_gem_dir(version), "bin"
+ end
+
+ def util_gem_dir(version = '2')
+ File.join @gemhome, "gems", "a-#{version}" # HACK
+ end
+
+ def util_inst_bindir
+ File.join @gemhome, "bin"
+ end
+
+ def util_make_exec(version = '2', shebang = "#!/usr/bin/ruby")
+ @spec.executables = ["my_exec"]
+
+ FileUtils.mkdir_p util_gem_bindir(version)
+ exec_file = @installer.formatted_program_filename "my_exec"
+ exec_path = File.join util_gem_bindir(version), exec_file
+ File.open exec_path, 'w' do |f|
+ f.puts shebang
+ end
+ end
+
+ def util_setup_gem(ui = @ui) # HACK fix use_ui to make this automatic
+ @spec.files = File.join('lib', 'code.rb')
+ @spec.executables << 'executable'
+ @spec.extensions << File.join('ext', 'a', 'mkrf_conf.rb')
+
+ Dir.chdir @tempdir do
+ FileUtils.mkdir_p 'bin'
+ FileUtils.mkdir_p 'lib'
+ FileUtils.mkdir_p File.join('ext', 'a')
+ File.open File.join('bin', 'executable'), 'w' do |f| f.puts '1' end
+ File.open File.join('lib', 'code.rb'), 'w' do |f| f.puts '1' end
+ File.open File.join('ext', 'a', 'mkrf_conf.rb'), 'w' do |f|
+ f << <<-EOF
+ File.open 'Rakefile', 'w' do |rf| rf.puts "task :default" end
+ EOF
+ end
+
+ use_ui ui do
+ FileUtils.rm @gem
+ Gem::Builder.new(@spec).build
+ end
+ end
+
+ @installer = Gem::Installer.new @gem
+ end
+
+end
+
diff --git a/test/rubygems/gem_package_tar_test_case.rb b/test/rubygems/gem_package_tar_test_case.rb
new file mode 100644
index 0000000000..756b30ef27
--- /dev/null
+++ b/test/rubygems/gem_package_tar_test_case.rb
@@ -0,0 +1,146 @@
+require File.join(File.expand_path(File.dirname(__FILE__)), 'gemutilities')
+require 'rubygems/package'
+
+class File
+
+ # straight from setup.rb
+ def self.dir?(path)
+ # for corrupted windows stat()
+ File.directory?((path[-1,1] == '/') ? path : path + '/')
+ end
+
+ def self.read_b(name)
+ File.open(name, "rb") { |f| f.read }
+ end
+
+end
+
+class TarTestCase < RubyGemTestCase
+
+ def ASCIIZ(str, length)
+ str + "\0" * (length - str.length)
+ end
+
+ def SP(s)
+ s + " "
+ end
+
+ def SP_Z(s)
+ s + " \0"
+ end
+
+ def Z(s)
+ s + "\0"
+ end
+
+ def assert_headers_equal(expected, actual)
+ expected = expected.to_s unless String === expected
+ actual = actual.to_s unless String === actual
+
+ fields = %w[
+ name 100
+ mode 8
+ uid 8
+ gid 8
+ size 12
+ mtime 12
+ checksum 8
+ typeflag 1
+ linkname 100
+ magic 6
+ version 2
+ uname 32
+ gname 32
+ devmajor 8
+ devminor 8
+ prefix 155
+ ]
+
+ offset = 0
+
+ until fields.empty? do
+ name = fields.shift
+ length = fields.shift.to_i
+
+ if name == "checksum" then
+ chksum_off = offset
+ offset += length
+ next
+ end
+
+ assert_equal expected[offset, length], actual[offset, length],
+ "Field #{name} of the tar header differs."
+
+ offset += length
+ end
+
+ assert_equal expected[chksum_off, 8], actual[chksum_off, 8]
+ end
+
+ def calc_checksum(header)
+ sum = header.unpack("C*").inject{|s,a| s + a}
+ SP(Z(to_oct(sum, 6)))
+ end
+
+ def header(type, fname, dname, length, mode, checksum = nil)
+ checksum ||= " " * 8
+
+ arr = [ # struct tarfile_entry_posix
+ ASCIIZ(fname, 100), # char name[100]; ASCII + (Z unless filled)
+ Z(to_oct(mode, 7)), # char mode[8]; 0 padded, octal null
+ Z(to_oct(0, 7)), # char uid[8]; ditto
+ Z(to_oct(0, 7)), # char gid[8]; ditto
+ Z(to_oct(length, 11)), # char size[12]; 0 padded, octal, null
+ Z(to_oct(0, 11)), # char mtime[12]; 0 padded, octal, null
+ checksum, # char checksum[8]; 0 padded, octal, null, space
+ type, # char typeflag[1]; file: "0" dir: "5"
+ "\0" * 100, # char linkname[100]; ASCII + (Z unless filled)
+ "ustar\0", # char magic[6]; "ustar\0"
+ "00", # char version[2]; "00"
+ ASCIIZ("wheel", 32), # char uname[32]; ASCIIZ
+ ASCIIZ("wheel", 32), # char gname[32]; ASCIIZ
+ Z(to_oct(0, 7)), # char devmajor[8]; 0 padded, octal, null
+ Z(to_oct(0, 7)), # char devminor[8]; 0 padded, octal, null
+ ASCIIZ(dname, 155) # char prefix[155]; ASCII + (Z unless filled)
+ ]
+
+ format = "C100C8C8C8C12C12C8CC100C6C2C32C32C8C8C155"
+ h = if RUBY_VERSION >= "1.9" then
+ arr.join
+ else
+ arr = arr.join("").split(//).map{|x| x[0]}
+ arr.pack format
+ end
+ ret = h + "\0" * (512 - h.size)
+ assert_equal(512, ret.size)
+ ret
+ end
+
+ def tar_dir_header(name, prefix, mode)
+ h = header("5", name, prefix, 0, mode)
+ checksum = calc_checksum(h)
+ header("5", name, prefix, 0, mode, checksum)
+ end
+
+ def tar_file_header(fname, dname, mode, length)
+ h = header("0", fname, dname, length, mode)
+ checksum = calc_checksum(h)
+ header("0", fname, dname, length, mode, checksum)
+ end
+
+ def to_oct(n, pad_size)
+ "%0#{pad_size}o" % n
+ end
+
+ def util_entry(tar)
+ io = TempIO.new tar
+ header = Gem::Package::TarHeader.from io
+ entry = Gem::Package::TarReader::Entry.new header, io
+ end
+
+ def util_dir_entry
+ util_entry tar_dir_header("foo", "bar", 0)
+ end
+
+end
+
diff --git a/test/rubygems/gemutilities.rb b/test/rubygems/gemutilities.rb
index fb41210ac8..f12f4e88a5 100644
--- a/test/rubygems/gemutilities.rb
+++ b/test/rubygems/gemutilities.rb
@@ -10,9 +10,10 @@ at_exit { $SAFE = 1 }
require 'fileutils'
require 'test/unit'
require 'tmpdir'
+require 'tempfile'
require 'uri'
-require 'rubygems/gem_open_uri'
require 'rubygems/source_info_cache'
+require 'rubygems/package'
require File.join(File.expand_path(File.dirname(__FILE__)), 'mockgemui')
@@ -56,6 +57,20 @@ class FakeFetcher
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)
+
+ 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
@@ -76,6 +91,7 @@ class RubyGemTestCase < Test::Unit::TestCase
@gemhome = File.join @tempdir, "gemhome"
@gemcache = File.join(@gemhome, "source_cache")
@usrcache = File.join(@gemhome, ".gem", "user_cache")
+ @latest_usrcache = File.join(@gemhome, ".gem", "latest_user_cache")
FileUtils.mkdir_p @gemhome
@@ -101,6 +117,11 @@ class RubyGemTestCase < Test::Unit::TestCase
end
@marshal_version = "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
+
+ @private_key = File.expand_path File.join(File.dirname(__FILE__),
+ 'private_key.pem')
+ @public_cert = File.expand_path File.join(File.dirname(__FILE__),
+ 'public_cert.pem')
end
def teardown
@@ -135,25 +156,52 @@ class RubyGemTestCase < Test::Unit::TestCase
end
def prep_cache_files(lc)
- [ [lc.system_cache_file, 'sys'],
- [lc.user_cache_file, 'usr'],
- ].each do |fn, data|
- FileUtils.mkdir_p File.dirname(fn).untaint
- open(fn.dup.untaint, "wb") { |f| f.write(Marshal.dump({'key' => data})) }
+ @usr_si ||= Gem::SourceIndex.new
+ @usr_sice ||= Gem::SourceInfoCacheEntry.new @usr_si, 0
+
+ @sys_si ||= Gem::SourceIndex.new
+ @sys_sice ||= Gem::SourceInfoCacheEntry.new @sys_si, 0
+
+ latest_si = Gem::SourceIndex.new
+ latest_si.add_specs(*@sys_si.latest_specs)
+ latest_sys_sice = Gem::SourceInfoCacheEntry.new latest_si, 0
+
+ latest_si = Gem::SourceIndex.new
+ latest_si.add_specs(*@usr_si.latest_specs)
+ latest_usr_sice = Gem::SourceInfoCacheEntry.new latest_si, 0
+
+ [ [lc.system_cache_file, @sys_sice],
+ [lc.latest_system_cache_file, latest_sys_sice],
+ [lc.user_cache_file, @usr_sice],
+ [lc.latest_user_cache_file, latest_usr_sice],
+ ].each do |filename, data|
+ FileUtils.mkdir_p File.dirname(filename).untaint
+
+ open filename.dup.untaint, 'wb' do |f|
+ f.write Marshal.dump({ @gem_repo => data })
+ end
+ end
+ end
+
+ def read_cache(path)
+ open path.dup.untaint, 'rb' do |io|
+ Marshal.load io.read
end
end
- def read_cache(fn)
- open(fn.dup.untaint) { |f| Marshal.load f.read }
+ def read_binary(path)
+ Gem.read_binary path
end
def write_file(path)
path = File.join(@gemhome, path)
dir = File.dirname path
FileUtils.mkdir_p dir
- File.open(path, "w") { |io|
- yield(io)
- }
+
+ open path, 'wb' do |io|
+ yield io
+ end
+
path
end
@@ -204,6 +252,23 @@ class RubyGemTestCase < Test::Unit::TestCase
end
end
+ def util_gem(name, version, &block)
+ spec = quick_gem(name, version, &block)
+
+ util_build_gem spec
+
+ cache_file = File.join @tempdir, 'gems', "#{spec.original_name}.gem"
+ FileUtils.mv File.join(@gemhome, 'cache', "#{spec.original_name}.gem"),
+ cache_file
+ FileUtils.rm File.join(@gemhome, 'specifications',
+ "#{spec.full_name}.gemspec")
+
+ spec.loaded_from = nil
+ spec.loaded = false
+
+ [spec, cache_file]
+ end
+
def util_make_gems
init = proc do |s|
s.files = %w[lib/code.rb]
@@ -212,6 +277,7 @@ class RubyGemTestCase < Test::Unit::TestCase
@a1 = quick_gem('a', '1', &init)
@a2 = quick_gem('a', '2', &init)
+ @a_evil9 = quick_gem('a_evil', '9', &init)
@b2 = quick_gem('b', '2', &init)
@c1_2 = quick_gem('c', '1.2', &init)
@pl1 = quick_gem 'pl', '1' do |s| # l for legacy
@@ -227,7 +293,7 @@ class RubyGemTestCase < Test::Unit::TestCase
write_file File.join(*%W[gems #{@c1_2.original_name} lib code.rb]) do end
write_file File.join(*%W[gems #{@pl1.original_name} lib code.rb]) do end
- [@a1, @a2, @b2, @c1_2, @pl1].each { |spec| util_build_gem spec }
+ [@a1, @a2, @a_evil9, @b2, @c1_2, @pl1].each { |spec| util_build_gem spec }
FileUtils.rm_r File.join(@gemhome, 'gems', @pl1.original_name)
@@ -256,32 +322,19 @@ class RubyGemTestCase < Test::Unit::TestCase
@fetcher = FakeFetcher.new
@fetcher.uri = @uri
- @gem1 = quick_gem 'gem_one' do |gem|
- gem.files = %w[Rakefile lib/gem_one.rb]
- end
-
- @gem2 = quick_gem 'gem_two' do |gem|
- gem.files = %w[Rakefile lib/gem_two.rb]
- end
-
- @gem3 = quick_gem 'gem_three' do |gem| # missing gem
- gem.files = %w[Rakefile lib/gem_three.rb]
- end
-
- # this gem has a higher version and longer name than the gem we want
- @gem4 = quick_gem 'gem_one_evil', '666' do |gem|
- gem.files = %w[Rakefile lib/gem_one.rb]
- end
+ util_make_gems
- @all_gems = [@gem1, @gem2, @gem3, @gem4].sort
+ @all_gems = [@a1, @a2, @a_evil9, @b2, @c1_2].sort
@all_gem_names = @all_gems.map { |gem| gem.full_name }
- gem_names = [@gem1.full_name, @gem2.full_name, @gem4.full_name]
+ gem_names = [@a1.full_name, @a2.full_name, @b2.full_name]
@gem_names = gem_names.sort.join("\n")
- @source_index = Gem::SourceIndex.new @gem1.full_name => @gem1,
- @gem2.full_name => @gem2,
- @gem4.full_name => @gem4
+ @source_index = Gem::SourceIndex.new
+ @source_index.add_spec @a1
+ @source_index.add_spec @a2
+ @source_index.add_spec @a_evil9
+ @source_index.add_spec @c1_2
Gem::RemoteFetcher.instance_variable_set :@fetcher, @fetcher
end
@@ -294,7 +347,12 @@ class RubyGemTestCase < Test::Unit::TestCase
sice = Gem::SourceInfoCacheEntry.new si, 0
sic = Gem::SourceInfoCache.new
+
sic.set_cache_data( { @gem_repo => sice } )
+ sic.update
+ sic.write_cache
+ sic.reset_cache_data
+
Gem::SourceInfoCache.instance_variable_set :@cache, sic
si
end
@@ -313,3 +371,30 @@ 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/mockgemui.rb b/test/rubygems/mockgemui.rb
index d9bc2a8134..95a95fbf98 100644
--- a/test/rubygems/mockgemui.rb
+++ b/test/rubygems/mockgemui.rb
@@ -15,9 +15,8 @@ class MockGemUi < Gem::StreamUI
def initialize(input="")
super(StringIO.new(input), StringIO.new, StringIO.new)
@terminated = false
- @banged = false
end
-
+
def input
@ins.string
end
@@ -30,22 +29,15 @@ class MockGemUi < Gem::StreamUI
@errs.string
end
- def banged?
- @banged
- end
-
def terminated?
@terminated
end
- def terminate_interaction!(status=1)
- @terminated = true
- @banged = true
- fail TermError
- end
-
def terminate_interaction(status=0)
@terminated = true
- fail TermError
+
+ raise TermError
end
+
end
+
diff --git a/test/rubygems/private_key.pem b/test/rubygems/private_key.pem
new file mode 100644
index 0000000000..95b3dc76d8
--- /dev/null
+++ b/test/rubygems/private_key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAm24C6xixiAxO+i1f3L8XRMwrmLkt6BvT60mZ7g8HsklH3af7
+KNHA6vo/G6sujs2UsNO4HY8BTEneiVOXXWQlcsJ+Z5wEPlIu4zFueAmLefx+n9lE
+ulNIUDoyUenKX4spoMRnX8k4lXL05ho/6JFq0JdDY2DmAaQ4vvTz5mh9kZiybtHQ
+fzcpbA51uY+sjdQRCPDHyUUfh0SmWJlLYMwcBdVeCiGUPBLi+iP5x1btO4uiJK6Q
+IMaV1H3SUCYtKGQKl7qwFd8k8ZBcHYOtmK61tupg3vqWQc0em6SxPj5lws8+1MVK
+twBNIDx24jF4ntxBRNKMZ7FN5SHbobAgDYkPAQIDAQABAoIBAGQilgK8X/PUajVH
+clEXU3hhSV0VQHwfIYKeYms6h6zXBVPKW0dLC0zXeDztJgueasMZQ67XaPCrTpGO
+px/l2zJ6F1HM8/bqn4aDXDY9f/xRLYryQRMBgL8fHzgitNylHWaT4j2Vt7yg2SI9
+mxrMRNKqASJPVR+Nm3l6+n9gpjVb99wEucWplPPHI6KhXLYPZOqSwt+zaH5roz3k
+UQmMs0Bs4hF1SzVl0n+KNoXHOwswVrmBWXgWvm2OhnwY2e26jfejc8toJc/ShAJ7
+C9exnrdimcgEKbd22Sum4G00CDYhcrG5LHHqkgwifcAEVctrvBZBZHGgpxlO8a8U
+eF2Vr7kCgYEAykdrBlzp7Fn9xzUInBQ3NXTTYAq51lpuJdmHQmPuTSY0buoHkd9f
+xbUCZ2qR9QAesrx4hI0qGLetc8IOKDoWx2rPepCCvO3Kx61o1SB5fAvBue03qVoq
+HqACX3Uk24Em8zAz9xuP13ETH/wU7sUbUxRHMCre6ZDmlxn4g5l+Nl8CgYEAxLVl
+22yBx0dfRr3UsHY9rxll2gIlnfnYfiJzq8wetzt/TfztRV5ILz7FyWqL5d7IoqkA
+fT2V4HAasRJASnKohwJe7z5M/H2ExwkGNFvY+jefb2CoUl5WouK9AlhbqBk3zmHi
+sY5GqQkAp/kHMntEin+sErJw6mkgAGdser3a9p8CgYEAqi31w++tunRnxw4+RRnY
+7Pdx0k6T1NxV6TAe1ONAHNY0rM/mOHqml65W7GzDiU1lhlh8SIB/VzZJDqfHw15D
+xdh94A7uf0bMILwrA4wDyTIW9Xa3Kpq57vQNqwPiU25QN69pOM+Ob+IpBfLOJafc
++kOINOUMj5Kh/aQS6Zzci58CgYEAk24dlFKEBjbRCvU2FrfYTYcsljPru7ZJc2gg
+588J6m0WYf5CWy5pzbcviGFpzvSlzXv7GOLylQ+QgcxbETFUbDPzsT4xd0AgJwj1
+dIKuYgMUZOa94VZBer2TydEtiRS1heJJhKhM/1329u4nXceTvHYqIq1JAfeee48I
+eAoZtaMCgYBz1FjWFQnMTD5nmyPEEZneoBPAR5+9jwOps+IYOoHtazoMFszzd0qo
+JZW3Ihn9KRrVSxfFApKS/ZwjiZ+tJUk7DE/v/0l0sszefY7s8b0pL1lpeZSoL71e
+QoG1WLXUiDV3BRlmyOAF1h3p12KRTLgwubN51ajECwcs3QwE+ZT8Gg==
+-----END RSA PRIVATE KEY-----
diff --git a/test/rubygems/public_cert.pem b/test/rubygems/public_cert.pem
new file mode 100644
index 0000000000..9b7c3d8e98
--- /dev/null
+++ b/test/rubygems/public_cert.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDNjCCAh6gAwIBAgIBADANBgkqhkiG9w0BAQUFADBBMRAwDgYDVQQDDAdkcmJy
+YWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZFgNu
+ZXQwHhcNMDcxMjIxMDIwNDE0WhcNMDgxMjIwMDIwNDE0WjBBMRAwDgYDVQQDDAdk
+cmJyYWluMRgwFgYKCZImiZPyLGQBGRYIc2VnbWVudDcxEzARBgoJkiaJk/IsZAEZ
+FgNuZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbbgLrGLGIDE76
+LV/cvxdEzCuYuS3oG9PrSZnuDweySUfdp/so0cDq+j8bqy6OzZSw07gdjwFMSd6J
+U5ddZCVywn5nnAQ+Ui7jMW54CYt5/H6f2US6U0hQOjJR6cpfiymgxGdfyTiVcvTm
+Gj/okWrQl0NjYOYBpDi+9PPmaH2RmLJu0dB/NylsDnW5j6yN1BEI8MfJRR+HRKZY
+mUtgzBwF1V4KIZQ8EuL6I/nHVu07i6IkrpAgxpXUfdJQJi0oZAqXurAV3yTxkFwd
+g62YrrW26mDe+pZBzR6bpLE+PmXCzz7UxUq3AE0gPHbiMXie3EFE0oxnsU3lIduh
+sCANiQ8BAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
+BBS5k4Z75VSpdM0AclG2UvzFA/VW5DANBgkqhkiG9w0BAQUFAAOCAQEAHagT4lfX
+kP/hDaiwGct7XPuVGbrOsKRVD59FF5kETBxEc9UQ1clKWngf8JoVuEoKD774dW19
+bU0GOVWO+J6FMmT/Cp7nuFJ79egMf/gy4gfUfQMuvfcr6DvZUPIs9P/TlK59iMYF
+DIOQ3DxdF3rMzztNUCizN4taVscEsjCcgW6WkUJnGdqlu3OHWpQxZBJkBTjPCoc6
+UW6on70SFPmAy/5Cq0OJNGEWBfgD9q7rrs/X8GGwUWqXb85RXnUVi/P8Up75E0ag
+14jEc90kN+C7oI/AGCBN0j6JnEtYIEJZibjjDJTSMWlUKKkj30kq7hlUC2CepJ4v
+x52qPcexcYZR7w==
+-----END CERTIFICATE-----
diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb
index e0160950f8..b04d69d509 100644
--- a/test/rubygems/test_gem.rb
+++ b/test/rubygems/test_gem.rb
@@ -19,6 +19,7 @@ class TestGem < RubyGemTestCase
expected = [
File.join(@gemhome, *%W[gems #{@a1.full_name} lib]),
File.join(@gemhome, *%W[gems #{@a2.full_name} lib]),
+ File.join(@gemhome, *%W[gems #{@a_evil9.full_name} lib]),
File.join(@gemhome, *%W[gems #{@b2.full_name} lib]),
File.join(@gemhome, *%W[gems #{@c1_2.full_name} lib]),
File.join(@gemhome, *%W[gems #{@pl1.full_name} lib]),
@@ -213,6 +214,7 @@ class TestGem < RubyGemTestCase
expected = [
File.join(@gemhome, *%W[gems #{@a2.full_name} lib]),
+ File.join(@gemhome, *%W[gems #{@a_evil9.full_name} lib]),
File.join(@gemhome, *%W[gems #{@b2.full_name} lib]),
File.join(@gemhome, *%W[gems #{@c1_2.full_name} lib]),
File.join(@gemhome, *%W[gems #{@pl1.full_name} lib]),
@@ -226,7 +228,7 @@ class TestGem < RubyGemTestCase
install_gem foo
Gem.source_index = nil
- Gem.activate 'foo', false
+ Gem.activate 'foo'
assert_equal true, Gem.loaded_specs.keys.include?('foo')
end
@@ -235,9 +237,29 @@ class TestGem < RubyGemTestCase
assert_equal [Gem.dir], Gem.path
end
+ def test_self_path_APPLE_GEM_HOME
+ Gem.clear_paths
+ Gem.const_set :APPLE_GEM_HOME, '/tmp/apple_gem_home'
+
+ assert Gem.path.include?('/tmp/apple_gem_home')
+ ensure
+ Gem.send :remove_const, :APPLE_GEM_HOME
+ end
+
+ def test_self_path_APPLE_GEM_HOME_GEM_PATH
+ Gem.clear_paths
+ ENV['GEM_PATH'] = @gemhome
+ Gem.const_set :APPLE_GEM_HOME, '/tmp/apple_gem_home'
+
+ assert !Gem.path.include?('/tmp/apple_gem_home')
+ ensure
+ Gem.send :remove_const, :APPLE_GEM_HOME
+ end
+
def test_self_path_ENV_PATH
Gem.clear_paths
path_count = Gem.path.size
+ path_count -= 1 if defined? APPLE_GEM_HOME
Gem.clear_paths
util_ensure_gem_dirs
@@ -257,8 +279,8 @@ class TestGem < RubyGemTestCase
ENV['GEM_PATH'] = dirs.join File::PATH_SEPARATOR
assert_equal @gemhome, Gem.dir
+
paths = [Gem.dir]
- paths << APPLE_GEM_HOME if defined? APPLE_GEM_HOME
assert_equal @additional + paths, Gem.path
end
@@ -270,8 +292,8 @@ class TestGem < RubyGemTestCase
ENV['GEM_PATH'] = @additional.join(File::PATH_SEPARATOR)
assert_equal @gemhome, Gem.dir
+
paths = [Gem.dir]
- paths.insert(0, APPLE_GEM_HOME) if defined? APPLE_GEM_HOME
assert_equal @additional + paths, Gem.path
end
@@ -284,6 +306,18 @@ class TestGem < RubyGemTestCase
assert_equal File.dirname(File.dirname(file_name)), Gem.prefix
end
+ def test_self_prefix_odd
+ orig_sitelibdir = Gem::ConfigMap[:sitelibdir]
+
+ file_name = File.expand_path __FILE__
+ prefix = File.join File.dirname(File.dirname(file_name)), 'lib'
+ Gem::ConfigMap[:sitelibdir] = prefix.sub(/[\w]\//, '\&/')
+
+ assert_nil Gem.prefix
+ ensure
+ Gem::ConfigMap[:sitelibdir] = orig_sitelibdir
+ end
+
def test_self_required_location
util_make_gems
@@ -295,6 +329,13 @@ class TestGem < RubyGemTestCase
Gem.required_location("a", "code.rb", "= 2")
end
+ def test_self_ruby_version
+ version = RUBY_VERSION.dup
+ version << ".#{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
+
+ assert_equal Gem::Version.new(version), Gem.ruby_version
+ end
+
def test_self_searcher
assert_kind_of Gem::GemPathSearcher, Gem.searcher
end
diff --git a/test/rubygems/test_gem_command_manager.rb b/test/rubygems/test_gem_command_manager.rb
index 4198bb9a2a..b7767f421d 100644
--- a/test/rubygems/test_gem_command_manager.rb
+++ b/test/rubygems/test_gem_command_manager.rb
@@ -67,11 +67,12 @@ class TestGemCommandManager < RubyGemTestCase
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[:bin_dir]
#check settings
check_options = nil
@command_manager.process_args(
- "install --force --test --local --rdoc --install-dir . --version 3.0 --no-wrapper")
+ "install --force --test --local --rdoc --install-dir . --version 3.0 --no-wrapper --bindir . ")
assert_equal true, check_options[:test]
assert_equal true, check_options[:generate_rdoc]
assert_equal true, check_options[:force]
@@ -79,6 +80,7 @@ class TestGemCommandManager < RubyGemTestCase
assert_equal false, check_options[:wrappers]
assert_equal Gem::Requirement.new('3.0'), check_options[:version]
assert_equal Dir.pwd, check_options[:install_dir]
+ assert_equal Dir.pwd, check_options[:bin_dir]
#check remote domain
check_options = nil
@@ -164,7 +166,7 @@ class TestGemCommandManager < RubyGemTestCase
#check defaults
@command_manager.process_args("query")
- assert_equal(/.*/, check_options[:name])
+ assert_equal(//, check_options[:name])
assert_equal :local, check_options[:domain]
assert_equal false, check_options[:details]
diff --git a/test/rubygems/test_gem_commands_environment_command.rb b/test/rubygems/test_gem_commands_environment_command.rb
index 0913888a6d..5568478652 100644
--- a/test/rubygems/test_gem_commands_environment_command.rb
+++ b/test/rubygems/test_gem_commands_environment_command.rb
@@ -62,6 +62,21 @@ class TestGemCommandsEnvironmentCommand < RubyGemTestCase
assert_equal '', @ui.error
end
+ def test_execute_gempath_multiple
+ Gem.clear_paths
+ path = [@gemhome, "#{@gemhome}2"].join File::PATH_SEPARATOR
+ ENV['GEM_PATH'] = path
+
+ @cmd.send :handle_options, %w[gempath]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ assert_equal "#{Gem.path.join File::PATH_SEPARATOR}\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
def test_execute_packageversion
@cmd.send :handle_options, %w[packageversion]
diff --git a/test/rubygems/test_gem_commands_fetch_command.rb b/test/rubygems/test_gem_commands_fetch_command.rb
index d8651680b0..5a42e4e81e 100644
--- a/test/rubygems/test_gem_commands_fetch_command.rb
+++ b/test/rubygems/test_gem_commands_fetch_command.rb
@@ -15,13 +15,12 @@ class TestGemCommandsFetchCommand < RubyGemTestCase
def test_execute
util_setup_fake_fetcher
- util_build_gem @gem1
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] =
@source_index.dump
- @fetcher.data["#{@gem_repo}/gems/#{@gem1.full_name}.gem"] =
- File.read(File.join(@gemhome, 'cache', "#{@gem1.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] = [@gem1.name]
+ @cmd.options[:args] = [@a2.name]
use_ui @ui do
Dir.chdir @tempdir do
@@ -29,7 +28,7 @@ class TestGemCommandsFetchCommand < RubyGemTestCase
end
end
- assert File.exist?(File.join(@tempdir, "#{@gem1.full_name}.gem"))
+ assert File.exist?(File.join(@tempdir, "#{@a2.full_name}.gem"))
end
end
diff --git a/test/rubygems/test_gem_commands_install_command.rb b/test/rubygems/test_gem_commands_install_command.rb
index 78840be8c6..101195a43e 100644
--- a/test/rubygems/test_gem_commands_install_command.rb
+++ b/test/rubygems/test_gem_commands_install_command.rb
@@ -34,25 +34,26 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
util_setup_fake_fetcher
@cmd.options[:domain] = :local
- gem1 = quick_gem 'gem_one'
- util_build_gem gem1
- FileUtils.mv File.join(@gemhome, 'cache', "#{@gem1.full_name}.gem"),
+ FileUtils.mv File.join(@gemhome, 'cache', "#{@a2.full_name}.gem"),
File.join(@tempdir)
- @cmd.options[:args] = [gem1.name]
+ @cmd.options[:args] = [@a2.name]
use_ui @ui do
orig_dir = Dir.pwd
begin
Dir.chdir @tempdir
- @cmd.execute
+ e = assert_raises Gem::SystemExitException do
+ @cmd.execute
+ end
+ assert_equal 0, e.exit_code
ensure
Dir.chdir orig_dir
end
end
out = @ui.output.split "\n"
- assert_equal "Successfully installed #{@gem1.full_name}", out.shift
+ assert_equal "Successfully installed #{@a2.full_name}", out.shift
assert_equal "1 gem installed", out.shift
assert out.empty?, out.inspect
end
@@ -61,14 +62,17 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
util_setup_fake_fetcher
@cmd.options[:domain] = :local
- @cmd.options[:args] = %w[gem_one]
+ @cmd.options[:args] = %w[no_such_gem]
use_ui @ui do
- @cmd.execute
+ e = assert_raises Gem::SystemExitException do
+ @cmd.execute
+ end
+ assert_equal 2, e.exit_code
end
# HACK no repository was checked
- assert_equal "ERROR: could not find gem_one locally or in a repository\n",
+ assert_equal "ERROR: could not find no_such_gem locally or in a repository\n",
@ui.error
end
@@ -88,7 +92,10 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
@cmd.options[:args] = %w[nonexistent]
use_ui @ui do
- @cmd.execute
+ e = assert_raises Gem::SystemExitException do
+ @cmd.execute
+ end
+ assert_equal 2, e.exit_code
end
assert_equal "ERROR: could not find nonexistent locally or in a repository\n",
@@ -100,25 +107,27 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
@cmd.options[:generate_ri] = true
util_setup_fake_fetcher
- util_build_gem @gem1
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] =
@source_index.dump
- @fetcher.data["#{@gem_repo}/gems/gem_one-0.0.2.gem"] =
- File.read(File.join(@gemhome, 'cache', "#{@gem1.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] = [@gem1.name]
+ @cmd.options[:args] = [@a2.name]
use_ui @ui do
- @cmd.execute
+ e = assert_raises Gem::SystemExitException do
+ @cmd.execute
+ end
+ assert_equal 0, e.exit_code
end
out = @ui.output.split "\n"
assert_match %r|Bulk updating|, out.shift
- assert_equal "Successfully installed #{@gem1.full_name}", out.shift
+ assert_equal "Successfully installed #{@a2.full_name}", out.shift
assert_equal "1 gem installed", out.shift
- assert_equal "Installing ri documentation for #{@gem1.full_name}...",
+ assert_equal "Installing ri documentation for #{@a2.full_name}...",
out.shift
- assert_equal "Installing RDoc documentation for #{@gem1.full_name}...",
+ assert_equal "Installing RDoc documentation for #{@a2.full_name}...",
out.shift
assert out.empty?, out.inspect
end
@@ -127,31 +136,30 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
util_setup_fake_fetcher
@cmd.options[:domain] = :local
- gem1 = quick_gem 'gem_one'
- util_build_gem gem1
- FileUtils.mv File.join(@gemhome, 'cache', "#{@gem1.full_name}.gem"),
+ FileUtils.mv File.join(@gemhome, 'cache', "#{@a2.full_name}.gem"),
File.join(@tempdir)
- gem2 = quick_gem 'gem_two'
- util_build_gem gem2
- FileUtils.mv File.join(@gemhome, 'cache', "#{@gem2.full_name}.gem"),
+ FileUtils.mv File.join(@gemhome, 'cache', "#{@b2.full_name}.gem"),
File.join(@tempdir)
- @cmd.options[:args] = [gem1.name, gem2.name]
+ @cmd.options[:args] = [@a2.name, @b2.name]
use_ui @ui do
orig_dir = Dir.pwd
begin
Dir.chdir @tempdir
- @cmd.execute
+ e = assert_raises Gem::SystemExitException do
+ @cmd.execute
+ end
+ assert_equal 0, e.exit_code
ensure
Dir.chdir orig_dir
end
end
out = @ui.output.split "\n"
- assert_equal "Successfully installed #{@gem1.full_name}", out.shift
- assert_equal "Successfully installed #{@gem2.full_name}", out.shift
+ assert_equal "Successfully installed #{@a2.full_name}", out.shift
+ assert_equal "Successfully installed #{@b2.full_name}", out.shift
assert_equal "2 gems installed", 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 4430ccfd33..7e71419089 100644
--- a/test/rubygems/test_gem_commands_query_command.rb
+++ b/test/rubygems/test_gem_commands_query_command.rb
@@ -7,20 +7,31 @@ class TestGemCommandsQueryCommand < RubyGemTestCase
def setup
super
- @foo_gem = quick_gem 'foo' do |spec|
- spec.summary = 'This is a lot of text. ' * 5
- end
- @foo_gem_p = quick_gem 'foo' do |spec|
- spec.summary = 'This is a lot of text. ' * 5
- spec.platform = Gem::Platform::CURRENT
- end
- @bar_gem = quick_gem 'bar'
+ 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
+ raise Gem::RemoteFetcher::FetchError
+ end
end
def test_execute
- util_setup_source_info_cache @foo_gem, @foo_gem_p
+ cache = Gem::SourceInfoCache.cache
+ cache.update
+ cache.write_cache
+ cache.reset_cache_data
+
+ 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]
@@ -32,16 +43,50 @@ class TestGemCommandsQueryCommand < RubyGemTestCase
*** REMOTE GEMS ***
-foo (2)
+a (2)
EOF
assert_equal expected, @ui.output
assert_equal '', @ui.error
end
- def test_execute_details
- util_setup_source_info_cache @foo_gem
+ def test_execute_all
+ cache = Gem::SourceInfoCache.cache
+ cache.update
+ cache.write_cache
+ cache.reset_cache_data
+
+ 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]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+*** REMOTE GEMS ***
+
+Updating metadata for 1 gems from http://gems.example.com/
+.
+complete
+a (2, 1)
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_details
@cmd.handle_options %w[-r -d]
use_ui @ui do
@@ -52,18 +97,94 @@ foo (2)
*** REMOTE GEMS ***
-foo (2)
- This is a lot of text. This is a lot of text. This is a lot of
- text. This is a lot of text. This is a lot of text.
+a (2, 1)
+ 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)
+ this is a summary
EOF
assert_equal expected, @ui.output
assert_equal '', @ui.error
end
- def test_execute_no_versions
- util_setup_source_info_cache @foo_gem, @bar_gem
+ def test_execute_installed
+ @cmd.handle_options %w[-n c --installed]
+ e = assert_raise Gem::SystemExitException do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal 0, e.exit_code
+
+ assert_equal "true\n", @ui.output
+ assert_equal '', @ui.error
+ end
+
+ def test_execute_installed_no_name
+ @cmd.handle_options %w[--installed]
+
+ e = assert_raise Gem::SystemExitException do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal '', @ui.output
+ assert_equal "ERROR: You must specify a gem name\n", @ui.error
+
+ assert_equal 4, e.exit_code
+ end
+
+ def test_execute_installed_not_installed
+ @cmd.handle_options %w[-n not_installed --installed]
+
+ e = assert_raise Gem::SystemExitException do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal "false\n", @ui.output
+ assert_equal '', @ui.error
+
+ assert_equal 1, e.exit_code
+ end
+
+ def test_execute_installed_version
+ @cmd.handle_options %w[-n c --installed --version 1.2]
+
+ e = assert_raise Gem::SystemExitException do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal "true\n", @ui.output
+ assert_equal '', @ui.error
+
+ assert_equal 0, e.exit_code
+ end
+
+ def test_execute_installed_version_not_installed
+ @cmd.handle_options %w[-n c --installed --version 2]
+
+ e = assert_raise Gem::SystemExitException do
+ use_ui @ui do
+ @cmd.execute
+ end
+ end
+
+ assert_equal "false\n", @ui.output
+ assert_equal '', @ui.error
+
+ assert_equal 1, e.exit_code
+ end
+
+ def test_execute_no_versions
@cmd.handle_options %w[-r --no-versions]
use_ui @ui do
@@ -74,8 +195,8 @@ foo (2)
*** REMOTE GEMS ***
-bar
-foo
+a
+pl
EOF
assert_equal expected, @ui.output
diff --git a/test/rubygems/test_gem_commands_server_command.rb b/test/rubygems/test_gem_commands_server_command.rb
index 3e6af2e11c..2985b036d8 100644
--- a/test/rubygems/test_gem_commands_server_command.rb
+++ b/test/rubygems/test_gem_commands_server_command.rb
@@ -20,7 +20,7 @@ class TestGemCommandsServerCommand < RubyGemTestCase
@cmd.send :handle_options, %w[-p 9999 -d /nonexistent --daemon]
assert_equal true, @cmd.options[:daemon]
- assert_equal '/nonexistent', @cmd.options[:gemdir]
+ assert_equal File.expand_path('/nonexistent'), @cmd.options[:gemdir]
assert_equal 9999, @cmd.options[:port]
end
end
diff --git a/test/rubygems/test_gem_commands_sources_command.rb b/test/rubygems/test_gem_commands_sources_command.rb
index 3d1ab801b1..7ba88fad98 100644
--- a/test/rubygems/test_gem_commands_sources_command.rb
+++ b/test/rubygems/test_gem_commands_sources_command.rb
@@ -31,10 +31,11 @@ class TestGemCommandsSourcesCommand < RubyGemTestCase
def test_execute_add
util_setup_fake_fetcher
- @si = Gem::SourceIndex.new @gem1.full_name => @gem1.name
+ si = Gem::SourceIndex.new
+ si.add_spec @a1
@fetcher.data["http://beta-gems.example.com/Marshal.#{@marshal_version}"] =
- @si.dump
+ si.dump
@cmd.handle_options %w[--add http://beta-gems.example.com]
@@ -45,7 +46,7 @@ class TestGemCommandsSourcesCommand < RubyGemTestCase
end
expected = <<-EOF
-Bulk updating Gem source index for: http://beta-gems.example.com
+Bulk updating Gem source index for: http://beta-gems.example.com/
http://beta-gems.example.com added to sources
EOF
@@ -60,14 +61,11 @@ http://beta-gems.example.com added to sources
def test_execute_add_nonexistent_source
util_setup_fake_fetcher
- @si = Gem::SourceIndex.new @gem1.full_name => @gem1.name
-
@fetcher.data["http://beta-gems.example.com/Marshal.#{@marshal_version}"] =
proc do
raise Gem::RemoteFetcher::FetchError, 'it died'
end
-
Gem::RemoteFetcher.instance_variable_set :@fetcher, @fetcher
@cmd.handle_options %w[--add http://beta-gems.example.com]
@@ -104,6 +102,41 @@ beta-gems.example.com is not a URI
assert_equal '', @ui.error
end
+ def test_execute_clear_all
+ @cmd.handle_options %w[--clear-all]
+
+ util_setup_source_info_cache
+
+ cache = Gem::SourceInfoCache.cache
+ cache.update
+ cache.write_cache
+
+ assert File.exist?(cache.system_cache_file),
+ 'system cache file'
+ assert File.exist?(cache.latest_system_cache_file),
+ 'latest system cache file'
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = <<-EOF
+*** Removed user source cache ***
+*** Removed latest user source cache ***
+*** Removed system source cache ***
+*** Removed latest system source cache ***
+ EOF
+
+ assert_equal expected, @ui.output
+ assert_equal '', @ui.error
+
+ assert !File.exist?(cache.system_cache_file),
+ 'system cache file'
+ assert !File.exist?(cache.latest_system_cache_file),
+ 'latest system cache file'
+
+ end
+
def test_execute_remove
@cmd.handle_options %W[--remove #{@gem_repo}]
@@ -122,20 +155,43 @@ beta-gems.example.com is not a URI
assert_equal [], Gem::SourceInfoCache.cache_data.keys
end
+ def test_execute_remove_no_network
+ @cmd.handle_options %W[--remove #{@gem_repo}]
+
+ util_setup_fake_fetcher
+
+ @fetcher.data["#{@gem_repo}/Marshal.#{Gem.marshal_version}"] = proc do
+ raise Gem::RemoteFetcher::FetchError
+ end
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ expected = "#{@gem_repo} removed from sources\n"
+
+ 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_source_info_cache
util_setup_fake_fetcher
- @si = Gem::SourceIndex.new @gem1.full_name => @gem1.name
- @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = @si.dump
+ si = Gem::SourceIndex.new
+ si.add_spec @a1
+ @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
diff --git a/test/rubygems/test_gem_commands_specification_command.rb b/test/rubygems/test_gem_commands_specification_command.rb
index c8f57fef58..f66f0c0d49 100644
--- a/test/rubygems/test_gem_commands_specification_command.rb
+++ b/test/rubygems/test_gem_commands_specification_command.rb
@@ -12,6 +12,7 @@ class TestGemCommandsSpecificationCommand < RubyGemTestCase
def test_execute
foo = quick_gem 'foo'
+ Gem.source_index.add_spec foo
@cmd.options[:args] = %w[foo]
@@ -87,7 +88,6 @@ class TestGemCommandsSpecificationCommand < RubyGemTestCase
assert_match %r|\A--- !ruby/object:Gem::Specification|, @ui.output
assert_match %r|name: foo|, @ui.output
- assert_equal "WARNING: Remote information is not complete\n\n", @ui.error
end
end
diff --git a/test/rubygems/test_gem_commands_unpack_command.rb b/test/rubygems/test_gem_commands_unpack_command.rb
index 427f9403c3..3a62a914a4 100644
--- a/test/rubygems/test_gem_commands_unpack_command.rb
+++ b/test/rubygems/test_gem_commands_unpack_command.rb
@@ -17,15 +17,57 @@ class TestGemCommandsUnpackCommand < RubyGemTestCase
@cmd.options[:args] = %w[a]
- use_ui @ui do
- Dir.chdir @tempdir do
- @cmd.execute
- end
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ assert File.exist?(File.join(@tempdir, 'a-2'))
+ end
+
+ def test_execute_gem_path
+ util_make_gems
+
+ Gem.clear_paths
+
+ gemhome2 = File.join @tempdir, 'gemhome2'
+
+ Gem.send :set_paths, [gemhome2, @gemhome].join(File::PATH_SEPARATOR)
+ Gem.send :set_home, gemhome2
+
+ @cmd.options[:args] = %w[a]
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
end
+ end
assert File.exist?(File.join(@tempdir, 'a-2'))
end
+ def test_execute_gem_path_missing
+ util_make_gems
+
+ Gem.clear_paths
+
+ gemhome2 = File.join @tempdir, 'gemhome2'
+
+ Gem.send :set_paths, [gemhome2, @gemhome].join(File::PATH_SEPARATOR)
+ Gem.send :set_home, gemhome2
+
+ @cmd.options[:args] = %w[z]
+
+ use_ui @ui do
+ Dir.chdir @tempdir do
+ @cmd.execute
+ end
+ end
+
+ assert_equal '', @ui.output
+ end
+
def test_execute_with_target_option
util_make_gems
diff --git a/test/rubygems/test_gem_commands_update_command.rb b/test/rubygems/test_gem_commands_update_command.rb
new file mode 100644
index 0000000000..1fcec6a075
--- /dev/null
+++ b/test/rubygems/test_gem_commands_update_command.rb
@@ -0,0 +1,174 @@
+require 'test/unit'
+require File.join(File.expand_path(File.dirname(__FILE__)), 'gemutilities')
+require 'rubygems/commands/update_command'
+
+class TestGemCommandsUpdateCommand < RubyGemTestCase
+
+ def setup
+ super
+
+ @cmd = Gem::Commands::UpdateCommand.new
+
+ util_setup_fake_fetcher
+
+ @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
+ end
+
+ def test_execute
+ util_remove_gems
+
+ Gem::Installer.new(@a1_path).install
+
+ @cmd.options[:args] = []
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ 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
+
+ assert out.empty?, out.inspect
+ end
+
+ # before:
+ # a1 -> c1.2
+ # after:
+ # a2 -> b2 # new dependency
+ # a2 -> c2
+
+ def test_execute_dependencies
+ @a1.add_dependency 'c', '1.2'
+
+ @c2 = quick_gem 'c', '2' do |s|
+ s.files = %w[lib/code.rb]
+ s.require_paths = %w[lib]
+ end
+
+ @a2.add_dependency 'c', '2'
+ @a2.add_dependency 'b', '2'
+
+ @b2_path = File.join @gemhome, 'cache', "#{@b2.full_name}.gem"
+ @c1_2_path = File.join @gemhome, 'cache', "#{@c1_2.full_name}.gem"
+ @c2_path = File.join @gemhome, 'cache', "#{@c2.full_name}.gem"
+
+ @source_index = Gem::SourceIndex.new
+ @source_index.add_spec @a1
+ @source_index.add_spec @a2
+ @source_index.add_spec @b2
+ @source_index.add_spec @c1_2
+ @source_index.add_spec @c2
+
+ util_build_gem @a1
+ 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"] =
+ read_binary @c1_2_path
+ @fetcher.data["#{@gem_repo}/gems/#{@c2.full_name}.gem"] = read_binary @c2_path
+
+ util_remove_gems
+
+ Gem::Installer.new(@c1_2_path).install
+ Gem::Installer.new(@a1_path).install
+
+ @cmd.options[:args] = []
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ 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 #{@c2.full_name}", out.shift
+ assert_equal "Successfully installed #{@b2.full_name}", out.shift
+ assert_equal "Successfully installed #{@a2.full_name}", out.shift
+ assert_equal "Gems updated: #{@c2.name}, #{@b2.name}, #{@a2.name}",
+ out.shift
+
+ assert out.empty?, out.inspect
+ end
+
+ def test_execute_named
+ util_remove_gems
+
+ Gem::Installer.new(@a1_path).install
+
+ @cmd.options[:args] = [@a1.name]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ 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
+
+ assert out.empty?, out.inspect
+ end
+
+ def test_execute_named_up_to_date
+ util_remove_gems
+
+ Gem::Installer.new(@a2_path).install
+
+ @cmd.options[:args] = [@a2.name]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ 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
+
+ Gem::Installer.new(@a2_path).install
+
+ @cmd.options[:args] = []
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ 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_dependency_installer.rb b/test/rubygems/test_gem_dependency_installer.rb
index d2d33fa313..576143904a 100644
--- a/test/rubygems/test_gem_dependency_installer.rb
+++ b/test/rubygems/test_gem_dependency_installer.rb
@@ -54,8 +54,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new 'a'
- inst.install
+ inst = Gem::DependencyInstaller.new
+ inst.install 'a'
end
assert_equal Gem::SourceIndex.new(@a1.full_name => @a1),
@@ -70,8 +70,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new 'b'
- inst.install
+ inst = Gem::DependencyInstaller.new
+ inst.install 'b'
end
assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
@@ -84,8 +84,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new 'b'
- inst.install
+ inst = Gem::DependencyInstaller.new
+ inst.install 'b'
end
assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
@@ -102,8 +102,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new 'f'
- inst.install
+ inst = Gem::DependencyInstaller.new
+ inst.install 'f'
end
assert_equal %w[f-2], inst.installed_gems.map { |s| s.full_name }
@@ -114,19 +114,49 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new 'a-1.gem'
- inst.install
+ inst = Gem::DependencyInstaller.new :domain => :local
+ inst.install 'a-1.gem'
end
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
end
+ def test_install_local_dependency
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :domain => :local
+ inst.install 'b-1.gem'
+ end
+
+ assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_local_dependency_installed
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+
+ inst = nil
+
+ Dir.chdir @tempdir do
+ Gem::Installer.new('a-1.gem').install
+
+ inst = Gem::DependencyInstaller.new :domain => :local
+ inst.install 'b-1.gem'
+ end
+
+ assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
def test_install_local_subdir
inst = nil
-
+
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new 'gems/a-1.gem'
- inst.install
+ inst = Gem::DependencyInstaller.new :domain => :local
+ inst.install 'gems/a-1.gem'
end
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
@@ -137,12 +167,11 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new 'a', nil, :env_shebang => true,
- :wrappers => true
- inst.install
+ inst = Gem::DependencyInstaller.new :env_shebang => true, :wrappers => true
+ inst.install 'a'
end
- assert_match %r|\A#!/usr/bin/env ruby\n|,
+ assert_match %r|\A#!/usr/bin/env #{Gem::ConfigMap[:RUBY_INSTALL_NAME]}\n|,
File.read(File.join(@gemhome, 'bin', 'a_bin'))
end
@@ -153,8 +182,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new 'b', nil, :force => true
- inst.install
+ inst = Gem::DependencyInstaller.new :force => true
+ inst.install 'b'
end
assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
@@ -165,8 +194,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new 'b', nil, :ignore_dependencies => true
- inst.install
+ inst = Gem::DependencyInstaller.new :ignore_dependencies => true
+ inst.install 'b'
end
assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
@@ -179,8 +208,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new 'a', nil, :install_dir => gemhome2
- inst.install
+ inst = Gem::DependencyInstaller.new :install_dir => gemhome2
+ inst.install 'a'
end
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
@@ -201,8 +230,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new 'b', nil, :domain => :both
- inst.install
+ inst = Gem::DependencyInstaller.new :domain => :both
+ inst.install 'b'
end
assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
@@ -217,14 +246,34 @@ class TestGemDependencyInstaller < RubyGemTestCase
assert_equal b1_expected, b1.loaded_from
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
+ end
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :domain => :both
+ inst.install 'b'
+ end
+
+ assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
def test_install_domain_local
FileUtils.mv @b1_gem, @tempdir
inst = nil
Dir.chdir @tempdir do
e = assert_raise Gem::InstallError do
- inst = Gem::DependencyInstaller.new 'b', nil, :domain => :local
- inst.install
+ inst = Gem::DependencyInstaller.new :domain => :local
+ inst.install 'b'
end
assert_equal 'b requires a (>= 0)', e.message
end
@@ -240,8 +289,43 @@ class TestGemDependencyInstaller < RubyGemTestCase
@fetcher.data['http://gems.example.com/gems/a-1.gem'] = a1_data
- inst = Gem::DependencyInstaller.new 'a', nil, :domain => :remote
- inst.install
+ inst = Gem::DependencyInstaller.new :domain => :remote
+ inst.install 'a'
+
+ assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_remote
+ a1_data = nil
+ File.open @a1_gem, 'rb' do |fp|
+ a1_data = fp.read
+ end
+
+ @fetcher.data['http://gems.example.com/gems/a-1.gem'] = a1_data
+
+ inst = Gem::DependencyInstaller.new
+
+ Dir.chdir @tempdir do
+ inst.install 'a'
+ end
+
+ assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
+ end
+
+ def test_install_remote_dep
+ a1_data = nil
+ File.open @a1_gem, 'rb' do |fp|
+ a1_data = fp.read
+ end
+
+ @fetcher.data['http://gems.example.com/gems/a-1.gem'] = a1_data
+
+ inst = Gem::DependencyInstaller.new
+
+ Dir.chdir @tempdir do
+ dep = Gem::Dependency.new @a1.name, @a1.version
+ inst.install dep
+ end
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
end
@@ -266,8 +350,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
@fetcher.data["http://gems.example.com/gems/#{a2_o.full_name}.gem"] =
a2_o_data
- inst = Gem::DependencyInstaller.new 'a', nil, :domain => :remote
- inst.install
+ inst = Gem::DependencyInstaller.new :domain => :remote
+ inst.install 'a'
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
end
@@ -278,8 +362,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new 'a'
- inst.install
+ inst = Gem::DependencyInstaller.new
+ inst.install 'a'
end
assert_equal Gem::SourceIndex.new(@a1.full_name => @a1),
@@ -290,13 +374,17 @@ class TestGemDependencyInstaller < RubyGemTestCase
if defined? OpenSSL then
def test_install_security_policy
- FileUtils.mv @a1_gem, @cache_dir
- FileUtils.mv @b1_gem, @cache_dir
+ data = File.open(@a1_gem, 'rb') { |f| f.read }
+ @fetcher.data['http://gems.example.com/gems/a-1.gem'] = data
+
+ data = File.open(@b1_gem, 'rb') { |f| f.read }
+ @fetcher.data['http://gems.example.com/gems/b-1.gem'] = data
+
policy = Gem::Security::HighSecurity
- inst = Gem::DependencyInstaller.new 'b', nil, :security_policy => policy
+ inst = Gem::DependencyInstaller.new :security_policy => policy
e = assert_raise Gem::Exception do
- inst.install
+ inst.install 'b'
end
assert_equal 'Unsigned gem', e.message
@@ -305,145 +393,48 @@ class TestGemDependencyInstaller < RubyGemTestCase
end
end
- def test_install_wrappers
- FileUtils.mv @a1_gem, @cache_dir
- inst = Gem::DependencyInstaller.new 'a', :wrappers => true
+ # Wrappers don't work on mswin
+ unless win_platform? then
+ def test_install_no_wrappers
+ @fetcher.data['http://gems.example.com/gems/a-1.gem'] = read_binary(@a1_gem)
- inst.install
+ inst = Gem::DependencyInstaller.new :wrappers => false
+ inst.install 'a'
- assert_match %r|This file was generated by RubyGems.|,
- File.read(File.join(@gemhome, 'bin', 'a_bin'))
- end
-
- def test_install_version
- FileUtils.mv @d1_gem, @cache_dir
- FileUtils.mv @d2_gem, @cache_dir
- inst = Gem::DependencyInstaller.new 'd', '= 1'
-
- inst.install
-
- assert_equal %w[d-1], inst.installed_gems.map { |s| s.full_name }
- end
-
- def test_install_version_default
- FileUtils.mv @d1_gem, @cache_dir
- FileUtils.mv @d2_gem, @cache_dir
- inst = Gem::DependencyInstaller.new 'd'
-
- inst.install
-
- assert_equal %w[d-2], inst.installed_gems.map { |s| s.full_name }
- end
-
- def test_download
- a1_data = nil
- File.open @a1_gem, 'rb' do |fp|
- a1_data = fp.read
+ assert_no_match(%r|This file was generated by RubyGems.|,
+ File.read(File.join(@gemhome, 'bin', 'a_bin')))
end
-
- @fetcher.data['http://gems.example.com/gems/a-1.gem'] = a1_data
-
- inst = Gem::DependencyInstaller.new 'a'
-
- a1_cache_gem = File.join(@gemhome, 'cache', "#{@a1.full_name}.gem")
- assert_equal a1_cache_gem, inst.download(@a1, 'http://gems.example.com')
-
- assert File.exist?(a1_cache_gem)
- end
-
- def test_download_cached
- FileUtils.mv @a1_gem, @cache_dir
-
- inst = Gem::DependencyInstaller.new 'a'
-
- assert_equal File.join(@gemhome, 'cache', "#{@a1.full_name}.gem"),
- inst.download(@a1, 'http://gems.example.com')
- end
-
- def test_download_local
- FileUtils.mv @a1_gem, @tempdir
- local_path = File.join @tempdir, "#{@a1.full_name}.gem"
- inst = nil
-
- Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new 'a'
- end
-
- assert_equal File.join(@gemhome, 'cache', "#{@a1.full_name}.gem"),
- inst.download(@a1, local_path)
end
- def test_download_install_dir
- a1_data = nil
- File.open @a1_gem, 'rb' do |fp|
- a1_data = fp.read
- end
-
- @fetcher.data['http://gems.example.com/gems/a-1.gem'] = a1_data
-
- install_dir = File.join @tempdir, 'more_gems'
-
- inst = Gem::DependencyInstaller.new 'a', nil, :install_dir => install_dir
-
- a1_cache_gem = File.join install_dir, 'cache', "#{@a1.full_name}.gem"
- assert_equal a1_cache_gem, inst.download(@a1, 'http://gems.example.com')
+ def test_install_version
+ data = File.open(@d2_gem, 'rb') { |f| f.read }
+ @fetcher.data['http://gems.example.com/gems/d-2.gem'] = data
- assert File.exist?(a1_cache_gem)
- end
+ data = File.open(@d1_gem, 'rb') { |f| f.read }
+ @fetcher.data['http://gems.example.com/gems/d-1.gem'] = data
- unless win_platform? then # File.chmod doesn't work
- def test_download_local_read_only
- FileUtils.mv @a1_gem, @tempdir
- local_path = File.join @tempdir, "#{@a1.full_name}.gem"
- inst = nil
- File.chmod 0555, File.join(@gemhome, 'cache')
+ inst = Gem::DependencyInstaller.new
- Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new 'a'
- end
+ inst.install 'd', '= 1'
- assert_equal File.join(@tempdir, "#{@a1.full_name}.gem"),
- inst.download(@a1, local_path)
- ensure
- File.chmod 0755, File.join(@gemhome, 'cache')
- end
+ assert_equal %w[d-1], inst.installed_gems.map { |s| s.full_name }
end
- def test_download_platform_legacy
- original_platform = 'old-platform'
-
- e1, e1_gem = util_gem 'e', '1' do |s|
- s.platform = Gem::Platform::CURRENT
- s.instance_variable_set :@original_platform, original_platform
- end
-
- e1_data = nil
- File.open e1_gem, 'rb' do |fp|
- e1_data = fp.read
- end
-
- @fetcher.data["http://gems.example.com/gems/e-1-#{original_platform}.gem"] = e1_data
-
- inst = Gem::DependencyInstaller.new 'a'
-
- e1_cache_gem = File.join(@gemhome, 'cache', "#{e1.full_name}.gem")
- assert_equal e1_cache_gem, inst.download(e1, 'http://gems.example.com')
-
- assert File.exist?(e1_cache_gem)
- end
+ def test_install_version_default
+ data = File.open(@d2_gem, 'rb') { |f| f.read }
+ @fetcher.data['http://gems.example.com/gems/d-2.gem'] = data
- def test_download_unsupported
- inst = Gem::DependencyInstaller.new 'a'
+ data = File.open(@d1_gem, 'rb') { |f| f.read }
+ @fetcher.data['http://gems.example.com/gems/d-1.gem'] = data
- e = assert_raise Gem::InstallError do
- inst.download @a1, 'ftp://gems.rubyforge.org'
- end
+ inst = Gem::DependencyInstaller.new
+ inst.install 'd'
- assert_equal 'unsupported URI scheme ftp', e.message
+ assert_equal %w[d-2], inst.installed_gems.map { |s| s.full_name }
end
def test_find_gems_gems_with_sources
- inst = Gem::DependencyInstaller.new 'a'
+ inst = Gem::DependencyInstaller.new
dep = Gem::Dependency.new 'b', '>= 0'
assert_equal [[@b1, 'http://gems.example.com']],
@@ -452,7 +443,7 @@ class TestGemDependencyInstaller < RubyGemTestCase
def test_find_gems_with_sources_local
FileUtils.mv @a1_gem, @tempdir
- inst = Gem::DependencyInstaller.new 'b'
+ inst = Gem::DependencyInstaller.new
dep = Gem::Dependency.new 'a', '>= 0'
gems = nil
@@ -462,7 +453,7 @@ class TestGemDependencyInstaller < RubyGemTestCase
assert_equal 2, gems.length
remote = gems.first
- assert_equal @a1, remote.first, 'remote spec'
+ assert_equal 'a-1', remote.first.full_name, 'remote spec'
assert_equal 'http://gems.example.com', remote.last, 'remote path'
local = gems.last
@@ -472,7 +463,9 @@ class TestGemDependencyInstaller < RubyGemTestCase
end
def test_gather_dependencies
- inst = Gem::DependencyInstaller.new 'b'
+ inst = Gem::DependencyInstaller.new
+ inst.find_spec_by_name_and_version 'b'
+ inst.gather_dependencies
assert_equal %w[a-1 b-1], inst.gems_to_install.map { |s| s.full_name }
end
@@ -488,7 +481,9 @@ class TestGemDependencyInstaller < RubyGemTestCase
@fetcher.uri = URI.parse 'http://gems.example.com'
@fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml
- inst = Gem::DependencyInstaller.new 'c'
+ inst = Gem::DependencyInstaller.new
+ inst.find_spec_by_name_and_version 'c'
+ inst.gather_dependencies
assert_equal %w[b-2 c-1], inst.gems_to_install.map { |s| s.full_name }
end
@@ -496,14 +491,18 @@ class TestGemDependencyInstaller < RubyGemTestCase
def test_gather_dependencies_platform_alternate
util_set_arch 'cpu-my_platform1'
- inst = Gem::DependencyInstaller.new 'w'
+ inst = Gem::DependencyInstaller.new
+ inst.find_spec_by_name_and_version 'w'
+ inst.gather_dependencies
assert_equal %w[x-1-cpu-my_platform-1 w-1],
inst.gems_to_install.map { |s| s.full_name }
end
def test_gather_dependencies_platform_bump
- inst = Gem::DependencyInstaller.new 'z'
+ inst = Gem::DependencyInstaller.new
+ inst.find_spec_by_name_and_version 'z'
+ inst.gather_dependencies
assert_equal %w[y-1 z-1], inst.gems_to_install.map { |s| s.full_name }
end
@@ -518,27 +517,11 @@ class TestGemDependencyInstaller < RubyGemTestCase
@fetcher.uri = URI.parse 'http://gems.example.com'
@fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml
- inst = Gem::DependencyInstaller.new 'e'
+ inst = Gem::DependencyInstaller.new
+ inst.find_spec_by_name_and_version 'e'
+ inst.gather_dependencies
assert_equal %w[d-1 e-1], inst.gems_to_install.map { |s| s.full_name }
end
-
- def util_gem(name, version, &block)
- spec = quick_gem(name, version, &block)
-
- util_build_gem spec
-
- cache_file = File.join @tempdir, 'gems', "#{spec.original_name}.gem"
- FileUtils.mv File.join(@gemhome, 'cache', "#{spec.original_name}.gem"),
- cache_file
- FileUtils.rm File.join(@gemhome, 'specifications',
- "#{spec.full_name}.gemspec")
-
- spec.loaded_from = nil
- spec.loaded = false
-
- [spec, cache_file]
- end
-
end
diff --git a/test/rubygems/test_gem_ext_configure_builder.rb b/test/rubygems/test_gem_ext_configure_builder.rb
index 25aa7412aa..d3d0efb489 100644
--- a/test/rubygems/test_gem_ext_configure_builder.rb
+++ b/test/rubygems/test_gem_ext_configure_builder.rb
@@ -47,16 +47,19 @@ class TestGemExtConfigureBuilder < RubyGemTestCase
end
end
- expected = %r|configure failed:
+ shell_error_msg = %r{(\./configure: No such file or directory)|(Can't open \./configure)}
+ sh_prefix_configure = "sh ./configure --prefix="
+
+ expected = %r(configure failed:
-sh \./configure --prefix=#{Regexp.escape @dest_path}
-.*?: \./configure: No such file or directory
-|
+#{Regexp.escape sh_prefix_configure}#{Regexp.escape @dest_path}
+.*?: #{shell_error_msg}
+)
assert_match expected, error.message
- assert_equal "sh ./configure --prefix=#{@dest_path}", output.shift
- assert_match %r|\./configure: No such file or directory\n|, output.shift
+ assert_equal "#{sh_prefix_configure}#{@dest_path}", output.shift
+ assert_match %r(#{shell_error_msg}\n), output.shift
assert_equal true, output.empty?
end
diff --git a/test/rubygems/test_gem_format.rb b/test/rubygems/test_gem_format.rb
index 35eee6bae2..4014acfed9 100644
--- a/test/rubygems/test_gem_format.rb
+++ b/test/rubygems/test_gem_format.rb
@@ -22,7 +22,7 @@ class TestGemFormat < RubyGemTestCase
gems = Dir[File.join(@gemhome, 'cache', '*.gem')]
- names = [@a1, @a2, @b2, @c1_2, @pl1].map do |spec|
+ names = [@a1, @a2, @a_evil9, @b2, @c1_2, @pl1].map do |spec|
spec.original_name
end
diff --git a/test/rubygems/test_gem_indexer.rb b/test/rubygems/test_gem_indexer.rb
index 9eb78500c9..12469b5d57 100644
--- a/test/rubygems/test_gem_indexer.rb
+++ b/test/rubygems/test_gem_indexer.rb
@@ -53,6 +53,11 @@ class TestGemIndexer < RubyGemTestCase
assert_indexed quickdir, "index"
assert_indexed quickdir, "index.rz"
+ assert_indexed quickdir, "latest_index"
+ assert_indexed quickdir, "latest_index.rz"
+
+ assert_no_match %r|a-1|, File.read(File.join(quickdir, 'latest_index'))
+
assert_indexed quickdir, "#{@a1.full_name}.gemspec.rz"
assert_indexed quickdir, "#{@a2.full_name}.gemspec.rz"
assert_indexed quickdir, "#{@b2.full_name}.gemspec.rz"
@@ -74,8 +79,8 @@ class TestGemIndexer < RubyGemTestCase
end
expected = <<-EOF
-Generating index for 5 gems in #{@tempdir}
-.....
+Generating index for 6 gems in #{@tempdir}
+......
Loaded all gems
Generating master indexes (this may take a while)
EOF
diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb
index 442ed6cfc3..f7d36c66ed 100644
--- a/test/rubygems/test_gem_installer.rb
+++ b/test/rubygems/test_gem_installer.rb
@@ -4,63 +4,10 @@
# See LICENSE.txt for permissions.
#++
-require 'test/unit'
-require File.join(File.expand_path(File.dirname(__FILE__)), 'gemutilities')
-require 'rubygems/installer'
-
-class Gem::Installer
- attr_accessor :gem_dir
-
- attr_writer :format
- attr_writer :gem_home
- attr_writer :env_shebang
- attr_writer :ignore_dependencies
- attr_writer :format_executable
- attr_writer :security_policy
- attr_writer :spec
- attr_writer :wrappers
-end
-
-class TestGemInstaller < RubyGemTestCase
-
- def setup
- super
-
- @spec = quick_gem "a"
- @gem = File.join @tempdir, "#{@spec.full_name}.gem"
-
- util_build_gem @spec
- FileUtils.mv File.join(@gemhome, 'cache', "#{@spec.full_name}.gem"),
- @tempdir
-
- @installer = Gem::Installer.new @gem
- @installer.gem_dir = util_gem_dir
- @installer.gem_home = @gemhome
- @installer.spec = @spec
- end
-
- def util_gem_dir(version = '2')
- File.join @gemhome, "gems", "a-#{version}" # HACK
- end
-
- def util_gem_bindir(version = '2')
- File.join util_gem_dir(version), "bin"
- end
+require File.join(File.expand_path(File.dirname(__FILE__)),
+ 'gem_installer_test_case')
- def util_inst_bindir
- File.join @gemhome, "bin"
- end
-
- def util_make_exec(version = '2', shebang = "#!/usr/bin/ruby")
- @spec.executables = ["my_exec"]
-
- FileUtils.mkdir_p util_gem_bindir(version)
- exec_file = @installer.formatted_program_filename "my_exec"
- exec_path = File.join util_gem_bindir(version), exec_file
- File.open exec_path, 'w' do |f|
- f.puts shebang
- end
- end
+class TestGemInstaller < GemInstallerTestCase
def test_app_script_text
util_make_exec '2', ''
@@ -162,7 +109,7 @@ load 'my_exec'
@installer.gem_dir = '/nonexistent'
expanded_gem_dir = @installer.send(:expand_and_validate_gem_dir)
if win_platform?
- expected = File.join(Config::CONFIG['bindir'][0..2], 'nonexistent').downcase
+ expected = File.expand_path('/nonexistent').downcase
expanded_gem_dir = expanded_gem_dir.downcase
else
expected = '/nonexistent'
@@ -768,7 +715,7 @@ load 'my_exec'
@installer.env_shebang = true
shebang = @installer.shebang 'my_exec'
- assert_equal "#!/usr/bin/env ruby", shebang
+ assert_equal "#!/usr/bin/env #{Gem::ConfigMap[:RUBY_INSTALL_NAME]}", shebang
end
def test_shebang_nested
@@ -855,31 +802,5 @@ load 'my_exec'
File.join @gemhome, 'cache', "#{spec.full_name}.gem"
end
- def util_setup_gem
- @spec.files = File.join('lib', 'code.rb')
- @spec.executables << 'executable'
- @spec.extensions << File.join('ext', 'a', 'mkrf_conf.rb')
-
- Dir.chdir @tempdir do
- FileUtils.mkdir_p 'bin'
- FileUtils.mkdir_p 'lib'
- FileUtils.mkdir_p File.join('ext', 'a')
- File.open File.join('bin', 'executable'), 'w' do |f| f.puts '1' end
- File.open File.join('lib', 'code.rb'), 'w' do |f| f.puts '1' end
- File.open File.join('ext', 'a', 'mkrf_conf.rb'), 'w' do |f|
- f << <<-EOF
- File.open 'Rakefile', 'w' do |rf| rf.puts "task :default" end
- EOF
- end
-
- use_ui @ui do
- FileUtils.rm @gem
- Gem::Builder.new(@spec).build
- end
- end
-
- @installer = Gem::Installer.new @gem
- end
-
end
diff --git a/test/rubygems/test_gem_package_tar_header.rb b/test/rubygems/test_gem_package_tar_header.rb
new file mode 100644
index 0000000000..9b7708dca8
--- /dev/null
+++ b/test/rubygems/test_gem_package_tar_header.rb
@@ -0,0 +1,137 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require File.join(File.expand_path(File.dirname(__FILE__)),
+ 'gem_package_tar_test_case')
+require 'rubygems/package'
+
+class TestGemPackageTarHeader < TarTestCase
+
+ def setup
+ super
+
+ header = {
+ :name => 'x',
+ :mode => 0644,
+ :uid => 1000,
+ :gid => 10000,
+ :size => 100,
+ :mtime => 12345,
+ :typeflag => '0',
+ :linkname => 'link',
+ :uname => 'user',
+ :gname => 'group',
+ :devmajor => 1,
+ :devminor => 2,
+ :prefix => 'y',
+ }
+
+ @tar_header = Gem::Package::TarHeader.new header
+ end
+
+ def test_self_from
+ io = TempIO.new @tar_header.to_s
+
+ new_header = Gem::Package::TarHeader.from io
+
+ assert_headers_equal @tar_header, new_header
+ end
+
+ def test_initialize
+ assert_equal '', @tar_header.checksum, 'checksum'
+ assert_equal 1, @tar_header.devmajor, 'devmajor'
+ assert_equal 2, @tar_header.devminor, 'devminor'
+ assert_equal 10000, @tar_header.gid, 'gid'
+ assert_equal 'group', @tar_header.gname, 'gname'
+ assert_equal 'link', @tar_header.linkname, 'linkname'
+ assert_equal 'ustar', @tar_header.magic, 'magic'
+ assert_equal 0644, @tar_header.mode, 'mode'
+ assert_equal 12345, @tar_header.mtime, 'mtime'
+ assert_equal 'x', @tar_header.name, 'name'
+ assert_equal 'y', @tar_header.prefix, 'prefix'
+ assert_equal 100, @tar_header.size, 'size'
+ assert_equal '0', @tar_header.typeflag, 'typeflag'
+ assert_equal 1000, @tar_header.uid, 'uid'
+ assert_equal 'user', @tar_header.uname, 'uname'
+ assert_equal '00', @tar_header.version, 'version'
+
+ assert !@tar_header.empty?, 'empty'
+ end
+
+ def test_initialize_bad
+ assert_raises ArgumentError do
+ Gem::Package::TarHeader.new :name => '', :size => '', :mode => ''
+ end
+
+ assert_raises ArgumentError do
+ Gem::Package::TarHeader.new :name => '', :size => '', :prefix => ''
+ end
+
+ assert_raises ArgumentError do
+ Gem::Package::TarHeader.new :name => '', :prefix => '', :mode => ''
+ end
+
+ assert_raises ArgumentError do
+ Gem::Package::TarHeader.new :prefix => '', :size => '', :mode => ''
+ end
+ end
+
+ def test_empty_eh
+ assert !@tar_header.empty?
+
+ @tar_header = Gem::Package::TarHeader.new :name => 'x', :prefix => '',
+ :mode => 0, :size => 0,
+ :empty => true
+
+ assert @tar_header.empty?
+ end
+
+ def test_equals2
+ assert_equal @tar_header, @tar_header
+ assert_equal @tar_header, @tar_header.dup
+ end
+
+ def test_to_s
+ expected = <<-EOF.split("\n").join
+x\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\0000000644\0000001750\0000023420\00000000000144\00000000030071
+\000012467\000 0link\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000ustar\00000user\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+group\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\0000000001\0000000002\000y\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000
+\000\000\000\000\000\000\000\000\000\000
+ EOF
+
+ assert_headers_equal expected, @tar_header
+ end
+
+ def test_update_checksum
+ assert_equal '', @tar_header.checksum
+
+ @tar_header.update_checksum
+
+ assert_equal '012467', @tar_header.checksum
+ end
+
+end
+
diff --git a/test/rubygems/test_gem_package_tar_input.rb b/test/rubygems/test_gem_package_tar_input.rb
new file mode 100644
index 0000000000..279026b149
--- /dev/null
+++ b/test/rubygems/test_gem_package_tar_input.rb
@@ -0,0 +1,119 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require File.join(File.expand_path(File.dirname(__FILE__)),
+ 'gem_package_tar_test_case')
+require 'rubygems/package/tar_input'
+
+class TestGemPackageTarInput < TarTestCase
+
+ # Sometimes the setgid bit doesn't take. Don't know if this is a problem on
+ # all systems, or just some. But for now, we will ignore it in the tests.
+ SETGID_BIT = 02000
+
+ def setup
+ super
+
+ inner_tar = tar_file_header("bla", "", 0612, 10)
+ inner_tar += "0123456789" + "\0" * 502
+ inner_tar += tar_file_header("foo", "", 0636, 5)
+ inner_tar += "01234" + "\0" * 507
+ inner_tar += tar_dir_header("__dir__", "", 0600)
+ inner_tar += "\0" * 1024
+ str = TempIO.new
+
+ begin
+ os = Zlib::GzipWriter.new str
+ os.write inner_tar
+ ensure
+ os.finish
+ end
+
+ str.rewind
+
+ @file = File.join @tempdir, 'bla.tar'
+
+ File.open @file, 'wb' do |f|
+ f.write tar_file_header("data.tar.gz", "", 0644, str.string.size)
+ f.write str.string
+ f.write "\0" * ((512 - (str.string.size % 512)) % 512 )
+
+ @spec = Gem::Specification.new do |spec|
+ spec.author = "Mauricio :)"
+ end
+
+ meta = @spec.to_yaml
+
+ f.write tar_file_header("metadata", "", 0644, meta.size)
+ f.write meta + "\0" * (1024 - meta.size)
+ f.write "\0" * 1024
+ end
+
+ @entry_names = %w{bla foo __dir__}
+ @entry_sizes = [10, 5, 0]
+ #FIXME: are these modes system dependent?
+ @entry_modes = [0100612, 0100636, 040600]
+ @entry_files = %W[#{@tempdir}/bla #{@tempdir}/foo]
+ @entry_contents = %w[0123456789 01234]
+ end
+
+ def test_each_works
+ open @file, 'rb' do |io|
+ Gem::Package::TarInput.open io do |tar_input|
+ count = 0
+
+ tar_input.each_with_index do |entry, i|
+ count = i
+
+ assert_kind_of Gem::Package::TarReader::Entry, entry
+ assert_equal @entry_names[i], entry.header.name
+ assert_equal @entry_sizes[i], entry.header.size
+ end
+
+ assert_equal 2, count
+
+ assert_equal @spec, tar_input.metadata
+ end
+ end
+ end
+
+ def test_extract_entry_works
+ open @file, 'rb' do |io|
+ Gem::Package::TarInput.open io do |tar_input|
+ assert_equal @spec, tar_input.metadata
+
+ count = 0
+
+ tar_input.each_with_index do |entry, i|
+ count = i
+ tar_input.extract_entry @tempdir, entry
+ name = File.join @tempdir, entry.header.name
+
+ if entry.directory?
+ assert File.dir?(name)
+ else
+ assert File.file?(name)
+ assert_equal @entry_sizes[i], File.stat(name).size
+ #FIXME: win32? !!
+ end
+
+ unless Gem.win_platform? then
+ assert_equal @entry_modes[i], File.stat(name).mode & (~SETGID_BIT)
+ end
+ end
+
+ assert_equal 2, count
+ end
+ end
+
+ @entry_files.each_with_index do |x, i|
+ assert File.file?(x)
+ assert_equal @entry_contents[i], File.read_b(x)
+ end
+ end
+
+end
+
diff --git a/test/rubygems/test_gem_package_tar_output.rb b/test/rubygems/test_gem_package_tar_output.rb
new file mode 100644
index 0000000000..06dbb1a4da
--- /dev/null
+++ b/test/rubygems/test_gem_package_tar_output.rb
@@ -0,0 +1,104 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require File.join(File.expand_path(File.dirname(__FILE__)),
+ 'gem_package_tar_test_case')
+require 'rubygems/package/tar_output'
+
+class TestGemPackageTarOutput < TarTestCase
+
+ def setup
+ super
+
+ @file = File.join @tempdir, 'bla2.tar'
+ end
+
+ def test_self_open
+ open @file, 'wb' do |tar_io|
+ Gem::Package::TarOutput.open tar_io do |tar_writer|
+ tar_writer.add_file_simple 'README', 0, 17 do |io|
+ io.write "This is a README\n"
+ end
+
+ tar_writer.metadata = "This is some metadata\n"
+ end
+ end
+
+ files = util_extract
+
+ name, data = files.shift
+ assert_equal 'data.tar.gz', name
+
+ gz = Zlib::GzipReader.new StringIO.new(data)
+
+ Gem::Package::TarReader.new gz do |tar_reader|
+ tar_reader.each do |entry|
+ assert_equal 'README', entry.full_name
+ assert_equal "This is a README\n", entry.read
+ end
+ end
+
+ gz.close
+
+ name, data = files.shift
+ assert_equal 'metadata.gz', name
+
+ gz = Zlib::GzipReader.new StringIO.new(data)
+ assert_equal "This is some metadata\n", gz.read
+
+ assert files.empty?
+ ensure
+ gz.close if gz
+ end
+
+ if defined? OpenSSL then
+ def test_self_open_signed
+ signer = Gem::Security::Signer.new @private_key, [@public_cert]
+
+ open @file, 'wb' do |tar_io|
+ Gem::Package::TarOutput.open tar_io, signer do |tar_writer|
+ tar_writer.add_file_simple 'README', 0, 17 do |io|
+ io.write "This is a README\n"
+ end
+
+ tar_writer.metadata = "This is some metadata\n"
+ end
+ end
+
+ files = util_extract
+
+ name, data = files.shift
+ assert_equal 'data.tar.gz', name
+
+ name, data = files.shift
+ assert_equal 'metadata.gz', name
+
+ name, data = files.shift
+ assert_equal 'data.tar.gz.sig', name
+
+ name, data = files.shift
+ assert_equal 'metadata.gz.sig', name
+
+ assert files.empty?
+ end
+ end
+
+ def util_extract
+ files = []
+
+ open @file, 'rb' do |io|
+ Gem::Package::TarReader.new io do |tar_reader|
+ tar_reader.each do |entry|
+ files << [entry.full_name, entry.read]
+ end
+ end
+ end
+
+ files
+ end
+
+end
+
diff --git a/test/rubygems/test_gem_package_tar_reader.rb b/test/rubygems/test_gem_package_tar_reader.rb
new file mode 100644
index 0000000000..6962088878
--- /dev/null
+++ b/test/rubygems/test_gem_package_tar_reader.rb
@@ -0,0 +1,53 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require File.join(File.expand_path(File.dirname(__FILE__)),
+ 'gem_package_tar_test_case')
+require 'rubygems/package'
+
+class TestGemPackageTarReader < TarTestCase
+
+ def test_each_entry
+ tar = tar_dir_header "foo", "bar", 0
+ tar << tar_file_header("bar", "baz", 0, 0)
+
+ io = TempIO.new tar
+
+ entries = 0
+
+ Gem::Package::TarReader.new io do |tar_reader|
+ tar_reader.each_entry do |entry|
+ assert_kind_of Gem::Package::TarReader::Entry, entry
+
+ entries += 1
+ end
+ end
+
+ assert_equal 2, entries
+ end
+
+ def test_rewind
+ content = ('a'..'z').to_a.join(" ")
+
+ str = tar_file_header("lib/foo", "", 010644, content.size) + content +
+ "\0" * (512 - content.size)
+ str << "\0" * 1024
+
+ Gem::Package::TarReader.new(TempIO.new(str)) do |tar_reader|
+ 3.times do
+ tar_reader.rewind
+ i = 0
+ tar_reader.each_entry do |entry|
+ assert_equal(content, entry.read)
+ i += 1
+ end
+ assert_equal(1, i)
+ end
+ end
+ end
+
+end
+
diff --git a/test/rubygems/test_gem_package_tar_reader_entry.rb b/test/rubygems/test_gem_package_tar_reader_entry.rb
new file mode 100644
index 0000000000..7e25143a85
--- /dev/null
+++ b/test/rubygems/test_gem_package_tar_reader_entry.rb
@@ -0,0 +1,116 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require File.join(File.expand_path(File.dirname(__FILE__)),
+ 'gem_package_tar_test_case')
+require 'rubygems/package'
+
+class TestGemPackageTarReaderEntry < TarTestCase
+
+ def setup
+ super
+
+ @contents = ('a'..'z').to_a.join * 100
+
+ @tar = ''
+ @tar << tar_file_header("lib/foo", "", 0, @contents.size)
+ @tar << @contents
+ @tar << "\0" * (512 - (@tar.size % 512))
+
+ @entry = util_entry @tar
+ end
+
+ def test_bytes_read
+ assert_equal 0, @entry.bytes_read
+
+ @entry.getc
+
+ assert_equal 1, @entry.bytes_read
+ end
+
+ def test_close
+ @entry.close
+
+ assert @entry.bytes_read
+
+ e = assert_raise IOError do @entry.eof? end
+ assert_equal 'closed Gem::Package::TarReader::Entry', e.message
+
+ e = assert_raise IOError do @entry.getc end
+ assert_equal 'closed Gem::Package::TarReader::Entry', e.message
+
+ e = assert_raise IOError do @entry.pos end
+ assert_equal 'closed Gem::Package::TarReader::Entry', e.message
+
+ e = assert_raise IOError do @entry.read end
+ assert_equal 'closed Gem::Package::TarReader::Entry', e.message
+
+ e = assert_raise IOError do @entry.rewind end
+ assert_equal 'closed Gem::Package::TarReader::Entry', e.message
+ end
+
+ def test_closed_eh
+ @entry.close
+
+ assert @entry.closed?
+ end
+
+ def test_eof_eh
+ @entry.read
+
+ assert @entry.eof?
+ end
+
+ def test_full_name
+ assert_equal 'lib/foo', @entry.full_name
+ end
+
+ def test_getc
+ assert_equal ?a, @entry.getc
+ end
+
+ def test_directory_eh
+ assert_equal false, @entry.directory?
+ assert_equal true, util_dir_entry.directory?
+ end
+
+ def test_file_eh
+ assert_equal true, @entry.file?
+ assert_equal false, util_dir_entry.file?
+ end
+
+ def test_pos
+ assert_equal 0, @entry.pos
+
+ @entry.getc
+
+ assert_equal 1, @entry.pos
+ end
+
+ def test_read
+ assert_equal @contents, @entry.read
+ end
+
+ def test_read_big
+ assert_equal @contents, @entry.read(@contents.size * 2)
+ end
+
+ def test_read_small
+ assert_equal @contents[0...100], @entry.read(100)
+ end
+
+ def test_rewind
+ char = @entry.getc
+
+ @entry.rewind
+
+ assert_equal 0, @entry.pos
+
+ assert_equal char, @entry.getc
+ end
+
+end
+
diff --git a/test/rubygems/test_gem_package_tar_writer.rb b/test/rubygems/test_gem_package_tar_writer.rb
new file mode 100644
index 0000000000..e066c2967f
--- /dev/null
+++ b/test/rubygems/test_gem_package_tar_writer.rb
@@ -0,0 +1,151 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require File.join(File.expand_path(File.dirname(__FILE__)),
+ 'gem_package_tar_test_case')
+require 'rubygems/package/tar_writer'
+
+class TestTarWriter < TarTestCase
+
+ def setup
+ super
+
+ @data = 'abcde12345'
+ @io = TempIO.new
+ @tar_writer = Gem::Package::TarWriter.new @io
+ end
+
+ def teardown
+ @tar_writer.close unless @tar_writer.closed?
+
+ super
+ end
+
+ def test_add_file
+ @tar_writer.add_file 'x', 0644 do |f| f.write 'a' * 10 end
+
+ assert_headers_equal(tar_file_header('x', '', 0644, 10),
+ @io.string[0, 512])
+ assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
+ assert_equal 1024, @io.pos
+ end
+
+ def test_add_file_simple
+ @tar_writer.add_file_simple 'x', 0644, 10 do |io| io.write "a" * 10 end
+
+ assert_headers_equal(tar_file_header('x', '', 0644, 10),
+ @io.string[0, 512])
+
+ assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
+ assert_equal 1024, @io.pos
+ end
+
+ def test_add_file_simple_padding
+ @tar_writer.add_file_simple 'x', 0, 100
+
+ assert_headers_equal tar_file_header('x', '', 0, 100),
+ @io.string[0, 512]
+
+ assert_equal "\0" * 512, @io.string[512, 512]
+ end
+
+ def test_add_file_simple_data
+ @tar_writer.add_file_simple("lib/foo/bar", 0, 10) { |f| f.write @data }
+ @tar_writer.flush
+
+ assert_equal @data + ("\0" * (512-@data.size)),
+ @io.string[512, 512]
+ end
+
+ def test_add_file_simple_size
+ assert_raise Gem::Package::TarWriter::FileOverflow do
+ @tar_writer.add_file_simple("lib/foo/bar", 0, 10) do |io|
+ io.write "1" * 11
+ end
+ end
+ end
+
+ def test_add_file_unseekable
+ assert_raise Gem::Package::NonSeekableIO do
+ Gem::Package::TarWriter.new(Object.new).add_file 'x', 0
+ end
+ end
+
+ def test_close
+ @tar_writer.close
+
+ assert_equal "\0" * 1024, @io.string
+
+ e = assert_raise IOError do
+ @tar_writer.close
+ end
+ assert_equal 'closed Gem::Package::TarWriter', e.message
+
+ e = assert_raise IOError do
+ @tar_writer.flush
+ end
+ assert_equal 'closed Gem::Package::TarWriter', e.message
+
+ e = assert_raise IOError do
+ @tar_writer.add_file 'x', 0
+ end
+ assert_equal 'closed Gem::Package::TarWriter', e.message
+
+ e = assert_raise IOError do
+ @tar_writer.add_file_simple 'x', 0, 0
+ end
+ assert_equal 'closed Gem::Package::TarWriter', e.message
+
+ e = assert_raise IOError do
+ @tar_writer.mkdir 'x', 0
+ end
+ assert_equal 'closed Gem::Package::TarWriter', e.message
+ end
+
+ def test_mkdir
+ @tar_writer.mkdir 'foo', 0644
+
+ assert_headers_equal tar_dir_header('foo', '', 0644),
+ @io.string[0, 512]
+ assert_equal 512, @io.pos
+ end
+
+ def test_split_name
+ assert_equal ['b' * 100, 'a' * 155],
+ @tar_writer.split_name("#{'a' * 155}/#{'b' * 100}")
+
+ assert_equal ["#{'qwer/' * 19}bla", 'a' * 151],
+ @tar_writer.split_name("#{'a' * 151}/#{'qwer/' * 19}bla")
+ end
+
+ def test_split_name_too_long_name
+ name = File.join 'a', 'b' * 100
+ assert_equal ['b' * 100, 'a'], @tar_writer.split_name(name)
+
+ assert_raise Gem::Package::TooLongFileName do
+ name = File.join 'a', 'b' * 101
+ @tar_writer.split_name name
+ end
+ end
+
+ def test_split_name_too_long_prefix
+ name = File.join 'a' * 155, 'b'
+ assert_equal ['b', 'a' * 155], @tar_writer.split_name(name)
+
+ assert_raise Gem::Package::TooLongFileName do
+ name = File.join 'a' * 156, 'b'
+ @tar_writer.split_name name
+ end
+ end
+
+ def test_split_name_too_long_total
+ assert_raise Gem::Package::TooLongFileName do
+ @tar_writer.split_name 'a' * 257
+ end
+ end
+
+end
+
diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb
index 5ffaed24e3..1ac71aabc2 100644
--- a/test/rubygems/test_gem_remote_fetcher.rb
+++ b/test/rubygems/test_gem_remote_fetcher.rb
@@ -97,6 +97,13 @@ gems:
@server_uri = base_server_uri + "/yaml"
@server_z_uri = base_server_uri + "/yaml.Z"
+ # REFACTOR: copied from test_gem_dependency_installer.rb
+ @gems_dir = File.join @tempdir, 'gems'
+ @cache_dir = File.join @gemhome, 'cache'
+ FileUtils.mkdir @gems_dir
+
+ @a1, @a1_gem = util_gem 'a', '1' do |s| s.executables << 'a_bin' end
+
Gem::RemoteFetcher.instance_variable_set :@fetcher, nil
end
@@ -156,6 +163,140 @@ gems:
end
end
+ def util_fuck_with_fetcher data, blow = false
+ fetcher = Gem::RemoteFetcher.fetcher
+ fetcher.instance_variable_set :@test_data, data
+
+ unless blow then
+ def fetcher.fetch_path arg
+ @test_arg = arg
+ @test_data
+ end
+ else
+ def fetcher.fetch_path arg
+ # OMG I'm such an ass
+ class << self; remove_method :fetch_path; end
+ def self.fetch_path arg
+ @test_arg = arg
+ @test_data
+ end
+
+ raise Gem::RemoteFetcher::FetchError, "haha!"
+ end
+ end
+
+ fetcher
+ end
+
+ def test_download
+ a1_data = nil
+ File.open @a1_gem, 'rb' do |fp|
+ a1_data = fp.read
+ end
+
+ fetcher = util_fuck_with_fetcher a1_data
+
+ a1_cache_gem = File.join(@gemhome, 'cache', "#{@a1.full_name}.gem")
+ assert_equal a1_cache_gem, fetcher.download(@a1, 'http://gems.example.com')
+ assert_equal("http://gems.example.com/gems/a-1.gem",
+ fetcher.instance_variable_get(:@test_arg).to_s)
+ assert File.exist?(a1_cache_gem)
+ end
+
+ def test_download_cached
+ FileUtils.mv @a1_gem, @cache_dir
+
+ inst = Gem::RemoteFetcher.fetcher
+
+ assert_equal File.join(@gemhome, 'cache', "#{@a1.full_name}.gem"),
+ inst.download(@a1, 'http://gems.example.com')
+ end
+
+ def test_download_local
+ FileUtils.mv @a1_gem, @tempdir
+ local_path = File.join @tempdir, "#{@a1.full_name}.gem"
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::RemoteFetcher.fetcher
+ end
+
+ assert_equal File.join(@gemhome, 'cache', "#{@a1.full_name}.gem"),
+ inst.download(@a1, local_path)
+ end
+
+ def test_download_install_dir
+ a1_data = nil
+ File.open @a1_gem, 'rb' do |fp|
+ a1_data = fp.read
+ end
+
+ fetcher = util_fuck_with_fetcher a1_data
+
+ install_dir = File.join @tempdir, 'more_gems'
+
+ a1_cache_gem = File.join install_dir, 'cache', "#{@a1.full_name}.gem"
+ actual = fetcher.download(@a1, 'http://gems.example.com', install_dir)
+
+ assert_equal a1_cache_gem, actual
+ assert_equal("http://gems.example.com/gems/a-1.gem",
+ fetcher.instance_variable_get(:@test_arg).to_s)
+
+ assert File.exist?(a1_cache_gem)
+ end
+
+ unless win_platform? then # File.chmod doesn't work
+ def test_download_local_read_only
+ FileUtils.mv @a1_gem, @tempdir
+ local_path = File.join @tempdir, "#{@a1.full_name}.gem"
+ inst = nil
+ File.chmod 0555, File.join(@gemhome, 'cache')
+
+ Dir.chdir @tempdir do
+ inst = Gem::RemoteFetcher.fetcher
+ end
+
+ assert_equal File.join(@tempdir, "#{@a1.full_name}.gem"),
+ inst.download(@a1, local_path)
+ ensure
+ File.chmod 0755, File.join(@gemhome, 'cache')
+ end
+ end
+
+ def test_download_platform_legacy
+ original_platform = 'old-platform'
+
+ e1, e1_gem = util_gem 'e', '1' do |s|
+ s.platform = Gem::Platform::CURRENT
+ s.instance_variable_set :@original_platform, original_platform
+ end
+
+ e1_data = nil
+ File.open e1_gem, 'rb' do |fp|
+ e1_data = fp.read
+ end
+
+ fetcher = util_fuck_with_fetcher e1_data, :blow_chunks
+
+ e1_cache_gem = File.join(@gemhome, 'cache', "#{e1.full_name}.gem")
+
+ assert_equal e1_cache_gem, fetcher.download(e1, 'http://gems.example.com')
+
+ assert_equal("http://gems.example.com/gems/#{e1.original_name}.gem",
+ fetcher.instance_variable_get(:@test_arg).to_s)
+ assert File.exist?(e1_cache_gem)
+ end
+
+ def test_download_unsupported
+ inst = Gem::RemoteFetcher.fetcher
+
+ e = assert_raise Gem::InstallError do
+ inst.download @a1, 'ftp://gems.rubyforge.org'
+ end
+
+ assert_equal 'unsupported URI scheme ftp', e.message
+ end
+
def test_explicit_proxy
use_ui @ui do
fetcher = Gem::RemoteFetcher.new @proxy_uri
@@ -232,22 +373,6 @@ gems:
assert_equal 'EOFError: EOFError reading uri', e.message
end
- def test_fetch_path_open_uri_http_error
- fetcher = Gem::RemoteFetcher.new nil
-
- def fetcher.open_uri_or_path(uri)
- io = StringIO.new 'went boom'
- err = OpenURI::HTTPError.new 'error', io
- raise err
- end
-
- e = assert_raise Gem::RemoteFetcher::FetchError do
- fetcher.fetch_path 'uri'
- end
-
- assert_equal "OpenURI::HTTPError: error reading uri\n\twent boom", e.message
- end
-
def test_fetch_path_socket_error
fetcher = Gem::RemoteFetcher.new nil
@@ -324,6 +449,53 @@ gems:
end
end
+ def test_open_uri_or_path
+ fetcher = Gem::RemoteFetcher.new nil
+
+ conn = Object.new
+ def conn.started?() true end
+ def conn.request(req)
+ unless defined? @requested then
+ @requested = true
+ res = Net::HTTPRedirection.new nil, 301, nil
+ res.add_field 'Location', 'http://gems.example.com/real_path'
+ res
+ else
+ res = Net::HTTPOK.new nil, 200, nil
+ def res.body() 'real_path' end
+ res
+ end
+ end
+
+ conn = { 'gems.example.com:80' => conn }
+ fetcher.instance_variable_set :@connections, conn
+
+ fetcher.send :open_uri_or_path, 'http://gems.example.com/redirect' do |io|
+ assert_equal 'real_path', io.read
+ end
+ end
+
+ def test_open_uri_or_path_limited_redirects
+ fetcher = Gem::RemoteFetcher.new nil
+
+ conn = Object.new
+ def conn.started?() true end
+ def conn.request(req)
+ res = Net::HTTPRedirection.new nil, 301, nil
+ res.add_field 'Location', 'http://gems.example.com/redirect'
+ res
+ end
+
+ conn = { 'gems.example.com:80' => conn }
+ fetcher.instance_variable_set :@connections, conn
+
+ e = assert_raise Gem::RemoteFetcher::FetchError do
+ fetcher.send :open_uri_or_path, 'http://gems.example.com/redirect'
+ end
+
+ assert_equal 'too many redirects', e.message
+ end
+
def test_zip
use_ui @ui do
self.class.enable_zip = true
diff --git a/test/rubygems/test_gem_source_index.rb b/test/rubygems/test_gem_source_index.rb
index 8fc8449814..74aa91d63e 100644
--- a/test/rubygems/test_gem_source_index.rb
+++ b/test/rubygems/test_gem_source_index.rb
@@ -36,7 +36,8 @@ class TestGemSourceIndex < RubyGemTestCase
use_ui @ui do
fetched_index = @source_index.fetch_bulk_index @uri
- assert_equal [@gem1.full_name, @gem4.full_name, @gem2.full_name].sort,
+ assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
+ @c1_2.full_name].sort,
fetched_index.gems.map { |n,s| n }.sort
end
@@ -82,7 +83,8 @@ class TestGemSourceIndex < RubyGemTestCase
use_ui @ui do
fetched_index = @source_index.fetch_bulk_index @uri
- assert_equal [@gem1.full_name, @gem4.full_name, @gem2.full_name].sort,
+ assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
+ @c1_2.full_name].sort,
fetched_index.gems.map { |n,s| n }.sort
end
@@ -105,7 +107,8 @@ class TestGemSourceIndex < RubyGemTestCase
use_ui @ui do
fetched_index = @source_index.fetch_bulk_index @uri
- assert_equal [@gem1.full_name, @gem4.full_name, @gem2.full_name].sort,
+ assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
+ @c1_2.full_name].sort,
fetched_index.gems.map { |n,s| n }.sort
end
@@ -123,7 +126,8 @@ class TestGemSourceIndex < RubyGemTestCase
util_setup_bulk_fetch false
use_ui @ui do
fetched_index = @source_index.fetch_bulk_index @uri
- assert_equal [@gem1.full_name, @gem4.full_name, @gem2.full_name].sort,
+ assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
+ @c1_2.full_name].sort,
fetched_index.gems.map { |n,s| n }.sort
end
@@ -136,11 +140,32 @@ class TestGemSourceIndex < RubyGemTestCase
end
def test_fetch_quick_index
- quick_index = util_zip @gem_names
- @fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index
+ 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
+
+ quick_index = @source_index.fetch_quick_index @uri, false
+ assert_equal [@a2.full_name, @b2.full_name].sort,
+ quick_index.sort
+
+ paths = @fetcher.paths
+
+ assert_equal "#{@gem_repo}/quick/latest_index.rz", paths.shift
+
+ assert paths.empty?, paths.join(', ')
+ end
+
+ def test_fetch_quick_index_all
+ 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
- quick_index = @source_index.fetch_quick_index @uri
- assert_equal [@gem1.full_name, @gem4.full_name, @gem2.full_name].sort,
+ quick_index = @source_index.fetch_quick_index @uri, true
+ assert_equal [@a1.full_name, @a2.full_name, @b2.full_name].sort,
quick_index.sort
paths = @fetcher.paths
@@ -155,7 +180,7 @@ class TestGemSourceIndex < RubyGemTestCase
proc { raise Exception }
e = assert_raise Gem::OperationNotSupportedError do
- @source_index.fetch_quick_index @uri
+ @source_index.fetch_quick_index @uri, true
end
assert_equal 'No quick index found: Exception', e.message
@@ -167,41 +192,201 @@ class TestGemSourceIndex < RubyGemTestCase
assert paths.empty?, paths.join(', ')
end
+ def test_fetch_quick_index_fallback
+ index = util_zip @gem_names
+
+ @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 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/"
+
+ @fetcher.data["#{repo}quick/latest_index.rz"] = latest_index
+
+ quick_index = @source_index.fetch_quick_index repo, false
+ assert_equal [@a2.full_name, @b2.full_name].sort,
+ quick_index.sort
+
+ paths = @fetcher.paths
+
+ assert_equal "#{repo}quick/latest_index.rz", paths.shift
+
+ assert paths.empty?, paths.join(', ')
+ end
+
+ def test_fetch_single_spec
+ 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),
+ @a1.full_name
+
+ assert_equal @a1.full_name, spec.full_name
+
+ paths = @fetcher.paths
+
+ assert_equal a1_spec_url, paths.shift
+
+ assert paths.empty?, paths.join(', ')
+ end
+
+ def test_fetch_single_spec_subdir
+ 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)
+
+ spec = @source_index.send :fetch_single_spec, repo, @a1.full_name
+
+ assert_equal @a1.full_name, spec.full_name
+
+ paths = @fetcher.paths
+
+ assert_equal a1_spec_url, paths.shift
+
+ assert paths.empty?, paths.join(', ')
+ end
+
+ def test_fetch_single_spec_yaml
+ 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
+
+ spec = @source_index.send :fetch_single_spec, repo, @a1.full_name
+
+ assert_equal @a1.full_name, spec.full_name
+
+ paths = @fetcher.paths
+
+ 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/"
+
+ a1_spec_url = "#{repo}quick/#{@a1.full_name}.gemspec.rz"
+ @fetcher.data[a1_spec_url] = util_zip @a1.to_yaml
+
+ spec = @source_index.send :fetch_single_spec, repo, @a1.full_name
+
+ assert_equal @a1.full_name, spec.full_name
+
+ paths = @fetcher.paths
+
+ assert_equal "#{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_find_missing
- missing = @source_index.find_missing [@gem3.full_name]
- assert_equal [@gem3.full_name], missing
+ missing = @source_index.find_missing [@b2.full_name]
+ assert_equal [@b2.full_name], missing
end
def test_find_missing_none_missing
- missing = @source_index.find_missing @gem_names.split
+ missing = @source_index.find_missing [
+ @a1.full_name, @a2.full_name, @c1_2.full_name
+ ]
+
assert_equal [], missing
end
def test_latest_specs
- spec = quick_gem @gem1.name, '1'
- @source_index.add_spec spec
+ p1_ruby = quick_gem 'p', '1'
+ p1_platform = quick_gem 'p', '1' do |spec|
+ spec.platform = Gem::Platform::CURRENT
+ end
+
+ a1_platform = quick_gem @a1.name, (@a1.version) do |s|
+ s.platform = Gem::Platform.new 'x86-my_platform1'
+ end
+
+ a2_platform = quick_gem @a2.name, (@a2.version) do |s|
+ s.platform = Gem::Platform.new 'x86-my_platform1'
+ end
+
+ a2_platform_other = quick_gem @a2.name, (@a2.version) do |s|
+ s.platform = Gem::Platform.new 'x86-other_platform1'
+ end
+
+ a3_platform_other = quick_gem @a2.name, (@a2.version.bump) do |s|
+ s.platform = Gem::Platform.new 'x86-other_platform1'
+ end
+
+ @source_index.add_spec p1_ruby
+ @source_index.add_spec p1_platform
+ @source_index.add_spec a1_platform
+ @source_index.add_spec a2_platform
+ @source_index.add_spec a2_platform_other
+ @source_index.add_spec a3_platform_other
expected = [
- @gem1.full_name,
- @gem2.full_name,
- @gem4.full_name,
+ @a2.full_name,
+ a2_platform.full_name,
+ a3_platform_other.full_name,
+ @c1_2.full_name,
+ @a_evil9.full_name,
+ p1_ruby.full_name,
+ p1_platform.full_name,
].sort
- assert_equal expected, @source_index.latest_specs.map { |s| s.full_name }.sort
+ latest_specs = @source_index.latest_specs.map { |s| s.full_name }.sort
+
+ assert_equal expected, latest_specs
+ end
+
+ def test_load_gems_in
+ spec_dir1 = File.join @gemhome, 'specifications'
+ spec_dir2 = File.join @tempdir, 'gemhome2', 'specifications'
+
+ FileUtils.rm_r spec_dir1
+
+ FileUtils.mkdir_p spec_dir1
+ FileUtils.mkdir_p spec_dir2
+
+ a1 = quick_gem 'a', '1' do |spec| spec.author = 'author 1' end
+ a2 = quick_gem 'a', '1' do |spec| spec.author = 'author 2' end
+
+ File.open File.join(spec_dir1, "#{a1.full_name}.gemspec"), 'w' do |fp|
+ fp.write a1.to_ruby
+ end
+
+ File.open File.join(spec_dir2, "#{a2.full_name}.gemspec"), 'w' do |fp|
+ fp.write a2.to_ruby
+ end
+
+ @source_index.load_gems_in spec_dir1, spec_dir2
+
+ assert_equal a1.author, @source_index.specification(a1.full_name).author
end
def test_outdated
- sic = Gem::SourceInfoCache.new
- Gem::SourceInfoCache.instance_variable_set :@cache, sic
+ util_setup_source_info_cache
assert_equal [], @source_index.outdated
- updated = quick_gem @gem1.name, (@gem1.version.bump)
+ updated = quick_gem @a2.name, (@a2.version.bump)
util_setup_source_info_cache updated
assert_equal [updated.name], @source_index.outdated
- updated_platform = quick_gem @gem1.name, (updated.version.bump) do |s|
+ updated_platform = quick_gem @a2.name, (updated.version.bump) do |s|
s.platform = Gem::Platform.new 'x86-other_platform1'
end
@@ -211,28 +396,34 @@ class TestGemSourceIndex < RubyGemTestCase
end
def test_remove_extra
- @source_index.remove_extra [@gem1.full_name]
- assert_equal [@gem1.full_name], @source_index.gems.map { |n,s| n }
+ @source_index.add_spec @a1
+ @source_index.add_spec @a2
+
+ @source_index.remove_extra [@a1.full_name]
+
+ assert_equal [@a1.full_name], @source_index.gems.map { |n,s| n }
end
def test_remove_extra_no_changes
- gems = @gem_names.split.sort
+ gems = [@a1.full_name, @a2.full_name]
+ @source_index.add_spec @a1
+ @source_index.add_spec @a2
+
@source_index.remove_extra gems
+
assert_equal gems, @source_index.gems.map { |n,s| n }.sort
end
def test_search
- assert_equal [@gem1, @gem4], @source_index.search("gem_one")
- assert_equal [@gem1], @source_index.search("gem_one", "= 2")
+ assert_equal [@a1, @a2, @a_evil9], @source_index.search('a')
+ assert_equal [@a2], @source_index.search('a', '= 2')
- assert_equal [], @source_index.search("bogusstring")
- assert_equal [], @source_index.search("gem_one", "= 3.2.1")
+ assert_equal [], @source_index.search('bogusstring')
+ assert_equal [], @source_index.search('a', '= 3')
- @a1 = quick_gem 'a', '1'
- @a2 = quick_gem 'a', '2'
-
- source_index = Gem::SourceIndex.new @a1.full_name => @a1,
- @a2.full_name => @a2
+ source_index = Gem::SourceIndex.new
+ source_index.add_spec @a1
+ source_index.add_spec @a2
assert_equal [@a1], source_index.search(@a1.name, '= 1')
@@ -276,7 +467,7 @@ class TestGemSourceIndex < RubyGemTestCase
end
def test_specification
- assert_equal @gem1, @source_index.specification(@gem1.full_name)
+ assert_equal @a1, @source_index.specification(@a1.full_name)
assert_nil @source_index.specification("foo-1.2.4")
end
@@ -298,9 +489,11 @@ class TestGemSourceIndex < RubyGemTestCase
assert_equal [], @source_index.gems.keys.sort
use_ui @ui do
- @source_index.update @uri
+ @source_index.update @uri, true
- assert_equal @gem_names.split, @source_index.gems.keys.sort
+ assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
+ @c1_2.full_name],
+ @source_index.gems.keys.sort
end
paths = @fetcher.paths
@@ -315,15 +508,42 @@ class TestGemSourceIndex < RubyGemTestCase
old_gem_conf = Gem.configuration
Gem.configuration = Gem::ConfigFile.new([])
+ 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
+
+ marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
+ "#{@b2.full_name}.gemspec.rz"
+ @fetcher.data[marshal_uri] = util_zip Marshal.dump(@b2)
+
+ use_ui @ui do
+ @source_index.update @uri, false
+
+ assert_equal latest_names, @source_index.gems.keys.sort
+ end
+
+ paths = @fetcher.paths
+ assert_equal "#{@gem_repo}/quick/latest_index.rz", paths.shift
+ assert_equal marshal_uri, paths.shift
+
+ assert paths.empty?, paths.join(', ')
+ ensure
+ Gem.configuration = old_gem_conf
+ end
+
+ def test_update_incremental_all
+ old_gem_conf = Gem.configuration
+ Gem.configuration = Gem::ConfigFile.new([])
+
quick_index = util_zip @all_gem_names.join("\n")
@fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
- "#{@gem3.full_name}.gemspec.rz"
- @fetcher.data[marshal_uri] = util_zip Marshal.dump(@gem3)
+ "#{@b2.full_name}.gemspec.rz"
+ @fetcher.data[marshal_uri] = util_zip Marshal.dump(@b2)
use_ui @ui do
- @source_index.update @uri
+ @source_index.update @uri, true
assert_equal @all_gem_names, @source_index.gems.keys.sort
end
@@ -345,13 +565,13 @@ class TestGemSourceIndex < RubyGemTestCase
@fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
- "#{@gem3.full_name}.gemspec.rz"
+ "#{@b2.full_name}.gemspec.rz"
- yaml_uri = "#{@gem_repo}/quick/#{@gem3.full_name}.gemspec.rz"
- @fetcher.data[yaml_uri] = util_zip @gem3.to_yaml
+ yaml_uri = "#{@gem_repo}/quick/#{@b2.full_name}.gemspec.rz"
+ @fetcher.data[yaml_uri] = util_zip @b2.to_yaml
use_ui @ui do
- @source_index.update @uri
+ @source_index.update @uri, true
assert_equal @all_gem_names, @source_index.gems.keys.sort
end
@@ -374,16 +594,16 @@ class TestGemSourceIndex < RubyGemTestCase
@fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
- "#{@gem3.full_name}.gemspec.rz"
- marshal_data = Marshal.dump(@gem3)
+ "#{@b2.full_name}.gemspec.rz"
+ marshal_data = Marshal.dump(@b2)
marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr
@fetcher.data[marshal_uri] = util_zip marshal_data
- yaml_uri = "#{@gem_repo}/quick/#{@gem3.full_name}.gemspec.rz"
- @fetcher.data[yaml_uri] = util_zip @gem3.to_yaml
+ yaml_uri = "#{@gem_repo}/quick/#{@b2.full_name}.gemspec.rz"
+ @fetcher.data[yaml_uri] = util_zip @b2.to_yaml
use_ui @ui do
- @source_index.update @uri
+ @source_index.update @uri, true
assert_equal @all_gem_names, @source_index.gems.keys.sort
end
@@ -398,22 +618,48 @@ class TestGemSourceIndex < RubyGemTestCase
Gem.configuration = old_gem_conf
end
+ def test_update_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"
+
+ use_ui @ui do
+ @source_index.update uri, true
+
+ assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
+ @c1_2.full_name],
+ @source_index.gems.keys.sort
+ end
+
+ paths = @fetcher.paths
+
+ 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
+
def test_update_with_missing
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
- "#{@gem3.full_name}.gemspec.rz"
- dumped = Marshal.dump @gem3
+ "#{@c1_2.full_name}.gemspec.rz"
+ dumped = Marshal.dump @c1_2
@fetcher.data[marshal_uri] = util_zip(dumped)
use_ui @ui do
- @source_index.update_with_missing @uri, [@gem3.full_name]
+ @source_index.update_with_missing @uri, [@c1_2.full_name]
end
- spec = @source_index.specification(@gem3.full_name)
+ spec = @source_index.specification(@c1_2.full_name)
# We don't care about the equality of undumped attributes
- @gem3.files = spec.files
- @gem3.loaded_from = spec.loaded_from
+ @c1_2.files = spec.files
+ @c1_2.loaded_from = spec.loaded_from
- assert_equal @gem3, spec
+ assert_equal @c1_2, spec
end
def util_setup_bulk_fetch(compressed)
@@ -427,3 +673,4 @@ class TestGemSourceIndex < RubyGemTestCase
end
end
+
diff --git a/test/rubygems/test_gem_source_info_cache.rb b/test/rubygems/test_gem_source_info_cache.rb
index 570b643bc5..3bdaef5aa9 100644
--- a/test/rubygems/test_gem_source_info_cache.rb
+++ b/test/rubygems/test_gem_source_info_cache.rb
@@ -25,7 +25,12 @@ class TestGemSourceInfoCache < RubyGemTestCase
@sic = Gem::SourceInfoCache.new
@sic.instance_variable_set :@fetcher, @fetcher
+ @si_new = Gem::SourceIndex.new
+ @sice_new = Gem::SourceInfoCacheEntry.new @si_new, 0
+
prep_cache_files @sic
+
+ @sic.reset_cache_data
end
def teardown
@@ -35,8 +40,10 @@ class TestGemSourceInfoCache < RubyGemTestCase
def test_self_cache_refreshes
Gem.configuration.update_sources = true #true by default
- source_index = Gem::SourceIndex.new 'key' => 'sys'
- @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = source_index.dump
+ si = Gem::SourceIndex.new
+ si.add_spec @a1
+
+ @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump
Gem.sources.replace %W[#{@gem_repo}]
@@ -51,8 +58,10 @@ class TestGemSourceInfoCache < RubyGemTestCase
def test_self_cache_skips_refresh_based_on_configuration
Gem.configuration.update_sources = false
- source_index = Gem::SourceIndex.new 'key' => 'sys'
- @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = source_index.dump
+ si = Gem::SourceIndex.new
+ si.add_spec @a1
+
+ @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump
Gem.sources.replace %w[#{@gem_repo}]
@@ -66,20 +75,24 @@ class TestGemSourceInfoCache < RubyGemTestCase
end
def test_self_cache_data
- source_index = Gem::SourceIndex.new 'key' => 'sys'
- @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = source_index.dump
+ si = Gem::SourceIndex.new
+ si.add_spec @a1
+
+ @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump
Gem::SourceInfoCache.instance_variable_set :@cache, nil
- sice = Gem::SourceInfoCacheEntry.new source_index, 0
+ sice = Gem::SourceInfoCacheEntry.new si, 0
use_ui @ui do
- assert_equal source_index.gems,
- Gem::SourceInfoCache.cache_data[@gem_repo].source_index.gems
+ gems = Gem::SourceInfoCache.cache_data[@gem_repo].source_index.gems
+ gem_names = gems.map { |_, spec| spec.full_name }
+
+ assert_equal si.gems.map { |_,spec| spec.full_name }, gem_names
end
end
def test_cache_data
- assert_equal [['key','sys']], @sic.cache_data.to_a.sort
+ assert_equal [[@gem_repo, @usr_sice]], @sic.cache_data.to_a.sort
end
def test_cache_data_dirty
@@ -97,7 +110,14 @@ class TestGemSourceInfoCache < RubyGemTestCase
data = { @gem_repo => { 'totally' => 'borked' } }
- [@sic.system_cache_file, @sic.user_cache_file].each do |fn|
+ cache_files = [
+ @sic.system_cache_file,
+ @sic.latest_system_cache_file,
+ @sic.user_cache_file,
+ @sic.latest_user_cache_file
+ ]
+
+ cache_files.each do |fn|
FileUtils.mkdir_p File.dirname(fn)
open(fn, "wb") { |f| f.write Marshal.dump(data) }
end
@@ -113,7 +133,9 @@ class TestGemSourceInfoCache < RubyGemTestCase
def test_cache_data_none_readable
FileUtils.chmod 0222, @sic.system_cache_file
+ FileUtils.chmod 0222, @sic.latest_system_cache_file
FileUtils.chmod 0222, @sic.user_cache_file
+ FileUtils.chmod 0222, @sic.latest_user_cache_file
return if (File.stat(@sic.system_cache_file).mode & 0222) != 0222
return if (File.stat(@sic.user_cache_file).mode & 0222) != 0222
# HACK for systems that don't support chmod
@@ -129,6 +151,16 @@ class TestGemSourceInfoCache < RubyGemTestCase
assert_equal 'unable to locate a writable cache file', e.message
end
+ def test_cache_data_nonexistent
+ FileUtils.rm @sic.system_cache_file
+ FileUtils.rm @sic.latest_system_cache_file
+ FileUtils.rm @sic.user_cache_file
+ FileUtils.rm @sic.latest_user_cache_file
+
+ # TODO test verbose output
+ assert_equal [], @sic.cache_data.to_a.sort
+ end
+
def test_cache_data_repair
data = {
@gem_repo => {
@@ -152,7 +184,8 @@ class TestGemSourceInfoCache < RubyGemTestCase
def test_cache_data_user_fallback
FileUtils.chmod 0444, @sic.system_cache_file
- assert_equal [['key','usr']], @sic.cache_data.to_a.sort
+
+ assert_equal [[@gem_repo, @usr_sice]], @sic.cache_data.to_a.sort
end
def test_cache_file
@@ -174,60 +207,118 @@ class TestGemSourceInfoCache < RubyGemTestCase
end
def test_flush
- @sic.cache_data['key'] = 'new'
+ @sic.cache_data[@gem_repo] = @sice_new
@sic.update
@sic.flush
- assert_equal [['key','new']], read_cache(@sic.system_cache_file).to_a.sort
+ assert_equal [[@gem_repo, @sice_new]],
+ read_cache(@sic.system_cache_file).to_a.sort
+ end
+
+ def test_latest_cache_data
+ util_make_gems
+
+ sice = Gem::SourceInfoCacheEntry.new @source_index, 0
+
+ @sic.set_cache_data @gem_repo => sice
+ latest = @sic.latest_cache_data
+ gems = latest[@gem_repo].source_index.search('a').map { |s| s.full_name }
+
+ assert_equal %w[a-2 a_evil-9], gems
+ end
+
+ def test_latest_cache_file
+ latest_cache_file = File.join File.dirname(@gemcache),
+ "latest_#{File.basename @gemcache}"
+ assert_equal latest_cache_file, @sic.latest_cache_file
+ end
+
+ def test_latest_system_cache_file
+ assert_equal File.join(Gem.dir, "latest_source_cache"),
+ @sic.latest_system_cache_file
+ end
+
+ def test_latest_user_cache_file
+ assert_equal @latest_usrcache, @sic.latest_user_cache_file
end
def test_read_system_cache
- assert_equal [['key','sys']], @sic.cache_data.to_a.sort
+ assert_equal [[@gem_repo, @sys_sice]], @sic.cache_data.to_a.sort
end
def test_read_user_cache
- FileUtils.chmod 0444, @sic.system_cache_file
+ FileUtils.chmod 0444, @sic.user_cache_file
+ FileUtils.chmod 0444, @sic.latest_user_cache_file
+
+ @si = Gem::SourceIndex.new
+ @si.add_specs @a1, @a2
+
+ @sice = Gem::SourceInfoCacheEntry.new @si, 0
+
+ @sic.set_cache_data({ @gem_repo => @sice })
+ @sic.update
+ @sic.write_cache
+ @sic.reset_cache_data
- assert_equal [['key','usr']], @sic.cache_data.to_a.sort
+ user_cache_data = @sic.cache_data.to_a.sort
+
+ assert_equal 1, user_cache_data.length
+ user_cache_data = user_cache_data.first
+
+ assert_equal @gem_repo, user_cache_data.first
+
+ gems = user_cache_data.last.source_index.map { |_,spec| spec.full_name }
+ assert_equal [@a2.full_name], gems
end
def test_search
- si = Gem::SourceIndex.new @gem1.full_name => @gem1
- cache_data = {
- @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil)
- }
+ si = Gem::SourceIndex.new
+ si.add_spec @a1
+ cache_data = { @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) }
@sic.instance_variable_set :@cache_data, cache_data
- assert_equal [@gem1], @sic.search(//)
+ assert_equal [@a1], @sic.search(//)
+ end
+
+ def test_search_all
+ util_make_gems
+
+ sice = Gem::SourceInfoCacheEntry.new @source_index, 0
+
+ @sic.set_cache_data @gem_repo => sice
+ @sic.update
+ @sic.write_cache
+ @sic.reset_cache_data
+
+ gem_names = @sic.search(//, false, true).map { |spec| spec.full_name }
+
+ assert_equal %w[a-1 a-2 a_evil-9 c-1.2], gem_names
end
def test_search_dependency
- si = Gem::SourceIndex.new @gem1.full_name => @gem1
- cache_data = {
- @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil)
- }
+ si = Gem::SourceIndex.new
+ si.add_spec @a1
+ cache_data = { @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) }
@sic.instance_variable_set :@cache_data, cache_data
- dep = Gem::Dependency.new @gem1.name, @gem1.version
+ dep = Gem::Dependency.new @a1.name, @a1.version
- assert_equal [@gem1], @sic.search(dep)
+ assert_equal [@a1], @sic.search(dep)
end
def test_search_no_matches
- si = Gem::SourceIndex.new @gem1.full_name => @gem1
- cache_data = {
- @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil)
- }
+ si = Gem::SourceIndex.new
+ si.add_spec @a1
+ cache_data = { @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) }
@sic.instance_variable_set :@cache_data, cache_data
assert_equal [], @sic.search(/nonexistent/)
end
def test_search_no_matches_in_source
- si = Gem::SourceIndex.new @gem1.full_name => @gem1
- cache_data = {
- @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil)
- }
+ si = Gem::SourceIndex.new
+ si.add_spec @a1
+ cache_data = { @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) }
@sic.instance_variable_set :@cache_data, cache_data
Gem.sources.replace %w[more-gems.example.com]
@@ -235,13 +326,12 @@ class TestGemSourceInfoCache < RubyGemTestCase
end
def test_search_with_source
- si = Gem::SourceIndex.new @gem1.full_name => @gem1
- cache_data = {
- @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil)
- }
+ si = Gem::SourceIndex.new
+ si.add_spec @a1
+ cache_data = { @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) }
@sic.instance_variable_set :@cache_data, cache_data
- assert_equal [[@gem1, @gem_repo]],
+ assert_equal [[@a1, @gem_repo]],
@sic.search_with_source(//)
end
@@ -254,45 +344,81 @@ class TestGemSourceInfoCache < RubyGemTestCase
end
def test_write_cache
- @sic.cache_data['key'] = 'new'
+ @sic.cache_data[@gem_repo] = @sice_new
@sic.write_cache
- assert_equal [['key', 'new']],
+ assert_equal [[@gem_repo, @sice_new]],
read_cache(@sic.system_cache_file).to_a.sort
- assert_equal [['key', 'usr']],
+ assert_equal [[@gem_repo, @usr_sice]],
read_cache(@sic.user_cache_file).to_a.sort
end
def test_write_cache_user
FileUtils.chmod 0444, @sic.system_cache_file
- @sic.set_cache_data({'key' => 'new'})
+ @sic.set_cache_data({@gem_repo => @sice_new})
@sic.update
@sic.write_cache
- assert_equal [['key', 'sys']], read_cache(@sic.system_cache_file).to_a.sort
- assert_equal [['key', 'new']], read_cache(@sic.user_cache_file).to_a.sort
+ assert File.exist?(@sic.user_cache_file), 'user_cache_file'
+ assert File.exist?(@sic.latest_user_cache_file),
+ 'latest_user_cache_file exists'
+
+ assert_equal [[@gem_repo, @sys_sice]],
+ read_cache(@sic.system_cache_file).to_a.sort
+ assert_equal [[@gem_repo, @sice_new]],
+ read_cache(@sic.user_cache_file).to_a.sort
end
def test_write_cache_user_from_scratch
FileUtils.rm_rf @sic.user_cache_file
+ FileUtils.rm_rf @sic.latest_user_cache_file
+
FileUtils.chmod 0444, @sic.system_cache_file
- @sic.set_cache_data({'key' => 'new'})
+ FileUtils.chmod 0444, @sic.latest_system_cache_file
+
+ @si = Gem::SourceIndex.new
+ @si.add_specs @a1, @a2
+
+ @sice = Gem::SourceInfoCacheEntry.new @si, 0
+
+ @sic.set_cache_data({ @gem_repo => @sice })
@sic.update
@sic.write_cache
- assert_equal [['key', 'sys']], read_cache(@sic.system_cache_file).to_a.sort
- assert_equal [['key', 'new']], read_cache(@sic.user_cache_file).to_a.sort
+ assert File.exist?(@sic.user_cache_file), 'system_cache_file'
+ assert File.exist?(@sic.latest_user_cache_file),
+ 'latest_system_cache_file'
+
+ user_cache_data = read_cache(@sic.user_cache_file).to_a.sort
+ assert_equal 1, user_cache_data.length
+ user_cache_data = user_cache_data.first
+
+ assert_equal @gem_repo, user_cache_data.first
+
+ gems = user_cache_data.last.source_index.map { |_,spec| spec.full_name }
+ assert_equal [@a1.full_name, @a2.full_name], gems
+
+ user_cache_data = read_cache(@sic.latest_user_cache_file).to_a.sort
+ assert_equal 1, user_cache_data.length
+ user_cache_data = user_cache_data.first
+
+ assert_equal @gem_repo, user_cache_data.first
+
+ gems = user_cache_data.last.source_index.map { |_,spec| spec.full_name }
+ assert_equal [@a2.full_name], gems
end
def test_write_cache_user_no_directory
FileUtils.rm_rf File.dirname(@sic.user_cache_file)
FileUtils.chmod 0444, @sic.system_cache_file
- @sic.set_cache_data({'key' => 'new'})
+ @sic.set_cache_data({ @gem_repo => @sice_new })
@sic.update
@sic.write_cache
- assert_equal [['key','sys']], read_cache(@sic.system_cache_file).to_a.sort
- assert_equal [['key','new']], read_cache(@sic.user_cache_file).to_a.sort
+ assert_equal [[@gem_repo, @sys_sice]],
+ read_cache(@sic.system_cache_file).to_a.sort
+ assert_equal [[@gem_repo, @sice_new]],
+ read_cache(@sic.user_cache_file).to_a.sort
end
end
diff --git a/test/rubygems/test_gem_source_info_cache_entry.rb b/test/rubygems/test_gem_source_info_cache_entry.rb
index 023baf948b..c1194e34bc 100644
--- a/test/rubygems/test_gem_source_info_cache_entry.rb
+++ b/test/rubygems/test_gem_source_info_cache_entry.rb
@@ -9,37 +9,68 @@ class TestGemSourceInfoCacheEntry < RubyGemTestCase
util_setup_fake_fetcher
- @si = Gem::SourceIndex.new @gem1.full_name => @gem1.name
+ @si = Gem::SourceIndex.new
+ @si.add_spec @a1
@sic_e = Gem::SourceInfoCacheEntry.new @si, @si.dump.size
end
def test_refresh
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}.Z"] =
- proc { raise Exception }
+ proc { raise }
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = @si.dump
- assert_nothing_raised do
- @sic_e.refresh @gem_repo
+ use_ui @ui do
+ @sic_e.refresh @gem_repo, true
+ end
+ end
+
+ def test_refresh_all
+ @si.add_spec @a2
+
+ 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
+
+ sic_e = Gem::SourceInfoCacheEntry.new Gem::SourceIndex.new, 0
+
+ use_ui @ui do
+ sic_e.refresh @gem_repo, false
end
+
+ assert_equal [a2_name], sic_e.source_index.map { |n,| n }.sort
+
+ use_ui @ui do
+ sic_e.refresh @gem_repo, true
+ end
+
+ assert_equal [a1_name, a2_name], sic_e.source_index.map { |n,| n }.sort
end
def test_refresh_bad_uri
assert_raise URI::BadURIError do
- @sic_e.refresh 'gems.example.com'
+ @sic_e.refresh 'gems.example.com', true
end
end
def test_refresh_update
- si = Gem::SourceIndex.new @gem1.full_name => @gem1,
- @gem2.full_name => @gem2
+ si = Gem::SourceIndex.new
+ si.add_spec @a1
+ si.add_spec @b2
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump
use_ui @ui do
- @sic_e.refresh @gem_repo
+ @sic_e.refresh @gem_repo, true
end
- new_gem = @sic_e.source_index.specification(@gem2.full_name)
- assert_equal @gem2.full_name, new_gem.full_name
+ new_gem = @sic_e.source_index.specification(@b2.full_name)
+ assert_equal @b2.full_name, new_gem.full_name
end
end
diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb
new file mode 100644
index 0000000000..d6e41814ed
--- /dev/null
+++ b/test/rubygems/test_gem_uninstaller.rb
@@ -0,0 +1,43 @@
+require File.join(File.expand_path(File.dirname(__FILE__)),
+ 'gem_installer_test_case')
+require 'rubygems/uninstaller'
+
+class TestGemUninstaller < GemInstallerTestCase
+
+ def setup
+ super
+
+ ui = MockGemUi.new
+ util_setup_gem ui
+
+ use_ui ui do
+ @installer.install
+ end
+ end
+
+ def test_remove_executables_force_keep
+ uninstaller = Gem::Uninstaller.new nil, :executables => false
+
+ use_ui @ui do
+ uninstaller.remove_executables @spec
+ end
+
+ assert_equal true, File.exist?(File.join(@gemhome, 'bin', 'executable'))
+
+ assert_equal "Executables and scripts will remain installed.\n", @ui.output
+ end
+
+ def test_remove_executables_force_remove
+ uninstaller = Gem::Uninstaller.new nil, :executables => true
+
+ use_ui @ui do
+ uninstaller.remove_executables @spec
+ end
+
+ assert_equal "Removing executable\n", @ui.output
+
+ assert_equal false, File.exist?(File.join(@gemhome, 'bin', 'executable'))
+ end
+
+end
+