1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Import RubyGems 1.1.0

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@15873 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
drbrain 2008-03-31 22:40:06 +00:00
parent dc8359969e
commit 8cc45aae94
82 changed files with 5776 additions and 2928 deletions

View file

@ -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> Tue Apr 1 03:20:40 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
* configure.in (RUBY_SETJMP, RUBY_LONGJMP, RUBY_JMP_BUF): prefers * configure.in (RUBY_SETJMP, RUBY_LONGJMP, RUBY_JMP_BUF): prefers

11
bin/gem
View file

@ -7,11 +7,12 @@
require 'rubygems' require 'rubygems'
require 'rubygems/gem_runner' 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 unless required_version.satisfied_by? Gem.ruby_version then
abort "Expected Ruby Version #{required_version}, was #{RUBY_VERSION}" abort "Expected Ruby Version #{required_version}, was #{Gem.ruby_version}"
end end
# We need to preserve the original ARGV to use for passing gem options # 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. # it...its for the source building process.
args = !ARGV.include?("--") ? ARGV.clone : ARGV[0...ARGV.index("--")] args = !ARGV.include?("--") ? ARGV.clone : ARGV[0...ARGV.index("--")]
begin
Gem::GemRunner.new.run args Gem::GemRunner.new.run args
rescue Gem::SystemExitException => e
exit e.exit_code
end

View file

@ -1,9 +1,10 @@
# depends on: array.rb dir.rb env.rb file.rb hash.rb module.rb regexp.rb
# empty gem_prelude.rb # empty gem_prelude.rb
# #
# p Gem::Enable # p Gem::Enable
if defined?(Gem::Enable) && Gem::Enable if defined?(Gem::Enable) && Gem::Enable then
#t = Time.now
module Kernel module Kernel
@ -102,6 +103,7 @@ module Gem
unless GemPaths.has_key?(gem_name) unless GemPaths.has_key?(gem_name)
raise LoadError.new("Could not find RubyGem #{gem_name} (>= 0)\n") raise LoadError.new("Could not find RubyGem #{gem_name} (>= 0)\n")
end end
# highest version gems already active # highest version gems already active
return false return false
else else
@ -109,8 +111,10 @@ module Gem
QuickLoader.load_full_rubygems_library QuickLoader.load_full_rubygems_library
return gem(gem_name, *version_requirements) return gem(gem_name, *version_requirements)
end end
requirement, version = version_requirements[0].split requirement, version = version_requirements[0].split
requirement.strip! requirement.strip!
if requirement == ">" || requirement == ">=" if requirement == ">" || requirement == ">="
if (GemVersions[gem_name] <=> Gem.calculate_integers_for_gem_version(version)) >= 0 if (GemVersions[gem_name] <=> Gem.calculate_integers_for_gem_version(version)) >= 0
return false return false
@ -122,6 +126,7 @@ module Gem
return false return false
end end
end end
QuickLoader.load_full_rubygems_library QuickLoader.load_full_rubygems_library
gem(gem_name, *version_requirements) gem(gem_name, *version_requirements)
end end
@ -157,7 +162,9 @@ module Gem
end end
end end
end end
require_paths = [] require_paths = []
GemPaths.values.each do |path| GemPaths.values.each do |path|
if File.exist?(File.join(path, ".require_paths")) 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)}) require_paths.concat(File.read(File.join(path, ".require_paths")).split.map {|require_path| File.join(path, require_path)})
@ -203,5 +210,5 @@ rescue Exception => e
puts e.backtrace.join("\n") puts e.backtrace.join("\n")
end end
#puts "Gem load in #{Time.now - t} seconds" end
end # Gem::Enable

View file

@ -17,63 +17,49 @@ end
module Kernel 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, # Use Kernel#gem to activate a specific version of +gem_name+.
# 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 # +version_requirements+ is a list of version requirements that the
# statements (otherwise rubygems might select a conflicting library # specified gem must match, most commonly "= example.version.number". See
# version). # Gem::Requirement for how to specify a version requirement.
# #
# You can define the environment variable GEM_SKIP as a way to not # If you will be activating the latest version of a gem, there is no need to
# load specified gems. You might do this to test out changes that # call Kernel#gem, Kernel#require will do the right thing for you.
# haven't been installed yet. Example: #
# 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.
#
# Kernel#gem should be called *before* any require statements (otherwise
# RubyGems may load a conflicting library version).
#
# 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.
#
# Example:
# #
# GEM_SKIP=libA:libB ruby -I../libA -I../libB ./mycode.rb # GEM_SKIP=libA:libB ruby -I../libA -I../libB ./mycode.rb
#
# gem:: [String or Gem::Dependency] The gem name or dependency
# instance.
#
# version_requirement:: [default=">= 0"] The version
# requirement.
#
# return:: [Boolean] true if the Gem is loaded, otherwise false.
#
# raises:: [Gem::LoadError] if Gem cannot be found, is listed in
# GEM_SKIP, or version requirement not met.
#
def gem(gem_name, *version_requirements) 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
def active_gem_with_options(gem_name, version_requirements, options={})
skip_list = (ENV['GEM_SKIP'] || "").split(/:/) skip_list = (ENV['GEM_SKIP'] || "").split(/:/)
raise Gem::LoadError, "skipping #{gem_name}" if skip_list.include? gem_name 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 end
end
##
# Main module to hold all RubyGem classes/modules. # Main module to hold all RubyGem classes/modules.
#
module Gem module Gem
ConfigMap = {} unless defined?(ConfigMap) ConfigMap = {} unless defined?(ConfigMap)
require 'rbconfig' require 'rbconfig'
RbConfig = Config unless defined? ::RbConfig RbConfig = Config unless defined? ::RbConfig
ConfigMap.merge!( ConfigMap.merge!(
:BASERUBY => RbConfig::CONFIG["BASERUBY"], :BASERUBY => RbConfig::CONFIG["BASERUBY"],
:EXEEXT => RbConfig::CONFIG["EXEEXT"], :EXEEXT => RbConfig::CONFIG["EXEEXT"],
@ -88,59 +74,12 @@ module Gem
:sitelibdir => RbConfig::CONFIG["sitelibdir"] :sitelibdir => RbConfig::CONFIG["sitelibdir"]
) )
DIRECTORIES = %w[cache doc gems specifications] unless defined?(DIRECTORIES)
MUTEX = Mutex.new MUTEX = Mutex.new
RubyGemsPackageVersion = RubyGemsVersion RubyGemsPackageVersion = RubyGemsVersion
DIRECTORIES = %w[cache doc gems specifications] unless defined?(DIRECTORIES)
@@source_index = nil
@@win_platform = nil
@configuration = nil
@loaded_specs = {}
@platforms = nil
@ruby = nil
@sources = []
# 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
@@source_index = nil
MUTEX.synchronize do
@searcher = nil
end
end
# The version of the Marshal format for your Ruby.
def self.marshal_version
"#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
end
##
# The directory prefix this RubyGems was installed at.
def self.prefix
prefix = File.dirname File.expand_path(__FILE__)
if prefix == ConfigMap[:sitelibdir] then
nil
else
File.dirname prefix
end
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
end
## ##
# An Array of Regexps that match windows ruby platforms. # An Array of Regexps that match windows ruby platforms.
@ -153,206 +92,67 @@ module Gem
/wince/i, /wince/i,
] ]
@@source_index = nil
@@win_platform = nil
@configuration = nil
@loaded_specs = {}
@platforms = nil
@ruby = nil
@sources = []
## ##
# Is this a windows platform? # Activates an installed gem matching +gem+. The gem must satisfy
# +version_requirements+.
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
# 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'
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
# 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
@sources
end
# Provide an alias for the old name.
alias cache source_index
# The directory path where Gems are to be installed.
# #
# return:: [String] The directory path # Returns true if the gem is activated, false if it is already
# loaded, or an exception otherwise.
# #
def dir # Gem#activate adds the library paths in +gem+ to $LOAD_PATH. Before a Gem
@gem_home ||= nil # is activated its required Gems are activated. If the version information
set_home(ENV['GEM_HOME'] || default_dir) unless @gem_home # is omitted, the highest version Gem of the supplied name is loaded. If a
@gem_home # Gem is not found that meets the version requirements or a required Gem is
end # not found, a Gem::LoadError is raised.
# The directory path where executables are to be installed.
# #
def bindir(install_dir=Gem.dir) # More information on version requirements can be found in the
return File.join(install_dir, 'bin') unless # Gem::Requirement and Gem::Version documentation.
install_dir.to_s == Gem.default_dir
if defined? RUBY_FRAMEWORK_VERSION then # mac framework support def self.activate(gem, *version_requirements)
'/usr/bin'
else # generic install
ConfigMap[:bindir]
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))
end
@gem_path
end
# The home directory for the user.
def user_home
@user_home ||= find_home
end
# Return the path to standard location of the users .gemrc file.
def config_file
File.join(Gem.user_home, '.gemrc')
end
# The standard configuration object for gems.
def configuration
return @configuration if @configuration
require 'rubygems/config_file'
@configuration = Gem::ConfigFile.new []
end
# Use the given configuration object (which implements the
# ConfigFile protocol) as the standard configuration object.
def configuration=(config)
@configuration = config
end
# 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
# Return the searcher object to search for matching gems.
def searcher
MUTEX.synchronize do
@searcher ||= Gem::GemPathSearcher.new
end
end
# 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
@ruby
end
# 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
index
end
# 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 if version_requirements.empty? then
version_requirements = Gem::Requirement.default version_requirements = Gem::Requirement.default
end end
unless gem.respond_to?(:name) && gem.respond_to?(:version_requirements) unless gem.respond_to?(:name) and
gem.respond_to?(:version_requirements) then
gem = Gem::Dependency.new(gem, version_requirements) gem = Gem::Dependency.new(gem, version_requirements)
end end
matches = Gem.source_index.find_name(gem.name, gem.version_requirements) matches = Gem.source_index.find_name(gem.name, gem.version_requirements)
report_activate_error(gem) if matches.empty? report_activate_error(gem) if matches.empty?
if @loaded_specs[gem.name] if @loaded_specs[gem.name] then
# This gem is already loaded. If the currently loaded gem is # This gem is already loaded. If the currently loaded gem is not in the
# not in the list of candidate gems, then we have a version # list of candidate gems, then we have a version conflict.
# conflict.
existing_spec = @loaded_specs[gem.name] 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}]" unless matches.any? { |spec| spec.version == existing_spec.version } then
raise Gem::Exception,
"can't activate #{gem}, already activated #{existing_spec.full_name}]"
end end
return false return false
end end
# new load # new load
spec = matches.last spec = matches.last
if spec.loaded? return false if spec.loaded?
return false unless autorequire
result = spec.autorequire ? require(spec.autorequire) : false
return result || false
end
spec.loaded = true spec.loaded = true
@loaded_specs[spec.name] = spec @loaded_specs[spec.name] = spec
# Load dependent gems first # Load dependent gems first
spec.dependencies.each do |dep_gem| spec.dependencies.each do |dep_gem|
activate(dep_gem, autorequire) activate dep_gem
end end
# bin directory must come before library directories # bin directory must come before library directories
@ -375,123 +175,112 @@ module Gem
$LOAD_PATH.unshift(*require_paths) $LOAD_PATH.unshift(*require_paths)
end end
# Now autorequire
if autorequire && spec.autorequire then # DEPRECATED
Array(spec.autorequire).each do |a_lib|
require a_lib
end
end
return true return true
end end
# Report a load error during activation. The message of load ##
# error depends on whether it was a version mismatch or if there # An Array of all possible load paths for all versions of all gems in the
# are not gems of any version by the requested name. # Gem installation.
def report_activate_error(gem)
matches = Gem.source_index.find_name(gem.name)
if matches.empty? then def self.all_load_paths
error = Gem::LoadError.new(
"Could not find RubyGem #{gem.name} (#{gem.version_requirements})\n")
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 = [] result = []
Gem.path.each do |gemdir| Gem.path.each do |gemdir|
each_load_path(all_partials(gemdir)) do |load_path| each_load_path all_partials(gemdir) do |load_path|
result << load_path result << load_path
end end
end end
result result
end end
# Return a list of all possible load paths for the latest version ##
# for all gems in the Gem installation. # Return all the partial paths in +gemdir+.
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
def required_location(gemname, libfile, *version_constraints) def self.all_partials(gemdir)
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
def suffix_pattern
@suffix_pattern ||= "{#{suffixes.join(',')}}"
end
# manage_gems is useless and deprecated. Don't call it anymore. This
# will warn in two releases.
def manage_gems
# do nothing
end
private
# Return all the partial paths in the given +gemdir+.
def all_partials(gemdir)
Dir[File.join(gemdir, 'gems/*')] Dir[File.join(gemdir, 'gems/*')]
end end
# Return only the latest partial paths in the given +gemdir+. private_class_method :all_partials
def latest_partials(gemdir)
latest = {} ##
all_partials(gemdir).each do |gp| # The mode needed to read a file as straight binary.
base = File.basename(gp)
if base =~ /(.*)-((\d+\.)*\d+)/ then def self.binary_mode
name, version = $1, $2 @binary_mode ||= RUBY_VERSION > '1.9' ? 'rb:ascii-8bit' : 'rb'
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] }
end end
# Expand each partial gem path with each of the required paths ##
# specified in the Gem spec. Each expanded path is yielded. # The path where gem executables are to be installed.
def each_load_path(partials)
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
@@source_index = nil
MUTEX.synchronize do
@searcher = nil
end
end
##
# The path to standard location of the user's .gemrc file.
def self.config_file
File.join Gem.user_home, '.gemrc'
end
##
# The standard configuration object for gems.
def self.configuration
return @configuration if @configuration
require 'rubygems/config_file'
@configuration = Gem::ConfigFile.new []
end
##
# Use the given configuration object (which implements the ConfigFile
# protocol) as the standard configuration object.
def self.configuration=(config)
@configuration = config
end
##
# The path the the data directory specified by the gem name. If the
# package is not available as a gem, return nil.
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
##
# The path where gems are to be installed.
def self.dir
@gem_home ||= nil
set_home(ENV['GEM_HOME'] || default_dir) unless @gem_home
@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 self.each_load_path(partials)
partials.each do |gp| partials.each do |gp|
base = File.basename(gp) base = File.basename(gp)
specfn = File.join(dir, "specifications", base + ".gemspec") specfn = File.join(dir, "specifications", base + ".gemspec")
@ -507,24 +296,25 @@ module Gem
end end
end end
# Set the Gem home directory (as reported by +dir+). private_class_method :each_load_path
def set_home(home)
@gem_home = home ##
ensure_gem_subdirectories(@gem_home) # Quietly ensure the named Gem directory contains all the proper
end # subdirectories. If we can't create a directory due to a permission
# problem, then we will silently continue.
# Set the Gem search path (as reported by +path+).
def set_paths(gpaths) def self.ensure_gem_subdirectories(gemdir)
if gpaths require 'fileutils'
@gem_path = gpaths.split(File::PATH_SEPARATOR)
@gem_path << Gem.dir Gem::DIRECTORIES.each do |filename|
else fn = File.join gemdir, filename
@gem_path = [Gem.dir] FileUtils.mkdir_p fn rescue nil unless File.exist? fn
end end
@gem_path.uniq!
@gem_path.each do |gp| ensure_gem_subdirectories(gp) end
end end
##
# Finds the user's home directory.
#--
# Some comments from the ruby-talk list regarding finding the home # Some comments from the ruby-talk list regarding finding the home
# directory: # directory:
# #
@ -532,18 +322,20 @@ module Gem
# to be depending on HOME in those code samples. I propose that # to be depending on HOME in those code samples. I propose that
# it should fallback to USERPROFILE and HOMEDRIVE + HOMEPATH (at # it should fallback to USERPROFILE and HOMEDRIVE + HOMEPATH (at
# least on Win32). # least on Win32).
#
def find_home def self.find_home
['HOME', 'USERPROFILE'].each do |homekey| ['HOME', 'USERPROFILE'].each do |homekey|
return ENV[homekey] if ENV[homekey] return ENV[homekey] if ENV[homekey]
end end
if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}" return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
end end
begin begin
File.expand_path("~") File.expand_path("~")
rescue StandardError => ex rescue
if File::ALT_SEPARATOR if File::ALT_SEPARATOR then
"C:/" "C:/"
else else
"/" "/"
@ -551,6 +343,320 @@ module Gem
end end
end end
private_class_method :find_home
##
# Return a list of all possible load paths for the latest version for all
# gems in the Gem installation.
def self.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
##
# 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
end
latest.collect { |k,v| v[1] }
end
private_class_method :latest_partials
##
# 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
index
end
##
# The file name and line number of the caller of the caller of this method.
def self.location_of_caller
file, lineno = caller[1].split(':')
lineno = lineno.to_i
[file, lineno]
end
private_class_method :location_of_caller
##
# manage_gems is useless and deprecated. Don't call it anymore.
#--
# TODO warn w/ RubyGems 1.2.x release.
def self.manage_gems
#file, lineno = location_of_caller
#warn "#{file}:#{lineno}:Warning: Gem#manage_gems is deprecated and will be removed on or after September 2008."
end
##
# The version of the Marshal format for your Ruby.
def self.marshal_version
"#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
end
##
# Array of paths to search for Gems.
def self.path
@gem_path ||= nil
unless @gem_path then
paths = [ENV['GEM_PATH']] || [default_path]
if defined?(APPLE_GEM_HOME) and not ENV['GEM_PATH'] then
paths << APPLE_GEM_HOME
end
set_paths paths.compact.join(File::PATH_SEPARATOR)
end
@gem_path
end
##
# Array of platforms this RubyGems supports.
def self.platforms
@platforms ||= [Gem::Platform::RUBY, Gem::Platform.local]
end
##
# The directory prefix this RubyGems was installed at.
def self.prefix
prefix = File.dirname File.expand_path(__FILE__)
if prefix == File.expand_path(ConfigMap[:sitelibdir]) then
nil
else
File.dirname prefix
end
end
##
# Safely read a file in binary mode on all platforms.
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.
def self.report_activate_error(gem)
matches = Gem.source_index.find_name(gem.name)
if matches.empty? then
error = Gem::LoadError.new(
"Could not find RubyGem #{gem.name} (#{gem.version_requirements})\n")
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_class_method :report_activate_error
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
##
# The path to the running Ruby interpreter.
def self.ruby
if @ruby.nil? then
@ruby = File.join(ConfigMap[:bindir],
ConfigMap[:ruby_install_name])
@ruby << ConfigMap[:EXEEXT]
end
@ruby
end
##
# A Gem::Version for the currently running ruby.
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
##
# 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
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
@gem_path.uniq!
@gem_path.each do |gp| ensure_gem_subdirectories(gp) end
end
private_class_method :set_paths
##
# 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
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
end end
@ -558,6 +664,7 @@ end
# Modify the non-gem version of datadir to handle gem package names. # Modify the non-gem version of datadir to handle gem package names.
require 'rbconfig/datadir' require 'rbconfig/datadir'
module Config # :nodoc: module Config # :nodoc:
class << self class << self
alias gem_original_datadir datadir alias gem_original_datadir datadir

View file

@ -65,13 +65,20 @@ EOM
end end
def write_package def write_package
Package.open(@spec.file_name, "w", @signer) do |pkg| open @spec.file_name, 'wb' do |gem_io|
Gem::Package.open gem_io, 'w', @signer do |pkg|
pkg.metadata = @spec.to_yaml pkg.metadata = @spec.to_yaml
@spec.files.each do |file| @spec.files.each do |file|
next if File.directory? file next if File.directory? file
pkg.add_file_simple(file, File.stat(@spec.file_name).mode & 0777,
File.size(file)) do |os| stat = File.stat file
os.write File.open(file, "rb"){|f|f.read} 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 end
end end

View file

@ -123,6 +123,7 @@ module Gem
end end
private private
def load_and_instantiate(command_name) def load_and_instantiate(command_name)
command_name = command_name.to_s command_name = command_name.to_s
retried = false retried = false

View file

@ -2,18 +2,13 @@ require 'rubygems/command'
require 'rubygems/source_index' require 'rubygems/source_index'
require 'rubygems/dependency_list' require 'rubygems/dependency_list'
module Gem class Gem::Commands::CleanupCommand < Gem::Command
module Commands
class CleanupCommand < Command
def initialize def initialize
super( super 'cleanup',
'cleanup',
'Clean up old versions of installed gems in the local repository', 'Clean up old versions of installed gems in the local repository',
{ :force => false, :test => false, :install_dir => Gem.dir
:force => false,
:test => false,
:install_dir => Gem.dir
})
add_option('-d', '--dryrun', "") do |value, options| add_option('-d', '--dryrun', "") do |value, options|
options[:dryrun] = true options[:dryrun] = true
end end
@ -33,11 +28,11 @@ module Gem
def execute def execute
say "Cleaning up installed gems..." say "Cleaning up installed gems..."
srcindex = Gem::SourceIndex.from_installed_gems
primary_gems = {} primary_gems = {}
srcindex.each do |name, spec| Gem.source_index.each do |name, spec|
if primary_gems[spec.name].nil? or primary_gems[spec.name].version < spec.version if primary_gems[spec.name].nil? or
primary_gems[spec.name].version < spec.version then
primary_gems[spec.name] = spec primary_gems[spec.name] = spec
end end
end end
@ -52,7 +47,7 @@ module Gem
end end
end end
else else
srcindex.each do |name, spec| Gem.source_index.each do |name, spec|
gems_to_cleanup << spec gems_to_cleanup << spec
end end
end end
@ -62,32 +57,35 @@ module Gem
} }
uninstall_command = Gem::CommandManager.instance['uninstall'] uninstall_command = Gem::CommandManager.instance['uninstall']
deplist = DependencyList.new deplist = Gem::DependencyList.new
gems_to_cleanup.uniq.each do |spec| deplist.add(spec) end gems_to_cleanup.uniq.each do |spec| deplist.add spec end
deplist.dependency_order.each do |spec| deps = deplist.strongly_connected_components.flatten.reverse
deps.each do |spec|
if options[:dryrun] then if options[:dryrun] then
say "Dry Run Mode: Would uninstall #{spec.full_name}" say "Dry Run Mode: Would uninstall #{spec.full_name}"
else else
say "Attempting uninstall on #{spec.full_name}" say "Attempting to uninstall #{spec.full_name}"
options[:args] = [spec.name] options[:args] = [spec.name]
options[:version] = "= #{spec.version}" options[:version] = "= #{spec.version}"
options[:executables] = true options[:executables] = false
uninstall_command.merge_options(options) uninstaller = Gem::Uninstaller.new spec.name, options
begin begin
uninstall_command.execute uninstaller.uninstall
rescue Gem::DependencyRemovalException => ex rescue Gem::DependencyRemovalException,
say "Unable to uninstall #{spec.full_name} ... continuing with remaining gems" Gem::GemNotInHomeException => e
say "Unable to uninstall #{spec.full_name}:"
say "\t#{e.class}: #{e.message}"
end end
end end
end end
say "Clean Up Complete" say "Clean Up Complete"
end end
end
end end
end

View file

@ -25,19 +25,18 @@ class Gem::Commands::EnvironmentCommand < Gem::Command
def execute def execute
out = '' out = ''
arg = options[:args][0] arg = options[:args][0]
if begins?("packageversion", arg) then case arg
when /^packageversion/ then
out << Gem::RubyGemsPackageVersion out << Gem::RubyGemsPackageVersion
elsif begins?("version", arg) then when /^version/ then
out << Gem::RubyGemsVersion out << Gem::RubyGemsVersion
elsif begins?("gemdir", arg) then when /^gemdir/, /^gemhome/, /^home/, /^GEM_HOME/ then
out << Gem.dir out << Gem.dir
elsif begins?("gempath", arg) then when /^gempath/, /^path/, /^GEM_PATH/ then
out << Gem.path.join("\n") out << Gem.path.join(File::PATH_SEPARATOR)
elsif begins?("remotesources", arg) then when /^remotesources/ then
out << Gem.sources.join("\n") out << Gem.sources.join("\n")
elsif arg then when nil then
fail Gem::CommandLineError, "Unknown enviroment option [#{arg}]"
else
out = "RubyGems Environment:\n" out = "RubyGems Environment:\n"
out << " - RUBYGEMS VERSION: #{Gem::RubyGemsVersion} (#{Gem::RubyGemsPackageVersion})\n" out << " - RUBYGEMS VERSION: #{Gem::RubyGemsVersion} (#{Gem::RubyGemsPackageVersion})\n"
@ -75,6 +74,9 @@ class Gem::Commands::EnvironmentCommand < Gem::Command
Gem.sources.each do |s| Gem.sources.each do |s|
out << " - #{s}\n" out << " - #{s}\n"
end end
else
fail Gem::CommandLineError, "Unknown enviroment option [#{arg}]"
end end
say out say out
true true

View file

@ -44,17 +44,15 @@ class Gem::Commands::FetchCommand < Gem::Command
spec, source_uri = specs_and_sources.last spec, source_uri = specs_and_sources.last
gem_file = "#{spec.full_name}.gem" if spec.nil? then
alert_error "Could not find #{gem_name} in any repository"
gem_path = File.join source_uri, 'gems', gem_file next
gem = Gem::RemoteFetcher.fetcher.fetch_path gem_path
File.open gem_file, 'wb' do |fp|
fp.write gem
end 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
end end

View file

@ -62,13 +62,15 @@ class Gem::Commands::InstallCommand < Gem::Command
:install_dir => options[:install_dir], :install_dir => options[:install_dir],
:security_policy => options[:security_policy], :security_policy => options[:security_policy],
:wrappers => options[:wrappers], :wrappers => options[:wrappers],
:bin_dir => options[:bin_dir]
} }
exit_code = 0
get_all_gem_names.each do |gem_name| get_all_gem_names.each do |gem_name|
begin begin
inst = Gem::DependencyInstaller.new gem_name, options[:version], inst = Gem::DependencyInstaller.new install_options
install_options inst.install gem_name, options[:version]
inst.install
inst.installed_gems.each do |spec| inst.installed_gems.each do |spec|
say "Successfully installed #{spec.full_name}" say "Successfully installed #{spec.full_name}"
@ -77,8 +79,10 @@ class Gem::Commands::InstallCommand < Gem::Command
installed_gems.push(*inst.installed_gems) installed_gems.push(*inst.installed_gems)
rescue Gem::InstallError => e rescue Gem::InstallError => e
alert_error "Error installing #{gem_name}:\n\t#{e.message}" alert_error "Error installing #{gem_name}:\n\t#{e.message}"
exit_code |= 1
rescue Gem::GemNotFoundException => e rescue Gem::GemNotFoundException => e
alert_error e.message alert_error e.message
exit_code |= 2
# rescue => e # rescue => e
# # TODO: Fix this handle to allow the error to propagate to # # TODO: Fix this handle to allow the error to propagate to
# # the top level handler. Examine the other errors as # # the top level handler. Examine the other errors as
@ -121,6 +125,8 @@ class Gem::Commands::InstallCommand < Gem::Command
end end
end end
end end
raise Gem::SystemExitException, exit_code
end end
end end

View file

@ -6,10 +6,8 @@ module Gem
class ListCommand < QueryCommand class ListCommand < QueryCommand
def initialize def initialize
super( super 'list', 'Display gems whose name starts with STRING'
'list',
'Display all gems whose name starts with STRING'
)
remove_option('--name-matches') remove_option('--name-matches')
end end

View file

@ -2,7 +2,7 @@ require 'yaml'
require 'zlib' require 'zlib'
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/gem_open_uri' require 'open-uri'
class Gem::Commands::MirrorCommand < Gem::Command class Gem::Commands::MirrorCommand < Gem::Command

View file

@ -1,15 +1,25 @@
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/local_remote_options' require 'rubygems/local_remote_options'
require 'rubygems/source_info_cache' require 'rubygems/source_info_cache'
require 'rubygems/version_option'
class Gem::Commands::QueryCommand < Gem::Command class Gem::Commands::QueryCommand < Gem::Command
include Gem::LocalRemoteOptions include Gem::LocalRemoteOptions
include Gem::VersionOption
def initialize(name = 'query', def initialize(name = 'query',
summary = 'Query gem information in local or remote repositories') summary = 'Query gem information in local or remote repositories')
super name, summary, 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', add_option('-n', '--name-matches REGEXP',
'Name of gem(s) to query on matches the', 'Name of gem(s) to query on matches the',
@ -28,33 +38,70 @@ class Gem::Commands::QueryCommand < Gem::Command
options[:details] = false unless value options[:details] = false unless value
end end
add_option('-a', '--all',
'Display all gem versions') do |value, options|
options[:all] = value
end
add_local_remote_options add_local_remote_options
end end
def defaults_str # :nodoc: def defaults_str # :nodoc:
"--local --name-matches '.*' --no-details --versions" "--local --name-matches // --no-details --versions --no-installed"
end end
def execute def execute
exit_code = 0
name = options[:name] 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 if local? then
say say
say "*** LOCAL GEMS ***" say "*** LOCAL GEMS ***"
say say
output_query_results Gem.cache.search(name)
output_query_results Gem.source_index.search(name)
end end
if remote? then if remote? then
say say
say "*** REMOTE GEMS ***" say "*** REMOTE GEMS ***"
say 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
end end
private 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) def output_query_results(gemspecs)
output = [] output = []
gem_list_with_version = {} gem_list_with_version = {}
@ -98,7 +145,7 @@ class Gem::Commands::QueryCommand < Gem::Command
## ##
# Used for wrapping and indenting text # Used for wrapping and indenting text
#
def format_text(text, wrap, indent=0) def format_text(text, wrap, indent=0)
result = [] result = []
work = text.dup work = text.dup

View file

@ -39,8 +39,11 @@ class Gem::Commands::SourcesCommand < Gem::Command
options[:list] = !(options[:add] || options[:remove] || options[:clear_all] || options[:update]) options[:list] = !(options[:add] || options[:remove] || options[:clear_all] || options[:update])
if options[:clear_all] then if options[:clear_all] then
remove_cache_file("user", Gem::SourceInfoCache.user_cache_file) sic = Gem::SourceInfoCache
remove_cache_file("system", Gem::SourceInfoCache.system_cache_file) 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 end
if options[:add] then if options[:add] then
@ -48,7 +51,7 @@ class Gem::Commands::SourcesCommand < Gem::Command
sice = Gem::SourceInfoCacheEntry.new nil, nil sice = Gem::SourceInfoCacheEntry.new nil, nil
begin begin
sice.refresh source_uri sice.refresh source_uri, true
Gem::SourceInfoCache.cache_data[source_uri] = sice Gem::SourceInfoCache.cache_data[source_uri] = sice
Gem::SourceInfoCache.cache.update Gem::SourceInfoCache.cache.update
@ -66,7 +69,7 @@ class Gem::Commands::SourcesCommand < Gem::Command
end end
if options[:update] then if options[:update] then
Gem::SourceInfoCache.cache.refresh Gem::SourceInfoCache.cache.refresh true
Gem::SourceInfoCache.cache.flush Gem::SourceInfoCache.cache.flush
say "source cache successfully updated" say "source cache successfully updated"
@ -78,6 +81,11 @@ class Gem::Commands::SourcesCommand < Gem::Command
unless Gem.sources.include? source_uri then unless Gem.sources.include? source_uri then
say "source #{source_uri} not present in cache" say "source #{source_uri} not present in cache"
else else
begin # HACK figure out how to get the cache w/o update
Gem::SourceInfoCache.cache
rescue Gem::RemoteFetcher::FetchError
end
Gem::SourceInfoCache.cache_data.delete source_uri Gem::SourceInfoCache.cache_data.delete source_uri
Gem::SourceInfoCache.cache.update Gem::SourceInfoCache.cache.update
Gem::SourceInfoCache.cache.flush Gem::SourceInfoCache.cache.flush
@ -100,11 +108,12 @@ class Gem::Commands::SourcesCommand < Gem::Command
private private
def remove_cache_file(desc, fn) def remove_cache_file(desc, path)
FileUtils.rm_rf fn rescue nil FileUtils.rm_rf path
if ! File.exist?(fn)
if not File.exist?(path) then
say "*** Removed #{desc} source cache ***" say "*** Removed #{desc} source cache ***"
elsif ! File.writable?(fn) elsif not File.writable?(path) then
say "*** Unable to remove #{desc} source cache (write protected) ***" say "*** Unable to remove #{desc} source cache (write protected) ***"
else else
say "*** Unable to remove #{desc} source cache ***" say "*** Unable to remove #{desc} source cache ***"

View file

@ -3,6 +3,7 @@ require 'rubygems/command'
require 'rubygems/local_remote_options' require 'rubygems/local_remote_options'
require 'rubygems/version_option' require 'rubygems/version_option'
require 'rubygems/source_info_cache' require 'rubygems/source_info_cache'
require 'rubygems/format'
class Gem::Commands::SpecificationCommand < Gem::Command class Gem::Commands::SpecificationCommand < Gem::Command
@ -41,13 +42,16 @@ class Gem::Commands::SpecificationCommand < Gem::Command
gem = get_one_gem_name gem = get_one_gem_name
if local? then if local? then
source_index = Gem::SourceIndex.from_installed_gems if File.exist? gem then
specs.push(*source_index.search(/\A#{gem}\z/, options[:version])) 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 end
if remote? then if remote? then
alert_warning "Remote information is not complete\n\n"
Gem::SourceInfoCache.cache_data.each do |_,sice| Gem::SourceInfoCache.cache_data.each do |_,sice|
specs.push(*sice.source_index.search(gem, options[:version])) specs.push(*sice.source_index.search(gem, options[:version]))
end end

View file

@ -35,6 +35,11 @@ module Gem
options[:install_dir] = File.expand_path(value) options[:install_dir] = File.expand_path(value)
end 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_version_option
add_platform_option add_platform_option
end end
@ -54,7 +59,13 @@ module Gem
def execute def execute
get_all_gem_names.each do |gem_name| get_all_gem_names.each do |gem_name|
begin
Gem::Uninstaller.new(gem_name, options).uninstall 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 end
end end

View file

@ -38,6 +38,7 @@ class Gem::Commands::UnpackCommand < Gem::Command
def execute def execute
gemname = get_one_gem_name gemname = get_one_gem_name
path = get_path(gemname, options[:version]) path = get_path(gemname, options[:version])
if path then if path then
basename = File.basename(path).sub(/\.gem$/, '') basename = File.basename(path).sub(/\.gem$/, '')
target_dir = File.expand_path File.join(options[:target], basename) target_dir = File.expand_path File.join(options[:target], basename)
@ -66,16 +67,27 @@ class Gem::Commands::UnpackCommand < Gem::Command
# source directories? # source directories?
def get_path(gemname, version_req) def get_path(gemname, version_req)
return gemname if gemname =~ /\.gem$/i 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 selected = specs.sort_by { |s| s.version }.last
return nil if selected.nil? return nil if selected.nil?
# We expect to find (basename).gem in the 'cache' directory. # We expect to find (basename).gem in the 'cache' directory.
# Furthermore, the name match must be exact (ignoring case). # Furthermore, the name match must be exact (ignoring case).
if gemname =~ /^#{selected.name}$/i if gemname =~ /^#{selected.name}$/i
filename = selected.full_name + '.gem' 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 else
return nil nil
end end
end end

View file

@ -1,8 +1,10 @@
require 'rubygems/command' require 'rubygems/command'
require 'rubygems/command_manager'
require 'rubygems/install_update_options' require 'rubygems/install_update_options'
require 'rubygems/local_remote_options' require 'rubygems/local_remote_options'
require 'rubygems/source_info_cache' require 'rubygems/source_info_cache'
require 'rubygems/version_option' require 'rubygems/version_option'
require 'rubygems/commands/install_command'
class Gem::Commands::UpdateCommand < Gem::Command class Gem::Commands::UpdateCommand < Gem::Command
@ -45,7 +47,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
def execute def execute
if options[:system] then if options[:system] then
say "Updating RubyGems..." say "Updating RubyGems"
unless options[:args].empty? then unless options[:args].empty? then
fail "No gem names are allowed with the --system option" fail "No gem names are allowed with the --system option"
@ -53,10 +55,10 @@ class Gem::Commands::UpdateCommand < Gem::Command
options[:args] = ["rubygems-update"] options[:args] = ["rubygems-update"]
else else
say "Updating installed gems..." say "Updating installed gems"
end end
hig = highest_installed_gems = {} hig = {}
Gem::SourceIndex.from_installed_gems.each do |name, spec| Gem::SourceIndex.from_installed_gems.each do |name, spec|
if hig[spec.name].nil? or hig[spec.name].version < spec.version then if hig[spec.name].nil? or hig[spec.name].version < spec.version then
@ -64,25 +66,28 @@ class Gem::Commands::UpdateCommand < Gem::Command
end end
end end
remote_gemspecs = Gem::SourceInfoCache.search(//) pattern = if options[:args].empty? then
//
gems_to_update = if options[:args].empty? then
which_to_update(highest_installed_gems, remote_gemspecs)
else else
options[:args] Regexp.union(*options[:args])
end end
options[:domain] = :remote # install from remote source remote_gemspecs = Gem::SourceInfoCache.search pattern
gems_to_update = which_to_update hig, remote_gemspecs
updated = []
# HACK use the real API # HACK use the real API
install_command = Gem::CommandManager.instance['install']
gems_to_update.uniq.sort.each do |name| gems_to_update.uniq.sort.each do |name|
say "Attempting remote update of #{name}" next if updated.any? { |spec| spec.name == name }
options[:args] = [name] say "Updating #{name}"
options[:ignore_dependencies] = true # HACK skip seen gems instead installer = Gem::DependencyInstaller.new options
install_command.merge_options(options) installer.install name
install_command.execute installer.installed_gems.each do |spec|
updated << spec
say "Successfully installed #{spec.full_name}"
end
end end
if gems_to_update.include? "rubygems-update" then 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 say "RubyGems system software updated" if installed
else else
updated = gems_to_update.uniq.sort.collect { |g| g.to_s }
if updated.empty? then if updated.empty? then
say "Nothing to update" say "Nothing to update"
else else
say "Gems updated: #{updated.join ', '}" say "Gems updated: #{updated.map { |spec| spec.name }.join ', '}"
end end
end end
end end

View file

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

View file

@ -11,6 +11,9 @@ module Gem
if defined? RUBY_FRAMEWORK_VERSION then if defined? RUBY_FRAMEWORK_VERSION then
File.join File.dirname(ConfigMap[:sitedir]), 'Gems', File.join File.dirname(ConfigMap[:sitedir]), 'Gems',
ConfigMap[:ruby_version] ConfigMap[:ruby_version]
elsif defined? RUBY_ENGINE then
File.join ConfigMap[:libdir], RUBY_ENGINE, 'gems',
ConfigMap[:ruby_version]
else else
File.join ConfigMap[:libdir], 'ruby', 'gems', ConfigMap[:ruby_version] File.join ConfigMap[:libdir], 'ruby', 'gems', ConfigMap[:ruby_version]
end end
@ -29,7 +32,11 @@ module Gem
# The default directory for binaries # The default directory for binaries
def self.default_bindir 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 end
# The default system-wide source info cache directory. # The default system-wide source info cache directory.

View file

@ -22,8 +22,7 @@ class Gem::DependencyInstaller
} }
## ##
# Creates a new installer instance that will install +gem_name+ using # Creates a new installer instance.
# version requirement +version+ and +options+.
# #
# Options are: # Options are:
# :env_shebang:: See Gem::Installer::new. # :env_shebang:: See Gem::Installer::new.
@ -36,7 +35,7 @@ class Gem::DependencyInstaller
# :install_dir: See Gem::Installer#install. # :install_dir: See Gem::Installer#install.
# :security_policy: See Gem::Installer::new and Gem::Security. # :security_policy: See Gem::Installer::new and Gem::Security.
# :wrappers: See Gem::Installer::new # :wrappers: See Gem::Installer::new
def initialize(gem_name, version = nil, options = {}) def initialize(options = {})
options = DEFAULT_OPTIONS.merge options options = DEFAULT_OPTIONS.merge options
@env_shebang = options[:env_shebang] @env_shebang = options[:env_shebang]
@domain = options[:domain] @domain = options[:domain]
@ -46,49 +45,9 @@ class Gem::DependencyInstaller
@install_dir = options[:install_dir] || Gem.dir @install_dir = options[:install_dir] || Gem.dir
@security_policy = options[:security_policy] @security_policy = options[:security_policy]
@wrappers = options[:wrappers] @wrappers = options[:wrappers]
@bin_dir = options[:bin_dir]
@installed_gems = [] @installed_gems = []
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 end
## ##
@ -107,73 +66,32 @@ class Gem::DependencyInstaller
end end
if @domain == :both or @domain == :remote then if @domain == :both or @domain == :remote then
gems_and_sources.push(*Gem::SourceInfoCache.search_with_source(dep, true)) begin
requirements = dep.version_requirements.requirements.map do |req, ver|
req
end
all = requirements.length > 1 ||
requirements.first != ">=" || requirements.first != ">"
found = Gem::SourceInfoCache.search_with_source dep, true, all
gems_and_sources.push(*found)
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
end end
gems_and_sources.sort_by do |gem, source| gems_and_sources.sort_by do |gem, source|
[gem, source !~ /^http:\/\// ? 1 : 0] # local gems win [gem, source =~ /^http:\/\// ? 0 : 1] # local gems win
end end
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"
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
## ##
# Gathers all dependencies necessary for the installation from local and # Gathers all dependencies necessary for the installation from local and
# remote sources unless the ignore_dependencies was given. # remote sources unless the ignore_dependencies was given.
@ -208,9 +126,57 @@ class Gem::DependencyInstaller
@gems_to_install = dependency_list.dependency_order.reverse @gems_to_install = dependency_list.dependency_order.reverse
end 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. # 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' spec_dir = File.join @install_dir, 'specifications'
source_index = Gem::SourceIndex.from_gems_in spec_dir source_index = Gem::SourceIndex.from_gems_in spec_dir
@ -219,10 +185,11 @@ class Gem::DependencyInstaller
# HACK is this test for full_name acceptable? # HACK is this test for full_name acceptable?
next if source_index.any? { |n,_| n == spec.full_name } and not last next if source_index.any? { |n,_| n == spec.full_name } and not last
# TODO: make this sorta_verbose so other users can benefit from it
say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose
_, source_uri = @specs_and_sources.assoc spec _, 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, inst = Gem::Installer.new local_gem_path,
:env_shebang => @env_shebang, :env_shebang => @env_shebang,
@ -231,7 +198,8 @@ class Gem::DependencyInstaller
:ignore_dependencies => @ignore_dependencies, :ignore_dependencies => @ignore_dependencies,
:install_dir => @install_dir, :install_dir => @install_dir,
:security_policy => @security_policy, :security_policy => @security_policy,
:wrappers => @wrappers :wrappers => @wrappers,
:bin_dir => @bin_dir
spec = inst.install spec = inst.install

View file

@ -13,7 +13,10 @@ class Gem::DependencyRemovalException < Gem::Exception; end
## ##
# Raised when attempting to uninstall a gem that isn't in GEM_HOME. # 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 class Gem::DocumentError < Gem::Exception; end
@ -65,3 +68,17 @@ class Gem::RemoteSourceException < Gem::Exception; end
class Gem::VerificationError < 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

View file

@ -43,15 +43,12 @@ module Gem
# check for old version gem # check for old version gem
if File.read(file_path, 20).include?("MD5SUM =") if File.read(file_path, 20).include?("MD5SUM =")
#alert_warning "Gem #{file_path} is in old format."
require 'rubygems/old_format' require 'rubygems/old_format'
format = OldFormat.from_file_by_path(file_path) format = OldFormat.from_file_by_path(file_path)
else else
begin open file_path, Gem.binary_mode do |io|
f = File.open(file_path, 'rb') format = from_io io, file_path, security_policy
format = from_io(f, file_path, security_policy)
ensure
f.close unless f.closed?
end end
end end
@ -65,15 +62,24 @@ module Gem
# io:: [IO] Stream from which to read the gem # io:: [IO] Stream from which to read the gem
# #
def self.from_io(io, gem_path="(io)", security_policy = nil) def self.from_io(io, gem_path="(io)", security_policy = nil)
format = self.new(gem_path) format = new gem_path
Package.open_from_io(io, 'r', security_policy) do |pkg|
Package.open io, 'r', security_policy do |pkg|
format.spec = pkg.metadata format.spec = pkg.metadata
format.file_entries = [] format.file_entries = []
pkg.each do |entry| pkg.each do |entry|
format.file_entries << [{"size" => entry.size, "mode" => entry.mode, size = entry.header.size
"path" => entry.full_name}, entry.read] mode = entry.header.mode
format.file_entries << [{
"size" => size, "mode" => mode, "path" => entry.full_name,
},
entry.read
]
end end
end end
format format
end end

View file

@ -11,6 +11,7 @@ end
## ##
# Top level class for building the gem repository index. # Top level class for building the gem repository index.
class Gem::Indexer class Gem::Indexer
include Gem::UserInteraction include Gem::UserInteraction
@ -25,7 +26,9 @@ class Gem::Indexer
attr_reader :directory attr_reader :directory
##
# Create an indexer that will index the gems in +directory+. # Create an indexer that will index the gems in +directory+.
def initialize(directory) def initialize(directory)
unless ''.respond_to? :to_xs then unless ''.respond_to? :to_xs then
fail "Gem::Indexer requires that the XML Builder library be installed:" \ fail "Gem::Indexer requires that the XML Builder library be installed:" \
@ -39,14 +42,20 @@ class Gem::Indexer
@master_index = Gem::Indexer::MasterIndexBuilder.new "yaml", @directory @master_index = Gem::Indexer::MasterIndexBuilder.new "yaml", @directory
@marshal_index = Gem::Indexer::MarshalIndexBuilder.new marshal_name, @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 end
##
# Build the index. # Build the index.
def build_index def build_index
@master_index.build do @master_index.build do
@quick_index.build do @quick_index.build do
@marshal_index.build do @marshal_index.build do
@latest_index.build do
progress = ui.progress_reporter gem_file_list.size, progress = ui.progress_reporter gem_file_list.size,
"Generating index for #{gem_file_list.size} gems in #{@dest_directory}", "Generating index for #{gem_file_list.size} gems in #{@dest_directory}",
"Loaded all gems" "Loaded all gems"
@ -71,6 +80,7 @@ class Gem::Indexer
@master_index.add spec @master_index.add spec
@quick_index.add spec @quick_index.add spec
@marshal_index.add spec @marshal_index.add spec
@latest_index.add spec
progress.updated spec.original_name progress.updated spec.original_name
@ -89,20 +99,22 @@ class Gem::Indexer
end end
end end
end end
end
def install_index def install_index
verbose = Gem.configuration.really_verbose verbose = Gem.configuration.really_verbose
say "Moving index into production dir #{@dest_directory}" if verbose 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| files.each do |file|
relative_name = file[/\A#{Regexp.escape @directory}.(.*)/, 1] src_name = File.join @directory, file
dest_name = File.join @dest_directory, relative_name dst_name = File.join @dest_directory, file
FileUtils.rm_rf dest_name, :verbose => verbose FileUtils.rm_rf dst_name, :verbose => verbose
FileUtils.mv file, @dest_directory, :verbose => verbose FileUtils.mv src_name, @dest_directory, :verbose => verbose
end end
end end
@ -160,4 +172,5 @@ require 'rubygems/indexer/abstract_index_builder'
require 'rubygems/indexer/master_index_builder' require 'rubygems/indexer/master_index_builder'
require 'rubygems/indexer/quick_index_builder' require 'rubygems/indexer/quick_index_builder'
require 'rubygems/indexer/marshal_index_builder' require 'rubygems/indexer/marshal_index_builder'
require 'rubygems/indexer/latest_index_builder'

View file

@ -22,16 +22,18 @@ class Gem::Indexer::AbstractIndexBuilder
@files = [] @files = []
end end
##
# Build a Gem index. Yields to block to handle the details of the # Build a Gem index. Yields to block to handle the details of the
# actual building. Calls +begin_index+, +end_index+ and +cleanup+ at # actual building. Calls +begin_index+, +end_index+ and +cleanup+ at
# appropriate times to customize basic operations. # appropriate times to customize basic operations.
def build def build
FileUtils.mkdir_p @directory unless File.exist? @directory FileUtils.mkdir_p @directory unless File.exist? @directory
raise "not a directory: #{@directory}" unless File.directory? @directory raise "not a directory: #{@directory}" unless File.directory? @directory
file_path = File.join @directory, @filename file_path = File.join @directory, @filename
@files << file_path @files << @filename
File.open file_path, "wb" do |file| File.open file_path, "wb" do |file|
@file = file @file = file
@ -39,14 +41,20 @@ class Gem::Indexer::AbstractIndexBuilder
yield yield
end_index end_index
end end
cleanup cleanup
ensure ensure
@file = nil @file = nil
end end
##
# Compress the given file. # Compress the given file.
def compress(filename, ext="rz") 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.open "#{filename}.#{ext}", "wb" do |file|
file.write zipped file.write zipped
end end

View file

@ -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

View file

@ -1,6 +1,8 @@
require 'rubygems/indexer' require 'rubygems/indexer'
##
# Construct the master Gem index file. # Construct the master Gem index file.
class Gem::Indexer::MasterIndexBuilder < Gem::Indexer::AbstractIndexBuilder class Gem::Indexer::MasterIndexBuilder < Gem::Indexer::AbstractIndexBuilder
def start_index def start_index
@ -10,6 +12,7 @@ class Gem::Indexer::MasterIndexBuilder < Gem::Indexer::AbstractIndexBuilder
def end_index def end_index
super super
@file.puts "--- !ruby/object:#{@index.class}" @file.puts "--- !ruby/object:#{@index.class}"
@file.puts "gems:" @file.puts "gems:"
@ -28,11 +31,9 @@ class Gem::Indexer::MasterIndexBuilder < Gem::Indexer::AbstractIndexBuilder
index_file_name = File.join @directory, @filename index_file_name = File.join @directory, @filename
compress index_file_name, "Z" compress index_file_name, "Z"
compressed_file_name = "#{index_file_name}.Z" paranoid index_file_name, "#{index_file_name}.Z"
paranoid index_file_name, compressed_file_name @files << "#{@filename}.Z"
@files << compressed_file_name
end end
def add(spec) def add(spec)
@ -41,12 +42,12 @@ class Gem::Indexer::MasterIndexBuilder < Gem::Indexer::AbstractIndexBuilder
private private
def paranoid(fn, compressed_fn) def paranoid(path, compressed_path)
data = File.open(fn, 'rb') do |fp| fp.read end data = Gem.read_binary path
compressed_data = File.open(compressed_fn, 'rb') do |fp| fp.read end compressed_data = Gem.read_binary compressed_path
if data != unzip(compressed_data) then 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
end end

View file

@ -1,7 +1,9 @@
require 'rubygems/indexer' require 'rubygems/indexer'
##
# Construct a quick index file and all of the individual specs to support # Construct a quick index file and all of the individual specs to support
# incremental loading. # incremental loading.
class Gem::Indexer::QuickIndexBuilder < Gem::Indexer::AbstractIndexBuilder class Gem::Indexer::QuickIndexBuilder < Gem::Indexer::AbstractIndexBuilder
def initialize(filename, directory) def initialize(filename, directory)
@ -13,12 +15,12 @@ class Gem::Indexer::QuickIndexBuilder < Gem::Indexer::AbstractIndexBuilder
def cleanup def cleanup
super super
quick_index_file = File.join(@directory, @filename) quick_index_file = File.join @directory, @filename
compress quick_index_file compress quick_index_file
# the complete quick index is in a directory, so move it as a whole # the complete quick index is in a directory, so move it as a whole
@files.delete quick_index_file @files.delete 'index'
@files << @directory @files << 'quick'
end end
def add(spec) def add(spec)

View file

@ -25,6 +25,12 @@ module Gem::InstallUpdateOptions
options[:install_dir] = File.expand_path(value) options[:install_dir] = File.expand_path(value)
end 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', add_option(:"Install/Update", '-d', '--[no-]rdoc',
'Generate RDoc documentation for the gem on', 'Generate RDoc documentation for the gem on',
'install') do |value, options| 'install') do |value, options|

View file

@ -63,7 +63,8 @@ class Gem::Installer
:force => false, :force => false,
:install_dir => Gem.dir, :install_dir => Gem.dir,
:exec_format => false, :exec_format => false,
:env_shebang => false :env_shebang => false,
:bin_dir => nil
}.merge options }.merge options
@env_shebang = options[:env_shebang] @env_shebang = options[:env_shebang]
@ -74,6 +75,7 @@ class Gem::Installer
@format_executable = options[:format_executable] @format_executable = options[:format_executable]
@security_policy = options[:security_policy] @security_policy = options[:security_policy]
@wrappers = options[:wrappers] @wrappers = options[:wrappers]
@bin_dir = options[:bin_dir]
begin begin
@format = Gem::Format.from_file_by_path @gem, @security_policy @format = Gem::Format.from_file_by_path @gem, @security_policy
@ -104,7 +106,7 @@ class Gem::Installer
unless @force then unless @force then
if rrv = @spec.required_ruby_version 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}" raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}"
end end
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 # If the user has asked for the gem to be installed in a directory that is
# the system gem directory, then use the system bin directory, else create # the system gem directory, then use the system bin directory, else create
# (or use) a new bin dir under the gem_home. # (or use) a new bin dir under the gem_home.
bindir = Gem.bindir @gem_home bindir = @bin_dir ? @bin_dir : (Gem.bindir @gem_home)
Dir.mkdir bindir unless File.exist? bindir Dir.mkdir bindir unless File.exist? bindir
raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir
@ -303,7 +305,7 @@ class Gem::Installer
# necessary. # necessary.
def shebang(bin_file_name) def shebang(bin_file_name)
if @env_shebang then if @env_shebang then
"#!/usr/bin/env ruby" "#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name]
else else
path = File.join @gem_dir, @spec.bindir, bin_file_name path = File.join @gem_dir, @spec.bindir, bin_file_name
@ -352,10 +354,10 @@ TEXT
<<-TEXT <<-TEXT
@ECHO OFF @ECHO OFF
IF NOT "%~f0" == "~f0" GOTO :WinNT 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 GOTO :EOF
:WinNT :WinNT
"%~dp0ruby.exe" "%~dpn0" %* @"#{File.basename(Gem.ruby)}" "%~dpn0" %*
TEXT TEXT
end end

View file

@ -45,768 +45,15 @@ module Gem::Package
class TooLongFileName < Error; end class TooLongFileName < Error; end
class FormatError < Error; end class FormatError < Error; end
module FSyncDir def self.open(io, mode = "r", signer = nil, &block)
private tar_type = case mode
def fsync_dir(dirname) when 'r' then TarInput
# make sure this hits the disc when 'w' then TarOutput
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 else
raise "Unknown Package open mode" raise "Unknown Package open mode"
end end
end
def self.open_from_io(io, mode = "r", signer = nil, &block) tar_type.open(io, signer, &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
end end
def self.pack(src, destname, signer = nil) def self.pack(src, destname, signer = nil)
@ -836,19 +83,13 @@ module Gem::Package
end end
end end
class << self
def file_class
File
end end
def dir_class require 'rubygems/package/f_sync_dir'
Dir require 'rubygems/package/tar_header'
end require 'rubygems/package/tar_input'
require 'rubygems/package/tar_output'
def find_class # HACK kill me require 'rubygems/package/tar_reader'
Find require 'rubygems/package/tar_reader/entry'
end require 'rubygems/package/tar_writer'
end
end

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -2,7 +2,6 @@ require 'net/http'
require 'uri' require 'uri'
require 'rubygems' require 'rubygems'
require 'rubygems/gem_open_uri'
## ##
# RemoteFetcher handles the details of fetching gems and gem information from # RemoteFetcher handles the details of fetching gems and gem information from
@ -10,6 +9,8 @@ require 'rubygems/gem_open_uri'
class Gem::RemoteFetcher class Gem::RemoteFetcher
include Gem::UserInteraction
class FetchError < Gem::Exception; end class FetchError < Gem::Exception; end
@fetcher = nil @fetcher = nil
@ -29,6 +30,10 @@ class Gem::RemoteFetcher
# HTTP_PROXY_PASS) # HTTP_PROXY_PASS)
# * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy # * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy
def initialize(proxy) def initialize(proxy)
Socket.do_not_reverse_lookup = true
@connections = {}
@requests = Hash.new 0
@proxy_uri = @proxy_uri =
case proxy case proxy
when :no_proxy then nil when :no_proxy then nil
@ -38,6 +43,65 @@ class Gem::RemoteFetcher
end end
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+. # Downloads +uri+.
def fetch_path(uri) def fetch_path(uri)
open_uri_or_path(uri) do |input| open_uri_or_path(uri) do |input|
@ -47,9 +111,8 @@ class Gem::RemoteFetcher
raise FetchError, "timed out fetching #{uri}" raise FetchError, "timed out fetching #{uri}"
rescue IOError, SocketError, SystemCallError => e rescue IOError, SocketError, SystemCallError => e
raise FetchError, "#{e.class}: #{e} reading #{uri}" raise FetchError, "#{e.class}: #{e} reading #{uri}"
rescue OpenURI::HTTPError => e rescue => e
body = e.io.readlines.join "\n\t" message = "#{e.class}: #{e} reading #{uri}"
message = "#{e.class}: #{e} reading #{uri}\n\t#{body}"
raise FetchError, message raise FetchError, message
end end
@ -83,7 +146,8 @@ class Gem::RemoteFetcher
end end
rescue SocketError, SystemCallError, Timeout::Error => e 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 end
private private
@ -131,26 +195,77 @@ class Gem::RemoteFetcher
# Read the data from the (source based) URI, but if it is a file:// URI, # Read the data from the (source based) URI, but if it is a file:// URI,
# read from the filesystem instead. # read from the filesystem instead.
def open_uri_or_path(uri, &block) def open_uri_or_path(uri, depth = 0, &block)
if file_uri?(uri) if file_uri?(uri)
open(get_file_uri_path(uri), &block) open(get_file_uri_path(uri), &block)
else else
connection_options = {
"User-Agent" => "RubyGems/#{Gem::RubyGemsVersion} #{Gem::Platform.local}"
}
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)||'']
end
uri = URI.parse uri unless URI::Generic === uri uri = URI.parse uri unless URI::Generic === uri
unless uri.nil? || uri.user.nil? || uri.user.empty? then net_http_args = [uri.host, uri.port]
connection_options[:http_basic_authentication] =
[unescape(uri.user), unescape(uri.password)] if @proxy_uri then
net_http_args += [ @proxy_uri.host,
@proxy_uri.port,
@proxy_uri.user,
@proxy_uri.password
]
end end
open(uri, connection_options, &block) connection_id = net_http_args.join ':'
@connections[connection_id] ||= Net::HTTP.new(*net_http_args)
connection = @connections[connection_id]
if uri.scheme == 'https' && ! connection.started?
http_obj.use_ssl = true
http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
connection.start unless connection.started?
request = Net::HTTP::Get.new(uri.request_uri)
unless uri.nil? || uri.user.nil? || uri.user.empty? then
request.basic_auth(uri.user, uri.password)
end
ua = "RubyGems/#{Gem::RubyGemsVersion} #{Gem::Platform.local}"
ua << " Ruby/#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}"
ua << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
ua << ")"
request.add_field 'User-Agent', ua
request.add_field 'Connection', 'keep-alive'
request.add_field 'Keep-Alive', '30'
# HACK work around EOFError bug in Net::HTTP
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
end end

View file

@ -16,6 +16,8 @@ class Gem::Requirement
include Comparable include Comparable
attr_reader :requirements
OPS = { OPS = {
"=" => lambda { |v, r| v == r }, "=" => lambda { |v, r| v == r },
"!=" => lambda { |v, r| v != r }, "!=" => lambda { |v, r| v != r },

View file

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

View file

@ -4,6 +4,7 @@
# See LICENSE.txt for permissions. # See LICENSE.txt for permissions.
#++ #++
require 'rubygems'
require 'rubygems/gem_openssl' require 'rubygems/gem_openssl'
# = Signed Gems README # = Signed Gems README

View file

@ -1,7 +1,7 @@
require 'webrick' require 'webrick'
require 'rdoc/template'
require 'yaml' require 'yaml'
require 'zlib' require 'zlib'
require 'erb'
require 'rubygems' require 'rubygems'
@ -27,7 +27,7 @@ class Gem::Server
include Gem::UserInteraction include Gem::UserInteraction
DOC_TEMPLATE = <<-WEBPAGE DOC_TEMPLATE = <<-'WEBPAGE'
<?xml version="1.0" encoding="iso-8859-1"?> <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html <!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
@ -49,75 +49,55 @@ class Gem::Server
<div id="contextContent"> <div id="contextContent">
<div id="description"> <div id="description">
<h1>Summary</h1> <h1>Summary</h1>
<p>There are %gem_count% gems installed:</p> <p>There are <%=values["gem_count"]%> gems installed:</p>
<p> <p>
START:specs <%= values["specs"].map { |v| "<a href=\"#{v["name"]}\">#{v["name"]}</a>" }.join ', ' %>.
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> <h1>Gems</h1>
<dl> <dl>
START:specs <% values["specs"].each do |spec| %>
<dt> <dt>
IF:first_name_entry <% if spec["first_name_entry"] then %>
<a name="%name%"></a> <a name="<%=spec["name"]%>"></a>
ENDIF:first_name_entry <% end %>
<b>%name% %version%</b>
IF:rdoc_installed <b><%=spec["name"]%> <%=spec["version"]%></b>
<a href="%doc_path%">[rdoc]</a>
ENDIF:rdoc_installed <% if spec["rdoc_installed"] then %>
IFNOT:rdoc_installed <a href="<%=spec["doc_path"]%>">[rdoc]</a>
<% else %>
<span title="rdoc not installed">[rdoc]</span> <span title="rdoc not installed">[rdoc]</span>
ENDIF:rdoc_installed <% end %>
IF:homepage
<a href="%homepage%" title="%homepage%">[www]</a> <% if spec["homepage"] then %>
ENDIF:homepage <a href="<%=spec["homepage"]%>" title="<%=spec["homepage"]%>">[www]</a>
IFNOT:homepage <% else %>
<span title="no homepage available">[www]</span> <span title="no homepage available">[www]</span>
ENDIF:homepage <% end %>
IF:has_deps
<% if spec["has_deps"] then %>
- depends on - depends on
START:dependencies <%= spec["dependencies"].map { |v| "<a href=\"#{v["name"]}\">#{v["name"]}</a>" }.join ', ' %>.
IFNOT:is_last <% end %>
<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> </dt>
<dd> <dd>
%summary% <%=spec["summary"]%>
IF:executables <% if spec["executables"] then %>
<br/> <br/>
IF:only_one_executable <% if spec["only_one_executable"] then %>
Executable is Executable is
ENDIF:only_one_executable <% else %>
IFNOT:only_one_executable
Executables are Executables are
ENDIF:only_one_executable <%end%>
START:executables <%= spec["executables"].map { |v| "<span class=\"context-item-name\">#{v["executable"]}</span>"}.join ', ' %>.
IFNOT:is_last
<span class="context-item-name">%executable%</span>, <%end%>
ENDIF:is_last
IF:is_last
<span class="context-item-name">%executable%</span>.
ENDIF:is_last
END:executables
ENDIF:executables
<br/> <br/>
<br/> <br/>
</dd> </dd>
END:specs <% end %>
</dl> </dl>
</div> </div>
@ -496,11 +476,12 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
end end
# create page from template # create page from template
template = TemplatePage.new(DOC_TEMPLATE) template = ERB.new(DOC_TEMPLATE)
res['content-type'] = 'text/html' res['content-type'] = 'text/html'
template.write_html_on res.body, values = { "gem_count" => specs.size.to_s, "specs" => specs,
"gem_count" => specs.size.to_s, "specs" => specs, "total_file_count" => total_file_count.to_s }
"total_file_count" => total_file_count.to_s result = template.result binding
res.body = result
end end
paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" } paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" }

View file

@ -8,8 +8,7 @@ require 'rubygems'
require 'rubygems/user_interaction' require 'rubygems/user_interaction'
require 'rubygems/specification' require 'rubygems/specification'
module Gem ##
# The SourceIndex object indexes all the gems available from a # The SourceIndex object indexes all the gems available from a
# particular source (e.g. a list of gem directories, or a remote # particular source (e.g. a list of gem directories, or a remote
# source). A SourceIndex maps a gem full name to a gem # source). A SourceIndex maps a gem full name to a gem
@ -19,17 +18,17 @@ module Gem
# confusing when cached source fetchers where introduced. The # confusing when cached source fetchers where introduced. The
# constant Gem::Cache is an alias for this class to allow old # constant Gem::Cache is an alias for this class to allow old
# YAMLized source index objects to load properly. # YAMLized source index objects to load properly.
#
class SourceIndex class Gem::SourceIndex
include Enumerable include Enumerable
include Gem::UserInteraction include Gem::UserInteraction
# Class Methods. -------------------------------------------------
class << self class << self
include Gem::UserInteraction include Gem::UserInteraction
##
# Factory method to construct a source index instance for a given # Factory method to construct a source index instance for a given
# path. # path.
# #
@ -41,7 +40,7 @@ module Gem
# #
# return:: # return::
# SourceIndex instance # SourceIndex instance
#
def from_installed_gems(*deprecated) def from_installed_gems(*deprecated)
if deprecated.empty? if deprecated.empty?
from_gems_in(*installed_spec_directories) from_gems_in(*installed_spec_directories)
@ -50,36 +49,29 @@ module Gem
end end
end end
##
# Return a list of directories in the current gem path that # Return a list of directories in the current gem path that
# contain specifications. # contain specifications.
# #
# return:: # return::
# List of directory paths (all ending in "../specifications"). # List of directory paths (all ending in "../specifications").
#
def installed_spec_directories def installed_spec_directories
Gem.path.collect { |dir| File.join(dir, "specifications") } Gem.path.collect { |dir| File.join(dir, "specifications") }
end end
# Factory method to construct a source index instance for a ##
# given path. # Creates a new SourceIndex from the ruby format gem specifications in
# # +spec_dirs+.
# 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) def from_gems_in(*spec_dirs)
self.new.load_gems_in(*spec_dirs) self.new.load_gems_in(*spec_dirs)
end end
# Load a specification from a file (eval'd Ruby code) ##
# # Loads a ruby-format specification from +file_name+ and returns the
# file_name:: [String] The .gemspec file # loaded spec.
# return:: Specification instance or nil if an error occurs
#
def load_specification(file_name) def load_specification(file_name)
begin begin
spec_code = File.read(file_name).untaint spec_code = File.read(file_name).untaint
@ -101,46 +93,61 @@ module Gem
end end
# Instance Methods ----------------------------------------------- ##
# Constructs a source index instance from the provided # Constructs a source index instance from the provided
# specifications # specifications
# #
# specifications:: # specifications::
# [Hash] hash of [Gem name, Gem::Specification] pairs # [Hash] hash of [Gem name, Gem::Specification] pairs
#
def initialize(specifications={}) def initialize(specifications={})
@gems = specifications @gems = specifications
end end
# Reconstruct the source index from the list of source ##
# directories. # Reconstruct the source index from the specifications in +spec_dirs+.
def load_gems_in(*spec_dirs) def load_gems_in(*spec_dirs)
@gems.clear @gems.clear
specs = Dir.glob File.join("{#{spec_dirs.join(',')}}", "*.gemspec")
specs.each do |file_name| spec_dirs.reverse_each do |spec_dir|
gemspec = self.class.load_specification(file_name.untaint) spec_files = Dir.glob File.join(spec_dir, '*.gemspec')
add_spec(gemspec) if gemspec
spec_files.each do |spec_file|
gemspec = self.class.load_specification spec_file.untaint
add_spec gemspec if gemspec
end end
end
self self
end end
##
# Returns a Hash of name => Specification of the latest versions of each # Returns a Hash of name => Specification of the latest versions of each
# gem in this index. # gem in this index.
def latest_specs
result, latest = Hash.new { |h,k| h[k] = [] }, {}
self.each do |_, spec| # SourceIndex is not a hash, so we're stuck with each def latest_specs
result = Hash.new { |h,k| h[k] = [] }
latest = {}
sort.each do |_, spec|
name = spec.name name = spec.name
curr_ver = spec.version curr_ver = spec.version
prev_ver = latest[name] prev_ver = latest.key?(name) ? latest[name].version : nil
next unless prev_ver.nil? or curr_ver >= prev_ver next unless prev_ver.nil? or curr_ver >= prev_ver or
latest[name].platform != Gem::Platform::RUBY
if prev_ver.nil? or curr_ver > prev_ver then if prev_ver.nil? or
(curr_ver > prev_ver and spec.platform == Gem::Platform::RUBY) then
result[name].clear result[name].clear
latest[name] = curr_ver latest[name] = spec
end
if spec.platform != Gem::Platform::RUBY then
result[name].delete_if do |result_spec|
result_spec.platform == spec.platform
end
end end
result[name] << spec result[name] << spec
@ -149,35 +156,56 @@ module Gem
result.values.flatten result.values.flatten
end end
##
# Add a gem specification to the source index. # Add a gem specification to the source index.
def add_spec(gem_spec) def add_spec(gem_spec)
@gems[gem_spec.full_name] = gem_spec @gems[gem_spec.full_name] = gem_spec
end end
##
# Add gem specifications to the source index.
def add_specs(*gem_specs)
gem_specs.each do |spec|
add_spec spec
end
end
##
# Remove a gem specification named +full_name+. # Remove a gem specification named +full_name+.
def remove_spec(full_name) def remove_spec(full_name)
@gems.delete(full_name) @gems.delete(full_name)
end end
##
# Iterate over the specifications in the source index. # Iterate over the specifications in the source index.
def each(&block) # :yields: gem.full_name, gem def each(&block) # :yields: gem.full_name, gem
@gems.each(&block) @gems.each(&block)
end end
##
# The gem specification given a full gem spec name. # The gem specification given a full gem spec name.
def specification(full_name) def specification(full_name)
@gems[full_name] @gems[full_name]
end end
# The signature for the source index. Changes in the signature ##
# indicate a change in the index. # The signature for the source index. Changes in the signature indicate a
# change in the index.
def index_signature def index_signature
require 'rubygems/digest/sha2' require 'rubygems/digest/sha2'
Gem::SHA256.new.hexdigest(@gems.keys.sort.join(',')).to_s Gem::SHA256.new.hexdigest(@gems.keys.sort.join(',')).to_s
end end
##
# The signature for the given gem specification. # The signature for the given gem specification.
def gem_signature(gem_full_name) def gem_signature(gem_full_name)
require 'rubygems/digest/sha2' require 'rubygems/digest/sha2'
@ -189,11 +217,14 @@ module Gem
end end
alias length size alias length size
##
# Find a gem by an exact match on the short name. # Find a gem by an exact match on the short name.
def find_name(gem_name, version_requirement = Gem::Requirement.default) def find_name(gem_name, version_requirement = Gem::Requirement.default)
search(/^#{gem_name}$/, version_requirement) search(/^#{gem_name}$/, version_requirement)
end end
##
# Search for a gem by Gem::Dependency +gem_pattern+. If +only_platform+ # Search for a gem by Gem::Dependency +gem_pattern+. If +only_platform+
# is true, only gems matching Gem::Platform.local will be returned. An # is true, only gems matching Gem::Platform.local will be returned. An
# Array of matching Gem::Specification objects is returned. # Array of matching Gem::Specification objects is returned.
@ -201,6 +232,7 @@ module Gem
# For backwards compatibility, a String or Regexp pattern may be passed as # For backwards compatibility, a String or Regexp pattern may be passed as
# +gem_pattern+, and a Gem::Requirement for +platform_only+. This # +gem_pattern+, and a Gem::Requirement for +platform_only+. This
# behavior is deprecated and will be removed. # behavior is deprecated and will be removed.
def search(gem_pattern, platform_only = false) def search(gem_pattern, platform_only = false)
version_requirement = nil version_requirement = nil
only_platform = false only_platform = false
@ -239,16 +271,18 @@ module Gem
specs.sort_by { |s| s.sort_obj } specs.sort_by { |s| s.sort_obj }
end end
##
# Refresh the source index from the local file system. # Refresh the source index from the local file system.
# #
# return:: Returns a pointer to itself. # return:: Returns a pointer to itself.
#
def refresh! def refresh!
load_gems_in(self.class.installed_spec_directories) load_gems_in(self.class.installed_spec_directories)
end end
##
# Returns an Array of Gem::Specifications that are not up to date. # Returns an Array of Gem::Specifications that are not up to date.
#
def outdated def outdated
dep = Gem::Dependency.new '', Gem::Requirement.default dep = Gem::Dependency.new '', Gem::Requirement.default
@ -261,23 +295,31 @@ module Gem
remote = remotes.select { |spec| spec.name == name }. remote = remotes.select { |spec| spec.name == name }.
sort_by { |spec| spec.version.to_ints }. sort_by { |spec| spec.version.to_ints }.
last last
outdateds << name if remote and local.version < remote.version outdateds << name if remote and local.version < remote.version
end end
outdateds outdateds
end end
def update(source_uri) ##
# Updates this SourceIndex from +source_uri+. If +all+ is false, only the
# latest gems are fetched.
def update(source_uri, all)
source_uri = URI.parse source_uri unless URI::Generic === source_uri
source_uri.path += '/' unless source_uri.path =~ /\/$/
use_incremental = false use_incremental = false
begin begin
gem_names = fetch_quick_index source_uri gem_names = fetch_quick_index source_uri, all
remove_extra gem_names remove_extra gem_names
missing_gems = find_missing gem_names missing_gems = find_missing gem_names
return false if missing_gems.size.zero? return false if missing_gems.size.zero?
say "missing #{missing_gems.size} gems" if say "Missing metadata for #{missing_gems.size} gems" if
missing_gems.size > 0 and Gem.configuration.really_verbose missing_gems.size > 0 and Gem.configuration.really_verbose
use_incremental = missing_gems.size <= Gem.configuration.bulk_threshold use_incremental = missing_gems.size <= Gem.configuration.bulk_threshold
@ -329,9 +371,11 @@ module Gem
indexes.each do |name| indexes.each do |name|
spec_data = nil spec_data = nil
index = source_uri + name
begin begin
spec_data = fetcher.fetch_path("#{source_uri}/#{name}") spec_data = fetcher.fetch_path index
spec_data = unzip(spec_data) if name =~ /\.Z$/ spec_data = unzip(spec_data) if name =~ /\.Z$/
if name =~ /Marshal/ then if name =~ /Marshal/ then
return Marshal.load(spec_data) return Marshal.load(spec_data)
else else
@ -341,9 +385,11 @@ module Gem
if Gem.configuration.really_verbose then if Gem.configuration.really_verbose then
alert_error "Unable to fetch #{name}: #{e.message}" alert_error "Unable to fetch #{name}: #{e.message}"
end end
@fetch_error = e @fetch_error = e
end end
end end
nil nil
end end
@ -359,16 +405,30 @@ module Gem
index index
end end
##
# Get the quick index needed for incremental updates. # Get the quick index needed for incremental updates.
def fetch_quick_index(source_uri)
zipped_index = fetcher.fetch_path source_uri + '/quick/index.rz' def fetch_quick_index(source_uri, all)
index = all ? 'index' : 'latest_index'
zipped_index = fetcher.fetch_path source_uri + "quick/#{index}.rz"
unzip(zipped_index).split("\n") unzip(zipped_index).split("\n")
rescue ::Exception => ex 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, raise Gem::OperationNotSupportedError,
"No quick index found: " + ex.message "No quick index found: #{e.message}"
end
end end
##
# Make a list of full names for all the missing gemspecs. # Make a list of full names for all the missing gemspecs.
def find_missing(spec_names) def find_missing(spec_names)
spec_names.find_all { |full_name| spec_names.find_all { |full_name|
specification(full_name).nil? specification(full_name).nil?
@ -382,28 +442,34 @@ module Gem
end end
end end
##
# Unzip the given string. # Unzip the given string.
def unzip(string) def unzip(string)
require 'zlib' require 'zlib'
Zlib::Inflate.inflate(string) Zlib::Inflate.inflate(string)
end end
##
# Tries to fetch Marshal representation first, then YAML # Tries to fetch Marshal representation first, then YAML
def fetch_single_spec(source_uri, spec_name) def fetch_single_spec(source_uri, spec_name)
@fetch_error = nil @fetch_error = nil
begin begin
marshal_uri = source_uri + "/quick/Marshal.#{Gem.marshal_version}/#{spec_name}.gemspec.rz" marshal_uri = source_uri + "quick/Marshal.#{Gem.marshal_version}/#{spec_name}.gemspec.rz"
zipped = fetcher.fetch_path marshal_uri zipped = fetcher.fetch_path marshal_uri
return Marshal.load(unzip(zipped)) return Marshal.load(unzip(zipped))
rescue => ex rescue => ex
@fetch_error = ex @fetch_error = ex
if Gem.configuration.really_verbose then if Gem.configuration.really_verbose then
say "unable to fetch marshal gemspec #{marshal_uri}: #{ex.class} - #{ex}" say "unable to fetch marshal gemspec #{marshal_uri}: #{ex.class} - #{ex}"
end end
end end
begin begin
yaml_uri = source_uri + "/quick/#{spec_name}.gemspec.rz" yaml_uri = source_uri + "quick/#{spec_name}.gemspec.rz"
zipped = fetcher.fetch_path yaml_uri zipped = fetcher.fetch_path yaml_uri
return YAML.load(unzip(zipped)) return YAML.load(unzip(zipped))
rescue => ex rescue => ex
@ -412,10 +478,13 @@ module Gem
say "unable to fetch YAML gemspec #{yaml_uri}: #{ex.class} - #{ex}" say "unable to fetch YAML gemspec #{yaml_uri}: #{ex.class} - #{ex}"
end end
end end
nil nil
end end
##
# Update the cached source index with the missing names. # Update the cached source index with the missing names.
def update_with_missing(source_uri, missing_names) def update_with_missing(source_uri, missing_names)
progress = ui.progress_reporter(missing_names.size, progress = ui.progress_reporter(missing_names.size,
"Updating metadata for #{missing_names.size} gems from #{source_uri}") "Updating metadata for #{missing_names.size} gems from #{source_uri}")
@ -436,9 +505,15 @@ module Gem
end end
# Cache is an alias for SourceIndex to allow older YAMLized source module Gem
# index objects to load properly.
# :stopdoc:
# Cache is an alias for SourceIndex to allow older YAMLized source index
# objects to load properly.
Cache = SourceIndex Cache = SourceIndex
# :starddoc:
end end

View file

@ -4,6 +4,7 @@ require 'rubygems'
require 'rubygems/source_info_cache_entry' require 'rubygems/source_info_cache_entry'
require 'rubygems/user_interaction' require 'rubygems/user_interaction'
##
# SourceInfoCache stores a copy of the gem index for each gem source. # 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: # 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 # @source_index => Gem::SourceIndex
# ... # ...
# } # }
#
class Gem::SourceInfoCache class Gem::SourceInfoCache
include Gem::UserInteraction include Gem::UserInteraction
@ -37,7 +38,7 @@ class Gem::SourceInfoCache
def self.cache def self.cache
return @cache if @cache return @cache if @cache
@cache = new @cache = new
@cache.refresh if Gem.configuration.update_sources @cache.refresh false if Gem.configuration.update_sources
@cache @cache
end end
@ -45,71 +46,76 @@ class Gem::SourceInfoCache
cache.cache_data cache.cache_data
end end
# Search all source indexes for +pattern+. ##
def self.search(pattern, platform_only = false) # The name of the system cache file.
cache.search pattern, platform_only
def self.latest_system_cache_file
File.join File.dirname(system_cache_file),
"latest_#{File.basename system_cache_file}"
end end
# Search all source indexes for +pattern+. Only returns gems matching ##
# Gem.platforms when +only_platform+ is true. See #search_with_source. # The name of the latest user cache file.
def self.search_with_source(pattern, only_platform = false)
cache.search_with_source(pattern, only_platform) 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 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 end
def initialize # :nodoc: def initialize # :nodoc:
@cache_data = nil @cache_data = nil
@cache_file = nil @cache_file = nil
@dirty = false @dirty = false
@only_latest = true
end end
##
# The most recent cache data. # The most recent cache data.
def cache_data def cache_data
return @cache_data if @cache_data return @cache_data if @cache_data
cache_file # HACK writable check cache_file # HACK writable check
begin @only_latest = true
# 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| @cache_data = read_cache_data latest_cache_file
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
sice = Gem::SourceInfoCacheEntry.new Gem::SourceIndex.new, 0
sice.refresh url # HACK may be unnecessary, see ::cache and #refresh
@cache_data[url] = sice
@cache_data @cache_data
end end
def reset_cache_data ##
@cache_data = {} # The name of the cache file.
end
# The name of the cache file to be read
def cache_file def cache_file
return @cache_file if @cache_file return @cache_file if @cache_file
@cache_file = (try_file(system_cache_file) or @cache_file = (try_file(system_cache_file) or
@ -117,14 +123,101 @@ class Gem::SourceInfoCache
raise "unable to locate a writable cache file") raise "unable to locate a writable cache file")
end 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). # Write the cache to a local file (if it is dirty).
def flush def flush
write_cache if @dirty write_cache if @dirty
@dirty = false @dirty = false
end end
# Refreshes each source in the cache from its repository. def latest_cache_data
def refresh 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| Gem.sources.each do |source_uri|
cache_entry = cache_data[source_uri] cache_entry = cache_data[source_uri]
if cache_entry.nil? then if cache_entry.nil? then
@ -132,14 +225,34 @@ class Gem::SourceInfoCache
cache_data[source_uri] = cache_entry cache_data[source_uri] = cache_entry
end end
update if cache_entry.refresh source_uri update if cache_entry.refresh source_uri, all
end end
flush flush
end end
# Searches all source indexes for +pattern+. def reset_cache_for(url, cache_data)
def search(pattern, platform_only = false) 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| cache_data.map do |source_uri, sic_entry|
next unless Gem.sources.include? source_uri next unless Gem.sources.include? source_uri
sic_entry.source_index.search pattern, platform_only 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 # only gems matching Gem.platforms will be selected. Returns an Array of
# pairs containing the Gem::Specification found and the source_uri it was # pairs containing the Gem::Specification found and the source_uri it was
# found at. # 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 = [] results = []
cache_data.map do |source_uri, sic_entry| cache_data.map do |source_uri, sic_entry|
@ -164,69 +279,76 @@ class Gem::SourceInfoCache
results results
end end
# Mark the cache as updated (i.e. dirty). ##
def update
@dirty = true
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
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)
end
end
# Set the source info cache data directly. This is mainly used for unit # 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 # 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 # index information. The +hash+ should map a source URL into a
# SourceInfoCacheEntry. # SourceInfoCacheEntry.
def set_cache_data(hash) def set_cache_data(hash)
@cache_data = hash @cache_data = hash
update update
end end
private ##
# The name of the system cache file.
def system_cache_file
self.class.system_cache_file
end
##
# 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
# 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 unless File.exist? dir then
begin begin
FileUtils.mkdir_p(dir) FileUtils.mkdir_p dir
rescue RuntimeError, SystemCallError rescue RuntimeError, SystemCallError
return nil return nil
end end
end end
if File.writable?(dir)
File.open(fn, "wb") { |f| f << Marshal.dump({}) } if File.writable? dir then
return fn open path, "wb" do |io| io.write Marshal.dump({}) end
return path
end end
nil nil
end 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
##
# Write data to the proper cache.
def write_cache
open cache_file, 'wb' do |io|
io.write Marshal.dump(cache_data)
end
open latest_cache_file, 'wb' do |io|
io.write Marshal.dump(latest_cache_data)
end
end
end end

View file

@ -3,24 +3,31 @@ require 'rubygems/source_index'
require 'rubygems/remote_fetcher' require 'rubygems/remote_fetcher'
## ##
# Entrys held by a SourceInfoCache. # Entries held by a SourceInfoCache.
class Gem::SourceInfoCacheEntry class Gem::SourceInfoCacheEntry
##
# The source index for this cache entry. # The source index for this cache entry.
attr_reader :source_index attr_reader :source_index
##
# The size of the of the source entry. Used to determine if the # The size of the of the source entry. Used to determine if the
# source index has changed. # source index has changed.
attr_reader :size attr_reader :size
##
# Create a cache entry. # Create a cache entry.
def initialize(si, size) def initialize(si, size)
@source_index = si || Gem::SourceIndex.new({}) @source_index = si || Gem::SourceIndex.new({})
@size = size @size = size
@all = false
end end
def refresh(source_uri) def refresh(source_uri, all)
begin begin
marshal_uri = URI.join source_uri.to_s, "Marshal.#{Gem.marshal_version}" marshal_uri = URI.join source_uri.to_s, "Marshal.#{Gem.marshal_version}"
remote_size = Gem::RemoteFetcher.fetcher.fetch_size marshal_uri 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 remote_size = Gem::RemoteFetcher.fetcher.fetch_size yaml_uri
end end
return false if @size == remote_size # TODO Use index_signature instead of size? # TODO Use index_signature instead of size?
updated = @source_index.update source_uri return false if @size == remote_size and @all
updated = @source_index.update source_uri, all
@size = remote_size @size = remote_size
@all = all
updated updated
end end

View file

@ -13,7 +13,7 @@ require 'rubygems/platform'
if RUBY_VERSION < '1.9' then if RUBY_VERSION < '1.9' then
def Time.today def Time.today
t = Time.now 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 unless defined? Time.today
end end
# :startdoc: # :startdoc:

View file

@ -12,7 +12,7 @@ require 'rubygems/user_interaction'
## ##
# An Uninstaller. # An Uninstaller.
#
class Gem::Uninstaller class Gem::Uninstaller
include Gem::UserInteraction include Gem::UserInteraction
@ -21,8 +21,8 @@ class Gem::Uninstaller
# Constructs an Uninstaller instance # Constructs an Uninstaller instance
# #
# gem:: [String] The Gem name to uninstall # gem:: [String] The Gem name to uninstall
#
def initialize(gem, options) def initialize(gem, options = {})
@gem = gem @gem = gem
@version = options[:version] || Gem::Requirement.default @version = options[:version] || Gem::Requirement.default
gem_home = options[:install_dir] || Gem.dir gem_home = options[:install_dir] || Gem.dir
@ -30,12 +30,13 @@ class Gem::Uninstaller
@force_executables = options[:executables] @force_executables = options[:executables]
@force_all = options[:all] @force_all = options[:all]
@force_ignore = options[:ignore] @force_ignore = options[:ignore]
@bin_dir = options[:bin_dir]
end end
## ##
# Performs the uninstall of the Gem. This removes the spec, the # Performs the uninstall of the Gem. This removes the spec, the
# Gem directory, and the cached .gem file, # Gem directory, and the cached .gem file,
#
def uninstall def uninstall
list = Gem.source_index.search(/^#{@gem}$/, @version) list = Gem.source_index.search(/^#{@gem}$/, @version)
@ -66,18 +67,14 @@ class Gem::Uninstaller
end end
## ##
# Remove executables and batch files (windows only) for the gem as # Removes installed executables and batch files (windows only) for
# it is being installed # +gemspec+.
#
# gemspec::[Specification] the gem whose executables need to be removed.
#
def remove_executables(gemspec) def remove_executables(gemspec)
return if gemspec.nil? return if gemspec.nil?
if gemspec.executables.size > 0 then if gemspec.executables.size > 0 then
bindir = Gem.bindir @gem_home bindir = @bin_dir ? @bin_dir : (Gem.bindir @gem_home)
raise Gem::FilePermissionError, bindir unless File.writable? bindir
list = Gem.source_index.search(gemspec.name).delete_if { |spec| list = Gem.source_index.search(gemspec.name).delete_if { |spec|
spec.version == gemspec.version spec.version == gemspec.version
@ -93,14 +90,19 @@ class Gem::Uninstaller
return if executables.size == 0 return if executables.size == 0
answer = @force_executables || ask_yes_no( answer = if @force_executables.nil? then
"Remove executables:\n" \ ask_yes_no("Remove executables:\n" \
"\t#{gemspec.executables.join(", ")}\n\nin addition to the gem?", "\t#{gemspec.executables.join(", ")}\n\nin addition to the gem?",
true) # " # appease ruby-mode - don't ask true) # " # appease ruby-mode - don't ask
else
@force_executables
end
unless answer then unless answer then
say "Executables and scripts will remain installed." say "Executables and scripts will remain installed."
else else
raise Gem::FilePermissionError, bindir unless File.writable? bindir
gemspec.executables.each do |exe_name| gemspec.executables.each do |exe_name|
say "Removing #{exe_name}" say "Removing #{exe_name}"
FileUtils.rm_f File.join(bindir, exe_name) FileUtils.rm_f File.join(bindir, exe_name)
@ -110,23 +112,22 @@ class Gem::Uninstaller
end end
end end
##
# Removes all gems in +list+.
# #
# list:: the list of all gems to remove # NOTE: removes uninstalled gems from +list+.
#
# Warning: this method modifies the +list+ parameter. Once it has
# uninstalled a gem, it is removed from that list.
#
def remove_all(list) def remove_all(list)
list.dup.each { |gem| remove(gem, list) } list.dup.each { |spec| remove spec, list }
end end
# ##
# spec:: the spec of the gem to be uninstalled # spec:: the spec of the gem to be uninstalled
# list:: the list of all such gems # list:: the list of all such gems
# #
# Warning: this method modifies the +list+ parameter. Once it has # Warning: this method modifies the +list+ parameter. Once it has
# uninstalled a gem, it is removed from that list. # uninstalled a gem, it is removed from that list.
#
def remove(spec, list) def remove(spec, list)
unless dependencies_ok? spec then unless dependencies_ok? spec then
raise Gem::DependencyRemovalException, raise Gem::DependencyRemovalException,
@ -134,10 +135,11 @@ class Gem::Uninstaller
end end
unless path_ok? spec then unless path_ok? spec then
alert("In order to remove #{spec.name}, please execute:\n" \ e = Gem::GemNotInHomeException.new \
"\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}")
raise Gem::GemNotInHomeException,
"Gem is not installed in directory #{@gem_home}" "Gem is not installed in directory #{@gem_home}"
e.spec = spec
raise e
end end
raise Gem::FilePermissionError, spec.installation_path unless raise Gem::FilePermissionError, spec.installation_path unless
@ -182,8 +184,8 @@ class Gem::Uninstaller
def dependencies_ok?(spec) def dependencies_ok?(spec)
return true if @force_ignore return true if @force_ignore
srcindex = Gem::SourceIndex.from_installed_gems source_index = Gem::SourceIndex.from_installed_gems
deplist = Gem::DependencyList.from_source_index srcindex deplist = Gem::DependencyList.from_source_index source_index
deplist.ok_to_remove?(spec.full_name) || ask_if_ok(spec) deplist.ok_to_remove?(spec.full_name) || ask_if_ok(spec)
end end

View file

@ -68,7 +68,7 @@ module Gem
include DefaultUserInteraction include DefaultUserInteraction
[ [
:choose_from_list, :ask, :ask_yes_no, :say, :alert, :alert_warning, :choose_from_list, :ask, :ask_yes_no, :say, :alert, :alert_warning,
:alert_error, :terminate_interaction!, :terminate_interaction :alert_error, :terminate_interaction
].each do |methname| ].each do |methname|
class_eval %{ class_eval %{
def #{methname}(*args) def #{methname}(*args)
@ -182,16 +182,10 @@ module Gem
ask(question) if question ask(question) if question
end 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 # Terminate the appliation normally, running any exit handlers
# that might have been defined. # that might have been defined.
def terminate_interaction(status = 0) def terminate_interaction(status = 0)
exit(status) raise Gem::SystemExitException, status
end end
# Return a progress reporter object # Return a progress reporter object

View file

@ -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

View file

@ -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

View file

@ -10,9 +10,10 @@ at_exit { $SAFE = 1 }
require 'fileutils' require 'fileutils'
require 'test/unit' require 'test/unit'
require 'tmpdir' require 'tmpdir'
require 'tempfile'
require 'uri' require 'uri'
require 'rubygems/gem_open_uri'
require 'rubygems/source_info_cache' require 'rubygems/source_info_cache'
require 'rubygems/package'
require File.join(File.expand_path(File.dirname(__FILE__)), 'mockgemui') require File.join(File.expand_path(File.dirname(__FILE__)), 'mockgemui')
@ -56,6 +57,20 @@ class FakeFetcher
data.respond_to?(:call) ? data.call : data.length data.respond_to?(:call) ? data.call : data.length
end 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 end
class RubyGemTestCase < Test::Unit::TestCase class RubyGemTestCase < Test::Unit::TestCase
@ -76,6 +91,7 @@ class RubyGemTestCase < Test::Unit::TestCase
@gemhome = File.join @tempdir, "gemhome" @gemhome = File.join @tempdir, "gemhome"
@gemcache = File.join(@gemhome, "source_cache") @gemcache = File.join(@gemhome, "source_cache")
@usrcache = File.join(@gemhome, ".gem", "user_cache") @usrcache = File.join(@gemhome, ".gem", "user_cache")
@latest_usrcache = File.join(@gemhome, ".gem", "latest_user_cache")
FileUtils.mkdir_p @gemhome FileUtils.mkdir_p @gemhome
@ -101,6 +117,11 @@ class RubyGemTestCase < Test::Unit::TestCase
end end
@marshal_version = "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}" @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 end
def teardown def teardown
@ -135,25 +156,52 @@ class RubyGemTestCase < Test::Unit::TestCase
end end
def prep_cache_files(lc) def prep_cache_files(lc)
[ [lc.system_cache_file, 'sys'], @usr_si ||= Gem::SourceIndex.new
[lc.user_cache_file, 'usr'], @usr_sice ||= Gem::SourceInfoCacheEntry.new @usr_si, 0
].each do |fn, data|
FileUtils.mkdir_p File.dirname(fn).untaint @sys_si ||= Gem::SourceIndex.new
open(fn.dup.untaint, "wb") { |f| f.write(Marshal.dump({'key' => data})) } @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
end end
def read_cache(fn) def read_cache(path)
open(fn.dup.untaint) { |f| Marshal.load f.read } open path.dup.untaint, 'rb' do |io|
Marshal.load io.read
end
end
def read_binary(path)
Gem.read_binary path
end end
def write_file(path) def write_file(path)
path = File.join(@gemhome, path) path = File.join(@gemhome, path)
dir = File.dirname path dir = File.dirname path
FileUtils.mkdir_p dir FileUtils.mkdir_p dir
File.open(path, "w") { |io|
yield(io) open path, 'wb' do |io|
} yield io
end
path path
end end
@ -204,6 +252,23 @@ class RubyGemTestCase < Test::Unit::TestCase
end end
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 def util_make_gems
init = proc do |s| init = proc do |s|
s.files = %w[lib/code.rb] s.files = %w[lib/code.rb]
@ -212,6 +277,7 @@ class RubyGemTestCase < Test::Unit::TestCase
@a1 = quick_gem('a', '1', &init) @a1 = quick_gem('a', '1', &init)
@a2 = quick_gem('a', '2', &init) @a2 = quick_gem('a', '2', &init)
@a_evil9 = quick_gem('a_evil', '9', &init)
@b2 = quick_gem('b', '2', &init) @b2 = quick_gem('b', '2', &init)
@c1_2 = quick_gem('c', '1.2', &init) @c1_2 = quick_gem('c', '1.2', &init)
@pl1 = quick_gem 'pl', '1' do |s| # l for legacy @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 #{@c1_2.original_name} lib code.rb]) do end
write_file File.join(*%W[gems #{@pl1.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) FileUtils.rm_r File.join(@gemhome, 'gems', @pl1.original_name)
@ -256,32 +322,19 @@ class RubyGemTestCase < Test::Unit::TestCase
@fetcher = FakeFetcher.new @fetcher = FakeFetcher.new
@fetcher.uri = @uri @fetcher.uri = @uri
@gem1 = quick_gem 'gem_one' do |gem| util_make_gems
gem.files = %w[Rakefile lib/gem_one.rb]
end
@gem2 = quick_gem 'gem_two' do |gem| @all_gems = [@a1, @a2, @a_evil9, @b2, @c1_2].sort
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
@all_gems = [@gem1, @gem2, @gem3, @gem4].sort
@all_gem_names = @all_gems.map { |gem| gem.full_name } @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") @gem_names = gem_names.sort.join("\n")
@source_index = Gem::SourceIndex.new @gem1.full_name => @gem1, @source_index = Gem::SourceIndex.new
@gem2.full_name => @gem2, @source_index.add_spec @a1
@gem4.full_name => @gem4 @source_index.add_spec @a2
@source_index.add_spec @a_evil9
@source_index.add_spec @c1_2
Gem::RemoteFetcher.instance_variable_set :@fetcher, @fetcher Gem::RemoteFetcher.instance_variable_set :@fetcher, @fetcher
end end
@ -294,7 +347,12 @@ class RubyGemTestCase < Test::Unit::TestCase
sice = Gem::SourceInfoCacheEntry.new si, 0 sice = Gem::SourceInfoCacheEntry.new si, 0
sic = Gem::SourceInfoCache.new sic = Gem::SourceInfoCache.new
sic.set_cache_data( { @gem_repo => sice } ) sic.set_cache_data( { @gem_repo => sice } )
sic.update
sic.write_cache
sic.reset_cache_data
Gem::SourceInfoCache.instance_variable_set :@cache, sic Gem::SourceInfoCache.instance_variable_set :@cache, sic
si si
end end
@ -313,3 +371,30 @@ class RubyGemTestCase < Test::Unit::TestCase
end end
class TempIO
@@count = 0
def initialize(string = '')
@tempfile = Tempfile.new "TempIO-#{@@count ++ 1}"
@tempfile.binmode
@tempfile.write string
@tempfile.rewind
end
def method_missing(meth, *args, &block)
@tempfile.send(meth, *args, &block)
end
def respond_to?(meth)
@tempfile.respond_to? meth
end
def string
@tempfile.flush
Gem.read_binary @tempfile.path
end
end

View file

@ -15,7 +15,6 @@ class MockGemUi < Gem::StreamUI
def initialize(input="") def initialize(input="")
super(StringIO.new(input), StringIO.new, StringIO.new) super(StringIO.new(input), StringIO.new, StringIO.new)
@terminated = false @terminated = false
@banged = false
end end
def input def input
@ -30,22 +29,15 @@ class MockGemUi < Gem::StreamUI
@errs.string @errs.string
end end
def banged?
@banged
end
def terminated? def terminated?
@terminated @terminated
end end
def terminate_interaction!(status=1)
@terminated = true
@banged = true
fail TermError
end
def terminate_interaction(status=0) def terminate_interaction(status=0)
@terminated = true @terminated = true
fail TermError
raise TermError
end end
end end

View file

@ -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-----

View file

@ -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-----

View file

@ -19,6 +19,7 @@ class TestGem < RubyGemTestCase
expected = [ expected = [
File.join(@gemhome, *%W[gems #{@a1.full_name} lib]), File.join(@gemhome, *%W[gems #{@a1.full_name} lib]),
File.join(@gemhome, *%W[gems #{@a2.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 #{@b2.full_name} lib]),
File.join(@gemhome, *%W[gems #{@c1_2.full_name} lib]), File.join(@gemhome, *%W[gems #{@c1_2.full_name} lib]),
File.join(@gemhome, *%W[gems #{@pl1.full_name} lib]), File.join(@gemhome, *%W[gems #{@pl1.full_name} lib]),
@ -213,6 +214,7 @@ class TestGem < RubyGemTestCase
expected = [ expected = [
File.join(@gemhome, *%W[gems #{@a2.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 #{@b2.full_name} lib]),
File.join(@gemhome, *%W[gems #{@c1_2.full_name} lib]), File.join(@gemhome, *%W[gems #{@c1_2.full_name} lib]),
File.join(@gemhome, *%W[gems #{@pl1.full_name} lib]), File.join(@gemhome, *%W[gems #{@pl1.full_name} lib]),
@ -226,7 +228,7 @@ class TestGem < RubyGemTestCase
install_gem foo install_gem foo
Gem.source_index = nil Gem.source_index = nil
Gem.activate 'foo', false Gem.activate 'foo'
assert_equal true, Gem.loaded_specs.keys.include?('foo') assert_equal true, Gem.loaded_specs.keys.include?('foo')
end end
@ -235,9 +237,29 @@ class TestGem < RubyGemTestCase
assert_equal [Gem.dir], Gem.path assert_equal [Gem.dir], Gem.path
end 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 def test_self_path_ENV_PATH
Gem.clear_paths Gem.clear_paths
path_count = Gem.path.size path_count = Gem.path.size
path_count -= 1 if defined? APPLE_GEM_HOME
Gem.clear_paths Gem.clear_paths
util_ensure_gem_dirs util_ensure_gem_dirs
@ -257,8 +279,8 @@ class TestGem < RubyGemTestCase
ENV['GEM_PATH'] = dirs.join File::PATH_SEPARATOR ENV['GEM_PATH'] = dirs.join File::PATH_SEPARATOR
assert_equal @gemhome, Gem.dir assert_equal @gemhome, Gem.dir
paths = [Gem.dir] paths = [Gem.dir]
paths << APPLE_GEM_HOME if defined? APPLE_GEM_HOME
assert_equal @additional + paths, Gem.path assert_equal @additional + paths, Gem.path
end end
@ -270,8 +292,8 @@ class TestGem < RubyGemTestCase
ENV['GEM_PATH'] = @additional.join(File::PATH_SEPARATOR) ENV['GEM_PATH'] = @additional.join(File::PATH_SEPARATOR)
assert_equal @gemhome, Gem.dir assert_equal @gemhome, Gem.dir
paths = [Gem.dir] paths = [Gem.dir]
paths.insert(0, APPLE_GEM_HOME) if defined? APPLE_GEM_HOME
assert_equal @additional + paths, Gem.path assert_equal @additional + paths, Gem.path
end end
@ -284,6 +306,18 @@ class TestGem < RubyGemTestCase
assert_equal File.dirname(File.dirname(file_name)), Gem.prefix assert_equal File.dirname(File.dirname(file_name)), Gem.prefix
end 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 def test_self_required_location
util_make_gems util_make_gems
@ -295,6 +329,13 @@ class TestGem < RubyGemTestCase
Gem.required_location("a", "code.rb", "= 2") Gem.required_location("a", "code.rb", "= 2")
end 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 def test_self_searcher
assert_kind_of Gem::GemPathSearcher, Gem.searcher assert_kind_of Gem::GemPathSearcher, Gem.searcher
end end

View file

@ -67,11 +67,12 @@ class TestGemCommandManager < RubyGemTestCase
assert_equal true, check_options[:wrappers] assert_equal true, check_options[:wrappers]
assert_equal Gem::Requirement.default, check_options[:version] assert_equal Gem::Requirement.default, check_options[:version]
assert_equal Gem.dir, check_options[:install_dir] assert_equal Gem.dir, check_options[:install_dir]
assert_equal nil, check_options[:bin_dir]
#check settings #check settings
check_options = nil check_options = nil
@command_manager.process_args( @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[:test]
assert_equal true, check_options[:generate_rdoc] assert_equal true, check_options[:generate_rdoc]
assert_equal true, check_options[:force] assert_equal true, check_options[:force]
@ -79,6 +80,7 @@ class TestGemCommandManager < RubyGemTestCase
assert_equal false, check_options[:wrappers] assert_equal false, check_options[:wrappers]
assert_equal Gem::Requirement.new('3.0'), check_options[:version] assert_equal Gem::Requirement.new('3.0'), check_options[:version]
assert_equal Dir.pwd, check_options[:install_dir] assert_equal Dir.pwd, check_options[:install_dir]
assert_equal Dir.pwd, check_options[:bin_dir]
#check remote domain #check remote domain
check_options = nil check_options = nil
@ -164,7 +166,7 @@ class TestGemCommandManager < RubyGemTestCase
#check defaults #check defaults
@command_manager.process_args("query") @command_manager.process_args("query")
assert_equal(/.*/, check_options[:name]) assert_equal(//, check_options[:name])
assert_equal :local, check_options[:domain] assert_equal :local, check_options[:domain]
assert_equal false, check_options[:details] assert_equal false, check_options[:details]

View file

@ -62,6 +62,21 @@ class TestGemCommandsEnvironmentCommand < RubyGemTestCase
assert_equal '', @ui.error assert_equal '', @ui.error
end 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 def test_execute_packageversion
@cmd.send :handle_options, %w[packageversion] @cmd.send :handle_options, %w[packageversion]

View file

@ -15,13 +15,12 @@ class TestGemCommandsFetchCommand < RubyGemTestCase
def test_execute def test_execute
util_setup_fake_fetcher util_setup_fake_fetcher
util_build_gem @gem1
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] =
@source_index.dump @source_index.dump
@fetcher.data["#{@gem_repo}/gems/#{@gem1.full_name}.gem"] = @fetcher.data["#{@gem_repo}/gems/#{@a2.full_name}.gem"] =
File.read(File.join(@gemhome, 'cache', "#{@gem1.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 use_ui @ui do
Dir.chdir @tempdir do Dir.chdir @tempdir do
@ -29,7 +28,7 @@ class TestGemCommandsFetchCommand < RubyGemTestCase
end end
end end
assert File.exist?(File.join(@tempdir, "#{@gem1.full_name}.gem")) assert File.exist?(File.join(@tempdir, "#{@a2.full_name}.gem"))
end end
end end

View file

@ -34,25 +34,26 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
util_setup_fake_fetcher util_setup_fake_fetcher
@cmd.options[:domain] = :local @cmd.options[:domain] = :local
gem1 = quick_gem 'gem_one' FileUtils.mv File.join(@gemhome, 'cache', "#{@a2.full_name}.gem"),
util_build_gem gem1
FileUtils.mv File.join(@gemhome, 'cache', "#{@gem1.full_name}.gem"),
File.join(@tempdir) File.join(@tempdir)
@cmd.options[:args] = [gem1.name] @cmd.options[:args] = [@a2.name]
use_ui @ui do use_ui @ui do
orig_dir = Dir.pwd orig_dir = Dir.pwd
begin begin
Dir.chdir @tempdir Dir.chdir @tempdir
e = assert_raises Gem::SystemExitException do
@cmd.execute @cmd.execute
end
assert_equal 0, e.exit_code
ensure ensure
Dir.chdir orig_dir Dir.chdir orig_dir
end end
end end
out = @ui.output.split "\n" 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_equal "1 gem installed", out.shift
assert out.empty?, out.inspect assert out.empty?, out.inspect
end end
@ -61,14 +62,17 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
util_setup_fake_fetcher util_setup_fake_fetcher
@cmd.options[:domain] = :local @cmd.options[:domain] = :local
@cmd.options[:args] = %w[gem_one] @cmd.options[:args] = %w[no_such_gem]
use_ui @ui do use_ui @ui do
e = assert_raises Gem::SystemExitException do
@cmd.execute @cmd.execute
end end
assert_equal 2, e.exit_code
end
# HACK no repository was checked # 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 @ui.error
end end
@ -88,8 +92,11 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
@cmd.options[:args] = %w[nonexistent] @cmd.options[:args] = %w[nonexistent]
use_ui @ui do use_ui @ui do
e = assert_raises Gem::SystemExitException do
@cmd.execute @cmd.execute
end end
assert_equal 2, e.exit_code
end
assert_equal "ERROR: could not find nonexistent locally or in a repository\n", assert_equal "ERROR: could not find nonexistent locally or in a repository\n",
@ui.error @ui.error
@ -100,25 +107,27 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
@cmd.options[:generate_ri] = true @cmd.options[:generate_ri] = true
util_setup_fake_fetcher util_setup_fake_fetcher
util_build_gem @gem1
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] =
@source_index.dump @source_index.dump
@fetcher.data["#{@gem_repo}/gems/gem_one-0.0.2.gem"] = @fetcher.data["#{@gem_repo}/gems/#{@a2.full_name}.gem"] =
File.read(File.join(@gemhome, 'cache', "#{@gem1.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 use_ui @ui do
e = assert_raises Gem::SystemExitException do
@cmd.execute @cmd.execute
end end
assert_equal 0, e.exit_code
end
out = @ui.output.split "\n" out = @ui.output.split "\n"
assert_match %r|Bulk updating|, out.shift 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 "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 out.shift
assert_equal "Installing RDoc documentation for #{@gem1.full_name}...", assert_equal "Installing RDoc documentation for #{@a2.full_name}...",
out.shift out.shift
assert out.empty?, out.inspect assert out.empty?, out.inspect
end end
@ -127,31 +136,30 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
util_setup_fake_fetcher util_setup_fake_fetcher
@cmd.options[:domain] = :local @cmd.options[:domain] = :local
gem1 = quick_gem 'gem_one' FileUtils.mv File.join(@gemhome, 'cache', "#{@a2.full_name}.gem"),
util_build_gem gem1
FileUtils.mv File.join(@gemhome, 'cache', "#{@gem1.full_name}.gem"),
File.join(@tempdir) File.join(@tempdir)
gem2 = quick_gem 'gem_two' FileUtils.mv File.join(@gemhome, 'cache', "#{@b2.full_name}.gem"),
util_build_gem gem2
FileUtils.mv File.join(@gemhome, 'cache', "#{@gem2.full_name}.gem"),
File.join(@tempdir) File.join(@tempdir)
@cmd.options[:args] = [gem1.name, gem2.name] @cmd.options[:args] = [@a2.name, @b2.name]
use_ui @ui do use_ui @ui do
orig_dir = Dir.pwd orig_dir = Dir.pwd
begin begin
Dir.chdir @tempdir Dir.chdir @tempdir
e = assert_raises Gem::SystemExitException do
@cmd.execute @cmd.execute
end
assert_equal 0, e.exit_code
ensure ensure
Dir.chdir orig_dir Dir.chdir orig_dir
end end
end end
out = @ui.output.split "\n" 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 "Successfully installed #{@gem2.full_name}", out.shift assert_equal "Successfully installed #{@b2.full_name}", out.shift
assert_equal "2 gems installed", out.shift assert_equal "2 gems installed", out.shift
assert out.empty?, out.inspect assert out.empty?, out.inspect
end end

View file

@ -7,20 +7,31 @@ class TestGemCommandsQueryCommand < RubyGemTestCase
def setup def setup
super super
@foo_gem = quick_gem 'foo' do |spec| util_make_gems
spec.summary = 'This is a lot of text. ' * 5
end @a2.summary = 'This is a lot of text. ' * 4
@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'
@cmd = Gem::Commands::QueryCommand.new @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 end
def test_execute 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] @cmd.handle_options %w[-r]
@ -32,7 +43,43 @@ class TestGemCommandsQueryCommand < RubyGemTestCase
*** REMOTE GEMS *** *** REMOTE GEMS ***
foo (2) a (2)
EOF
assert_equal expected, @ui.output
assert_equal '', @ui.error
end
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 EOF
assert_equal expected, @ui.output assert_equal expected, @ui.output
@ -40,8 +87,6 @@ foo (2)
end end
def test_execute_details def test_execute_details
util_setup_source_info_cache @foo_gem
@cmd.handle_options %w[-r -d] @cmd.handle_options %w[-r -d]
use_ui @ui do use_ui @ui do
@ -52,18 +97,94 @@ foo (2)
*** REMOTE GEMS *** *** REMOTE GEMS ***
foo (2) a (2, 1)
This is a lot of text. This is a lot of text. This is a lot of This is a lot of text. This is a lot of text. This is a lot of text.
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 EOF
assert_equal expected, @ui.output assert_equal expected, @ui.output
assert_equal '', @ui.error assert_equal '', @ui.error
end end
def test_execute_no_versions def test_execute_installed
util_setup_source_info_cache @foo_gem, @bar_gem @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] @cmd.handle_options %w[-r --no-versions]
use_ui @ui do use_ui @ui do
@ -74,8 +195,8 @@ foo (2)
*** REMOTE GEMS *** *** REMOTE GEMS ***
bar a
foo pl
EOF EOF
assert_equal expected, @ui.output assert_equal expected, @ui.output

View file

@ -20,7 +20,7 @@ class TestGemCommandsServerCommand < RubyGemTestCase
@cmd.send :handle_options, %w[-p 9999 -d /nonexistent --daemon] @cmd.send :handle_options, %w[-p 9999 -d /nonexistent --daemon]
assert_equal true, @cmd.options[: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] assert_equal 9999, @cmd.options[:port]
end end
end end

View file

@ -31,10 +31,11 @@ class TestGemCommandsSourcesCommand < RubyGemTestCase
def test_execute_add def test_execute_add
util_setup_fake_fetcher 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}"] = @fetcher.data["http://beta-gems.example.com/Marshal.#{@marshal_version}"] =
@si.dump si.dump
@cmd.handle_options %w[--add http://beta-gems.example.com] @cmd.handle_options %w[--add http://beta-gems.example.com]
@ -45,7 +46,7 @@ class TestGemCommandsSourcesCommand < RubyGemTestCase
end end
expected = <<-EOF 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 http://beta-gems.example.com added to sources
EOF EOF
@ -60,14 +61,11 @@ http://beta-gems.example.com added to sources
def test_execute_add_nonexistent_source def test_execute_add_nonexistent_source
util_setup_fake_fetcher util_setup_fake_fetcher
@si = Gem::SourceIndex.new @gem1.full_name => @gem1.name
@fetcher.data["http://beta-gems.example.com/Marshal.#{@marshal_version}"] = @fetcher.data["http://beta-gems.example.com/Marshal.#{@marshal_version}"] =
proc do proc do
raise Gem::RemoteFetcher::FetchError, 'it died' raise Gem::RemoteFetcher::FetchError, 'it died'
end end
Gem::RemoteFetcher.instance_variable_set :@fetcher, @fetcher Gem::RemoteFetcher.instance_variable_set :@fetcher, @fetcher
@cmd.handle_options %w[--add http://beta-gems.example.com] @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 assert_equal '', @ui.error
end 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 def test_execute_remove
@cmd.handle_options %W[--remove #{@gem_repo}] @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 assert_equal [], Gem::SourceInfoCache.cache_data.keys
end 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 def test_execute_update
@cmd.handle_options %w[--update] @cmd.handle_options %w[--update]
util_setup_source_info_cache util_setup_source_info_cache
util_setup_fake_fetcher util_setup_fake_fetcher
@si = Gem::SourceIndex.new @gem1.full_name => @gem1.name si = Gem::SourceIndex.new
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = @si.dump si.add_spec @a1
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump
use_ui @ui do use_ui @ui do
@cmd.execute @cmd.execute
end end
expected = <<-EOF expected = <<-EOF
Bulk updating Gem source index for: #{@gem_repo} Bulk updating Gem source index for: #{@gem_repo}/
source cache successfully updated source cache successfully updated
EOF EOF

View file

@ -12,6 +12,7 @@ class TestGemCommandsSpecificationCommand < RubyGemTestCase
def test_execute def test_execute
foo = quick_gem 'foo' foo = quick_gem 'foo'
Gem.source_index.add_spec foo
@cmd.options[:args] = %w[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|\A--- !ruby/object:Gem::Specification|, @ui.output
assert_match %r|name: foo|, @ui.output assert_match %r|name: foo|, @ui.output
assert_equal "WARNING: Remote information is not complete\n\n", @ui.error
end end
end end

View file

@ -26,6 +26,48 @@ class TestGemCommandsUnpackCommand < RubyGemTestCase
assert File.exist?(File.join(@tempdir, 'a-2')) assert File.exist?(File.join(@tempdir, 'a-2'))
end 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 def test_execute_with_target_option
util_make_gems util_make_gems

View file

@ -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

View file

@ -54,8 +54,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil inst = nil
Dir.chdir @tempdir do Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new 'a' inst = Gem::DependencyInstaller.new
inst.install inst.install 'a'
end end
assert_equal Gem::SourceIndex.new(@a1.full_name => @a1), assert_equal Gem::SourceIndex.new(@a1.full_name => @a1),
@ -70,8 +70,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil inst = nil
Dir.chdir @tempdir do Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new 'b' inst = Gem::DependencyInstaller.new
inst.install inst.install 'b'
end end
assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name } assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
@ -84,8 +84,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil inst = nil
Dir.chdir @tempdir do Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new 'b' inst = Gem::DependencyInstaller.new
inst.install inst.install 'b'
end end
assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name } assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
@ -102,8 +102,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil inst = nil
Dir.chdir @tempdir do Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new 'f' inst = Gem::DependencyInstaller.new
inst.install inst.install 'f'
end end
assert_equal %w[f-2], inst.installed_gems.map { |s| s.full_name } assert_equal %w[f-2], inst.installed_gems.map { |s| s.full_name }
@ -114,19 +114,49 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil inst = nil
Dir.chdir @tempdir do Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new 'a-1.gem' inst = Gem::DependencyInstaller.new :domain => :local
inst.install inst.install 'a-1.gem'
end end
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name } assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
end end
def test_install_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 def test_install_local_subdir
inst = nil inst = nil
Dir.chdir @tempdir do Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new 'gems/a-1.gem' inst = Gem::DependencyInstaller.new :domain => :local
inst.install inst.install 'gems/a-1.gem'
end end
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name } assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
@ -137,12 +167,11 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil inst = nil
Dir.chdir @tempdir do Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new 'a', nil, :env_shebang => true, inst = Gem::DependencyInstaller.new :env_shebang => true, :wrappers => true
:wrappers => true inst.install 'a'
inst.install
end 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')) File.read(File.join(@gemhome, 'bin', 'a_bin'))
end end
@ -153,8 +182,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil inst = nil
Dir.chdir @tempdir do Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new 'b', nil, :force => true inst = Gem::DependencyInstaller.new :force => true
inst.install inst.install 'b'
end end
assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name } assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
@ -165,8 +194,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil inst = nil
Dir.chdir @tempdir do Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new 'b', nil, :ignore_dependencies => true inst = Gem::DependencyInstaller.new :ignore_dependencies => true
inst.install inst.install 'b'
end end
assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name } assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
@ -179,8 +208,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil inst = nil
Dir.chdir @tempdir do Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new 'a', nil, :install_dir => gemhome2 inst = Gem::DependencyInstaller.new :install_dir => gemhome2
inst.install inst.install 'a'
end end
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name } assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
@ -201,8 +230,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil inst = nil
Dir.chdir @tempdir do Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new 'b', nil, :domain => :both inst = Gem::DependencyInstaller.new :domain => :both
inst.install inst.install 'b'
end end
assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name } assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
@ -217,14 +246,34 @@ class TestGemDependencyInstaller < RubyGemTestCase
assert_equal b1_expected, b1.loaded_from assert_equal b1_expected, b1.loaded_from
end 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 def test_install_domain_local
FileUtils.mv @b1_gem, @tempdir FileUtils.mv @b1_gem, @tempdir
inst = nil inst = nil
Dir.chdir @tempdir do Dir.chdir @tempdir do
e = assert_raise Gem::InstallError do e = assert_raise Gem::InstallError do
inst = Gem::DependencyInstaller.new 'b', nil, :domain => :local inst = Gem::DependencyInstaller.new :domain => :local
inst.install inst.install 'b'
end end
assert_equal 'b requires a (>= 0)', e.message assert_equal 'b requires a (>= 0)', e.message
end end
@ -240,8 +289,43 @@ class TestGemDependencyInstaller < RubyGemTestCase
@fetcher.data['http://gems.example.com/gems/a-1.gem'] = a1_data @fetcher.data['http://gems.example.com/gems/a-1.gem'] = a1_data
inst = Gem::DependencyInstaller.new 'a', nil, :domain => :remote inst = Gem::DependencyInstaller.new :domain => :remote
inst.install 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 } assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
end end
@ -266,8 +350,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
@fetcher.data["http://gems.example.com/gems/#{a2_o.full_name}.gem"] = @fetcher.data["http://gems.example.com/gems/#{a2_o.full_name}.gem"] =
a2_o_data a2_o_data
inst = Gem::DependencyInstaller.new 'a', nil, :domain => :remote inst = Gem::DependencyInstaller.new :domain => :remote
inst.install inst.install 'a'
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name } assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
end end
@ -278,8 +362,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
inst = nil inst = nil
Dir.chdir @tempdir do Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new 'a' inst = Gem::DependencyInstaller.new
inst.install inst.install 'a'
end end
assert_equal Gem::SourceIndex.new(@a1.full_name => @a1), assert_equal Gem::SourceIndex.new(@a1.full_name => @a1),
@ -290,13 +374,17 @@ class TestGemDependencyInstaller < RubyGemTestCase
if defined? OpenSSL then if defined? OpenSSL then
def test_install_security_policy def test_install_security_policy
FileUtils.mv @a1_gem, @cache_dir data = File.open(@a1_gem, 'rb') { |f| f.read }
FileUtils.mv @b1_gem, @cache_dir @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 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 e = assert_raise Gem::Exception do
inst.install inst.install 'b'
end end
assert_equal 'Unsigned gem', e.message assert_equal 'Unsigned gem', e.message
@ -305,145 +393,48 @@ class TestGemDependencyInstaller < RubyGemTestCase
end end
end end
def test_install_wrappers # Wrappers don't work on mswin
FileUtils.mv @a1_gem, @cache_dir unless win_platform? then
inst = Gem::DependencyInstaller.new 'a', :wrappers => true 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.|, assert_no_match(%r|This file was generated by RubyGems.|,
File.read(File.join(@gemhome, 'bin', 'a_bin')) File.read(File.join(@gemhome, 'bin', 'a_bin')))
end
end end
def test_install_version def test_install_version
FileUtils.mv @d1_gem, @cache_dir data = File.open(@d2_gem, 'rb') { |f| f.read }
FileUtils.mv @d2_gem, @cache_dir @fetcher.data['http://gems.example.com/gems/d-2.gem'] = data
inst = Gem::DependencyInstaller.new 'd', '= 1'
inst.install data = File.open(@d1_gem, 'rb') { |f| f.read }
@fetcher.data['http://gems.example.com/gems/d-1.gem'] = data
inst = Gem::DependencyInstaller.new
inst.install 'd', '= 1'
assert_equal %w[d-1], inst.installed_gems.map { |s| s.full_name } assert_equal %w[d-1], inst.installed_gems.map { |s| s.full_name }
end end
def test_install_version_default def test_install_version_default
FileUtils.mv @d1_gem, @cache_dir data = File.open(@d2_gem, 'rb') { |f| f.read }
FileUtils.mv @d2_gem, @cache_dir @fetcher.data['http://gems.example.com/gems/d-2.gem'] = data
inst = Gem::DependencyInstaller.new 'd'
inst.install data = File.open(@d1_gem, 'rb') { |f| f.read }
@fetcher.data['http://gems.example.com/gems/d-1.gem'] = data
inst = Gem::DependencyInstaller.new
inst.install 'd'
assert_equal %w[d-2], inst.installed_gems.map { |s| s.full_name } assert_equal %w[d-2], inst.installed_gems.map { |s| s.full_name }
end end
def test_download
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 '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')
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::DependencyInstaller.new 'a'
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.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_download_unsupported
inst = Gem::DependencyInstaller.new 'a'
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_find_gems_gems_with_sources def test_find_gems_gems_with_sources
inst = Gem::DependencyInstaller.new 'a' inst = Gem::DependencyInstaller.new
dep = Gem::Dependency.new 'b', '>= 0' dep = Gem::Dependency.new 'b', '>= 0'
assert_equal [[@b1, 'http://gems.example.com']], assert_equal [[@b1, 'http://gems.example.com']],
@ -452,7 +443,7 @@ class TestGemDependencyInstaller < RubyGemTestCase
def test_find_gems_with_sources_local def test_find_gems_with_sources_local
FileUtils.mv @a1_gem, @tempdir FileUtils.mv @a1_gem, @tempdir
inst = Gem::DependencyInstaller.new 'b' inst = Gem::DependencyInstaller.new
dep = Gem::Dependency.new 'a', '>= 0' dep = Gem::Dependency.new 'a', '>= 0'
gems = nil gems = nil
@ -462,7 +453,7 @@ class TestGemDependencyInstaller < RubyGemTestCase
assert_equal 2, gems.length assert_equal 2, gems.length
remote = gems.first 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' assert_equal 'http://gems.example.com', remote.last, 'remote path'
local = gems.last local = gems.last
@ -472,7 +463,9 @@ class TestGemDependencyInstaller < RubyGemTestCase
end end
def test_gather_dependencies 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 } assert_equal %w[a-1 b-1], inst.gems_to_install.map { |s| s.full_name }
end end
@ -488,7 +481,9 @@ class TestGemDependencyInstaller < RubyGemTestCase
@fetcher.uri = URI.parse 'http://gems.example.com' @fetcher.uri = URI.parse 'http://gems.example.com'
@fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml @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 } assert_equal %w[b-2 c-1], inst.gems_to_install.map { |s| s.full_name }
end end
@ -496,14 +491,18 @@ class TestGemDependencyInstaller < RubyGemTestCase
def test_gather_dependencies_platform_alternate def test_gather_dependencies_platform_alternate
util_set_arch 'cpu-my_platform1' 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], assert_equal %w[x-1-cpu-my_platform-1 w-1],
inst.gems_to_install.map { |s| s.full_name } inst.gems_to_install.map { |s| s.full_name }
end end
def test_gather_dependencies_platform_bump 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 } assert_equal %w[y-1 z-1], inst.gems_to_install.map { |s| s.full_name }
end end
@ -518,27 +517,11 @@ class TestGemDependencyInstaller < RubyGemTestCase
@fetcher.uri = URI.parse 'http://gems.example.com' @fetcher.uri = URI.parse 'http://gems.example.com'
@fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml @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 } assert_equal %w[d-1 e-1], inst.gems_to_install.map { |s| s.full_name }
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
end end

View file

@ -47,16 +47,19 @@ class TestGemExtConfigureBuilder < RubyGemTestCase
end end
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="
sh \./configure --prefix=#{Regexp.escape @dest_path} expected = %r(configure failed:
.*?: \./configure: No such file or directory
| #{Regexp.escape sh_prefix_configure}#{Regexp.escape @dest_path}
.*?: #{shell_error_msg}
)
assert_match expected, error.message assert_match expected, error.message
assert_equal "sh ./configure --prefix=#{@dest_path}", output.shift assert_equal "#{sh_prefix_configure}#{@dest_path}", output.shift
assert_match %r|\./configure: No such file or directory\n|, output.shift assert_match %r(#{shell_error_msg}\n), output.shift
assert_equal true, output.empty? assert_equal true, output.empty?
end end

View file

@ -22,7 +22,7 @@ class TestGemFormat < RubyGemTestCase
gems = Dir[File.join(@gemhome, 'cache', '*.gem')] 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 spec.original_name
end end

View file

@ -53,6 +53,11 @@ class TestGemIndexer < RubyGemTestCase
assert_indexed quickdir, "index" assert_indexed quickdir, "index"
assert_indexed quickdir, "index.rz" 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, "#{@a1.full_name}.gemspec.rz"
assert_indexed quickdir, "#{@a2.full_name}.gemspec.rz" assert_indexed quickdir, "#{@a2.full_name}.gemspec.rz"
assert_indexed quickdir, "#{@b2.full_name}.gemspec.rz" assert_indexed quickdir, "#{@b2.full_name}.gemspec.rz"
@ -74,8 +79,8 @@ class TestGemIndexer < RubyGemTestCase
end end
expected = <<-EOF expected = <<-EOF
Generating index for 5 gems in #{@tempdir} Generating index for 6 gems in #{@tempdir}
..... ......
Loaded all gems Loaded all gems
Generating master indexes (this may take a while) Generating master indexes (this may take a while)
EOF EOF

View file

@ -4,63 +4,10 @@
# See LICENSE.txt for permissions. # See LICENSE.txt for permissions.
#++ #++
require 'test/unit' require File.join(File.expand_path(File.dirname(__FILE__)),
require File.join(File.expand_path(File.dirname(__FILE__)), 'gemutilities') 'gem_installer_test_case')
require 'rubygems/installer'
class Gem::Installer class TestGemInstaller < GemInstallerTestCase
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
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 test_app_script_text def test_app_script_text
util_make_exec '2', '' util_make_exec '2', ''
@ -162,7 +109,7 @@ load 'my_exec'
@installer.gem_dir = '/nonexistent' @installer.gem_dir = '/nonexistent'
expanded_gem_dir = @installer.send(:expand_and_validate_gem_dir) expanded_gem_dir = @installer.send(:expand_and_validate_gem_dir)
if win_platform? 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 expanded_gem_dir = expanded_gem_dir.downcase
else else
expected = '/nonexistent' expected = '/nonexistent'
@ -768,7 +715,7 @@ load 'my_exec'
@installer.env_shebang = true @installer.env_shebang = true
shebang = @installer.shebang 'my_exec' shebang = @installer.shebang 'my_exec'
assert_equal "#!/usr/bin/env ruby", shebang assert_equal "#!/usr/bin/env #{Gem::ConfigMap[:RUBY_INSTALL_NAME]}", shebang
end end
def test_shebang_nested def test_shebang_nested
@ -855,31 +802,5 @@ load 'my_exec'
File.join @gemhome, 'cache', "#{spec.full_name}.gem" File.join @gemhome, 'cache', "#{spec.full_name}.gem"
end 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 end

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -97,6 +97,13 @@ gems:
@server_uri = base_server_uri + "/yaml" @server_uri = base_server_uri + "/yaml"
@server_z_uri = base_server_uri + "/yaml.Z" @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 Gem::RemoteFetcher.instance_variable_set :@fetcher, nil
end end
@ -156,6 +163,140 @@ gems:
end end
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 def test_explicit_proxy
use_ui @ui do use_ui @ui do
fetcher = Gem::RemoteFetcher.new @proxy_uri fetcher = Gem::RemoteFetcher.new @proxy_uri
@ -232,22 +373,6 @@ gems:
assert_equal 'EOFError: EOFError reading uri', e.message assert_equal 'EOFError: EOFError reading uri', e.message
end 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 def test_fetch_path_socket_error
fetcher = Gem::RemoteFetcher.new nil fetcher = Gem::RemoteFetcher.new nil
@ -324,6 +449,53 @@ gems:
end end
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 def test_zip
use_ui @ui do use_ui @ui do
self.class.enable_zip = true self.class.enable_zip = true

View file

@ -36,7 +36,8 @@ class TestGemSourceIndex < RubyGemTestCase
use_ui @ui do use_ui @ui do
fetched_index = @source_index.fetch_bulk_index @uri 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 fetched_index.gems.map { |n,s| n }.sort
end end
@ -82,7 +83,8 @@ class TestGemSourceIndex < RubyGemTestCase
use_ui @ui do use_ui @ui do
fetched_index = @source_index.fetch_bulk_index @uri 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 fetched_index.gems.map { |n,s| n }.sort
end end
@ -105,7 +107,8 @@ class TestGemSourceIndex < RubyGemTestCase
use_ui @ui do use_ui @ui do
fetched_index = @source_index.fetch_bulk_index @uri 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 fetched_index.gems.map { |n,s| n }.sort
end end
@ -123,7 +126,8 @@ class TestGemSourceIndex < RubyGemTestCase
util_setup_bulk_fetch false util_setup_bulk_fetch false
use_ui @ui do use_ui @ui do
fetched_index = @source_index.fetch_bulk_index @uri 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 fetched_index.gems.map { |n,s| n }.sort
end end
@ -136,11 +140,32 @@ class TestGemSourceIndex < RubyGemTestCase
end end
def test_fetch_quick_index def test_fetch_quick_index
quick_index = util_zip @gem_names index = util_zip @gem_names
@fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index latest_index = util_zip [@a2.full_name, @b2.full_name].join("\n")
quick_index = @source_index.fetch_quick_index @uri @fetcher.data["#{@gem_repo}/quick/index.rz"] = index
assert_equal [@gem1.full_name, @gem4.full_name, @gem2.full_name].sort, @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, true
assert_equal [@a1.full_name, @a2.full_name, @b2.full_name].sort,
quick_index.sort quick_index.sort
paths = @fetcher.paths paths = @fetcher.paths
@ -155,7 +180,7 @@ class TestGemSourceIndex < RubyGemTestCase
proc { raise Exception } proc { raise Exception }
e = assert_raise Gem::OperationNotSupportedError do e = assert_raise Gem::OperationNotSupportedError do
@source_index.fetch_quick_index @uri @source_index.fetch_quick_index @uri, true
end end
assert_equal 'No quick index found: Exception', e.message assert_equal 'No quick index found: Exception', e.message
@ -167,41 +192,201 @@ class TestGemSourceIndex < RubyGemTestCase
assert paths.empty?, paths.join(', ') assert paths.empty?, paths.join(', ')
end 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 def test_find_missing
missing = @source_index.find_missing [@gem3.full_name] missing = @source_index.find_missing [@b2.full_name]
assert_equal [@gem3.full_name], missing assert_equal [@b2.full_name], missing
end end
def test_find_missing_none_missing 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 assert_equal [], missing
end end
def test_latest_specs def test_latest_specs
spec = quick_gem @gem1.name, '1' p1_ruby = quick_gem 'p', '1'
@source_index.add_spec spec 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 = [ expected = [
@gem1.full_name, @a2.full_name,
@gem2.full_name, a2_platform.full_name,
@gem4.full_name, a3_platform_other.full_name,
@c1_2.full_name,
@a_evil9.full_name,
p1_ruby.full_name,
p1_platform.full_name,
].sort ].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 end
def test_outdated def test_outdated
sic = Gem::SourceInfoCache.new util_setup_source_info_cache
Gem::SourceInfoCache.instance_variable_set :@cache, sic
assert_equal [], @source_index.outdated 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 util_setup_source_info_cache updated
assert_equal [updated.name], @source_index.outdated 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' s.platform = Gem::Platform.new 'x86-other_platform1'
end end
@ -211,28 +396,34 @@ class TestGemSourceIndex < RubyGemTestCase
end end
def test_remove_extra def test_remove_extra
@source_index.remove_extra [@gem1.full_name] @source_index.add_spec @a1
assert_equal [@gem1.full_name], @source_index.gems.map { |n,s| n } @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 end
def test_remove_extra_no_changes 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 @source_index.remove_extra gems
assert_equal gems, @source_index.gems.map { |n,s| n }.sort assert_equal gems, @source_index.gems.map { |n,s| n }.sort
end end
def test_search def test_search
assert_equal [@gem1, @gem4], @source_index.search("gem_one") assert_equal [@a1, @a2, @a_evil9], @source_index.search('a')
assert_equal [@gem1], @source_index.search("gem_one", "= 2") assert_equal [@a2], @source_index.search('a', '= 2')
assert_equal [], @source_index.search("bogusstring") assert_equal [], @source_index.search('bogusstring')
assert_equal [], @source_index.search("gem_one", "= 3.2.1") assert_equal [], @source_index.search('a', '= 3')
@a1 = quick_gem 'a', '1' source_index = Gem::SourceIndex.new
@a2 = quick_gem 'a', '2' source_index.add_spec @a1
source_index.add_spec @a2
source_index = Gem::SourceIndex.new @a1.full_name => @a1,
@a2.full_name => @a2
assert_equal [@a1], source_index.search(@a1.name, '= 1') assert_equal [@a1], source_index.search(@a1.name, '= 1')
@ -276,7 +467,7 @@ class TestGemSourceIndex < RubyGemTestCase
end end
def test_specification 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") assert_nil @source_index.specification("foo-1.2.4")
end end
@ -298,9 +489,11 @@ class TestGemSourceIndex < RubyGemTestCase
assert_equal [], @source_index.gems.keys.sort assert_equal [], @source_index.gems.keys.sort
use_ui @ui do 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 end
paths = @fetcher.paths paths = @fetcher.paths
@ -315,15 +508,42 @@ class TestGemSourceIndex < RubyGemTestCase
old_gem_conf = Gem.configuration old_gem_conf = Gem.configuration
Gem.configuration = Gem::ConfigFile.new([]) 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") quick_index = util_zip @all_gem_names.join("\n")
@fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index @fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}", marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
"#{@gem3.full_name}.gemspec.rz" "#{@b2.full_name}.gemspec.rz"
@fetcher.data[marshal_uri] = util_zip Marshal.dump(@gem3) @fetcher.data[marshal_uri] = util_zip Marshal.dump(@b2)
use_ui @ui do use_ui @ui do
@source_index.update @uri @source_index.update @uri, true
assert_equal @all_gem_names, @source_index.gems.keys.sort assert_equal @all_gem_names, @source_index.gems.keys.sort
end end
@ -345,13 +565,13 @@ class TestGemSourceIndex < RubyGemTestCase
@fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index @fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}", marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
"#{@gem3.full_name}.gemspec.rz" "#{@b2.full_name}.gemspec.rz"
yaml_uri = "#{@gem_repo}/quick/#{@gem3.full_name}.gemspec.rz" yaml_uri = "#{@gem_repo}/quick/#{@b2.full_name}.gemspec.rz"
@fetcher.data[yaml_uri] = util_zip @gem3.to_yaml @fetcher.data[yaml_uri] = util_zip @b2.to_yaml
use_ui @ui do use_ui @ui do
@source_index.update @uri @source_index.update @uri, true
assert_equal @all_gem_names, @source_index.gems.keys.sort assert_equal @all_gem_names, @source_index.gems.keys.sort
end end
@ -374,16 +594,16 @@ class TestGemSourceIndex < RubyGemTestCase
@fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index @fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}", marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
"#{@gem3.full_name}.gemspec.rz" "#{@b2.full_name}.gemspec.rz"
marshal_data = Marshal.dump(@gem3) marshal_data = Marshal.dump(@b2)
marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr
@fetcher.data[marshal_uri] = util_zip marshal_data @fetcher.data[marshal_uri] = util_zip marshal_data
yaml_uri = "#{@gem_repo}/quick/#{@gem3.full_name}.gemspec.rz" yaml_uri = "#{@gem_repo}/quick/#{@b2.full_name}.gemspec.rz"
@fetcher.data[yaml_uri] = util_zip @gem3.to_yaml @fetcher.data[yaml_uri] = util_zip @b2.to_yaml
use_ui @ui do use_ui @ui do
@source_index.update @uri @source_index.update @uri, true
assert_equal @all_gem_names, @source_index.gems.keys.sort assert_equal @all_gem_names, @source_index.gems.keys.sort
end end
@ -398,22 +618,48 @@ class TestGemSourceIndex < RubyGemTestCase
Gem.configuration = old_gem_conf Gem.configuration = old_gem_conf
end 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 def test_update_with_missing
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}", marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
"#{@gem3.full_name}.gemspec.rz" "#{@c1_2.full_name}.gemspec.rz"
dumped = Marshal.dump @gem3 dumped = Marshal.dump @c1_2
@fetcher.data[marshal_uri] = util_zip(dumped) @fetcher.data[marshal_uri] = util_zip(dumped)
use_ui @ui do use_ui @ui do
@source_index.update_with_missing @uri, [@gem3.full_name] @source_index.update_with_missing @uri, [@c1_2.full_name]
end 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 # We don't care about the equality of undumped attributes
@gem3.files = spec.files @c1_2.files = spec.files
@gem3.loaded_from = spec.loaded_from @c1_2.loaded_from = spec.loaded_from
assert_equal @gem3, spec assert_equal @c1_2, spec
end end
def util_setup_bulk_fetch(compressed) def util_setup_bulk_fetch(compressed)
@ -427,3 +673,4 @@ class TestGemSourceIndex < RubyGemTestCase
end end
end end

View file

@ -25,7 +25,12 @@ class TestGemSourceInfoCache < RubyGemTestCase
@sic = Gem::SourceInfoCache.new @sic = Gem::SourceInfoCache.new
@sic.instance_variable_set :@fetcher, @fetcher @sic.instance_variable_set :@fetcher, @fetcher
@si_new = Gem::SourceIndex.new
@sice_new = Gem::SourceInfoCacheEntry.new @si_new, 0
prep_cache_files @sic prep_cache_files @sic
@sic.reset_cache_data
end end
def teardown def teardown
@ -35,8 +40,10 @@ class TestGemSourceInfoCache < RubyGemTestCase
def test_self_cache_refreshes def test_self_cache_refreshes
Gem.configuration.update_sources = true #true by default Gem.configuration.update_sources = true #true by default
source_index = Gem::SourceIndex.new 'key' => 'sys' si = Gem::SourceIndex.new
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = source_index.dump si.add_spec @a1
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump
Gem.sources.replace %W[#{@gem_repo}] Gem.sources.replace %W[#{@gem_repo}]
@ -51,8 +58,10 @@ class TestGemSourceInfoCache < RubyGemTestCase
def test_self_cache_skips_refresh_based_on_configuration def test_self_cache_skips_refresh_based_on_configuration
Gem.configuration.update_sources = false Gem.configuration.update_sources = false
source_index = Gem::SourceIndex.new 'key' => 'sys' si = Gem::SourceIndex.new
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = source_index.dump si.add_spec @a1
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump
Gem.sources.replace %w[#{@gem_repo}] Gem.sources.replace %w[#{@gem_repo}]
@ -66,20 +75,24 @@ class TestGemSourceInfoCache < RubyGemTestCase
end end
def test_self_cache_data def test_self_cache_data
source_index = Gem::SourceIndex.new 'key' => 'sys' si = Gem::SourceIndex.new
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = source_index.dump si.add_spec @a1
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump
Gem::SourceInfoCache.instance_variable_set :@cache, nil Gem::SourceInfoCache.instance_variable_set :@cache, nil
sice = Gem::SourceInfoCacheEntry.new source_index, 0 sice = Gem::SourceInfoCacheEntry.new si, 0
use_ui @ui do use_ui @ui do
assert_equal source_index.gems, gems = Gem::SourceInfoCache.cache_data[@gem_repo].source_index.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
end end
def test_cache_data 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 end
def test_cache_data_dirty def test_cache_data_dirty
@ -97,7 +110,14 @@ class TestGemSourceInfoCache < RubyGemTestCase
data = { @gem_repo => { 'totally' => 'borked' } } 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) FileUtils.mkdir_p File.dirname(fn)
open(fn, "wb") { |f| f.write Marshal.dump(data) } open(fn, "wb") { |f| f.write Marshal.dump(data) }
end end
@ -113,7 +133,9 @@ class TestGemSourceInfoCache < RubyGemTestCase
def test_cache_data_none_readable def test_cache_data_none_readable
FileUtils.chmod 0222, @sic.system_cache_file 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.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.system_cache_file).mode & 0222) != 0222
return if (File.stat(@sic.user_cache_file).mode & 0222) != 0222 return if (File.stat(@sic.user_cache_file).mode & 0222) != 0222
# HACK for systems that don't support chmod # 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 assert_equal 'unable to locate a writable cache file', e.message
end 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 def test_cache_data_repair
data = { data = {
@gem_repo => { @gem_repo => {
@ -152,7 +184,8 @@ class TestGemSourceInfoCache < RubyGemTestCase
def test_cache_data_user_fallback def test_cache_data_user_fallback
FileUtils.chmod 0444, @sic.system_cache_file 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 end
def test_cache_file def test_cache_file
@ -174,60 +207,118 @@ class TestGemSourceInfoCache < RubyGemTestCase
end end
def test_flush def test_flush
@sic.cache_data['key'] = 'new' @sic.cache_data[@gem_repo] = @sice_new
@sic.update @sic.update
@sic.flush @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 end
def test_read_system_cache 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 end
def test_read_user_cache 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
assert_equal [['key','usr']], @sic.cache_data.to_a.sort @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
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 end
def test_search def test_search
si = Gem::SourceIndex.new @gem1.full_name => @gem1 si = Gem::SourceIndex.new
cache_data = { si.add_spec @a1
@gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) cache_data = { @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) }
}
@sic.instance_variable_set :@cache_data, cache_data @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 end
def test_search_dependency def test_search_dependency
si = Gem::SourceIndex.new @gem1.full_name => @gem1 si = Gem::SourceIndex.new
cache_data = { si.add_spec @a1
@gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) cache_data = { @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) }
}
@sic.instance_variable_set :@cache_data, cache_data @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 end
def test_search_no_matches def test_search_no_matches
si = Gem::SourceIndex.new @gem1.full_name => @gem1 si = Gem::SourceIndex.new
cache_data = { si.add_spec @a1
@gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) cache_data = { @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) }
}
@sic.instance_variable_set :@cache_data, cache_data @sic.instance_variable_set :@cache_data, cache_data
assert_equal [], @sic.search(/nonexistent/) assert_equal [], @sic.search(/nonexistent/)
end end
def test_search_no_matches_in_source def test_search_no_matches_in_source
si = Gem::SourceIndex.new @gem1.full_name => @gem1 si = Gem::SourceIndex.new
cache_data = { si.add_spec @a1
@gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) cache_data = { @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) }
}
@sic.instance_variable_set :@cache_data, cache_data @sic.instance_variable_set :@cache_data, cache_data
Gem.sources.replace %w[more-gems.example.com] Gem.sources.replace %w[more-gems.example.com]
@ -235,13 +326,12 @@ class TestGemSourceInfoCache < RubyGemTestCase
end end
def test_search_with_source def test_search_with_source
si = Gem::SourceIndex.new @gem1.full_name => @gem1 si = Gem::SourceIndex.new
cache_data = { si.add_spec @a1
@gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) cache_data = { @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) }
}
@sic.instance_variable_set :@cache_data, cache_data @sic.instance_variable_set :@cache_data, cache_data
assert_equal [[@gem1, @gem_repo]], assert_equal [[@a1, @gem_repo]],
@sic.search_with_source(//) @sic.search_with_source(//)
end end
@ -254,45 +344,81 @@ class TestGemSourceInfoCache < RubyGemTestCase
end end
def test_write_cache def test_write_cache
@sic.cache_data['key'] = 'new' @sic.cache_data[@gem_repo] = @sice_new
@sic.write_cache @sic.write_cache
assert_equal [['key', 'new']], assert_equal [[@gem_repo, @sice_new]],
read_cache(@sic.system_cache_file).to_a.sort 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 read_cache(@sic.user_cache_file).to_a.sort
end end
def test_write_cache_user def test_write_cache_user
FileUtils.chmod 0444, @sic.system_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.update
@sic.write_cache @sic.write_cache
assert_equal [['key', 'sys']], read_cache(@sic.system_cache_file).to_a.sort assert File.exist?(@sic.user_cache_file), 'user_cache_file'
assert_equal [['key', 'new']], read_cache(@sic.user_cache_file).to_a.sort 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 end
def test_write_cache_user_from_scratch def test_write_cache_user_from_scratch
FileUtils.rm_rf @sic.user_cache_file FileUtils.rm_rf @sic.user_cache_file
FileUtils.rm_rf @sic.latest_user_cache_file
FileUtils.chmod 0444, @sic.system_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.update
@sic.write_cache @sic.write_cache
assert_equal [['key', 'sys']], read_cache(@sic.system_cache_file).to_a.sort assert File.exist?(@sic.user_cache_file), 'system_cache_file'
assert_equal [['key', 'new']], read_cache(@sic.user_cache_file).to_a.sort 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 end
def test_write_cache_user_no_directory def test_write_cache_user_no_directory
FileUtils.rm_rf File.dirname(@sic.user_cache_file) FileUtils.rm_rf File.dirname(@sic.user_cache_file)
FileUtils.chmod 0444, @sic.system_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.update
@sic.write_cache @sic.write_cache
assert_equal [['key','sys']], read_cache(@sic.system_cache_file).to_a.sort assert_equal [[@gem_repo, @sys_sice]],
assert_equal [['key','new']], read_cache(@sic.user_cache_file).to_a.sort 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
end end

View file

@ -9,37 +9,68 @@ class TestGemSourceInfoCacheEntry < RubyGemTestCase
util_setup_fake_fetcher 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 @sic_e = Gem::SourceInfoCacheEntry.new @si, @si.dump.size
end end
def test_refresh def test_refresh
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}.Z"] = @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}.Z"] =
proc { raise Exception } proc { raise }
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = @si.dump @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = @si.dump
assert_nothing_raised do use_ui @ui do
@sic_e.refresh @gem_repo @sic_e.refresh @gem_repo, true
end end
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 def test_refresh_bad_uri
assert_raise URI::BadURIError do assert_raise URI::BadURIError do
@sic_e.refresh 'gems.example.com' @sic_e.refresh 'gems.example.com', true
end end
end end
def test_refresh_update def test_refresh_update
si = Gem::SourceIndex.new @gem1.full_name => @gem1, si = Gem::SourceIndex.new
@gem2.full_name => @gem2 si.add_spec @a1
si.add_spec @b2
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump @fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump
use_ui @ui do use_ui @ui do
@sic_e.refresh @gem_repo @sic_e.refresh @gem_repo, true
end end
new_gem = @sic_e.source_index.specification(@gem2.full_name) new_gem = @sic_e.source_index.specification(@b2.full_name)
assert_equal @gem2.full_name, new_gem.full_name assert_equal @b2.full_name, new_gem.full_name
end end
end end

View file

@ -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