зеркало из https://github.com/github/ruby.git
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:
Родитель
dc8359969e
Коммит
8cc45aae94
|
@ -1,3 +1,7 @@
|
|||
Tue Apr 1 07:31:58 2008 Eric Hodel <drbrain@segment7.net>
|
||||
|
||||
* lib/rubygems* test/rubygems*: Import RubyGems 1.1.0.
|
||||
|
||||
Tue Apr 1 03:20:40 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||
|
||||
* configure.in (RUBY_SETJMP, RUBY_LONGJMP, RUBY_JMP_BUF): prefers
|
||||
|
|
13
bin/gem
13
bin/gem
|
@ -7,11 +7,12 @@
|
|||
|
||||
require 'rubygems'
|
||||
require 'rubygems/gem_runner'
|
||||
require 'rubygems/exceptions'
|
||||
|
||||
required_version = Gem::Requirement.new ">= 1.8.3"
|
||||
required_version = Gem::Requirement.new "> 1.8.3"
|
||||
|
||||
unless required_version.satisfied_by? Gem::Version.new(RUBY_VERSION) then
|
||||
abort "Expected Ruby Version #{required_version}, was #{RUBY_VERSION}"
|
||||
unless required_version.satisfied_by? Gem.ruby_version then
|
||||
abort "Expected Ruby Version #{required_version}, was #{Gem.ruby_version}"
|
||||
end
|
||||
|
||||
# We need to preserve the original ARGV to use for passing gem options
|
||||
|
@ -19,5 +20,9 @@ end
|
|||
# it...its for the source building process.
|
||||
args = !ARGV.include?("--") ? ARGV.clone : ARGV[0...ARGV.index("--")]
|
||||
|
||||
Gem::GemRunner.new.run args
|
||||
begin
|
||||
Gem::GemRunner.new.run args
|
||||
rescue Gem::SystemExitException => e
|
||||
exit e.exit_code
|
||||
end
|
||||
|
||||
|
|
347
gem_prelude.rb
347
gem_prelude.rb
|
@ -1,207 +1,214 @@
|
|||
# depends on: array.rb dir.rb env.rb file.rb hash.rb module.rb regexp.rb
|
||||
|
||||
# empty gem_prelude.rb
|
||||
#
|
||||
# p Gem::Enable
|
||||
|
||||
if defined?(Gem::Enable) && Gem::Enable
|
||||
#t = Time.now
|
||||
|
||||
module Kernel
|
||||
if defined?(Gem::Enable) && Gem::Enable then
|
||||
|
||||
def gem(gem_name, *version_requirements)
|
||||
Gem.push_gem_version_on_load_path(gem_name, *version_requirements)
|
||||
end
|
||||
module Kernel
|
||||
|
||||
end
|
||||
|
||||
module Gem
|
||||
|
||||
ConfigMap = {
|
||||
:sitedir => RbConfig::CONFIG["sitedir"],
|
||||
:ruby_version => RbConfig::CONFIG["ruby_version"],
|
||||
:libdir => RbConfig::CONFIG["libdir"],
|
||||
:sitelibdir => RbConfig::CONFIG["sitelibdir"],
|
||||
:arch => RbConfig::CONFIG["arch"],
|
||||
:bindir => RbConfig::CONFIG["bindir"],
|
||||
:EXEEXT => RbConfig::CONFIG["EXEEXT"],
|
||||
:RUBY_SO_NAME => RbConfig::CONFIG["RUBY_SO_NAME"],
|
||||
:ruby_install_name => RbConfig::CONFIG["ruby_install_name"]
|
||||
}
|
||||
|
||||
class << self
|
||||
|
||||
def default_dir
|
||||
if defined? RUBY_FRAMEWORK_VERSION
|
||||
return File.join(File.dirname(ConfigMap[:sitedir]), "Gems")
|
||||
else
|
||||
File.join(ConfigMap[:libdir], 'ruby', 'gems', ConfigMap[:ruby_version])
|
||||
end
|
||||
end
|
||||
|
||||
def dir
|
||||
@gem_home ||= nil
|
||||
set_home(ENV['GEM_HOME'] || default_dir) unless @gem_home
|
||||
@gem_home
|
||||
end
|
||||
|
||||
def path
|
||||
@gem_path ||= nil
|
||||
unless @gem_path
|
||||
paths = [ENV['GEM_PATH']]
|
||||
paths << APPLE_GEM_HOME if defined? APPLE_GEM_HOME
|
||||
set_paths(paths.compact.join(File::PATH_SEPARATOR))
|
||||
end
|
||||
@gem_path
|
||||
end
|
||||
|
||||
# Set the Gem home directory (as reported by +dir+).
|
||||
def set_home(home)
|
||||
@gem_home = home
|
||||
ensure_gem_subdirectories(@gem_home)
|
||||
end
|
||||
|
||||
def set_paths(gpaths)
|
||||
if gpaths
|
||||
@gem_path = gpaths.split(File::PATH_SEPARATOR)
|
||||
@gem_path << Gem.dir
|
||||
else
|
||||
@gem_path = [Gem.dir]
|
||||
end
|
||||
@gem_path.uniq!
|
||||
@gem_path.each do |gp| ensure_gem_subdirectories(gp) end
|
||||
end
|
||||
|
||||
def ensure_gem_subdirectories(path)
|
||||
def gem(gem_name, *version_requirements)
|
||||
Gem.push_gem_version_on_load_path(gem_name, *version_requirements)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module QuickLoader
|
||||
module Gem
|
||||
|
||||
ConfigMap = {
|
||||
:sitedir => RbConfig::CONFIG["sitedir"],
|
||||
:ruby_version => RbConfig::CONFIG["ruby_version"],
|
||||
:libdir => RbConfig::CONFIG["libdir"],
|
||||
:sitelibdir => RbConfig::CONFIG["sitelibdir"],
|
||||
:arch => RbConfig::CONFIG["arch"],
|
||||
:bindir => RbConfig::CONFIG["bindir"],
|
||||
:EXEEXT => RbConfig::CONFIG["EXEEXT"],
|
||||
:RUBY_SO_NAME => RbConfig::CONFIG["RUBY_SO_NAME"],
|
||||
:ruby_install_name => RbConfig::CONFIG["ruby_install_name"]
|
||||
}
|
||||
|
||||
class << self
|
||||
def load_full_rubygems_library
|
||||
class << Gem
|
||||
Gem.methods(false).each do |method_name|
|
||||
undef_method method_name
|
||||
end
|
||||
|
||||
def default_dir
|
||||
if defined? RUBY_FRAMEWORK_VERSION
|
||||
return File.join(File.dirname(ConfigMap[:sitedir]), "Gems")
|
||||
else
|
||||
File.join(ConfigMap[:libdir], 'ruby', 'gems', ConfigMap[:ruby_version])
|
||||
end
|
||||
|
||||
Kernel.send :undef_method, :gem
|
||||
|
||||
$".delete File.join(Gem::ConfigMap[:libdir], 'ruby',
|
||||
Gem::ConfigMap[:ruby_version], 'rubygems.rb')
|
||||
|
||||
require 'rubygems'
|
||||
end
|
||||
|
||||
def dir
|
||||
@gem_home ||= nil
|
||||
set_home(ENV['GEM_HOME'] || default_dir) unless @gem_home
|
||||
@gem_home
|
||||
end
|
||||
|
||||
def path
|
||||
@gem_path ||= nil
|
||||
unless @gem_path
|
||||
paths = [ENV['GEM_PATH']]
|
||||
paths << APPLE_GEM_HOME if defined? APPLE_GEM_HOME
|
||||
set_paths(paths.compact.join(File::PATH_SEPARATOR))
|
||||
end
|
||||
@gem_path
|
||||
end
|
||||
|
||||
# Set the Gem home directory (as reported by +dir+).
|
||||
def set_home(home)
|
||||
@gem_home = home
|
||||
ensure_gem_subdirectories(@gem_home)
|
||||
end
|
||||
|
||||
def set_paths(gpaths)
|
||||
if gpaths
|
||||
@gem_path = gpaths.split(File::PATH_SEPARATOR)
|
||||
@gem_path << Gem.dir
|
||||
else
|
||||
@gem_path = [Gem.dir]
|
||||
end
|
||||
@gem_path.uniq!
|
||||
@gem_path.each do |gp| ensure_gem_subdirectories(gp) end
|
||||
end
|
||||
|
||||
def ensure_gem_subdirectories(path)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
GemPaths = {}
|
||||
GemVersions = {}
|
||||
|
||||
def push_gem_version_on_load_path(gem_name, *version_requirements)
|
||||
if version_requirements.empty?
|
||||
unless GemPaths.has_key?(gem_name)
|
||||
raise LoadError.new("Could not find RubyGem #{gem_name} (>= 0)\n")
|
||||
module QuickLoader
|
||||
|
||||
class << self
|
||||
def load_full_rubygems_library
|
||||
class << Gem
|
||||
Gem.methods(false).each do |method_name|
|
||||
undef_method method_name
|
||||
end
|
||||
end
|
||||
|
||||
Kernel.send :undef_method, :gem
|
||||
|
||||
$".delete File.join(Gem::ConfigMap[:libdir], 'ruby',
|
||||
Gem::ConfigMap[:ruby_version], 'rubygems.rb')
|
||||
|
||||
require 'rubygems'
|
||||
end
|
||||
# highest version gems already active
|
||||
return false
|
||||
else
|
||||
if version_requirements.length > 1
|
||||
end
|
||||
|
||||
GemPaths = {}
|
||||
GemVersions = {}
|
||||
|
||||
def push_gem_version_on_load_path(gem_name, *version_requirements)
|
||||
if version_requirements.empty?
|
||||
unless GemPaths.has_key?(gem_name)
|
||||
raise LoadError.new("Could not find RubyGem #{gem_name} (>= 0)\n")
|
||||
end
|
||||
|
||||
# highest version gems already active
|
||||
return false
|
||||
else
|
||||
if version_requirements.length > 1
|
||||
QuickLoader.load_full_rubygems_library
|
||||
return gem(gem_name, *version_requirements)
|
||||
end
|
||||
|
||||
requirement, version = version_requirements[0].split
|
||||
requirement.strip!
|
||||
|
||||
if requirement == ">" || requirement == ">="
|
||||
if (GemVersions[gem_name] <=> Gem.calculate_integers_for_gem_version(version)) >= 0
|
||||
return false
|
||||
end
|
||||
elsif requirement == "~>"
|
||||
loaded_version = GemVersions[gem_name]
|
||||
required_version = Gem.calculate_integers_for_gem_version(version)
|
||||
if loaded_version && (loaded_version[0] == required_version[0])
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
QuickLoader.load_full_rubygems_library
|
||||
return gem(gem_name, *version_requirements)
|
||||
gem(gem_name, *version_requirements)
|
||||
end
|
||||
requirement, version = version_requirements[0].split
|
||||
requirement.strip!
|
||||
if requirement == ">" || requirement == ">="
|
||||
if (GemVersions[gem_name] <=> Gem.calculate_integers_for_gem_version(version)) >= 0
|
||||
return false
|
||||
end
|
||||
elsif requirement == "~>"
|
||||
loaded_version = GemVersions[gem_name]
|
||||
required_version = Gem.calculate_integers_for_gem_version(version)
|
||||
if loaded_version && (loaded_version[0] == required_version[0])
|
||||
return false
|
||||
end
|
||||
end
|
||||
QuickLoader.load_full_rubygems_library
|
||||
gem(gem_name, *version_requirements)
|
||||
end
|
||||
end
|
||||
|
||||
def calculate_integers_for_gem_version(gem_version)
|
||||
numbers = gem_version.split(".").collect {|n| n.to_i}
|
||||
numbers.pop while numbers.last == 0
|
||||
numbers << 0 if numbers.empty?
|
||||
numbers
|
||||
end
|
||||
|
||||
def push_all_highest_version_gems_on_load_path
|
||||
Gem.path.each do |path|
|
||||
gems_directory = File.join(path, "gems")
|
||||
if File.exist?(gems_directory)
|
||||
Dir.entries(gems_directory).each do |gem_directory_name|
|
||||
next if gem_directory_name == "." || gem_directory_name == ".."
|
||||
dash = gem_directory_name.rindex("-")
|
||||
next if dash.nil?
|
||||
gem_name = gem_directory_name[0...dash]
|
||||
current_version = GemVersions[gem_name]
|
||||
new_version = calculate_integers_for_gem_version(gem_directory_name[dash+1..-1])
|
||||
if current_version
|
||||
if (current_version <=> new_version) == -1
|
||||
def calculate_integers_for_gem_version(gem_version)
|
||||
numbers = gem_version.split(".").collect {|n| n.to_i}
|
||||
numbers.pop while numbers.last == 0
|
||||
numbers << 0 if numbers.empty?
|
||||
numbers
|
||||
end
|
||||
|
||||
def push_all_highest_version_gems_on_load_path
|
||||
Gem.path.each do |path|
|
||||
gems_directory = File.join(path, "gems")
|
||||
if File.exist?(gems_directory)
|
||||
Dir.entries(gems_directory).each do |gem_directory_name|
|
||||
next if gem_directory_name == "." || gem_directory_name == ".."
|
||||
dash = gem_directory_name.rindex("-")
|
||||
next if dash.nil?
|
||||
gem_name = gem_directory_name[0...dash]
|
||||
current_version = GemVersions[gem_name]
|
||||
new_version = calculate_integers_for_gem_version(gem_directory_name[dash+1..-1])
|
||||
if current_version
|
||||
if (current_version <=> new_version) == -1
|
||||
GemVersions[gem_name] = new_version
|
||||
GemPaths[gem_name] = File.join(gems_directory, gem_directory_name)
|
||||
end
|
||||
else
|
||||
GemVersions[gem_name] = new_version
|
||||
GemPaths[gem_name] = File.join(gems_directory, gem_directory_name)
|
||||
end
|
||||
else
|
||||
GemVersions[gem_name] = new_version
|
||||
GemPaths[gem_name] = File.join(gems_directory, gem_directory_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
require_paths = []
|
||||
GemPaths.values.each do |path|
|
||||
if File.exist?(File.join(path, ".require_paths"))
|
||||
require_paths.concat(File.read(File.join(path, ".require_paths")).split.map {|require_path| File.join(path, require_path)})
|
||||
else
|
||||
require_paths << File.join(path, "bin") if File.exist?(File.join(path, "bin"))
|
||||
require_paths << File.join(path, "lib") if File.exist?(File.join(path, "lib"))
|
||||
|
||||
require_paths = []
|
||||
|
||||
GemPaths.values.each do |path|
|
||||
if File.exist?(File.join(path, ".require_paths"))
|
||||
require_paths.concat(File.read(File.join(path, ".require_paths")).split.map {|require_path| File.join(path, require_path)})
|
||||
else
|
||||
require_paths << File.join(path, "bin") if File.exist?(File.join(path, "bin"))
|
||||
require_paths << File.join(path, "lib") if File.exist?(File.join(path, "lib"))
|
||||
end
|
||||
end
|
||||
|
||||
# "tag" the first require_path inserted into the $LOAD_PATH to enable
|
||||
# indexing correctly with rubygems proper when it inserts an explicitly
|
||||
# gem version
|
||||
unless require_paths.empty?
|
||||
require_paths.first.instance_variable_set(:@gem_prelude_index, true)
|
||||
end
|
||||
# gem directories must come after -I and ENV['RUBYLIB']
|
||||
$:[$:.index(ConfigMap[:sitelibdir]),0] = require_paths
|
||||
end
|
||||
|
||||
# "tag" the first require_path inserted into the $LOAD_PATH to enable
|
||||
# indexing correctly with rubygems proper when it inserts an explicitly
|
||||
# gem version
|
||||
unless require_paths.empty?
|
||||
require_paths.first.instance_variable_set(:@gem_prelude_index, true)
|
||||
|
||||
def const_missing(constant)
|
||||
QuickLoader.load_full_rubygems_library
|
||||
Gem.const_get(constant)
|
||||
end
|
||||
|
||||
def method_missing(method, *args, &block)
|
||||
QuickLoader.load_full_rubygems_library
|
||||
super unless Gem.respond_to?(method)
|
||||
Gem.send(method, *args, &block)
|
||||
end
|
||||
# gem directories must come after -I and ENV['RUBYLIB']
|
||||
$:[$:.index(ConfigMap[:sitelibdir]),0] = require_paths
|
||||
end
|
||||
|
||||
def const_missing(constant)
|
||||
QuickLoader.load_full_rubygems_library
|
||||
Gem.const_get(constant)
|
||||
end
|
||||
extend QuickLoader
|
||||
|
||||
def method_missing(method, *args, &block)
|
||||
QuickLoader.load_full_rubygems_library
|
||||
super unless Gem.respond_to?(method)
|
||||
Gem.send(method, *args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
extend QuickLoader
|
||||
|
||||
begin
|
||||
Gem.push_all_highest_version_gems_on_load_path
|
||||
$" << File.join(Gem::ConfigMap[:libdir], "ruby",
|
||||
Gem::ConfigMap[:ruby_version], "rubygems.rb")
|
||||
rescue Exception => e
|
||||
puts "Error loading gem paths on load path in gem_prelude"
|
||||
puts e
|
||||
puts e.backtrace.join("\n")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
begin
|
||||
Gem.push_all_highest_version_gems_on_load_path
|
||||
$" << File.join(Gem::ConfigMap[:libdir], "ruby",
|
||||
Gem::ConfigMap[:ruby_version], "rubygems.rb")
|
||||
rescue Exception => e
|
||||
puts "Error loading gem paths on load path in gem_prelude"
|
||||
puts e
|
||||
puts e.backtrace.join("\n")
|
||||
end
|
||||
|
||||
#puts "Gem load in #{Time.now - t} seconds"
|
||||
end # Gem::Enable
|
||||
|
|
1063
lib/rubygems.rb
1063
lib/rubygems.rb
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -65,13 +65,20 @@ EOM
|
|||
end
|
||||
|
||||
def write_package
|
||||
Package.open(@spec.file_name, "w", @signer) do |pkg|
|
||||
pkg.metadata = @spec.to_yaml
|
||||
@spec.files.each do |file|
|
||||
next if File.directory? file
|
||||
pkg.add_file_simple(file, File.stat(@spec.file_name).mode & 0777,
|
||||
File.size(file)) do |os|
|
||||
os.write File.open(file, "rb"){|f|f.read}
|
||||
open @spec.file_name, 'wb' do |gem_io|
|
||||
Gem::Package.open gem_io, 'w', @signer do |pkg|
|
||||
pkg.metadata = @spec.to_yaml
|
||||
|
||||
@spec.files.each do |file|
|
||||
next if File.directory? file
|
||||
|
||||
stat = File.stat file
|
||||
mode = stat.mode & 0777
|
||||
size = stat.size
|
||||
|
||||
pkg.add_file_simple file, mode, size do |tar_io|
|
||||
tar_io.write open(file, "rb") { |f| f.read }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -123,6 +123,7 @@ module Gem
|
|||
end
|
||||
|
||||
private
|
||||
|
||||
def load_and_instantiate(command_name)
|
||||
command_name = command_name.to_s
|
||||
retried = false
|
||||
|
|
|
@ -2,92 +2,90 @@ require 'rubygems/command'
|
|||
require 'rubygems/source_index'
|
||||
require 'rubygems/dependency_list'
|
||||
|
||||
module Gem
|
||||
module Commands
|
||||
class CleanupCommand < Command
|
||||
def initialize
|
||||
super(
|
||||
'cleanup',
|
||||
class Gem::Commands::CleanupCommand < Gem::Command
|
||||
|
||||
def initialize
|
||||
super 'cleanup',
|
||||
'Clean up old versions of installed gems in the local repository',
|
||||
{
|
||||
:force => false,
|
||||
:test => false,
|
||||
:install_dir => Gem.dir
|
||||
})
|
||||
add_option('-d', '--dryrun', "") do |value, options|
|
||||
options[:dryrun] = true
|
||||
end
|
||||
end
|
||||
:force => false, :test => false, :install_dir => Gem.dir
|
||||
|
||||
def arguments # :nodoc:
|
||||
"GEMNAME name of gem to cleanup"
|
||||
end
|
||||
add_option('-d', '--dryrun', "") do |value, options|
|
||||
options[:dryrun] = true
|
||||
end
|
||||
end
|
||||
|
||||
def defaults_str # :nodoc:
|
||||
"--no-dryrun"
|
||||
end
|
||||
def arguments # :nodoc:
|
||||
"GEMNAME name of gem to cleanup"
|
||||
end
|
||||
|
||||
def usage # :nodoc:
|
||||
"#{program_name} [GEMNAME ...]"
|
||||
end
|
||||
def defaults_str # :nodoc:
|
||||
"--no-dryrun"
|
||||
end
|
||||
|
||||
def execute
|
||||
say "Cleaning up installed gems..."
|
||||
srcindex = Gem::SourceIndex.from_installed_gems
|
||||
primary_gems = {}
|
||||
def usage # :nodoc:
|
||||
"#{program_name} [GEMNAME ...]"
|
||||
end
|
||||
|
||||
srcindex.each do |name, spec|
|
||||
if primary_gems[spec.name].nil? or primary_gems[spec.name].version < spec.version
|
||||
primary_gems[spec.name] = spec
|
||||
end
|
||||
end
|
||||
def execute
|
||||
say "Cleaning up installed gems..."
|
||||
primary_gems = {}
|
||||
|
||||
gems_to_cleanup = []
|
||||
|
||||
unless options[:args].empty? then
|
||||
options[:args].each do |gem_name|
|
||||
specs = Gem.cache.search(/^#{gem_name}$/i)
|
||||
specs.each do |spec|
|
||||
gems_to_cleanup << spec
|
||||
end
|
||||
end
|
||||
else
|
||||
srcindex.each do |name, spec|
|
||||
gems_to_cleanup << spec
|
||||
end
|
||||
end
|
||||
|
||||
gems_to_cleanup = gems_to_cleanup.select { |spec|
|
||||
primary_gems[spec.name].version != spec.version
|
||||
}
|
||||
|
||||
uninstall_command = Gem::CommandManager.instance['uninstall']
|
||||
deplist = DependencyList.new
|
||||
gems_to_cleanup.uniq.each do |spec| deplist.add(spec) end
|
||||
|
||||
deplist.dependency_order.each do |spec|
|
||||
if options[:dryrun] then
|
||||
say "Dry Run Mode: Would uninstall #{spec.full_name}"
|
||||
else
|
||||
say "Attempting uninstall on #{spec.full_name}"
|
||||
|
||||
options[:args] = [spec.name]
|
||||
options[:version] = "= #{spec.version}"
|
||||
options[:executables] = true
|
||||
|
||||
uninstall_command.merge_options(options)
|
||||
|
||||
begin
|
||||
uninstall_command.execute
|
||||
rescue Gem::DependencyRemovalException => ex
|
||||
say "Unable to uninstall #{spec.full_name} ... continuing with remaining gems"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
say "Clean Up Complete"
|
||||
Gem.source_index.each do |name, spec|
|
||||
if primary_gems[spec.name].nil? or
|
||||
primary_gems[spec.name].version < spec.version then
|
||||
primary_gems[spec.name] = spec
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
gems_to_cleanup = []
|
||||
|
||||
unless options[:args].empty? then
|
||||
options[:args].each do |gem_name|
|
||||
specs = Gem.cache.search(/^#{gem_name}$/i)
|
||||
specs.each do |spec|
|
||||
gems_to_cleanup << spec
|
||||
end
|
||||
end
|
||||
else
|
||||
Gem.source_index.each do |name, spec|
|
||||
gems_to_cleanup << spec
|
||||
end
|
||||
end
|
||||
|
||||
gems_to_cleanup = gems_to_cleanup.select { |spec|
|
||||
primary_gems[spec.name].version != spec.version
|
||||
}
|
||||
|
||||
uninstall_command = Gem::CommandManager.instance['uninstall']
|
||||
deplist = Gem::DependencyList.new
|
||||
gems_to_cleanup.uniq.each do |spec| deplist.add spec end
|
||||
|
||||
deps = deplist.strongly_connected_components.flatten.reverse
|
||||
|
||||
deps.each do |spec|
|
||||
if options[:dryrun] then
|
||||
say "Dry Run Mode: Would uninstall #{spec.full_name}"
|
||||
else
|
||||
say "Attempting to uninstall #{spec.full_name}"
|
||||
|
||||
options[:args] = [spec.name]
|
||||
options[:version] = "= #{spec.version}"
|
||||
options[:executables] = false
|
||||
|
||||
uninstaller = Gem::Uninstaller.new spec.name, options
|
||||
|
||||
begin
|
||||
uninstaller.uninstall
|
||||
rescue Gem::DependencyRemovalException,
|
||||
Gem::GemNotInHomeException => e
|
||||
say "Unable to uninstall #{spec.full_name}:"
|
||||
say "\t#{e.class}: #{e.message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
say "Clean Up Complete"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -25,19 +25,18 @@ class Gem::Commands::EnvironmentCommand < Gem::Command
|
|||
def execute
|
||||
out = ''
|
||||
arg = options[:args][0]
|
||||
if begins?("packageversion", arg) then
|
||||
case arg
|
||||
when /^packageversion/ then
|
||||
out << Gem::RubyGemsPackageVersion
|
||||
elsif begins?("version", arg) then
|
||||
when /^version/ then
|
||||
out << Gem::RubyGemsVersion
|
||||
elsif begins?("gemdir", arg) then
|
||||
when /^gemdir/, /^gemhome/, /^home/, /^GEM_HOME/ then
|
||||
out << Gem.dir
|
||||
elsif begins?("gempath", arg) then
|
||||
out << Gem.path.join("\n")
|
||||
elsif begins?("remotesources", arg) then
|
||||
when /^gempath/, /^path/, /^GEM_PATH/ then
|
||||
out << Gem.path.join(File::PATH_SEPARATOR)
|
||||
when /^remotesources/ then
|
||||
out << Gem.sources.join("\n")
|
||||
elsif arg then
|
||||
fail Gem::CommandLineError, "Unknown enviroment option [#{arg}]"
|
||||
else
|
||||
when nil then
|
||||
out = "RubyGems Environment:\n"
|
||||
|
||||
out << " - RUBYGEMS VERSION: #{Gem::RubyGemsVersion} (#{Gem::RubyGemsPackageVersion})\n"
|
||||
|
@ -75,6 +74,9 @@ class Gem::Commands::EnvironmentCommand < Gem::Command
|
|||
Gem.sources.each do |s|
|
||||
out << " - #{s}\n"
|
||||
end
|
||||
|
||||
else
|
||||
fail Gem::CommandLineError, "Unknown enviroment option [#{arg}]"
|
||||
end
|
||||
say out
|
||||
true
|
||||
|
|
|
@ -44,17 +44,15 @@ class Gem::Commands::FetchCommand < Gem::Command
|
|||
|
||||
spec, source_uri = specs_and_sources.last
|
||||
|
||||
gem_file = "#{spec.full_name}.gem"
|
||||
|
||||
gem_path = File.join source_uri, 'gems', gem_file
|
||||
|
||||
gem = Gem::RemoteFetcher.fetcher.fetch_path gem_path
|
||||
|
||||
File.open gem_file, 'wb' do |fp|
|
||||
fp.write gem
|
||||
if spec.nil? then
|
||||
alert_error "Could not find #{gem_name} in any repository"
|
||||
next
|
||||
end
|
||||
|
||||
say "Downloaded #{gem_file}"
|
||||
path = Gem::RemoteFetcher.fetcher.download spec, source_uri
|
||||
FileUtils.mv path, "#{spec.full_name}.gem"
|
||||
|
||||
say "Downloaded #{spec.full_name}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -62,13 +62,15 @@ class Gem::Commands::InstallCommand < Gem::Command
|
|||
:install_dir => options[:install_dir],
|
||||
:security_policy => options[:security_policy],
|
||||
:wrappers => options[:wrappers],
|
||||
:bin_dir => options[:bin_dir]
|
||||
}
|
||||
|
||||
exit_code = 0
|
||||
|
||||
get_all_gem_names.each do |gem_name|
|
||||
begin
|
||||
inst = Gem::DependencyInstaller.new gem_name, options[:version],
|
||||
install_options
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new install_options
|
||||
inst.install gem_name, options[:version]
|
||||
|
||||
inst.installed_gems.each do |spec|
|
||||
say "Successfully installed #{spec.full_name}"
|
||||
|
@ -77,8 +79,10 @@ class Gem::Commands::InstallCommand < Gem::Command
|
|||
installed_gems.push(*inst.installed_gems)
|
||||
rescue Gem::InstallError => e
|
||||
alert_error "Error installing #{gem_name}:\n\t#{e.message}"
|
||||
exit_code |= 1
|
||||
rescue Gem::GemNotFoundException => e
|
||||
alert_error e.message
|
||||
exit_code |= 2
|
||||
# rescue => e
|
||||
# # TODO: Fix this handle to allow the error to propagate to
|
||||
# # the top level handler. Examine the other errors as
|
||||
|
@ -121,6 +125,8 @@ class Gem::Commands::InstallCommand < Gem::Command
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
raise Gem::SystemExitException, exit_code
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -6,10 +6,8 @@ module Gem
|
|||
class ListCommand < QueryCommand
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'list',
|
||||
'Display all gems whose name starts with STRING'
|
||||
)
|
||||
super 'list', 'Display gems whose name starts with STRING'
|
||||
|
||||
remove_option('--name-matches')
|
||||
end
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ require 'yaml'
|
|||
require 'zlib'
|
||||
|
||||
require 'rubygems/command'
|
||||
require 'rubygems/gem_open_uri'
|
||||
require 'open-uri'
|
||||
|
||||
class Gem::Commands::MirrorCommand < Gem::Command
|
||||
|
||||
|
|
|
@ -1,15 +1,25 @@
|
|||
require 'rubygems/command'
|
||||
require 'rubygems/local_remote_options'
|
||||
require 'rubygems/source_info_cache'
|
||||
require 'rubygems/version_option'
|
||||
|
||||
class Gem::Commands::QueryCommand < Gem::Command
|
||||
|
||||
include Gem::LocalRemoteOptions
|
||||
include Gem::VersionOption
|
||||
|
||||
def initialize(name = 'query',
|
||||
summary = 'Query gem information in local or remote repositories')
|
||||
super name, summary,
|
||||
:name => /.*/, :domain => :local, :details => false, :versions => true
|
||||
:name => //, :domain => :local, :details => false, :versions => true,
|
||||
:installed => false, :version => Gem::Requirement.default
|
||||
|
||||
add_option('-i', '--[no-]installed',
|
||||
'Check for installed gem') do |value, options|
|
||||
options[:installed] = value
|
||||
end
|
||||
|
||||
add_version_option
|
||||
|
||||
add_option('-n', '--name-matches REGEXP',
|
||||
'Name of gem(s) to query on matches the',
|
||||
|
@ -28,33 +38,70 @@ class Gem::Commands::QueryCommand < Gem::Command
|
|||
options[:details] = false unless value
|
||||
end
|
||||
|
||||
add_option('-a', '--all',
|
||||
'Display all gem versions') do |value, options|
|
||||
options[:all] = value
|
||||
end
|
||||
|
||||
add_local_remote_options
|
||||
end
|
||||
|
||||
def defaults_str # :nodoc:
|
||||
"--local --name-matches '.*' --no-details --versions"
|
||||
"--local --name-matches // --no-details --versions --no-installed"
|
||||
end
|
||||
|
||||
def execute
|
||||
exit_code = 0
|
||||
|
||||
name = options[:name]
|
||||
|
||||
if options[:installed] then
|
||||
if name.source.empty? then
|
||||
alert_error "You must specify a gem name"
|
||||
exit_code |= 4
|
||||
elsif installed? name.source, options[:version] then
|
||||
say "true"
|
||||
else
|
||||
say "false"
|
||||
exit_code |= 1
|
||||
end
|
||||
|
||||
raise Gem::SystemExitException, exit_code
|
||||
end
|
||||
|
||||
if local? then
|
||||
say
|
||||
say "*** LOCAL GEMS ***"
|
||||
say
|
||||
output_query_results Gem.cache.search(name)
|
||||
|
||||
output_query_results Gem.source_index.search(name)
|
||||
end
|
||||
|
||||
if remote? then
|
||||
say
|
||||
say "*** REMOTE GEMS ***"
|
||||
say
|
||||
output_query_results Gem::SourceInfoCache.search(name)
|
||||
|
||||
begin
|
||||
Gem::SourceInfoCache.cache.refresh options[:all]
|
||||
rescue Gem::RemoteFetcher::FetchError
|
||||
# no network
|
||||
end
|
||||
|
||||
output_query_results Gem::SourceInfoCache.search(name, false, true)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
##
|
||||
# Check if gem +name+ version +version+ is installed.
|
||||
|
||||
def installed?(name, version = Gem::Requirement.default)
|
||||
dep = Gem::Dependency.new name, version
|
||||
!Gem.source_index.search(dep).empty?
|
||||
end
|
||||
|
||||
def output_query_results(gemspecs)
|
||||
output = []
|
||||
gem_list_with_version = {}
|
||||
|
@ -98,7 +145,7 @@ class Gem::Commands::QueryCommand < Gem::Command
|
|||
|
||||
##
|
||||
# Used for wrapping and indenting text
|
||||
#
|
||||
|
||||
def format_text(text, wrap, indent=0)
|
||||
result = []
|
||||
work = text.dup
|
||||
|
|
|
@ -39,8 +39,11 @@ class Gem::Commands::SourcesCommand < Gem::Command
|
|||
options[:list] = !(options[:add] || options[:remove] || options[:clear_all] || options[:update])
|
||||
|
||||
if options[:clear_all] then
|
||||
remove_cache_file("user", Gem::SourceInfoCache.user_cache_file)
|
||||
remove_cache_file("system", Gem::SourceInfoCache.system_cache_file)
|
||||
sic = Gem::SourceInfoCache
|
||||
remove_cache_file 'user', sic.user_cache_file
|
||||
remove_cache_file 'latest user', sic.latest_user_cache_file
|
||||
remove_cache_file 'system', sic.system_cache_file
|
||||
remove_cache_file 'latest system', sic.latest_system_cache_file
|
||||
end
|
||||
|
||||
if options[:add] then
|
||||
|
@ -48,7 +51,7 @@ class Gem::Commands::SourcesCommand < Gem::Command
|
|||
|
||||
sice = Gem::SourceInfoCacheEntry.new nil, nil
|
||||
begin
|
||||
sice.refresh source_uri
|
||||
sice.refresh source_uri, true
|
||||
|
||||
Gem::SourceInfoCache.cache_data[source_uri] = sice
|
||||
Gem::SourceInfoCache.cache.update
|
||||
|
@ -66,7 +69,7 @@ class Gem::Commands::SourcesCommand < Gem::Command
|
|||
end
|
||||
|
||||
if options[:update] then
|
||||
Gem::SourceInfoCache.cache.refresh
|
||||
Gem::SourceInfoCache.cache.refresh true
|
||||
Gem::SourceInfoCache.cache.flush
|
||||
|
||||
say "source cache successfully updated"
|
||||
|
@ -78,6 +81,11 @@ class Gem::Commands::SourcesCommand < Gem::Command
|
|||
unless Gem.sources.include? source_uri then
|
||||
say "source #{source_uri} not present in cache"
|
||||
else
|
||||
begin # HACK figure out how to get the cache w/o update
|
||||
Gem::SourceInfoCache.cache
|
||||
rescue Gem::RemoteFetcher::FetchError
|
||||
end
|
||||
|
||||
Gem::SourceInfoCache.cache_data.delete source_uri
|
||||
Gem::SourceInfoCache.cache.update
|
||||
Gem::SourceInfoCache.cache.flush
|
||||
|
@ -100,11 +108,12 @@ class Gem::Commands::SourcesCommand < Gem::Command
|
|||
|
||||
private
|
||||
|
||||
def remove_cache_file(desc, fn)
|
||||
FileUtils.rm_rf fn rescue nil
|
||||
if ! File.exist?(fn)
|
||||
def remove_cache_file(desc, path)
|
||||
FileUtils.rm_rf path
|
||||
|
||||
if not File.exist?(path) then
|
||||
say "*** Removed #{desc} source cache ***"
|
||||
elsif ! File.writable?(fn)
|
||||
elsif not File.writable?(path) then
|
||||
say "*** Unable to remove #{desc} source cache (write protected) ***"
|
||||
else
|
||||
say "*** Unable to remove #{desc} source cache ***"
|
||||
|
|
|
@ -3,6 +3,7 @@ require 'rubygems/command'
|
|||
require 'rubygems/local_remote_options'
|
||||
require 'rubygems/version_option'
|
||||
require 'rubygems/source_info_cache'
|
||||
require 'rubygems/format'
|
||||
|
||||
class Gem::Commands::SpecificationCommand < Gem::Command
|
||||
|
||||
|
@ -41,13 +42,16 @@ class Gem::Commands::SpecificationCommand < Gem::Command
|
|||
gem = get_one_gem_name
|
||||
|
||||
if local? then
|
||||
source_index = Gem::SourceIndex.from_installed_gems
|
||||
specs.push(*source_index.search(/\A#{gem}\z/, options[:version]))
|
||||
if File.exist? gem then
|
||||
specs << Gem::Format.from_file_by_path(gem).spec rescue nil
|
||||
end
|
||||
|
||||
if specs.empty? then
|
||||
specs.push(*Gem.source_index.search(/\A#{gem}\z/, options[:version]))
|
||||
end
|
||||
end
|
||||
|
||||
if remote? then
|
||||
alert_warning "Remote information is not complete\n\n"
|
||||
|
||||
Gem::SourceInfoCache.cache_data.each do |_,sice|
|
||||
specs.push(*sice.source_index.search(gem, options[:version]))
|
||||
end
|
||||
|
|
|
@ -35,6 +35,11 @@ module Gem
|
|||
options[:install_dir] = File.expand_path(value)
|
||||
end
|
||||
|
||||
add_option('-n', '--bindir DIR',
|
||||
'Directory to remove binaries from') do |value, options|
|
||||
options[:bin_dir] = File.expand_path(value)
|
||||
end
|
||||
|
||||
add_version_option
|
||||
add_platform_option
|
||||
end
|
||||
|
@ -54,7 +59,13 @@ module Gem
|
|||
|
||||
def execute
|
||||
get_all_gem_names.each do |gem_name|
|
||||
Gem::Uninstaller.new(gem_name, options).uninstall
|
||||
begin
|
||||
Gem::Uninstaller.new(gem_name, options).uninstall
|
||||
rescue Gem::GemNotInHomeException => e
|
||||
spec = e.spec
|
||||
alert("In order to remove #{spec.name}, please execute:\n" \
|
||||
"\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,6 +38,7 @@ class Gem::Commands::UnpackCommand < Gem::Command
|
|||
def execute
|
||||
gemname = get_one_gem_name
|
||||
path = get_path(gemname, options[:version])
|
||||
|
||||
if path then
|
||||
basename = File.basename(path).sub(/\.gem$/, '')
|
||||
target_dir = File.expand_path File.join(options[:target], basename)
|
||||
|
@ -66,16 +67,27 @@ class Gem::Commands::UnpackCommand < Gem::Command
|
|||
# source directories?
|
||||
def get_path(gemname, version_req)
|
||||
return gemname if gemname =~ /\.gem$/i
|
||||
specs = Gem::SourceIndex.from_installed_gems.search(/\A#{gemname}\z/, version_req)
|
||||
|
||||
specs = Gem::source_index.search(/\A#{gemname}\z/, version_req)
|
||||
|
||||
selected = specs.sort_by { |s| s.version }.last
|
||||
|
||||
return nil if selected.nil?
|
||||
|
||||
# We expect to find (basename).gem in the 'cache' directory.
|
||||
# Furthermore, the name match must be exact (ignoring case).
|
||||
if gemname =~ /^#{selected.name}$/i
|
||||
filename = selected.full_name + '.gem'
|
||||
return File.join(Gem.dir, 'cache', filename)
|
||||
path = nil
|
||||
|
||||
Gem.path.find do |gem_dir|
|
||||
path = File.join gem_dir, 'cache', filename
|
||||
File.exist? path
|
||||
end
|
||||
|
||||
path
|
||||
else
|
||||
return nil
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
require 'rubygems/command'
|
||||
require 'rubygems/command_manager'
|
||||
require 'rubygems/install_update_options'
|
||||
require 'rubygems/local_remote_options'
|
||||
require 'rubygems/source_info_cache'
|
||||
require 'rubygems/version_option'
|
||||
require 'rubygems/commands/install_command'
|
||||
|
||||
class Gem::Commands::UpdateCommand < Gem::Command
|
||||
|
||||
|
@ -45,7 +47,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
|
|||
|
||||
def execute
|
||||
if options[:system] then
|
||||
say "Updating RubyGems..."
|
||||
say "Updating RubyGems"
|
||||
|
||||
unless options[:args].empty? then
|
||||
fail "No gem names are allowed with the --system option"
|
||||
|
@ -53,10 +55,10 @@ class Gem::Commands::UpdateCommand < Gem::Command
|
|||
|
||||
options[:args] = ["rubygems-update"]
|
||||
else
|
||||
say "Updating installed gems..."
|
||||
say "Updating installed gems"
|
||||
end
|
||||
|
||||
hig = highest_installed_gems = {}
|
||||
hig = {}
|
||||
|
||||
Gem::SourceIndex.from_installed_gems.each do |name, spec|
|
||||
if hig[spec.name].nil? or hig[spec.name].version < spec.version then
|
||||
|
@ -64,25 +66,28 @@ class Gem::Commands::UpdateCommand < Gem::Command
|
|||
end
|
||||
end
|
||||
|
||||
remote_gemspecs = Gem::SourceInfoCache.search(//)
|
||||
pattern = if options[:args].empty? then
|
||||
//
|
||||
else
|
||||
Regexp.union(*options[:args])
|
||||
end
|
||||
|
||||
gems_to_update = if options[:args].empty? then
|
||||
which_to_update(highest_installed_gems, remote_gemspecs)
|
||||
else
|
||||
options[:args]
|
||||
end
|
||||
remote_gemspecs = Gem::SourceInfoCache.search pattern
|
||||
|
||||
options[:domain] = :remote # install from remote source
|
||||
gems_to_update = which_to_update hig, remote_gemspecs
|
||||
|
||||
updated = []
|
||||
|
||||
# HACK use the real API
|
||||
install_command = Gem::CommandManager.instance['install']
|
||||
|
||||
gems_to_update.uniq.sort.each do |name|
|
||||
say "Attempting remote update of #{name}"
|
||||
options[:args] = [name]
|
||||
options[:ignore_dependencies] = true # HACK skip seen gems instead
|
||||
install_command.merge_options(options)
|
||||
install_command.execute
|
||||
next if updated.any? { |spec| spec.name == name }
|
||||
say "Updating #{name}"
|
||||
installer = Gem::DependencyInstaller.new options
|
||||
installer.install name
|
||||
installer.installed_gems.each do |spec|
|
||||
updated << spec
|
||||
say "Successfully installed #{spec.full_name}"
|
||||
end
|
||||
end
|
||||
|
||||
if gems_to_update.include? "rubygems-update" then
|
||||
|
@ -97,12 +102,10 @@ class Gem::Commands::UpdateCommand < Gem::Command
|
|||
|
||||
say "RubyGems system software updated" if installed
|
||||
else
|
||||
updated = gems_to_update.uniq.sort.collect { |g| g.to_s }
|
||||
|
||||
if updated.empty? then
|
||||
say "Nothing to update"
|
||||
else
|
||||
say "Gems updated: #{updated.join ', '}"
|
||||
say "Gems updated: #{updated.map { |spec| spec.name }.join ', '}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,7 +28,7 @@ module Kernel
|
|||
rescue LoadError => load_error
|
||||
if load_error.message =~ /\A[Nn]o such file to load -- #{Regexp.escape path}\z/ and
|
||||
spec = Gem.searcher.find(path) then
|
||||
Gem.activate(spec.name, false, "= #{spec.version}")
|
||||
Gem.activate(spec.name, "= #{spec.version}")
|
||||
gem_original_require path
|
||||
else
|
||||
raise load_error
|
||||
|
|
|
@ -11,6 +11,9 @@ module Gem
|
|||
if defined? RUBY_FRAMEWORK_VERSION then
|
||||
File.join File.dirname(ConfigMap[:sitedir]), 'Gems',
|
||||
ConfigMap[:ruby_version]
|
||||
elsif defined? RUBY_ENGINE then
|
||||
File.join ConfigMap[:libdir], RUBY_ENGINE, 'gems',
|
||||
ConfigMap[:ruby_version]
|
||||
else
|
||||
File.join ConfigMap[:libdir], 'ruby', 'gems', ConfigMap[:ruby_version]
|
||||
end
|
||||
|
@ -29,7 +32,11 @@ module Gem
|
|||
|
||||
# The default directory for binaries
|
||||
def self.default_bindir
|
||||
Config::CONFIG['bindir']
|
||||
if defined? RUBY_FRAMEWORK_VERSION then # mac framework support
|
||||
'/usr/bin'
|
||||
else # generic install
|
||||
ConfigMap[:bindir]
|
||||
end
|
||||
end
|
||||
|
||||
# The default system-wide source info cache directory.
|
||||
|
|
|
@ -22,8 +22,7 @@ class Gem::DependencyInstaller
|
|||
}
|
||||
|
||||
##
|
||||
# Creates a new installer instance that will install +gem_name+ using
|
||||
# version requirement +version+ and +options+.
|
||||
# Creates a new installer instance.
|
||||
#
|
||||
# Options are:
|
||||
# :env_shebang:: See Gem::Installer::new.
|
||||
|
@ -36,7 +35,7 @@ class Gem::DependencyInstaller
|
|||
# :install_dir: See Gem::Installer#install.
|
||||
# :security_policy: See Gem::Installer::new and Gem::Security.
|
||||
# :wrappers: See Gem::Installer::new
|
||||
def initialize(gem_name, version = nil, options = {})
|
||||
def initialize(options = {})
|
||||
options = DEFAULT_OPTIONS.merge options
|
||||
@env_shebang = options[:env_shebang]
|
||||
@domain = options[:domain]
|
||||
|
@ -46,49 +45,9 @@ class Gem::DependencyInstaller
|
|||
@install_dir = options[:install_dir] || Gem.dir
|
||||
@security_policy = options[:security_policy]
|
||||
@wrappers = options[:wrappers]
|
||||
@bin_dir = options[:bin_dir]
|
||||
|
||||
@installed_gems = []
|
||||
|
||||
spec_and_source = nil
|
||||
|
||||
glob = if File::ALT_SEPARATOR then
|
||||
gem_name.gsub File::ALT_SEPARATOR, File::SEPARATOR
|
||||
else
|
||||
gem_name
|
||||
end
|
||||
|
||||
local_gems = Dir["#{glob}*"].sort.reverse
|
||||
|
||||
unless local_gems.empty? then
|
||||
local_gems.each do |gem_file|
|
||||
next unless gem_file =~ /gem$/
|
||||
begin
|
||||
spec = Gem::Format.from_file_by_path(gem_file).spec
|
||||
spec_and_source = [spec, gem_file]
|
||||
break
|
||||
rescue SystemCallError, Gem::Package::FormatError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if spec_and_source.nil? then
|
||||
version ||= Gem::Requirement.default
|
||||
@dep = Gem::Dependency.new gem_name, version
|
||||
spec_and_sources = find_gems_with_sources(@dep).reverse
|
||||
|
||||
spec_and_source = spec_and_sources.find do |spec, source|
|
||||
Gem::Platform.match spec.platform
|
||||
end
|
||||
end
|
||||
|
||||
if spec_and_source.nil? then
|
||||
raise Gem::GemNotFoundException,
|
||||
"could not find #{gem_name} locally or in a repository"
|
||||
end
|
||||
|
||||
@specs_and_sources = [spec_and_source]
|
||||
|
||||
gather_dependencies
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -107,73 +66,32 @@ class Gem::DependencyInstaller
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
##
|
||||
# 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
|
||||
# remote sources unless the ignore_dependencies was given.
|
||||
|
@ -208,9 +126,57 @@ class Gem::DependencyInstaller
|
|||
@gems_to_install = dependency_list.dependency_order.reverse
|
||||
end
|
||||
|
||||
def find_spec_by_name_and_version gem_name, version = Gem::Requirement.default
|
||||
spec_and_source = nil
|
||||
|
||||
glob = if File::ALT_SEPARATOR then
|
||||
gem_name.gsub File::ALT_SEPARATOR, File::SEPARATOR
|
||||
else
|
||||
gem_name
|
||||
end
|
||||
|
||||
local_gems = Dir["#{glob}*"].sort.reverse
|
||||
|
||||
unless local_gems.empty? then
|
||||
local_gems.each do |gem_file|
|
||||
next unless gem_file =~ /gem$/
|
||||
begin
|
||||
spec = Gem::Format.from_file_by_path(gem_file).spec
|
||||
spec_and_source = [spec, gem_file]
|
||||
break
|
||||
rescue SystemCallError, Gem::Package::FormatError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if spec_and_source.nil? then
|
||||
dep = Gem::Dependency.new gem_name, version
|
||||
spec_and_sources = find_gems_with_sources(dep).reverse
|
||||
|
||||
spec_and_source = spec_and_sources.find { |spec, source|
|
||||
Gem::Platform.match spec.platform
|
||||
}
|
||||
end
|
||||
|
||||
if spec_and_source.nil? then
|
||||
raise Gem::GemNotFoundException,
|
||||
"could not find #{gem_name} locally or in a repository"
|
||||
end
|
||||
|
||||
@specs_and_sources = [spec_and_source]
|
||||
end
|
||||
|
||||
##
|
||||
# Installs the gem and all its dependencies.
|
||||
def install
|
||||
def install dep_or_name, version = Gem::Requirement.default
|
||||
if String === dep_or_name then
|
||||
find_spec_by_name_and_version dep_or_name, version
|
||||
else
|
||||
@specs_and_sources = [find_gems_with_sources(dep_or_name).last]
|
||||
end
|
||||
|
||||
gather_dependencies
|
||||
|
||||
spec_dir = File.join @install_dir, 'specifications'
|
||||
source_index = Gem::SourceIndex.from_gems_in spec_dir
|
||||
|
||||
|
@ -219,10 +185,11 @@ class Gem::DependencyInstaller
|
|||
# HACK is this test for full_name acceptable?
|
||||
next if source_index.any? { |n,_| n == spec.full_name } and not last
|
||||
|
||||
# TODO: make this sorta_verbose so other users can benefit from it
|
||||
say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose
|
||||
|
||||
_, source_uri = @specs_and_sources.assoc spec
|
||||
local_gem_path = download spec, source_uri
|
||||
local_gem_path = Gem::RemoteFetcher.fetcher.download spec, source_uri
|
||||
|
||||
inst = Gem::Installer.new local_gem_path,
|
||||
:env_shebang => @env_shebang,
|
||||
|
@ -231,7 +198,8 @@ class Gem::DependencyInstaller
|
|||
:ignore_dependencies => @ignore_dependencies,
|
||||
:install_dir => @install_dir,
|
||||
:security_policy => @security_policy,
|
||||
:wrappers => @wrappers
|
||||
:wrappers => @wrappers,
|
||||
:bin_dir => @bin_dir
|
||||
|
||||
spec = inst.install
|
||||
|
||||
|
|
|
@ -13,7 +13,10 @@ class Gem::DependencyRemovalException < Gem::Exception; end
|
|||
|
||||
##
|
||||
# Raised when attempting to uninstall a gem that isn't in GEM_HOME.
|
||||
class Gem::GemNotInHomeException < Gem::Exception; end
|
||||
|
||||
class Gem::GemNotInHomeException < Gem::Exception
|
||||
attr_accessor :spec
|
||||
end
|
||||
|
||||
class Gem::DocumentError < Gem::Exception; end
|
||||
|
||||
|
@ -65,3 +68,17 @@ class Gem::RemoteSourceException < Gem::Exception; end
|
|||
|
||||
class Gem::VerificationError < Gem::Exception; end
|
||||
|
||||
##
|
||||
# Raised to indicate that a system exit should occur with the specified
|
||||
# exit_code
|
||||
|
||||
class Gem::SystemExitException < SystemExit
|
||||
attr_accessor :exit_code
|
||||
|
||||
def initialize(exit_code)
|
||||
@exit_code = exit_code
|
||||
|
||||
super "Exiting RubyGems with exit_code #{exit_code}"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -43,15 +43,12 @@ module Gem
|
|||
|
||||
# check for old version gem
|
||||
if File.read(file_path, 20).include?("MD5SUM =")
|
||||
#alert_warning "Gem #{file_path} is in old format."
|
||||
require 'rubygems/old_format'
|
||||
|
||||
format = OldFormat.from_file_by_path(file_path)
|
||||
else
|
||||
begin
|
||||
f = File.open(file_path, 'rb')
|
||||
format = from_io(f, file_path, security_policy)
|
||||
ensure
|
||||
f.close unless f.closed?
|
||||
open file_path, Gem.binary_mode do |io|
|
||||
format = from_io io, file_path, security_policy
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -65,15 +62,24 @@ module Gem
|
|||
# io:: [IO] Stream from which to read the gem
|
||||
#
|
||||
def self.from_io(io, gem_path="(io)", security_policy = nil)
|
||||
format = self.new(gem_path)
|
||||
Package.open_from_io(io, 'r', security_policy) do |pkg|
|
||||
format = new gem_path
|
||||
|
||||
Package.open io, 'r', security_policy do |pkg|
|
||||
format.spec = pkg.metadata
|
||||
format.file_entries = []
|
||||
|
||||
pkg.each do |entry|
|
||||
format.file_entries << [{"size" => entry.size, "mode" => entry.mode,
|
||||
"path" => entry.full_name}, entry.read]
|
||||
size = entry.header.size
|
||||
mode = entry.header.mode
|
||||
|
||||
format.file_entries << [{
|
||||
"size" => size, "mode" => mode, "path" => entry.full_name,
|
||||
},
|
||||
entry.read
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
format
|
||||
end
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ end
|
|||
|
||||
##
|
||||
# Top level class for building the gem repository index.
|
||||
|
||||
class Gem::Indexer
|
||||
|
||||
include Gem::UserInteraction
|
||||
|
@ -25,7 +26,9 @@ class Gem::Indexer
|
|||
|
||||
attr_reader :directory
|
||||
|
||||
##
|
||||
# Create an indexer that will index the gems in +directory+.
|
||||
|
||||
def initialize(directory)
|
||||
unless ''.respond_to? :to_xs then
|
||||
fail "Gem::Indexer requires that the XML Builder library be installed:" \
|
||||
|
@ -39,52 +42,60 @@ class Gem::Indexer
|
|||
|
||||
@master_index = Gem::Indexer::MasterIndexBuilder.new "yaml", @directory
|
||||
@marshal_index = Gem::Indexer::MarshalIndexBuilder.new marshal_name, @directory
|
||||
@quick_index = Gem::Indexer::QuickIndexBuilder.new "index", @directory
|
||||
@quick_index = Gem::Indexer::QuickIndexBuilder.new 'index', @directory
|
||||
|
||||
quick_dir = File.join @directory, 'quick'
|
||||
@latest_index = Gem::Indexer::LatestIndexBuilder.new 'latest_index', quick_dir
|
||||
end
|
||||
|
||||
##
|
||||
# Build the index.
|
||||
|
||||
def build_index
|
||||
@master_index.build do
|
||||
@quick_index.build do
|
||||
@marshal_index.build do
|
||||
progress = ui.progress_reporter gem_file_list.size,
|
||||
@latest_index.build do
|
||||
progress = ui.progress_reporter gem_file_list.size,
|
||||
"Generating index for #{gem_file_list.size} gems in #{@dest_directory}",
|
||||
"Loaded all gems"
|
||||
|
||||
gem_file_list.each do |gemfile|
|
||||
if File.size(gemfile.to_s) == 0 then
|
||||
alert_warning "Skipping zero-length gem: #{gemfile}"
|
||||
next
|
||||
end
|
||||
|
||||
begin
|
||||
spec = Gem::Format.from_file_by_path(gemfile).spec
|
||||
|
||||
unless gemfile =~ /\/#{Regexp.escape spec.original_name}.*\.gem\z/i then
|
||||
alert_warning "Skipping misnamed gem: #{gemfile} => #{spec.full_name} (#{spec.original_name})"
|
||||
gem_file_list.each do |gemfile|
|
||||
if File.size(gemfile.to_s) == 0 then
|
||||
alert_warning "Skipping zero-length gem: #{gemfile}"
|
||||
next
|
||||
end
|
||||
|
||||
abbreviate spec
|
||||
sanitize spec
|
||||
begin
|
||||
spec = Gem::Format.from_file_by_path(gemfile).spec
|
||||
|
||||
@master_index.add spec
|
||||
@quick_index.add spec
|
||||
@marshal_index.add spec
|
||||
unless gemfile =~ /\/#{Regexp.escape spec.original_name}.*\.gem\z/i then
|
||||
alert_warning "Skipping misnamed gem: #{gemfile} => #{spec.full_name} (#{spec.original_name})"
|
||||
next
|
||||
end
|
||||
|
||||
progress.updated spec.original_name
|
||||
abbreviate spec
|
||||
sanitize spec
|
||||
|
||||
rescue SignalException => e
|
||||
alert_error "Recieved signal, exiting"
|
||||
raise
|
||||
rescue Exception => e
|
||||
alert_error "Unable to process #{gemfile}\n#{e.message} (#{e.class})\n\t#{e.backtrace.join "\n\t"}"
|
||||
end
|
||||
@master_index.add spec
|
||||
@quick_index.add spec
|
||||
@marshal_index.add spec
|
||||
@latest_index.add spec
|
||||
|
||||
progress.updated spec.original_name
|
||||
|
||||
rescue SignalException => e
|
||||
alert_error "Recieved signal, exiting"
|
||||
raise
|
||||
rescue Exception => e
|
||||
alert_error "Unable to process #{gemfile}\n#{e.message} (#{e.class})\n\t#{e.backtrace.join "\n\t"}"
|
||||
end
|
||||
end
|
||||
|
||||
progress.done
|
||||
|
||||
say "Generating master indexes (this may take a while)"
|
||||
end
|
||||
|
||||
progress.done
|
||||
|
||||
say "Generating master indexes (this may take a while)"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -95,14 +106,15 @@ class Gem::Indexer
|
|||
|
||||
say "Moving index into production dir #{@dest_directory}" if verbose
|
||||
|
||||
files = @master_index.files + @quick_index.files + @marshal_index.files
|
||||
files = @master_index.files + @quick_index.files + @marshal_index.files +
|
||||
@latest_index.files
|
||||
|
||||
files.each do |file|
|
||||
relative_name = file[/\A#{Regexp.escape @directory}.(.*)/, 1]
|
||||
dest_name = File.join @dest_directory, relative_name
|
||||
src_name = File.join @directory, file
|
||||
dst_name = File.join @dest_directory, file
|
||||
|
||||
FileUtils.rm_rf dest_name, :verbose => verbose
|
||||
FileUtils.mv file, @dest_directory, :verbose => verbose
|
||||
FileUtils.rm_rf dst_name, :verbose => verbose
|
||||
FileUtils.mv src_name, @dest_directory, :verbose => verbose
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -160,4 +172,5 @@ require 'rubygems/indexer/abstract_index_builder'
|
|||
require 'rubygems/indexer/master_index_builder'
|
||||
require 'rubygems/indexer/quick_index_builder'
|
||||
require 'rubygems/indexer/marshal_index_builder'
|
||||
require 'rubygems/indexer/latest_index_builder'
|
||||
|
||||
|
|
|
@ -22,16 +22,18 @@ class Gem::Indexer::AbstractIndexBuilder
|
|||
@files = []
|
||||
end
|
||||
|
||||
##
|
||||
# Build a Gem index. Yields to block to handle the details of the
|
||||
# actual building. Calls +begin_index+, +end_index+ and +cleanup+ at
|
||||
# appropriate times to customize basic operations.
|
||||
|
||||
def build
|
||||
FileUtils.mkdir_p @directory unless File.exist? @directory
|
||||
raise "not a directory: #{@directory}" unless File.directory? @directory
|
||||
|
||||
file_path = File.join @directory, @filename
|
||||
|
||||
@files << file_path
|
||||
@files << @filename
|
||||
|
||||
File.open file_path, "wb" do |file|
|
||||
@file = file
|
||||
|
@ -39,14 +41,20 @@ class Gem::Indexer::AbstractIndexBuilder
|
|||
yield
|
||||
end_index
|
||||
end
|
||||
|
||||
cleanup
|
||||
ensure
|
||||
@file = nil
|
||||
end
|
||||
|
||||
##
|
||||
# Compress the given file.
|
||||
|
||||
def compress(filename, ext="rz")
|
||||
zipped = zip(File.open(filename, 'rb'){ |fp| fp.read })
|
||||
data = open filename, 'rb' do |fp| fp.read end
|
||||
|
||||
zipped = zip data
|
||||
|
||||
File.open "#{filename}.#{ext}", "wb" do |file|
|
||||
file.write zipped
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
require 'rubygems/indexer'
|
||||
|
||||
##
|
||||
# Construct the master Gem index file.
|
||||
|
||||
class Gem::Indexer::MasterIndexBuilder < Gem::Indexer::AbstractIndexBuilder
|
||||
|
||||
def start_index
|
||||
|
@ -10,6 +12,7 @@ class Gem::Indexer::MasterIndexBuilder < Gem::Indexer::AbstractIndexBuilder
|
|||
|
||||
def end_index
|
||||
super
|
||||
|
||||
@file.puts "--- !ruby/object:#{@index.class}"
|
||||
@file.puts "gems:"
|
||||
|
||||
|
@ -28,11 +31,9 @@ class Gem::Indexer::MasterIndexBuilder < Gem::Indexer::AbstractIndexBuilder
|
|||
index_file_name = File.join @directory, @filename
|
||||
|
||||
compress index_file_name, "Z"
|
||||
compressed_file_name = "#{index_file_name}.Z"
|
||||
paranoid index_file_name, "#{index_file_name}.Z"
|
||||
|
||||
paranoid index_file_name, compressed_file_name
|
||||
|
||||
@files << compressed_file_name
|
||||
@files << "#{@filename}.Z"
|
||||
end
|
||||
|
||||
def add(spec)
|
||||
|
@ -41,12 +42,12 @@ class Gem::Indexer::MasterIndexBuilder < Gem::Indexer::AbstractIndexBuilder
|
|||
|
||||
private
|
||||
|
||||
def paranoid(fn, compressed_fn)
|
||||
data = File.open(fn, 'rb') do |fp| fp.read end
|
||||
compressed_data = File.open(compressed_fn, 'rb') do |fp| fp.read end
|
||||
def paranoid(path, compressed_path)
|
||||
data = Gem.read_binary path
|
||||
compressed_data = Gem.read_binary compressed_path
|
||||
|
||||
if data != unzip(compressed_data) then
|
||||
fail "Compressed file #{compressed_fn} does not match uncompressed file #{fn}"
|
||||
raise "Compressed file #{compressed_path} does not match uncompressed file #{path}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
require 'rubygems/indexer'
|
||||
|
||||
##
|
||||
# Construct a quick index file and all of the individual specs to support
|
||||
# incremental loading.
|
||||
|
||||
class Gem::Indexer::QuickIndexBuilder < Gem::Indexer::AbstractIndexBuilder
|
||||
|
||||
def initialize(filename, directory)
|
||||
|
@ -13,12 +15,12 @@ class Gem::Indexer::QuickIndexBuilder < Gem::Indexer::AbstractIndexBuilder
|
|||
def cleanup
|
||||
super
|
||||
|
||||
quick_index_file = File.join(@directory, @filename)
|
||||
quick_index_file = File.join @directory, @filename
|
||||
compress quick_index_file
|
||||
|
||||
# the complete quick index is in a directory, so move it as a whole
|
||||
@files.delete quick_index_file
|
||||
@files << @directory
|
||||
@files.delete 'index'
|
||||
@files << 'quick'
|
||||
end
|
||||
|
||||
def add(spec)
|
||||
|
|
|
@ -25,6 +25,12 @@ module Gem::InstallUpdateOptions
|
|||
options[:install_dir] = File.expand_path(value)
|
||||
end
|
||||
|
||||
add_option(:"Install/Update", '-n', '--bindir DIR',
|
||||
'Directory where binary files are',
|
||||
'located') do |value, options|
|
||||
options[:bin_dir] = File.expand_path(value)
|
||||
end
|
||||
|
||||
add_option(:"Install/Update", '-d', '--[no-]rdoc',
|
||||
'Generate RDoc documentation for the gem on',
|
||||
'install') do |value, options|
|
||||
|
|
|
@ -63,7 +63,8 @@ class Gem::Installer
|
|||
:force => false,
|
||||
:install_dir => Gem.dir,
|
||||
:exec_format => false,
|
||||
:env_shebang => false
|
||||
:env_shebang => false,
|
||||
:bin_dir => nil
|
||||
}.merge options
|
||||
|
||||
@env_shebang = options[:env_shebang]
|
||||
|
@ -74,6 +75,7 @@ class Gem::Installer
|
|||
@format_executable = options[:format_executable]
|
||||
@security_policy = options[:security_policy]
|
||||
@wrappers = options[:wrappers]
|
||||
@bin_dir = options[:bin_dir]
|
||||
|
||||
begin
|
||||
@format = Gem::Format.from_file_by_path @gem, @security_policy
|
||||
|
@ -104,7 +106,7 @@ class Gem::Installer
|
|||
|
||||
unless @force then
|
||||
if rrv = @spec.required_ruby_version then
|
||||
unless rrv.satisfied_by? Gem::Version.new(RUBY_VERSION) then
|
||||
unless rrv.satisfied_by? Gem.ruby_version then
|
||||
raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}"
|
||||
end
|
||||
end
|
||||
|
@ -225,7 +227,7 @@ class Gem::Installer
|
|||
# If the user has asked for the gem to be installed in a directory that is
|
||||
# the system gem directory, then use the system bin directory, else create
|
||||
# (or use) a new bin dir under the gem_home.
|
||||
bindir = Gem.bindir @gem_home
|
||||
bindir = @bin_dir ? @bin_dir : (Gem.bindir @gem_home)
|
||||
|
||||
Dir.mkdir bindir unless File.exist? bindir
|
||||
raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir
|
||||
|
@ -303,7 +305,7 @@ class Gem::Installer
|
|||
# necessary.
|
||||
def shebang(bin_file_name)
|
||||
if @env_shebang then
|
||||
"#!/usr/bin/env ruby"
|
||||
"#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name]
|
||||
else
|
||||
path = File.join @gem_dir, @spec.bindir, bin_file_name
|
||||
|
||||
|
@ -352,10 +354,10 @@ TEXT
|
|||
<<-TEXT
|
||||
@ECHO OFF
|
||||
IF NOT "%~f0" == "~f0" GOTO :WinNT
|
||||
@"#{Gem.ruby}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
|
||||
@"#{File.basename(Gem.ruby)}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
|
||||
GOTO :EOF
|
||||
:WinNT
|
||||
"%~dp0ruby.exe" "%~dpn0" %*
|
||||
@"#{File.basename(Gem.ruby)}" "%~dpn0" %*
|
||||
TEXT
|
||||
end
|
||||
|
||||
|
|
|
@ -45,768 +45,15 @@ module Gem::Package
|
|||
class TooLongFileName < Error; end
|
||||
class FormatError < Error; end
|
||||
|
||||
module FSyncDir
|
||||
private
|
||||
def fsync_dir(dirname)
|
||||
# make sure this hits the disc
|
||||
begin
|
||||
dir = open(dirname, "r")
|
||||
dir.fsync
|
||||
rescue # ignore IOError if it's an unpatched (old) Ruby
|
||||
ensure
|
||||
dir.close if dir rescue nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TarHeader
|
||||
FIELDS = [:name, :mode, :uid, :gid, :size, :mtime, :checksum, :typeflag,
|
||||
:linkname, :magic, :version, :uname, :gname, :devmajor,
|
||||
:devminor, :prefix]
|
||||
FIELDS.each {|x| attr_reader x}
|
||||
|
||||
def self.new_from_stream(stream)
|
||||
data = stream.read(512)
|
||||
fields = data.unpack("A100" + # record name
|
||||
"A8A8A8" + # mode, uid, gid
|
||||
"A12A12" + # size, mtime
|
||||
"A8A" + # checksum, typeflag
|
||||
"A100" + # linkname
|
||||
"A6A2" + # magic, version
|
||||
"A32" + # uname
|
||||
"A32" + # gname
|
||||
"A8A8" + # devmajor, devminor
|
||||
"A155") # prefix
|
||||
name = fields.shift
|
||||
mode = fields.shift.oct
|
||||
uid = fields.shift.oct
|
||||
gid = fields.shift.oct
|
||||
size = fields.shift.oct
|
||||
mtime = fields.shift.oct
|
||||
checksum = fields.shift.oct
|
||||
typeflag = fields.shift
|
||||
linkname = fields.shift
|
||||
magic = fields.shift
|
||||
version = fields.shift.oct
|
||||
uname = fields.shift
|
||||
gname = fields.shift
|
||||
devmajor = fields.shift.oct
|
||||
devminor = fields.shift.oct
|
||||
prefix = fields.shift
|
||||
|
||||
empty = (data == "\0" * 512)
|
||||
|
||||
new(:name=>name, :mode=>mode, :uid=>uid, :gid=>gid, :size=>size,
|
||||
:mtime=>mtime, :checksum=>checksum, :typeflag=>typeflag,
|
||||
:magic=>magic, :version=>version, :uname=>uname, :gname=>gname,
|
||||
:devmajor=>devmajor, :devminor=>devminor, :prefix=>prefix,
|
||||
:empty => empty )
|
||||
end
|
||||
|
||||
def initialize(vals)
|
||||
unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode]
|
||||
raise ArgumentError, ":name, :size, :prefix and :mode required"
|
||||
end
|
||||
vals[:uid] ||= 0
|
||||
vals[:gid] ||= 0
|
||||
vals[:mtime] ||= 0
|
||||
vals[:checksum] ||= ""
|
||||
vals[:typeflag] ||= "0"
|
||||
vals[:magic] ||= "ustar"
|
||||
vals[:version] ||= "00"
|
||||
vals[:uname] ||= "wheel"
|
||||
vals[:gname] ||= "wheel"
|
||||
vals[:devmajor] ||= 0
|
||||
vals[:devminor] ||= 0
|
||||
FIELDS.each {|x| instance_variable_set "@#{x.to_s}", vals[x]}
|
||||
@empty = vals[:empty]
|
||||
end
|
||||
|
||||
def empty?
|
||||
@empty
|
||||
end
|
||||
|
||||
def to_s
|
||||
update_checksum
|
||||
header(checksum)
|
||||
end
|
||||
|
||||
def update_checksum
|
||||
h = header(" " * 8)
|
||||
@checksum = oct(calculate_checksum(h), 6)
|
||||
end
|
||||
|
||||
private
|
||||
def oct(num, len)
|
||||
"%0#{len}o" % num
|
||||
end
|
||||
|
||||
def calculate_checksum(hdr)
|
||||
#hdr.split('').map { |c| c[0] }.inject { |a, b| a + b } # HACK rubinius
|
||||
hdr.unpack("C*").inject{|a,b| a+b}
|
||||
end
|
||||
|
||||
def header(chksum)
|
||||
# struct tarfile_entry_posix {
|
||||
# char name[100]; # ASCII + (Z unless filled)
|
||||
# char mode[8]; # 0 padded, octal, null
|
||||
# char uid[8]; # ditto
|
||||
# char gid[8]; # ditto
|
||||
# char size[12]; # 0 padded, octal, null
|
||||
# char mtime[12]; # 0 padded, octal, null
|
||||
# char checksum[8]; # 0 padded, octal, null, space
|
||||
# char typeflag[1]; # file: "0" dir: "5"
|
||||
# char linkname[100]; # ASCII + (Z unless filled)
|
||||
# char magic[6]; # "ustar\0"
|
||||
# char version[2]; # "00"
|
||||
# char uname[32]; # ASCIIZ
|
||||
# char gname[32]; # ASCIIZ
|
||||
# char devmajor[8]; # 0 padded, octal, null
|
||||
# char devminor[8]; # o padded, octal, null
|
||||
# char prefix[155]; # ASCII + (Z unless filled)
|
||||
# };
|
||||
arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11),
|
||||
oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version,
|
||||
uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix]
|
||||
str = arr.pack("a100a8a8a8a12a12" + # name, mode, uid, gid, size, mtime
|
||||
"a7aaa100a6a2" + # chksum, typeflag, linkname, magic, version
|
||||
"a32a32a8a8a155") # uname, gname, devmajor, devminor, prefix
|
||||
str + "\0" * ((512 - str.size) % 512)
|
||||
end
|
||||
end
|
||||
|
||||
class TarWriter
|
||||
class FileOverflow < StandardError; end
|
||||
class BlockNeeded < StandardError; end
|
||||
|
||||
class BoundedStream
|
||||
attr_reader :limit, :written
|
||||
def initialize(io, limit)
|
||||
@io = io
|
||||
@limit = limit
|
||||
@written = 0
|
||||
end
|
||||
|
||||
def write(data)
|
||||
if data.size + @written > @limit
|
||||
raise FileOverflow,
|
||||
"You tried to feed more data than fits in the file."
|
||||
end
|
||||
@io.write data
|
||||
@written += data.size
|
||||
data.size
|
||||
end
|
||||
end
|
||||
|
||||
class RestrictedStream
|
||||
def initialize(anIO)
|
||||
@io = anIO
|
||||
end
|
||||
|
||||
def write(data)
|
||||
@io.write data
|
||||
end
|
||||
end
|
||||
|
||||
def self.new(anIO)
|
||||
writer = super(anIO)
|
||||
return writer unless block_given?
|
||||
begin
|
||||
yield writer
|
||||
ensure
|
||||
writer.close
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def initialize(anIO)
|
||||
@io = anIO
|
||||
@closed = false
|
||||
end
|
||||
|
||||
def add_file_simple(name, mode, size)
|
||||
raise BlockNeeded unless block_given?
|
||||
raise ClosedIO if @closed
|
||||
name, prefix = split_name(name)
|
||||
header = TarHeader.new(:name => name, :mode => mode,
|
||||
:size => size, :prefix => prefix).to_s
|
||||
@io.write header
|
||||
os = BoundedStream.new(@io, size)
|
||||
yield os
|
||||
#FIXME: what if an exception is raised in the block?
|
||||
min_padding = size - os.written
|
||||
@io.write("\0" * min_padding)
|
||||
remainder = (512 - (size % 512)) % 512
|
||||
@io.write("\0" * remainder)
|
||||
end
|
||||
|
||||
def add_file(name, mode)
|
||||
raise BlockNeeded unless block_given?
|
||||
raise ClosedIO if @closed
|
||||
raise NonSeekableIO unless @io.respond_to? :pos=
|
||||
name, prefix = split_name(name)
|
||||
init_pos = @io.pos
|
||||
@io.write "\0" * 512 # placeholder for the header
|
||||
yield RestrictedStream.new(@io)
|
||||
#FIXME: what if an exception is raised in the block?
|
||||
#FIXME: what if an exception is raised in the block?
|
||||
size = @io.pos - init_pos - 512
|
||||
remainder = (512 - (size % 512)) % 512
|
||||
@io.write("\0" * remainder)
|
||||
final_pos = @io.pos
|
||||
@io.pos = init_pos
|
||||
header = TarHeader.new(:name => name, :mode => mode,
|
||||
:size => size, :prefix => prefix).to_s
|
||||
@io.write header
|
||||
@io.pos = final_pos
|
||||
end
|
||||
|
||||
def mkdir(name, mode)
|
||||
raise ClosedIO if @closed
|
||||
name, prefix = split_name(name)
|
||||
header = TarHeader.new(:name => name, :mode => mode, :typeflag => "5",
|
||||
:size => 0, :prefix => prefix).to_s
|
||||
@io.write header
|
||||
nil
|
||||
end
|
||||
|
||||
def flush
|
||||
raise ClosedIO if @closed
|
||||
@io.flush if @io.respond_to? :flush
|
||||
end
|
||||
|
||||
def close
|
||||
#raise ClosedIO if @closed
|
||||
return if @closed
|
||||
@io.write "\0" * 1024
|
||||
@closed = true
|
||||
end
|
||||
|
||||
private
|
||||
def split_name name
|
||||
raise TooLongFileName if name.size > 256
|
||||
if name.size <= 100
|
||||
prefix = ""
|
||||
else
|
||||
parts = name.split(/\//)
|
||||
newname = parts.pop
|
||||
nxt = ""
|
||||
loop do
|
||||
nxt = parts.pop
|
||||
break if newname.size + 1 + nxt.size > 100
|
||||
newname = nxt + "/" + newname
|
||||
end
|
||||
prefix = (parts + [nxt]).join "/"
|
||||
name = newname
|
||||
raise TooLongFileName if name.size > 100 || prefix.size > 155
|
||||
end
|
||||
return name, prefix
|
||||
end
|
||||
end
|
||||
|
||||
class TarReader
|
||||
|
||||
include Gem::Package
|
||||
|
||||
class UnexpectedEOF < StandardError; end
|
||||
|
||||
module InvalidEntry
|
||||
def read(len=nil); raise ClosedIO; end
|
||||
def getc; raise ClosedIO; end
|
||||
def rewind; raise ClosedIO; end
|
||||
end
|
||||
|
||||
class Entry
|
||||
TarHeader::FIELDS.each{|x| attr_reader x}
|
||||
|
||||
def initialize(header, anIO)
|
||||
@io = anIO
|
||||
@name = header.name
|
||||
@mode = header.mode
|
||||
@uid = header.uid
|
||||
@gid = header.gid
|
||||
@size = header.size
|
||||
@mtime = header.mtime
|
||||
@checksum = header.checksum
|
||||
@typeflag = header.typeflag
|
||||
@linkname = header.linkname
|
||||
@magic = header.magic
|
||||
@version = header.version
|
||||
@uname = header.uname
|
||||
@gname = header.gname
|
||||
@devmajor = header.devmajor
|
||||
@devminor = header.devminor
|
||||
@prefix = header.prefix
|
||||
@read = 0
|
||||
@orig_pos = @io.pos
|
||||
end
|
||||
|
||||
def read(len = nil)
|
||||
return nil if @read >= @size
|
||||
len ||= @size - @read
|
||||
max_read = [len, @size - @read].min
|
||||
ret = @io.read(max_read)
|
||||
@read += ret.size
|
||||
ret
|
||||
end
|
||||
|
||||
def getc
|
||||
return nil if @read >= @size
|
||||
ret = @io.getc
|
||||
@read += 1 if ret
|
||||
ret
|
||||
end
|
||||
|
||||
def is_directory?
|
||||
@typeflag == "5"
|
||||
end
|
||||
|
||||
def is_file?
|
||||
@typeflag == "0"
|
||||
end
|
||||
|
||||
def eof?
|
||||
@read >= @size
|
||||
end
|
||||
|
||||
def pos
|
||||
@read
|
||||
end
|
||||
|
||||
def rewind
|
||||
raise NonSeekableIO unless @io.respond_to? :pos=
|
||||
@io.pos = @orig_pos
|
||||
@read = 0
|
||||
end
|
||||
|
||||
alias_method :is_directory, :is_directory?
|
||||
alias_method :is_file, :is_file?
|
||||
|
||||
def bytes_read
|
||||
@read
|
||||
end
|
||||
|
||||
def full_name
|
||||
if @prefix != ""
|
||||
File.join(@prefix, @name)
|
||||
else
|
||||
@name
|
||||
end
|
||||
end
|
||||
|
||||
def close
|
||||
invalidate
|
||||
end
|
||||
|
||||
private
|
||||
def invalidate
|
||||
extend InvalidEntry
|
||||
end
|
||||
end
|
||||
|
||||
def self.new(anIO)
|
||||
reader = super(anIO)
|
||||
return reader unless block_given?
|
||||
begin
|
||||
yield reader
|
||||
ensure
|
||||
reader.close
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def initialize(anIO)
|
||||
@io = anIO
|
||||
@init_pos = anIO.pos
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
each_entry(&block)
|
||||
end
|
||||
|
||||
# do not call this during a #each or #each_entry iteration
|
||||
def rewind
|
||||
if @init_pos == 0
|
||||
raise NonSeekableIO unless @io.respond_to? :rewind
|
||||
@io.rewind
|
||||
else
|
||||
raise NonSeekableIO unless @io.respond_to? :pos=
|
||||
@io.pos = @init_pos
|
||||
end
|
||||
end
|
||||
|
||||
def each_entry
|
||||
loop do
|
||||
return if @io.eof?
|
||||
header = TarHeader.new_from_stream(@io)
|
||||
return if header.empty?
|
||||
entry = Entry.new header, @io
|
||||
size = entry.size
|
||||
yield entry
|
||||
skip = (512 - (size % 512)) % 512
|
||||
if @io.respond_to? :seek
|
||||
# avoid reading...
|
||||
@io.seek(size - entry.bytes_read, IO::SEEK_CUR)
|
||||
else
|
||||
pending = size - entry.bytes_read
|
||||
while pending > 0
|
||||
bread = @io.read([pending, 4096].min).size
|
||||
raise UnexpectedEOF if @io.eof?
|
||||
pending -= bread
|
||||
end
|
||||
end
|
||||
@io.read(skip) # discard trailing zeros
|
||||
# make sure nobody can use #read, #getc or #rewind anymore
|
||||
entry.close
|
||||
end
|
||||
end
|
||||
|
||||
def close
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class TarInput
|
||||
|
||||
include FSyncDir
|
||||
include Enumerable
|
||||
|
||||
attr_reader :metadata
|
||||
|
||||
class << self; private :new end
|
||||
|
||||
def initialize(io, security_policy = nil)
|
||||
@io = io
|
||||
@tarreader = TarReader.new(@io)
|
||||
has_meta = false
|
||||
data_sig, meta_sig, data_dgst, meta_dgst = nil, nil, nil, nil
|
||||
dgst_algo = security_policy ? Gem::Security::OPT[:dgst_algo] : nil
|
||||
|
||||
@tarreader.each do |entry|
|
||||
case entry.full_name
|
||||
when "metadata"
|
||||
@metadata = load_gemspec(entry.read)
|
||||
has_meta = true
|
||||
break
|
||||
when "metadata.gz"
|
||||
begin
|
||||
# if we have a security_policy, then pre-read the metadata file
|
||||
# and calculate it's digest
|
||||
sio = nil
|
||||
if security_policy
|
||||
Gem.ensure_ssl_available
|
||||
sio = StringIO.new(entry.read)
|
||||
meta_dgst = dgst_algo.digest(sio.string)
|
||||
sio.rewind
|
||||
end
|
||||
|
||||
gzis = Zlib::GzipReader.new(sio || entry)
|
||||
# YAML wants an instance of IO
|
||||
@metadata = load_gemspec(gzis)
|
||||
has_meta = true
|
||||
ensure
|
||||
gzis.close unless gzis.nil?
|
||||
end
|
||||
when 'metadata.gz.sig'
|
||||
meta_sig = entry.read
|
||||
when 'data.tar.gz.sig'
|
||||
data_sig = entry.read
|
||||
when 'data.tar.gz'
|
||||
if security_policy
|
||||
Gem.ensure_ssl_available
|
||||
data_dgst = dgst_algo.digest(entry.read)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if security_policy then
|
||||
Gem.ensure_ssl_available
|
||||
|
||||
# map trust policy from string to actual class (or a serialized YAML
|
||||
# file, if that exists)
|
||||
if String === security_policy then
|
||||
if Gem::Security::Policy.key? security_policy then
|
||||
# load one of the pre-defined security policies
|
||||
security_policy = Gem::Security::Policy[security_policy]
|
||||
elsif File.exist? security_policy then
|
||||
# FIXME: this doesn't work yet
|
||||
security_policy = YAML.load File.read(security_policy)
|
||||
else
|
||||
raise Gem::Exception, "Unknown trust policy '#{security_policy}'"
|
||||
end
|
||||
end
|
||||
|
||||
if data_sig && data_dgst && meta_sig && meta_dgst then
|
||||
# the user has a trust policy, and we have a signed gem
|
||||
# file, so use the trust policy to verify the gem signature
|
||||
|
||||
begin
|
||||
security_policy.verify_gem(data_sig, data_dgst, @metadata.cert_chain)
|
||||
rescue Exception => e
|
||||
raise "Couldn't verify data signature: #{e}"
|
||||
end
|
||||
|
||||
begin
|
||||
security_policy.verify_gem(meta_sig, meta_dgst, @metadata.cert_chain)
|
||||
rescue Exception => e
|
||||
raise "Couldn't verify metadata signature: #{e}"
|
||||
end
|
||||
elsif security_policy.only_signed
|
||||
raise Gem::Exception, "Unsigned gem"
|
||||
else
|
||||
# FIXME: should display warning here (trust policy, but
|
||||
# either unsigned or badly signed gem file)
|
||||
end
|
||||
end
|
||||
|
||||
@tarreader.rewind
|
||||
@fileops = Gem::FileOperations.new
|
||||
raise FormatError, "No metadata found!" unless has_meta
|
||||
end
|
||||
|
||||
# Attempt to YAML-load a gemspec from the given _io_ parameter. Return
|
||||
# nil if it fails.
|
||||
def load_gemspec(io)
|
||||
Gem::Specification.from_yaml(io)
|
||||
rescue Gem::Exception
|
||||
nil
|
||||
end
|
||||
|
||||
def self.open(filename, security_policy = nil, &block)
|
||||
open_from_io(File.open(filename, "rb"), security_policy, &block)
|
||||
end
|
||||
|
||||
def self.open_from_io(io, security_policy = nil, &block)
|
||||
raise "Want a block" unless block_given?
|
||||
begin
|
||||
is = new(io, security_policy)
|
||||
yield is
|
||||
ensure
|
||||
is.close if is
|
||||
end
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
@tarreader.each do |entry|
|
||||
next unless entry.full_name == "data.tar.gz"
|
||||
is = zipped_stream(entry)
|
||||
begin
|
||||
TarReader.new(is) do |inner|
|
||||
inner.each(&block)
|
||||
end
|
||||
ensure
|
||||
is.close if is
|
||||
end
|
||||
end
|
||||
@tarreader.rewind
|
||||
end
|
||||
|
||||
# Return an IO stream for the zipped entry.
|
||||
#
|
||||
# NOTE: Originally this method used two approaches, Return a GZipReader
|
||||
# directly, or read the GZipReader into a string and return a StringIO on
|
||||
# the string. The string IO approach was used for versions of ZLib before
|
||||
# 1.2.1 to avoid buffer errors on windows machines. Then we found that
|
||||
# errors happened with 1.2.1 as well, so we changed the condition. Then
|
||||
# we discovered errors occurred with versions as late as 1.2.3. At this
|
||||
# point (after some benchmarking to show we weren't seriously crippling
|
||||
# the unpacking speed) we threw our hands in the air and declared that
|
||||
# this method would use the String IO approach on all platforms at all
|
||||
# times. And that's the way it is.
|
||||
def zipped_stream(entry)
|
||||
if defined? Rubinius then
|
||||
zis = Zlib::GzipReader.new entry
|
||||
dis = zis.read
|
||||
is = StringIO.new(dis)
|
||||
else
|
||||
# This is Jamis Buck's ZLib workaround for some unknown issue
|
||||
entry.read(10) # skip the gzip header
|
||||
zis = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
||||
is = StringIO.new(zis.inflate(entry.read))
|
||||
end
|
||||
ensure
|
||||
zis.finish if zis
|
||||
end
|
||||
|
||||
def extract_entry(destdir, entry, expected_md5sum = nil)
|
||||
if entry.is_directory?
|
||||
dest = File.join(destdir, entry.full_name)
|
||||
if file_class.dir? dest
|
||||
@fileops.chmod entry.mode, dest, :verbose=>false
|
||||
else
|
||||
@fileops.mkdir_p(dest, :mode => entry.mode, :verbose=>false)
|
||||
end
|
||||
fsync_dir dest
|
||||
fsync_dir File.join(dest, "..")
|
||||
return
|
||||
end
|
||||
# it's a file
|
||||
md5 = Digest::MD5.new if expected_md5sum
|
||||
destdir = File.join(destdir, File.dirname(entry.full_name))
|
||||
@fileops.mkdir_p(destdir, :mode => 0755, :verbose=>false)
|
||||
destfile = File.join(destdir, File.basename(entry.full_name))
|
||||
@fileops.chmod(0600, destfile, :verbose=>false) rescue nil # Errno::ENOENT
|
||||
file_class.open(destfile, "wb", entry.mode) do |os|
|
||||
loop do
|
||||
data = entry.read(4096)
|
||||
break unless data
|
||||
md5 << data if expected_md5sum
|
||||
os.write(data)
|
||||
end
|
||||
os.fsync
|
||||
end
|
||||
@fileops.chmod(entry.mode, destfile, :verbose=>false)
|
||||
fsync_dir File.dirname(destfile)
|
||||
fsync_dir File.join(File.dirname(destfile), "..")
|
||||
if expected_md5sum && expected_md5sum != md5.hexdigest
|
||||
raise BadCheckSum
|
||||
end
|
||||
end
|
||||
|
||||
def close
|
||||
@io.close
|
||||
@tarreader.close
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def file_class
|
||||
File
|
||||
end
|
||||
end
|
||||
|
||||
class TarOutput
|
||||
|
||||
class << self; private :new end
|
||||
|
||||
def initialize(io)
|
||||
@io = io
|
||||
@external = TarWriter.new @io
|
||||
end
|
||||
|
||||
def external_handle
|
||||
@external
|
||||
end
|
||||
|
||||
def self.open(filename, signer = nil, &block)
|
||||
io = File.open(filename, "wb")
|
||||
open_from_io(io, signer, &block)
|
||||
nil
|
||||
end
|
||||
|
||||
def self.open_from_io(io, signer = nil, &block)
|
||||
outputter = new(io)
|
||||
metadata = nil
|
||||
set_meta = lambda{|x| metadata = x}
|
||||
raise "Want a block" unless block_given?
|
||||
begin
|
||||
data_sig, meta_sig = nil, nil
|
||||
|
||||
outputter.external_handle.add_file("data.tar.gz", 0644) do |inner|
|
||||
begin
|
||||
sio = signer ? StringIO.new : nil
|
||||
os = Zlib::GzipWriter.new(sio || inner)
|
||||
|
||||
TarWriter.new(os) do |inner_tar_stream|
|
||||
klass = class << inner_tar_stream; self end
|
||||
klass.send(:define_method, :metadata=, &set_meta)
|
||||
block.call inner_tar_stream
|
||||
end
|
||||
ensure
|
||||
os.flush
|
||||
os.finish
|
||||
#os.close
|
||||
|
||||
# if we have a signing key, then sign the data
|
||||
# digest and return the signature
|
||||
data_sig = nil
|
||||
if signer
|
||||
dgst_algo = Gem::Security::OPT[:dgst_algo]
|
||||
dig = dgst_algo.digest(sio.string)
|
||||
data_sig = signer.sign(dig)
|
||||
inner.write(sio.string)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# if we have a data signature, then write it to the gem too
|
||||
if data_sig
|
||||
sig_file = 'data.tar.gz.sig'
|
||||
outputter.external_handle.add_file(sig_file, 0644) do |os|
|
||||
os.write(data_sig)
|
||||
end
|
||||
end
|
||||
|
||||
outputter.external_handle.add_file("metadata.gz", 0644) do |os|
|
||||
begin
|
||||
sio = signer ? StringIO.new : nil
|
||||
gzos = Zlib::GzipWriter.new(sio || os)
|
||||
gzos.write metadata
|
||||
ensure
|
||||
gzos.flush
|
||||
gzos.finish
|
||||
|
||||
# if we have a signing key, then sign the metadata
|
||||
# digest and return the signature
|
||||
if signer
|
||||
dgst_algo = Gem::Security::OPT[:dgst_algo]
|
||||
dig = dgst_algo.digest(sio.string)
|
||||
meta_sig = signer.sign(dig)
|
||||
os.write(sio.string)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# if we have a metadata signature, then write to the gem as
|
||||
# well
|
||||
if meta_sig
|
||||
sig_file = 'metadata.gz.sig'
|
||||
outputter.external_handle.add_file(sig_file, 0644) do |os|
|
||||
os.write(meta_sig)
|
||||
end
|
||||
end
|
||||
|
||||
ensure
|
||||
outputter.close
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def close
|
||||
@external.close
|
||||
@io.close
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#FIXME: refactor the following 2 methods
|
||||
|
||||
def self.open(dest, mode = "r", signer = nil, &block)
|
||||
raise "Block needed" unless block_given?
|
||||
|
||||
case mode
|
||||
when "r"
|
||||
security_policy = signer
|
||||
TarInput.open(dest, security_policy, &block)
|
||||
when "w"
|
||||
TarOutput.open(dest, signer, &block)
|
||||
else
|
||||
raise "Unknown Package open mode"
|
||||
end
|
||||
end
|
||||
|
||||
def self.open_from_io(io, mode = "r", signer = nil, &block)
|
||||
raise "Block needed" unless block_given?
|
||||
|
||||
case mode
|
||||
when "r"
|
||||
security_policy = signer
|
||||
TarInput.open_from_io(io, security_policy, &block)
|
||||
when "w"
|
||||
TarOutput.open_from_io(io, signer, &block)
|
||||
else
|
||||
raise "Unknown Package open mode"
|
||||
end
|
||||
def self.open(io, mode = "r", signer = nil, &block)
|
||||
tar_type = case mode
|
||||
when 'r' then TarInput
|
||||
when 'w' then TarOutput
|
||||
else
|
||||
raise "Unknown Package open mode"
|
||||
end
|
||||
|
||||
tar_type.open(io, signer, &block)
|
||||
end
|
||||
|
||||
def self.pack(src, destname, signer = nil)
|
||||
|
@ -836,19 +83,13 @@ module Gem::Package
|
|||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
def file_class
|
||||
File
|
||||
end
|
||||
|
||||
def dir_class
|
||||
Dir
|
||||
end
|
||||
|
||||
def find_class # HACK kill me
|
||||
Find
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
require 'rubygems/package/f_sync_dir'
|
||||
require 'rubygems/package/tar_header'
|
||||
require 'rubygems/package/tar_input'
|
||||
require 'rubygems/package/tar_output'
|
||||
require 'rubygems/package/tar_reader'
|
||||
require 'rubygems/package/tar_reader/entry'
|
||||
require 'rubygems/package/tar_writer'
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -2,7 +2,6 @@ require 'net/http'
|
|||
require 'uri'
|
||||
|
||||
require 'rubygems'
|
||||
require 'rubygems/gem_open_uri'
|
||||
|
||||
##
|
||||
# RemoteFetcher handles the details of fetching gems and gem information from
|
||||
|
@ -10,6 +9,8 @@ require 'rubygems/gem_open_uri'
|
|||
|
||||
class Gem::RemoteFetcher
|
||||
|
||||
include Gem::UserInteraction
|
||||
|
||||
class FetchError < Gem::Exception; end
|
||||
|
||||
@fetcher = nil
|
||||
|
@ -29,6 +30,10 @@ class Gem::RemoteFetcher
|
|||
# HTTP_PROXY_PASS)
|
||||
# * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy
|
||||
def initialize(proxy)
|
||||
Socket.do_not_reverse_lookup = true
|
||||
|
||||
@connections = {}
|
||||
@requests = Hash.new 0
|
||||
@proxy_uri =
|
||||
case proxy
|
||||
when :no_proxy then nil
|
||||
|
@ -38,6 +43,65 @@ class Gem::RemoteFetcher
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Moves the gem +spec+ from +source_uri+ to the cache dir unless it is
|
||||
# already there. If the source_uri is local the gem cache dir copy is
|
||||
# always replaced.
|
||||
def download(spec, source_uri, install_dir = Gem.dir)
|
||||
gem_file_name = "#{spec.full_name}.gem"
|
||||
local_gem_path = File.join install_dir, 'cache', gem_file_name
|
||||
|
||||
Gem.ensure_gem_subdirectories install_dir
|
||||
|
||||
source_uri = URI.parse source_uri unless URI::Generic === source_uri
|
||||
scheme = source_uri.scheme
|
||||
|
||||
# URI.parse gets confused by MS Windows paths with forward slashes.
|
||||
scheme = nil if scheme =~ /^[a-z]$/i
|
||||
|
||||
case scheme
|
||||
when 'http' then
|
||||
unless File.exist? local_gem_path then
|
||||
begin
|
||||
say "Downloading gem #{gem_file_name}" if
|
||||
Gem.configuration.really_verbose
|
||||
|
||||
remote_gem_path = source_uri + "gems/#{gem_file_name}"
|
||||
|
||||
gem = Gem::RemoteFetcher.fetcher.fetch_path remote_gem_path
|
||||
rescue Gem::RemoteFetcher::FetchError
|
||||
raise if spec.original_platform == spec.platform
|
||||
|
||||
alternate_name = "#{spec.original_name}.gem"
|
||||
|
||||
say "Failed, downloading gem #{alternate_name}" if
|
||||
Gem.configuration.really_verbose
|
||||
|
||||
remote_gem_path = source_uri + "gems/#{alternate_name}"
|
||||
|
||||
gem = Gem::RemoteFetcher.fetcher.fetch_path remote_gem_path
|
||||
end
|
||||
|
||||
File.open local_gem_path, 'wb' do |fp|
|
||||
fp.write gem
|
||||
end
|
||||
end
|
||||
when nil, 'file' then # TODO test for local overriding cache
|
||||
begin
|
||||
FileUtils.cp source_uri.to_s, local_gem_path
|
||||
rescue Errno::EACCES
|
||||
local_gem_path = source_uri.to_s
|
||||
end
|
||||
|
||||
say "Using local gem #{local_gem_path}" if
|
||||
Gem.configuration.really_verbose
|
||||
else
|
||||
raise Gem::InstallError, "unsupported URI scheme #{source_uri.scheme}"
|
||||
end
|
||||
|
||||
local_gem_path
|
||||
end
|
||||
|
||||
# Downloads +uri+.
|
||||
def fetch_path(uri)
|
||||
open_uri_or_path(uri) do |input|
|
||||
|
@ -47,9 +111,8 @@ class Gem::RemoteFetcher
|
|||
raise FetchError, "timed out fetching #{uri}"
|
||||
rescue IOError, SocketError, SystemCallError => e
|
||||
raise FetchError, "#{e.class}: #{e} reading #{uri}"
|
||||
rescue OpenURI::HTTPError => e
|
||||
body = e.io.readlines.join "\n\t"
|
||||
message = "#{e.class}: #{e} reading #{uri}\n\t#{body}"
|
||||
rescue => e
|
||||
message = "#{e.class}: #{e} reading #{uri}"
|
||||
raise FetchError, message
|
||||
end
|
||||
|
||||
|
@ -83,7 +146,8 @@ class Gem::RemoteFetcher
|
|||
end
|
||||
|
||||
rescue SocketError, SystemCallError, Timeout::Error => e
|
||||
raise FetchError, "#{e.message} (#{e.class})\n\tgetting size of #{uri}"
|
||||
raise Gem::RemoteFetcher::FetchError,
|
||||
"#{e.message} (#{e.class})\n\tgetting size of #{uri}"
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -131,26 +195,77 @@ class Gem::RemoteFetcher
|
|||
|
||||
# Read the data from the (source based) URI, but if it is a file:// URI,
|
||||
# read from the filesystem instead.
|
||||
def open_uri_or_path(uri, &block)
|
||||
def open_uri_or_path(uri, depth = 0, &block)
|
||||
if file_uri?(uri)
|
||||
open(get_file_uri_path(uri), &block)
|
||||
else
|
||||
connection_options = {
|
||||
"User-Agent" => "RubyGems/#{Gem::RubyGemsVersion} #{Gem::Platform.local}"
|
||||
}
|
||||
|
||||
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
|
||||
unless uri.nil? || uri.user.nil? || uri.user.empty? then
|
||||
connection_options[:http_basic_authentication] =
|
||||
[unescape(uri.user), unescape(uri.password)]
|
||||
net_http_args = [uri.host, uri.port]
|
||||
|
||||
if @proxy_uri then
|
||||
net_http_args += [ @proxy_uri.host,
|
||||
@proxy_uri.port,
|
||||
@proxy_uri.user,
|
||||
@proxy_uri.password
|
||||
]
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ class Gem::Requirement
|
|||
|
||||
include Comparable
|
||||
|
||||
attr_reader :requirements
|
||||
|
||||
OPS = {
|
||||
"=" => lambda { |v, r| v == r },
|
||||
"!=" => lambda { |v, r| v != r },
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
# This file is auto-generated by build scripts.
|
||||
# See: rake update_version
|
||||
module Gem
|
||||
RubyGemsVersion = '1.0.1'
|
||||
RubyGemsVersion = '1.1.0'
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
# See LICENSE.txt for permissions.
|
||||
#++
|
||||
|
||||
require 'rubygems'
|
||||
require 'rubygems/gem_openssl'
|
||||
|
||||
# = Signed Gems README
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'webrick'
|
||||
require 'rdoc/template'
|
||||
require 'yaml'
|
||||
require 'zlib'
|
||||
require 'erb'
|
||||
|
||||
require 'rubygems'
|
||||
|
||||
|
@ -27,107 +27,87 @@ class Gem::Server
|
|||
|
||||
include Gem::UserInteraction
|
||||
|
||||
DOC_TEMPLATE = <<-WEBPAGE
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>RubyGems Documentation Index</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
<link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="fileHeader">
|
||||
<h1>RubyGems Documentation Index</h1>
|
||||
</div>
|
||||
<!-- banner header -->
|
||||
|
||||
<div id="bodyContent">
|
||||
<div id="contextContent">
|
||||
<div id="description">
|
||||
<h1>Summary</h1>
|
||||
<p>There are %gem_count% gems installed:</p>
|
||||
<p>
|
||||
START:specs
|
||||
IFNOT:is_last
|
||||
<a href="#%name%">%name%</a>,
|
||||
ENDIF:is_last
|
||||
IF:is_last
|
||||
<a href="#%name%">%name%</a>.
|
||||
ENDIF:is_last
|
||||
END:specs
|
||||
<h1>Gems</h1>
|
||||
|
||||
<dl>
|
||||
START:specs
|
||||
<dt>
|
||||
IF:first_name_entry
|
||||
<a name="%name%"></a>
|
||||
ENDIF:first_name_entry
|
||||
<b>%name% %version%</b>
|
||||
IF:rdoc_installed
|
||||
<a href="%doc_path%">[rdoc]</a>
|
||||
ENDIF:rdoc_installed
|
||||
IFNOT:rdoc_installed
|
||||
<span title="rdoc not installed">[rdoc]</span>
|
||||
ENDIF:rdoc_installed
|
||||
IF:homepage
|
||||
<a href="%homepage%" title="%homepage%">[www]</a>
|
||||
ENDIF:homepage
|
||||
IFNOT:homepage
|
||||
<span title="no homepage available">[www]</span>
|
||||
ENDIF:homepage
|
||||
IF:has_deps
|
||||
- depends on
|
||||
START:dependencies
|
||||
IFNOT:is_last
|
||||
<a href="#%name%" title="%version%">%name%</a>,
|
||||
ENDIF:is_last
|
||||
IF:is_last
|
||||
<a href="#%name%" title="%version%">%name%</a>.
|
||||
ENDIF:is_last
|
||||
END:dependencies
|
||||
ENDIF:has_deps
|
||||
</dt>
|
||||
<dd>
|
||||
%summary%
|
||||
IF:executables
|
||||
<br/>
|
||||
|
||||
IF:only_one_executable
|
||||
Executable is
|
||||
ENDIF:only_one_executable
|
||||
|
||||
IFNOT:only_one_executable
|
||||
Executables are
|
||||
ENDIF:only_one_executable
|
||||
|
||||
START:executables
|
||||
IFNOT:is_last
|
||||
<span class="context-item-name">%executable%</span>,
|
||||
ENDIF:is_last
|
||||
IF:is_last
|
||||
<span class="context-item-name">%executable%</span>.
|
||||
ENDIF:is_last
|
||||
END:executables
|
||||
ENDIF:executables
|
||||
<br/>
|
||||
<br/>
|
||||
</dd>
|
||||
END:specs
|
||||
</dl>
|
||||
DOC_TEMPLATE = <<-'WEBPAGE'
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE html
|
||||
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>RubyGems Documentation Index</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
<link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="fileHeader">
|
||||
<h1>RubyGems Documentation Index</h1>
|
||||
</div>
|
||||
</div>
|
||||
<!-- banner header -->
|
||||
|
||||
<div id="bodyContent">
|
||||
<div id="contextContent">
|
||||
<div id="description">
|
||||
<h1>Summary</h1>
|
||||
<p>There are <%=values["gem_count"]%> gems installed:</p>
|
||||
<p>
|
||||
<%= values["specs"].map { |v| "<a href=\"#{v["name"]}\">#{v["name"]}</a>" }.join ', ' %>.
|
||||
<h1>Gems</h1>
|
||||
|
||||
<dl>
|
||||
<% values["specs"].each do |spec| %>
|
||||
<dt>
|
||||
<% if spec["first_name_entry"] then %>
|
||||
<a name="<%=spec["name"]%>"></a>
|
||||
<% end %>
|
||||
|
||||
<b><%=spec["name"]%> <%=spec["version"]%></b>
|
||||
|
||||
<% if spec["rdoc_installed"] then %>
|
||||
<a href="<%=spec["doc_path"]%>">[rdoc]</a>
|
||||
<% else %>
|
||||
<span title="rdoc not installed">[rdoc]</span>
|
||||
<% end %>
|
||||
|
||||
<% if spec["homepage"] then %>
|
||||
<a href="<%=spec["homepage"]%>" title="<%=spec["homepage"]%>">[www]</a>
|
||||
<% else %>
|
||||
<span title="no homepage available">[www]</span>
|
||||
<% end %>
|
||||
|
||||
<% if spec["has_deps"] then %>
|
||||
- depends on
|
||||
<%= spec["dependencies"].map { |v| "<a href=\"#{v["name"]}\">#{v["name"]}</a>" }.join ', ' %>.
|
||||
<% end %>
|
||||
</dt>
|
||||
<dd>
|
||||
<%=spec["summary"]%>
|
||||
<% if spec["executables"] then %>
|
||||
<br/>
|
||||
|
||||
<% if spec["only_one_executable"] then %>
|
||||
Executable is
|
||||
<% else %>
|
||||
Executables are
|
||||
<%end%>
|
||||
|
||||
<%= spec["executables"].map { |v| "<span class=\"context-item-name\">#{v["executable"]}</span>"}.join ', ' %>.
|
||||
|
||||
<%end%>
|
||||
<br/>
|
||||
<br/>
|
||||
</dd>
|
||||
<% end %>
|
||||
</dl>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="validator-badges">
|
||||
<p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
|
||||
</div>
|
||||
<div id="validator-badges">
|
||||
<p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</body>
|
||||
</html>
|
||||
WEBPAGE
|
||||
|
||||
# CSS is copy & paste from rdoc-style.css, RDoc V1.0.1 - 20041108
|
||||
|
@ -496,11 +476,12 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
|||
end
|
||||
|
||||
# create page from template
|
||||
template = TemplatePage.new(DOC_TEMPLATE)
|
||||
template = ERB.new(DOC_TEMPLATE)
|
||||
res['content-type'] = 'text/html'
|
||||
template.write_html_on res.body,
|
||||
"gem_count" => specs.size.to_s, "specs" => specs,
|
||||
"total_file_count" => total_file_count.to_s
|
||||
values = { "gem_count" => specs.size.to_s, "specs" => specs,
|
||||
"total_file_count" => total_file_count.to_s }
|
||||
result = template.result binding
|
||||
res.body = result
|
||||
end
|
||||
|
||||
paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" }
|
||||
|
|
|
@ -8,437 +8,512 @@ require 'rubygems'
|
|||
require 'rubygems/user_interaction'
|
||||
require 'rubygems/specification'
|
||||
|
||||
module Gem
|
||||
##
|
||||
# The SourceIndex object indexes all the gems available from a
|
||||
# particular source (e.g. a list of gem directories, or a remote
|
||||
# source). A SourceIndex maps a gem full name to a gem
|
||||
# specification.
|
||||
#
|
||||
# NOTE:: The class used to be named Cache, but that became
|
||||
# confusing when cached source fetchers where introduced. The
|
||||
# constant Gem::Cache is an alias for this class to allow old
|
||||
# YAMLized source index objects to load properly.
|
||||
|
||||
# The SourceIndex object indexes all the gems available from a
|
||||
# particular source (e.g. a list of gem directories, or a remote
|
||||
# source). A SourceIndex maps a gem full name to a gem
|
||||
# specification.
|
||||
#
|
||||
# NOTE:: The class used to be named Cache, but that became
|
||||
# confusing when cached source fetchers where introduced. The
|
||||
# constant Gem::Cache is an alias for this class to allow old
|
||||
# YAMLized source index objects to load properly.
|
||||
#
|
||||
class SourceIndex
|
||||
class Gem::SourceIndex
|
||||
|
||||
include Enumerable
|
||||
include Enumerable
|
||||
|
||||
include Gem::UserInteraction
|
||||
|
||||
class << self
|
||||
include Gem::UserInteraction
|
||||
|
||||
# Class Methods. -------------------------------------------------
|
||||
class << self
|
||||
include Gem::UserInteraction
|
||||
|
||||
# Factory method to construct a source index instance for a given
|
||||
# path.
|
||||
#
|
||||
# deprecated::
|
||||
# If supplied, from_installed_gems will act just like
|
||||
# +from_gems_in+. This argument is deprecated and is provided
|
||||
# just for backwards compatibility, and should not generally
|
||||
# be used.
|
||||
#
|
||||
# return::
|
||||
# SourceIndex instance
|
||||
#
|
||||
def from_installed_gems(*deprecated)
|
||||
if deprecated.empty?
|
||||
from_gems_in(*installed_spec_directories)
|
||||
else
|
||||
from_gems_in(*deprecated) # HACK warn
|
||||
end
|
||||
end
|
||||
|
||||
# Return a list of directories in the current gem path that
|
||||
# contain specifications.
|
||||
#
|
||||
# return::
|
||||
# List of directory paths (all ending in "../specifications").
|
||||
#
|
||||
def installed_spec_directories
|
||||
Gem.path.collect { |dir| File.join(dir, "specifications") }
|
||||
end
|
||||
|
||||
# Factory method to construct a source index instance for a
|
||||
# given path.
|
||||
#
|
||||
# spec_dirs::
|
||||
# List of directories to search for specifications. Each
|
||||
# directory should have a "specifications" subdirectory
|
||||
# containing the gem specifications.
|
||||
#
|
||||
# return::
|
||||
# SourceIndex instance
|
||||
#
|
||||
def from_gems_in(*spec_dirs)
|
||||
self.new.load_gems_in(*spec_dirs)
|
||||
end
|
||||
|
||||
# Load a specification from a file (eval'd Ruby code)
|
||||
#
|
||||
# file_name:: [String] The .gemspec file
|
||||
# return:: Specification instance or nil if an error occurs
|
||||
#
|
||||
def load_specification(file_name)
|
||||
begin
|
||||
spec_code = File.read(file_name).untaint
|
||||
gemspec = eval spec_code, binding, file_name
|
||||
if gemspec.is_a?(Gem::Specification)
|
||||
gemspec.loaded_from = file_name
|
||||
return gemspec
|
||||
end
|
||||
alert_warning "File '#{file_name}' does not evaluate to a gem specification"
|
||||
rescue SyntaxError => e
|
||||
alert_warning e
|
||||
alert_warning spec_code
|
||||
rescue Exception => e
|
||||
alert_warning(e.inspect.to_s + "\n" + spec_code)
|
||||
alert_warning "Invalid .gemspec format in '#{file_name}'"
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Instance Methods -----------------------------------------------
|
||||
|
||||
# Constructs a source index instance from the provided
|
||||
# specifications
|
||||
##
|
||||
# Factory method to construct a source index instance for a given
|
||||
# path.
|
||||
#
|
||||
# specifications::
|
||||
# [Hash] hash of [Gem name, Gem::Specification] pairs
|
||||
#
|
||||
def initialize(specifications={})
|
||||
@gems = specifications
|
||||
end
|
||||
|
||||
# Reconstruct the source index from the list of source
|
||||
# directories.
|
||||
def load_gems_in(*spec_dirs)
|
||||
@gems.clear
|
||||
specs = Dir.glob File.join("{#{spec_dirs.join(',')}}", "*.gemspec")
|
||||
# deprecated::
|
||||
# If supplied, from_installed_gems will act just like
|
||||
# +from_gems_in+. This argument is deprecated and is provided
|
||||
# just for backwards compatibility, and should not generally
|
||||
# be used.
|
||||
#
|
||||
# return::
|
||||
# SourceIndex instance
|
||||
|
||||
specs.each do |file_name|
|
||||
gemspec = self.class.load_specification(file_name.untaint)
|
||||
add_spec(gemspec) if gemspec
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# Returns a Hash of name => Specification of the latest versions of each
|
||||
# gem in this index.
|
||||
def latest_specs
|
||||
result, latest = Hash.new { |h,k| h[k] = [] }, {}
|
||||
|
||||
self.each do |_, spec| # SourceIndex is not a hash, so we're stuck with each
|
||||
name = spec.name
|
||||
curr_ver = spec.version
|
||||
prev_ver = latest[name]
|
||||
|
||||
next unless prev_ver.nil? or curr_ver >= prev_ver
|
||||
|
||||
if prev_ver.nil? or curr_ver > prev_ver then
|
||||
result[name].clear
|
||||
latest[name] = curr_ver
|
||||
end
|
||||
|
||||
result[name] << spec
|
||||
end
|
||||
|
||||
result.values.flatten
|
||||
end
|
||||
|
||||
# Add a gem specification to the source index.
|
||||
def add_spec(gem_spec)
|
||||
@gems[gem_spec.full_name] = gem_spec
|
||||
end
|
||||
|
||||
# Remove a gem specification named +full_name+.
|
||||
def remove_spec(full_name)
|
||||
@gems.delete(full_name)
|
||||
end
|
||||
|
||||
# Iterate over the specifications in the source index.
|
||||
def each(&block) # :yields: gem.full_name, gem
|
||||
@gems.each(&block)
|
||||
end
|
||||
|
||||
# The gem specification given a full gem spec name.
|
||||
def specification(full_name)
|
||||
@gems[full_name]
|
||||
end
|
||||
|
||||
# The signature for the source index. Changes in the signature
|
||||
# indicate a change in the index.
|
||||
def index_signature
|
||||
require 'rubygems/digest/sha2'
|
||||
|
||||
Gem::SHA256.new.hexdigest(@gems.keys.sort.join(',')).to_s
|
||||
end
|
||||
|
||||
# The signature for the given gem specification.
|
||||
def gem_signature(gem_full_name)
|
||||
require 'rubygems/digest/sha2'
|
||||
|
||||
Gem::SHA256.new.hexdigest(@gems[gem_full_name].to_yaml).to_s
|
||||
end
|
||||
|
||||
def size
|
||||
@gems.size
|
||||
end
|
||||
alias length size
|
||||
|
||||
# Find a gem by an exact match on the short name.
|
||||
def find_name(gem_name, version_requirement = Gem::Requirement.default)
|
||||
search(/^#{gem_name}$/, version_requirement)
|
||||
end
|
||||
|
||||
# Search for a gem by Gem::Dependency +gem_pattern+. If +only_platform+
|
||||
# is true, only gems matching Gem::Platform.local will be returned. An
|
||||
# Array of matching Gem::Specification objects is returned.
|
||||
#
|
||||
# For backwards compatibility, a String or Regexp pattern may be passed as
|
||||
# +gem_pattern+, and a Gem::Requirement for +platform_only+. This
|
||||
# behavior is deprecated and will be removed.
|
||||
def search(gem_pattern, platform_only = false)
|
||||
version_requirement = nil
|
||||
only_platform = false
|
||||
|
||||
case gem_pattern # TODO warn after 2008/03, remove three months after
|
||||
when Regexp then
|
||||
version_requirement = platform_only || Gem::Requirement.default
|
||||
when Gem::Dependency then
|
||||
only_platform = platform_only
|
||||
version_requirement = gem_pattern.version_requirements
|
||||
gem_pattern = if gem_pattern.name.empty? then
|
||||
//
|
||||
else
|
||||
/^#{Regexp.escape gem_pattern.name}$/
|
||||
end
|
||||
def from_installed_gems(*deprecated)
|
||||
if deprecated.empty?
|
||||
from_gems_in(*installed_spec_directories)
|
||||
else
|
||||
version_requirement = platform_only || Gem::Requirement.default
|
||||
gem_pattern = /#{gem_pattern}/i
|
||||
from_gems_in(*deprecated) # HACK warn
|
||||
end
|
||||
|
||||
unless Gem::Requirement === version_requirement then
|
||||
version_requirement = Gem::Requirement.create version_requirement
|
||||
end
|
||||
|
||||
specs = @gems.values.select do |spec|
|
||||
spec.name =~ gem_pattern and
|
||||
version_requirement.satisfied_by? spec.version
|
||||
end
|
||||
|
||||
if only_platform then
|
||||
specs = specs.select do |spec|
|
||||
Gem::Platform.match spec.platform
|
||||
end
|
||||
end
|
||||
|
||||
specs.sort_by { |s| s.sort_obj }
|
||||
end
|
||||
|
||||
# Refresh the source index from the local file system.
|
||||
#
|
||||
# return:: Returns a pointer to itself.
|
||||
#
|
||||
def refresh!
|
||||
load_gems_in(self.class.installed_spec_directories)
|
||||
##
|
||||
# Return a list of directories in the current gem path that
|
||||
# contain specifications.
|
||||
#
|
||||
# return::
|
||||
# List of directory paths (all ending in "../specifications").
|
||||
|
||||
def installed_spec_directories
|
||||
Gem.path.collect { |dir| File.join(dir, "specifications") }
|
||||
end
|
||||
|
||||
# Returns an Array of Gem::Specifications that are not up to date.
|
||||
#
|
||||
def outdated
|
||||
dep = Gem::Dependency.new '', Gem::Requirement.default
|
||||
##
|
||||
# Creates a new SourceIndex from the ruby format gem specifications in
|
||||
# +spec_dirs+.
|
||||
|
||||
remotes = Gem::SourceInfoCache.search dep, true
|
||||
|
||||
outdateds = []
|
||||
|
||||
latest_specs.each do |local|
|
||||
name = local.name
|
||||
remote = remotes.select { |spec| spec.name == name }.
|
||||
sort_by { |spec| spec.version.to_ints }.
|
||||
last
|
||||
outdateds << name if remote and local.version < remote.version
|
||||
end
|
||||
|
||||
outdateds
|
||||
def from_gems_in(*spec_dirs)
|
||||
self.new.load_gems_in(*spec_dirs)
|
||||
end
|
||||
|
||||
def update(source_uri)
|
||||
use_incremental = false
|
||||
##
|
||||
# Loads a ruby-format specification from +file_name+ and returns the
|
||||
# loaded spec.
|
||||
|
||||
def load_specification(file_name)
|
||||
begin
|
||||
gem_names = fetch_quick_index source_uri
|
||||
remove_extra gem_names
|
||||
missing_gems = find_missing gem_names
|
||||
spec_code = File.read(file_name).untaint
|
||||
gemspec = eval spec_code, binding, file_name
|
||||
if gemspec.is_a?(Gem::Specification)
|
||||
gemspec.loaded_from = file_name
|
||||
return gemspec
|
||||
end
|
||||
alert_warning "File '#{file_name}' does not evaluate to a gem specification"
|
||||
rescue SyntaxError => e
|
||||
alert_warning e
|
||||
alert_warning spec_code
|
||||
rescue Exception => e
|
||||
alert_warning(e.inspect.to_s + "\n" + spec_code)
|
||||
alert_warning "Invalid .gemspec format in '#{file_name}'"
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
return false if missing_gems.size.zero?
|
||||
end
|
||||
|
||||
say "missing #{missing_gems.size} gems" if
|
||||
missing_gems.size > 0 and Gem.configuration.really_verbose
|
||||
##
|
||||
# Constructs a source index instance from the provided
|
||||
# specifications
|
||||
#
|
||||
# specifications::
|
||||
# [Hash] hash of [Gem name, Gem::Specification] pairs
|
||||
|
||||
use_incremental = missing_gems.size <= Gem.configuration.bulk_threshold
|
||||
rescue Gem::OperationNotSupportedError => ex
|
||||
alert_error "Falling back to bulk fetch: #{ex.message}" if
|
||||
Gem.configuration.really_verbose
|
||||
use_incremental = false
|
||||
def initialize(specifications={})
|
||||
@gems = specifications
|
||||
end
|
||||
|
||||
##
|
||||
# Reconstruct the source index from the specifications in +spec_dirs+.
|
||||
|
||||
def load_gems_in(*spec_dirs)
|
||||
@gems.clear
|
||||
|
||||
spec_dirs.reverse_each do |spec_dir|
|
||||
spec_files = Dir.glob File.join(spec_dir, '*.gemspec')
|
||||
|
||||
spec_files.each do |spec_file|
|
||||
gemspec = self.class.load_specification spec_file.untaint
|
||||
add_spec gemspec if gemspec
|
||||
end
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a Hash of name => Specification of the latest versions of each
|
||||
# gem in this index.
|
||||
|
||||
def latest_specs
|
||||
result = Hash.new { |h,k| h[k] = [] }
|
||||
latest = {}
|
||||
|
||||
sort.each do |_, spec|
|
||||
name = spec.name
|
||||
curr_ver = spec.version
|
||||
prev_ver = latest.key?(name) ? latest[name].version : nil
|
||||
|
||||
next unless prev_ver.nil? or curr_ver >= prev_ver or
|
||||
latest[name].platform != Gem::Platform::RUBY
|
||||
|
||||
if prev_ver.nil? or
|
||||
(curr_ver > prev_ver and spec.platform == Gem::Platform::RUBY) then
|
||||
result[name].clear
|
||||
latest[name] = spec
|
||||
end
|
||||
|
||||
if use_incremental then
|
||||
update_with_missing(source_uri, missing_gems)
|
||||
else
|
||||
new_index = fetch_bulk_index(source_uri)
|
||||
@gems.replace(new_index.gems)
|
||||
if spec.platform != Gem::Platform::RUBY then
|
||||
result[name].delete_if do |result_spec|
|
||||
result_spec.platform == spec.platform
|
||||
end
|
||||
end
|
||||
|
||||
true
|
||||
result[name] << spec
|
||||
end
|
||||
|
||||
def ==(other) # :nodoc:
|
||||
self.class === other and @gems == other.gems
|
||||
result.values.flatten
|
||||
end
|
||||
|
||||
##
|
||||
# Add a gem specification to the source index.
|
||||
|
||||
def add_spec(gem_spec)
|
||||
@gems[gem_spec.full_name] = gem_spec
|
||||
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+.
|
||||
|
||||
def remove_spec(full_name)
|
||||
@gems.delete(full_name)
|
||||
end
|
||||
|
||||
##
|
||||
# Iterate over the specifications in the source index.
|
||||
|
||||
def each(&block) # :yields: gem.full_name, gem
|
||||
@gems.each(&block)
|
||||
end
|
||||
|
||||
##
|
||||
# The gem specification given a full gem spec name.
|
||||
|
||||
def specification(full_name)
|
||||
@gems[full_name]
|
||||
end
|
||||
|
||||
##
|
||||
# The signature for the source index. Changes in the signature indicate a
|
||||
# change in the index.
|
||||
|
||||
def index_signature
|
||||
require 'rubygems/digest/sha2'
|
||||
|
||||
Gem::SHA256.new.hexdigest(@gems.keys.sort.join(',')).to_s
|
||||
end
|
||||
|
||||
##
|
||||
# The signature for the given gem specification.
|
||||
|
||||
def gem_signature(gem_full_name)
|
||||
require 'rubygems/digest/sha2'
|
||||
|
||||
Gem::SHA256.new.hexdigest(@gems[gem_full_name].to_yaml).to_s
|
||||
end
|
||||
|
||||
def size
|
||||
@gems.size
|
||||
end
|
||||
alias length size
|
||||
|
||||
##
|
||||
# Find a gem by an exact match on the short name.
|
||||
|
||||
def find_name(gem_name, version_requirement = Gem::Requirement.default)
|
||||
search(/^#{gem_name}$/, version_requirement)
|
||||
end
|
||||
|
||||
##
|
||||
# Search for a gem by Gem::Dependency +gem_pattern+. If +only_platform+
|
||||
# is true, only gems matching Gem::Platform.local will be returned. An
|
||||
# Array of matching Gem::Specification objects is returned.
|
||||
#
|
||||
# For backwards compatibility, a String or Regexp pattern may be passed as
|
||||
# +gem_pattern+, and a Gem::Requirement for +platform_only+. This
|
||||
# behavior is deprecated and will be removed.
|
||||
|
||||
def search(gem_pattern, platform_only = false)
|
||||
version_requirement = nil
|
||||
only_platform = false
|
||||
|
||||
case gem_pattern # TODO warn after 2008/03, remove three months after
|
||||
when Regexp then
|
||||
version_requirement = platform_only || Gem::Requirement.default
|
||||
when Gem::Dependency then
|
||||
only_platform = platform_only
|
||||
version_requirement = gem_pattern.version_requirements
|
||||
gem_pattern = if gem_pattern.name.empty? then
|
||||
//
|
||||
else
|
||||
/^#{Regexp.escape gem_pattern.name}$/
|
||||
end
|
||||
else
|
||||
version_requirement = platform_only || Gem::Requirement.default
|
||||
gem_pattern = /#{gem_pattern}/i
|
||||
end
|
||||
|
||||
def dump
|
||||
Marshal.dump(self)
|
||||
unless Gem::Requirement === version_requirement then
|
||||
version_requirement = Gem::Requirement.create version_requirement
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :gems
|
||||
|
||||
private
|
||||
|
||||
def fetcher
|
||||
require 'rubygems/remote_fetcher'
|
||||
|
||||
Gem::RemoteFetcher.fetcher
|
||||
specs = @gems.values.select do |spec|
|
||||
spec.name =~ gem_pattern and
|
||||
version_requirement.satisfied_by? spec.version
|
||||
end
|
||||
|
||||
def fetch_index_from(source_uri)
|
||||
@fetch_error = nil
|
||||
if only_platform then
|
||||
specs = specs.select do |spec|
|
||||
Gem::Platform.match spec.platform
|
||||
end
|
||||
end
|
||||
|
||||
indexes = %W[
|
||||
specs.sort_by { |s| s.sort_obj }
|
||||
end
|
||||
|
||||
##
|
||||
# Refresh the source index from the local file system.
|
||||
#
|
||||
# return:: Returns a pointer to itself.
|
||||
|
||||
def refresh!
|
||||
load_gems_in(self.class.installed_spec_directories)
|
||||
end
|
||||
|
||||
##
|
||||
# Returns an Array of Gem::Specifications that are not up to date.
|
||||
|
||||
def outdated
|
||||
dep = Gem::Dependency.new '', Gem::Requirement.default
|
||||
|
||||
remotes = Gem::SourceInfoCache.search dep, true
|
||||
|
||||
outdateds = []
|
||||
|
||||
latest_specs.each do |local|
|
||||
name = local.name
|
||||
remote = remotes.select { |spec| spec.name == name }.
|
||||
sort_by { |spec| spec.version.to_ints }.
|
||||
last
|
||||
|
||||
outdateds << name if remote and local.version < remote.version
|
||||
end
|
||||
|
||||
outdateds
|
||||
end
|
||||
|
||||
##
|
||||
# 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
|
||||
|
||||
begin
|
||||
gem_names = fetch_quick_index source_uri, all
|
||||
remove_extra gem_names
|
||||
missing_gems = find_missing gem_names
|
||||
|
||||
return false if missing_gems.size.zero?
|
||||
|
||||
say "Missing metadata for #{missing_gems.size} gems" if
|
||||
missing_gems.size > 0 and Gem.configuration.really_verbose
|
||||
|
||||
use_incremental = missing_gems.size <= Gem.configuration.bulk_threshold
|
||||
rescue Gem::OperationNotSupportedError => ex
|
||||
alert_error "Falling back to bulk fetch: #{ex.message}" if
|
||||
Gem.configuration.really_verbose
|
||||
use_incremental = false
|
||||
end
|
||||
|
||||
if use_incremental then
|
||||
update_with_missing(source_uri, missing_gems)
|
||||
else
|
||||
new_index = fetch_bulk_index(source_uri)
|
||||
@gems.replace(new_index.gems)
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def ==(other) # :nodoc:
|
||||
self.class === other and @gems == other.gems
|
||||
end
|
||||
|
||||
def dump
|
||||
Marshal.dump(self)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :gems
|
||||
|
||||
private
|
||||
|
||||
def fetcher
|
||||
require 'rubygems/remote_fetcher'
|
||||
|
||||
Gem::RemoteFetcher.fetcher
|
||||
end
|
||||
|
||||
def fetch_index_from(source_uri)
|
||||
@fetch_error = nil
|
||||
|
||||
indexes = %W[
|
||||
Marshal.#{Gem.marshal_version}.Z
|
||||
Marshal.#{Gem.marshal_version}
|
||||
yaml.Z
|
||||
yaml
|
||||
]
|
||||
|
||||
indexes.each do |name|
|
||||
spec_data = nil
|
||||
begin
|
||||
spec_data = fetcher.fetch_path("#{source_uri}/#{name}")
|
||||
spec_data = unzip(spec_data) if name =~ /\.Z$/
|
||||
if name =~ /Marshal/ then
|
||||
return Marshal.load(spec_data)
|
||||
else
|
||||
return YAML.load(spec_data)
|
||||
end
|
||||
rescue => e
|
||||
if Gem.configuration.really_verbose then
|
||||
alert_error "Unable to fetch #{name}: #{e.message}"
|
||||
end
|
||||
@fetch_error = e
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def fetch_bulk_index(source_uri)
|
||||
say "Bulk updating Gem source index for: #{source_uri}"
|
||||
|
||||
index = fetch_index_from(source_uri)
|
||||
if index.nil? then
|
||||
raise Gem::RemoteSourceException,
|
||||
"Error fetching remote gem cache: #{@fetch_error}"
|
||||
end
|
||||
@fetch_error = nil
|
||||
index
|
||||
end
|
||||
|
||||
# Get the quick index needed for incremental updates.
|
||||
def fetch_quick_index(source_uri)
|
||||
zipped_index = fetcher.fetch_path source_uri + '/quick/index.rz'
|
||||
unzip(zipped_index).split("\n")
|
||||
rescue ::Exception => ex
|
||||
raise Gem::OperationNotSupportedError,
|
||||
"No quick index found: " + ex.message
|
||||
end
|
||||
|
||||
# Make a list of full names for all the missing gemspecs.
|
||||
def find_missing(spec_names)
|
||||
spec_names.find_all { |full_name|
|
||||
specification(full_name).nil?
|
||||
}
|
||||
end
|
||||
|
||||
def remove_extra(spec_names)
|
||||
dictionary = spec_names.inject({}) { |h, k| h[k] = true; h }
|
||||
each do |name, spec|
|
||||
remove_spec name unless dictionary.include? name
|
||||
end
|
||||
end
|
||||
|
||||
# Unzip the given string.
|
||||
def unzip(string)
|
||||
require 'zlib'
|
||||
Zlib::Inflate.inflate(string)
|
||||
end
|
||||
|
||||
# Tries to fetch Marshal representation first, then YAML
|
||||
def fetch_single_spec(source_uri, spec_name)
|
||||
@fetch_error = nil
|
||||
indexes.each do |name|
|
||||
spec_data = nil
|
||||
index = source_uri + name
|
||||
begin
|
||||
marshal_uri = source_uri + "/quick/Marshal.#{Gem.marshal_version}/#{spec_name}.gemspec.rz"
|
||||
zipped = fetcher.fetch_path marshal_uri
|
||||
return Marshal.load(unzip(zipped))
|
||||
rescue => ex
|
||||
@fetch_error = ex
|
||||
if Gem.configuration.really_verbose then
|
||||
say "unable to fetch marshal gemspec #{marshal_uri}: #{ex.class} - #{ex}"
|
||||
end
|
||||
end
|
||||
spec_data = fetcher.fetch_path index
|
||||
spec_data = unzip(spec_data) if name =~ /\.Z$/
|
||||
|
||||
begin
|
||||
yaml_uri = source_uri + "/quick/#{spec_name}.gemspec.rz"
|
||||
zipped = fetcher.fetch_path yaml_uri
|
||||
return YAML.load(unzip(zipped))
|
||||
rescue => ex
|
||||
@fetch_error = ex
|
||||
if Gem.configuration.really_verbose then
|
||||
say "unable to fetch YAML gemspec #{yaml_uri}: #{ex.class} - #{ex}"
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
# Update the cached source index with the missing names.
|
||||
def update_with_missing(source_uri, missing_names)
|
||||
progress = ui.progress_reporter(missing_names.size,
|
||||
"Updating metadata for #{missing_names.size} gems from #{source_uri}")
|
||||
missing_names.each do |spec_name|
|
||||
gemspec = fetch_single_spec(source_uri, spec_name)
|
||||
if gemspec.nil? then
|
||||
ui.say "Failed to download spec #{spec_name} from #{source_uri}:\n" \
|
||||
"\t#{@fetch_error.message}"
|
||||
if name =~ /Marshal/ then
|
||||
return Marshal.load(spec_data)
|
||||
else
|
||||
add_spec gemspec
|
||||
progress.updated spec_name
|
||||
return YAML.load(spec_data)
|
||||
end
|
||||
@fetch_error = nil
|
||||
rescue => e
|
||||
if Gem.configuration.really_verbose then
|
||||
alert_error "Unable to fetch #{name}: #{e.message}"
|
||||
end
|
||||
|
||||
@fetch_error = e
|
||||
end
|
||||
progress.done
|
||||
progress.count
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
# Cache is an alias for SourceIndex to allow older YAMLized source
|
||||
# index objects to load properly.
|
||||
Cache = SourceIndex
|
||||
def fetch_bulk_index(source_uri)
|
||||
say "Bulk updating Gem source index for: #{source_uri}"
|
||||
|
||||
index = fetch_index_from(source_uri)
|
||||
if index.nil? then
|
||||
raise Gem::RemoteSourceException,
|
||||
"Error fetching remote gem cache: #{@fetch_error}"
|
||||
end
|
||||
@fetch_error = nil
|
||||
index
|
||||
end
|
||||
|
||||
##
|
||||
# Get the quick index needed for incremental updates.
|
||||
|
||||
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")
|
||||
rescue ::Exception => e
|
||||
unless all then
|
||||
say "Latest index not found, using quick index" if
|
||||
Gem.configuration.really_verbose
|
||||
|
||||
fetch_quick_index source_uri, true
|
||||
else
|
||||
raise Gem::OperationNotSupportedError,
|
||||
"No quick index found: #{e.message}"
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Make a list of full names for all the missing gemspecs.
|
||||
|
||||
def find_missing(spec_names)
|
||||
spec_names.find_all { |full_name|
|
||||
specification(full_name).nil?
|
||||
}
|
||||
end
|
||||
|
||||
def remove_extra(spec_names)
|
||||
dictionary = spec_names.inject({}) { |h, k| h[k] = true; h }
|
||||
each do |name, spec|
|
||||
remove_spec name unless dictionary.include? name
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Unzip the given string.
|
||||
|
||||
def unzip(string)
|
||||
require 'zlib'
|
||||
Zlib::Inflate.inflate(string)
|
||||
end
|
||||
|
||||
##
|
||||
# Tries to fetch Marshal representation first, then YAML
|
||||
|
||||
def fetch_single_spec(source_uri, spec_name)
|
||||
@fetch_error = nil
|
||||
|
||||
begin
|
||||
marshal_uri = source_uri + "quick/Marshal.#{Gem.marshal_version}/#{spec_name}.gemspec.rz"
|
||||
zipped = fetcher.fetch_path marshal_uri
|
||||
return Marshal.load(unzip(zipped))
|
||||
rescue => ex
|
||||
@fetch_error = ex
|
||||
|
||||
if Gem.configuration.really_verbose then
|
||||
say "unable to fetch marshal gemspec #{marshal_uri}: #{ex.class} - #{ex}"
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
yaml_uri = source_uri + "quick/#{spec_name}.gemspec.rz"
|
||||
zipped = fetcher.fetch_path yaml_uri
|
||||
return YAML.load(unzip(zipped))
|
||||
rescue => ex
|
||||
@fetch_error = ex
|
||||
if Gem.configuration.really_verbose then
|
||||
say "unable to fetch YAML gemspec #{yaml_uri}: #{ex.class} - #{ex}"
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
##
|
||||
# Update the cached source index with the missing names.
|
||||
|
||||
def update_with_missing(source_uri, missing_names)
|
||||
progress = ui.progress_reporter(missing_names.size,
|
||||
"Updating metadata for #{missing_names.size} gems from #{source_uri}")
|
||||
missing_names.each do |spec_name|
|
||||
gemspec = fetch_single_spec(source_uri, spec_name)
|
||||
if gemspec.nil? then
|
||||
ui.say "Failed to download spec #{spec_name} from #{source_uri}:\n" \
|
||||
"\t#{@fetch_error.message}"
|
||||
else
|
||||
add_spec gemspec
|
||||
progress.updated spec_name
|
||||
end
|
||||
@fetch_error = nil
|
||||
end
|
||||
progress.done
|
||||
progress.count
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module Gem
|
||||
|
||||
# :stopdoc:
|
||||
|
||||
# Cache is an alias for SourceIndex to allow older YAMLized source index
|
||||
# objects to load properly.
|
||||
Cache = SourceIndex
|
||||
|
||||
# :starddoc:
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ require 'rubygems'
|
|||
require 'rubygems/source_info_cache_entry'
|
||||
require 'rubygems/user_interaction'
|
||||
|
||||
##
|
||||
# SourceInfoCache stores a copy of the gem index for each gem source.
|
||||
#
|
||||
# There are two possible cache locations, the system cache and the user cache:
|
||||
|
@ -25,7 +26,7 @@ require 'rubygems/user_interaction'
|
|||
# @source_index => Gem::SourceIndex
|
||||
# ...
|
||||
# }
|
||||
#
|
||||
|
||||
class Gem::SourceInfoCache
|
||||
|
||||
include Gem::UserInteraction
|
||||
|
@ -37,7 +38,7 @@ class Gem::SourceInfoCache
|
|||
def self.cache
|
||||
return @cache if @cache
|
||||
@cache = new
|
||||
@cache.refresh if Gem.configuration.update_sources
|
||||
@cache.refresh false if Gem.configuration.update_sources
|
||||
@cache
|
||||
end
|
||||
|
||||
|
@ -45,86 +46,178 @@ class Gem::SourceInfoCache
|
|||
cache.cache_data
|
||||
end
|
||||
|
||||
# Search all source indexes for +pattern+.
|
||||
def self.search(pattern, platform_only = false)
|
||||
cache.search pattern, platform_only
|
||||
##
|
||||
# The name of the system cache file.
|
||||
|
||||
def self.latest_system_cache_file
|
||||
File.join File.dirname(system_cache_file),
|
||||
"latest_#{File.basename system_cache_file}"
|
||||
end
|
||||
|
||||
# Search all source indexes for +pattern+. Only returns gems matching
|
||||
# Gem.platforms when +only_platform+ is true. See #search_with_source.
|
||||
def self.search_with_source(pattern, only_platform = false)
|
||||
cache.search_with_source(pattern, only_platform)
|
||||
##
|
||||
# The name of the latest user cache file.
|
||||
|
||||
def self.latest_user_cache_file
|
||||
File.join File.dirname(user_cache_file),
|
||||
"latest_#{File.basename user_cache_file}"
|
||||
end
|
||||
|
||||
##
|
||||
# Search all source indexes. See Gem::SourceInfoCache#search.
|
||||
|
||||
def self.search(*args)
|
||||
cache.search(*args)
|
||||
end
|
||||
|
||||
##
|
||||
# Search all source indexes returning the source_uri. See
|
||||
# Gem::SourceInfoCache#search_with_source.
|
||||
|
||||
def self.search_with_source(*args)
|
||||
cache.search_with_source(*args)
|
||||
end
|
||||
|
||||
##
|
||||
# The name of the system cache file. (class method)
|
||||
|
||||
def self.system_cache_file
|
||||
@system_cache_file ||= Gem.default_system_source_cache_dir
|
||||
end
|
||||
|
||||
##
|
||||
# The name of the user cache file.
|
||||
|
||||
def self.user_cache_file
|
||||
@user_cache_file ||=
|
||||
ENV['GEMCACHE'] || Gem.default_user_source_cache_dir
|
||||
end
|
||||
|
||||
def initialize # :nodoc:
|
||||
@cache_data = nil
|
||||
@cache_file = nil
|
||||
@dirty = false
|
||||
@only_latest = true
|
||||
end
|
||||
|
||||
##
|
||||
# The most recent cache data.
|
||||
|
||||
def cache_data
|
||||
return @cache_data if @cache_data
|
||||
cache_file # HACK writable check
|
||||
|
||||
begin
|
||||
# Marshal loads 30-40% faster from a String, and 2MB on 20061116 is small
|
||||
data = File.open cache_file, 'rb' do |fp| fp.read end
|
||||
@cache_data = Marshal.load data
|
||||
@only_latest = true
|
||||
|
||||
@cache_data.each do |url, sice|
|
||||
next unless sice.is_a?(Hash)
|
||||
update
|
||||
cache = sice['cache']
|
||||
size = sice['size']
|
||||
if cache.is_a?(Gem::SourceIndex) and size.is_a?(Numeric) then
|
||||
new_sice = Gem::SourceInfoCacheEntry.new cache, size
|
||||
@cache_data[url] = new_sice
|
||||
else # irreperable, force refetch.
|
||||
reset_cache_for(url)
|
||||
end
|
||||
end
|
||||
@cache_data
|
||||
rescue => e
|
||||
if Gem.configuration.really_verbose then
|
||||
say "Exception during cache_data handling: #{ex.class} - #{ex}"
|
||||
say "Cache file was: #{cache_file}"
|
||||
say "\t#{e.backtrace.join "\n\t"}"
|
||||
end
|
||||
reset_cache_data
|
||||
end
|
||||
end
|
||||
@cache_data = read_cache_data latest_cache_file
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
def reset_cache_data
|
||||
@cache_data = {}
|
||||
end
|
||||
##
|
||||
# The name of the cache file.
|
||||
|
||||
# The name of the cache file to be read
|
||||
def cache_file
|
||||
return @cache_file if @cache_file
|
||||
@cache_file = (try_file(system_cache_file) or
|
||||
try_file(user_cache_file) or
|
||||
raise "unable to locate a writable cache file")
|
||||
end
|
||||
|
||||
##
|
||||
# Force cache file to be reset, useful for integration testing of rubygems
|
||||
|
||||
def reset_cache_file
|
||||
@cache_file = nil
|
||||
end
|
||||
|
||||
##
|
||||
# Write the cache to a local file (if it is dirty).
|
||||
|
||||
def flush
|
||||
write_cache if @dirty
|
||||
@dirty = false
|
||||
end
|
||||
|
||||
# Refreshes each source in the cache from its repository.
|
||||
def refresh
|
||||
def latest_cache_data
|
||||
latest_cache_data = {}
|
||||
|
||||
cache_data.each do |repo, sice|
|
||||
latest = sice.source_index.latest_specs
|
||||
|
||||
new_si = Gem::SourceIndex.new
|
||||
new_si.add_specs(*latest)
|
||||
|
||||
latest_sice = Gem::SourceInfoCacheEntry.new new_si, sice.size
|
||||
latest_cache_data[repo] = latest_sice
|
||||
end
|
||||
|
||||
latest_cache_data
|
||||
end
|
||||
|
||||
##
|
||||
# The name of the latest cache file.
|
||||
|
||||
def latest_cache_file
|
||||
File.join File.dirname(cache_file), "latest_#{File.basename cache_file}"
|
||||
end
|
||||
|
||||
##
|
||||
# The name of the latest system cache file.
|
||||
|
||||
def latest_system_cache_file
|
||||
self.class.latest_system_cache_file
|
||||
end
|
||||
|
||||
##
|
||||
# The name of the latest user cache file.
|
||||
|
||||
def latest_user_cache_file
|
||||
self.class.latest_user_cache_file
|
||||
end
|
||||
|
||||
def read_all_cache_data
|
||||
if @only_latest then
|
||||
@only_latest = false
|
||||
@cache_data = read_cache_data cache_file
|
||||
end
|
||||
end
|
||||
|
||||
def read_cache_data(file)
|
||||
# Marshal loads 30-40% faster from a String, and 2MB on 20061116 is small
|
||||
data = open file, 'rb' do |fp| fp.read end
|
||||
cache_data = Marshal.load data
|
||||
|
||||
cache_data.each do |url, sice|
|
||||
next unless sice.is_a?(Hash)
|
||||
update
|
||||
|
||||
cache = sice['cache']
|
||||
size = sice['size']
|
||||
|
||||
if cache.is_a?(Gem::SourceIndex) and size.is_a?(Numeric) then
|
||||
new_sice = Gem::SourceInfoCacheEntry.new cache, size
|
||||
cache_data[url] = new_sice
|
||||
else # irreperable, force refetch.
|
||||
reset_cache_for url, cache_data
|
||||
end
|
||||
end
|
||||
|
||||
cache_data
|
||||
rescue => e
|
||||
if Gem.configuration.really_verbose then
|
||||
say "Exception during cache_data handling: #{e.class} - #{e}"
|
||||
say "Cache file was: #{file}"
|
||||
say "\t#{e.backtrace.join "\n\t"}"
|
||||
end
|
||||
|
||||
{}
|
||||
end
|
||||
|
||||
##
|
||||
# Refreshes each source in the cache from its repository. If +all+ is
|
||||
# false, only latest gems are updated.
|
||||
|
||||
def refresh(all)
|
||||
Gem.sources.each do |source_uri|
|
||||
cache_entry = cache_data[source_uri]
|
||||
if cache_entry.nil? then
|
||||
|
@ -132,14 +225,34 @@ class Gem::SourceInfoCache
|
|||
cache_data[source_uri] = cache_entry
|
||||
end
|
||||
|
||||
update if cache_entry.refresh source_uri
|
||||
update if cache_entry.refresh source_uri, all
|
||||
end
|
||||
|
||||
flush
|
||||
end
|
||||
|
||||
# Searches all source indexes for +pattern+.
|
||||
def search(pattern, platform_only = false)
|
||||
def reset_cache_for(url, cache_data)
|
||||
say "Reseting cache for #{url}" if Gem.configuration.really_verbose
|
||||
|
||||
sice = Gem::SourceInfoCacheEntry.new Gem::SourceIndex.new, 0
|
||||
sice.refresh url, false # HACK may be unnecessary, see ::cache and #refresh
|
||||
|
||||
cache_data[url] = sice
|
||||
cache_data
|
||||
end
|
||||
|
||||
def reset_cache_data
|
||||
@cache_data = nil
|
||||
end
|
||||
|
||||
##
|
||||
# Searches all source indexes. See Gem::SourceIndex#search for details on
|
||||
# +pattern+ and +platform_only+. If +all+ is set to true, the full index
|
||||
# will be loaded before searching.
|
||||
|
||||
def search(pattern, platform_only = false, all = false)
|
||||
read_all_cache_data if all
|
||||
|
||||
cache_data.map do |source_uri, sic_entry|
|
||||
next unless Gem.sources.include? source_uri
|
||||
sic_entry.source_index.search pattern, platform_only
|
||||
|
@ -150,7 +263,9 @@ class Gem::SourceInfoCache
|
|||
# only gems matching Gem.platforms will be selected. Returns an Array of
|
||||
# pairs containing the Gem::Specification found and the source_uri it was
|
||||
# found at.
|
||||
def search_with_source(pattern, only_platform = false)
|
||||
def search_with_source(pattern, only_platform = false, all = false)
|
||||
read_all_cache_data if all
|
||||
|
||||
results = []
|
||||
|
||||
cache_data.map do |source_uri, sic_entry|
|
||||
|
@ -164,69 +279,76 @@ class Gem::SourceInfoCache
|
|||
results
|
||||
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
|
||||
# testing when we don't want to read a file system to grab the cached source
|
||||
# index information. The +hash+ should map a source URL into a
|
||||
# SourceInfoCacheEntry.
|
||||
|
||||
def set_cache_data(hash)
|
||||
@cache_data = hash
|
||||
update
|
||||
end
|
||||
|
||||
private
|
||||
##
|
||||
# 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
|
||||
begin
|
||||
FileUtils.mkdir_p(dir)
|
||||
FileUtils.mkdir_p dir
|
||||
rescue RuntimeError, SystemCallError
|
||||
return nil
|
||||
end
|
||||
end
|
||||
if File.writable?(dir)
|
||||
File.open(fn, "wb") { |f| f << Marshal.dump({}) }
|
||||
return fn
|
||||
|
||||
if File.writable? dir then
|
||||
open path, "wb" do |io| io.write Marshal.dump({}) end
|
||||
return path
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
##
|
||||
# Mark the cache as updated (i.e. dirty).
|
||||
|
||||
def update
|
||||
@dirty = true
|
||||
end
|
||||
|
||||
##
|
||||
# The name of the user cache file.
|
||||
|
||||
def user_cache_file
|
||||
self.class.user_cache_file
|
||||
end
|
||||
|
||||
##
|
||||
# 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
|
||||
|
||||
|
|
|
@ -3,24 +3,31 @@ require 'rubygems/source_index'
|
|||
require 'rubygems/remote_fetcher'
|
||||
|
||||
##
|
||||
# Entrys held by a SourceInfoCache.
|
||||
# Entries held by a SourceInfoCache.
|
||||
|
||||
class Gem::SourceInfoCacheEntry
|
||||
|
||||
##
|
||||
# The source index for this cache entry.
|
||||
|
||||
attr_reader :source_index
|
||||
|
||||
##
|
||||
# The size of the of the source entry. Used to determine if the
|
||||
# source index has changed.
|
||||
|
||||
attr_reader :size
|
||||
|
||||
##
|
||||
# Create a cache entry.
|
||||
|
||||
def initialize(si, size)
|
||||
@source_index = si || Gem::SourceIndex.new({})
|
||||
@size = size
|
||||
@all = false
|
||||
end
|
||||
|
||||
def refresh(source_uri)
|
||||
def refresh(source_uri, all)
|
||||
begin
|
||||
marshal_uri = URI.join source_uri.to_s, "Marshal.#{Gem.marshal_version}"
|
||||
remote_size = Gem::RemoteFetcher.fetcher.fetch_size marshal_uri
|
||||
|
@ -29,9 +36,12 @@ class Gem::SourceInfoCacheEntry
|
|||
remote_size = Gem::RemoteFetcher.fetcher.fetch_size yaml_uri
|
||||
end
|
||||
|
||||
return false if @size == remote_size # TODO Use index_signature instead of size?
|
||||
updated = @source_index.update source_uri
|
||||
# TODO Use index_signature instead of size?
|
||||
return false if @size == remote_size and @all
|
||||
|
||||
updated = @source_index.update source_uri, all
|
||||
@size = remote_size
|
||||
@all = all
|
||||
|
||||
updated
|
||||
end
|
||||
|
|
|
@ -13,7 +13,7 @@ require 'rubygems/platform'
|
|||
if RUBY_VERSION < '1.9' then
|
||||
def Time.today
|
||||
t = Time.now
|
||||
t - ((t.to_i + t.gmt_offset) % 86400)
|
||||
t - ((t.to_f + t.gmt_offset) % 86400)
|
||||
end unless defined? Time.today
|
||||
end
|
||||
# :startdoc:
|
||||
|
|
|
@ -12,7 +12,7 @@ require 'rubygems/user_interaction'
|
|||
|
||||
##
|
||||
# An Uninstaller.
|
||||
#
|
||||
|
||||
class Gem::Uninstaller
|
||||
|
||||
include Gem::UserInteraction
|
||||
|
@ -21,8 +21,8 @@ class Gem::Uninstaller
|
|||
# Constructs an Uninstaller instance
|
||||
#
|
||||
# gem:: [String] The Gem name to uninstall
|
||||
#
|
||||
def initialize(gem, options)
|
||||
|
||||
def initialize(gem, options = {})
|
||||
@gem = gem
|
||||
@version = options[:version] || Gem::Requirement.default
|
||||
gem_home = options[:install_dir] || Gem.dir
|
||||
|
@ -30,12 +30,13 @@ class Gem::Uninstaller
|
|||
@force_executables = options[:executables]
|
||||
@force_all = options[:all]
|
||||
@force_ignore = options[:ignore]
|
||||
@bin_dir = options[:bin_dir]
|
||||
end
|
||||
|
||||
##
|
||||
# Performs the uninstall of the Gem. This removes the spec, the
|
||||
# Gem directory, and the cached .gem file,
|
||||
#
|
||||
|
||||
def uninstall
|
||||
list = Gem.source_index.search(/^#{@gem}$/, @version)
|
||||
|
||||
|
@ -66,18 +67,14 @@ class Gem::Uninstaller
|
|||
end
|
||||
|
||||
##
|
||||
# Remove executables and batch files (windows only) for the gem as
|
||||
# it is being installed
|
||||
#
|
||||
# gemspec::[Specification] the gem whose executables need to be removed.
|
||||
#
|
||||
# Removes installed executables and batch files (windows only) for
|
||||
# +gemspec+.
|
||||
|
||||
def remove_executables(gemspec)
|
||||
return if gemspec.nil?
|
||||
|
||||
if gemspec.executables.size > 0 then
|
||||
bindir = Gem.bindir @gem_home
|
||||
|
||||
raise Gem::FilePermissionError, bindir unless File.writable? bindir
|
||||
bindir = @bin_dir ? @bin_dir : (Gem.bindir @gem_home)
|
||||
|
||||
list = Gem.source_index.search(gemspec.name).delete_if { |spec|
|
||||
spec.version == gemspec.version
|
||||
|
@ -93,14 +90,19 @@ class Gem::Uninstaller
|
|||
|
||||
return if executables.size == 0
|
||||
|
||||
answer = @force_executables || ask_yes_no(
|
||||
"Remove executables:\n" \
|
||||
"\t#{gemspec.executables.join(", ")}\n\nin addition to the gem?",
|
||||
true) # " # appease ruby-mode - don't ask
|
||||
answer = if @force_executables.nil? then
|
||||
ask_yes_no("Remove executables:\n" \
|
||||
"\t#{gemspec.executables.join(", ")}\n\nin addition to the gem?",
|
||||
true) # " # appease ruby-mode - don't ask
|
||||
else
|
||||
@force_executables
|
||||
end
|
||||
|
||||
unless answer then
|
||||
say "Executables and scripts will remain installed."
|
||||
else
|
||||
raise Gem::FilePermissionError, bindir unless File.writable? bindir
|
||||
|
||||
gemspec.executables.each do |exe_name|
|
||||
say "Removing #{exe_name}"
|
||||
FileUtils.rm_f File.join(bindir, exe_name)
|
||||
|
@ -110,23 +112,22 @@ class Gem::Uninstaller
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Removes all gems in +list+.
|
||||
#
|
||||
# list:: the list of all gems to remove
|
||||
#
|
||||
# Warning: this method modifies the +list+ parameter. Once it has
|
||||
# uninstalled a gem, it is removed from that list.
|
||||
#
|
||||
# NOTE: removes uninstalled gems from +list+.
|
||||
|
||||
def remove_all(list)
|
||||
list.dup.each { |gem| remove(gem, list) }
|
||||
list.dup.each { |spec| remove spec, list }
|
||||
end
|
||||
|
||||
#
|
||||
##
|
||||
# spec:: the spec of the gem to be uninstalled
|
||||
# list:: the list of all such gems
|
||||
#
|
||||
# Warning: this method modifies the +list+ parameter. Once it has
|
||||
# uninstalled a gem, it is removed from that list.
|
||||
#
|
||||
|
||||
def remove(spec, list)
|
||||
unless dependencies_ok? spec then
|
||||
raise Gem::DependencyRemovalException,
|
||||
|
@ -134,10 +135,11 @@ class Gem::Uninstaller
|
|||
end
|
||||
|
||||
unless path_ok? spec then
|
||||
alert("In order to remove #{spec.name}, please execute:\n" \
|
||||
"\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}")
|
||||
raise Gem::GemNotInHomeException,
|
||||
e = Gem::GemNotInHomeException.new \
|
||||
"Gem is not installed in directory #{@gem_home}"
|
||||
e.spec = spec
|
||||
|
||||
raise e
|
||||
end
|
||||
|
||||
raise Gem::FilePermissionError, spec.installation_path unless
|
||||
|
@ -182,8 +184,8 @@ class Gem::Uninstaller
|
|||
def dependencies_ok?(spec)
|
||||
return true if @force_ignore
|
||||
|
||||
srcindex = Gem::SourceIndex.from_installed_gems
|
||||
deplist = Gem::DependencyList.from_source_index srcindex
|
||||
source_index = Gem::SourceIndex.from_installed_gems
|
||||
deplist = Gem::DependencyList.from_source_index source_index
|
||||
deplist.ok_to_remove?(spec.full_name) || ask_if_ok(spec)
|
||||
end
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ module Gem
|
|||
include DefaultUserInteraction
|
||||
[
|
||||
:choose_from_list, :ask, :ask_yes_no, :say, :alert, :alert_warning,
|
||||
:alert_error, :terminate_interaction!, :terminate_interaction
|
||||
:alert_error, :terminate_interaction
|
||||
].each do |methname|
|
||||
class_eval %{
|
||||
def #{methname}(*args)
|
||||
|
@ -182,16 +182,10 @@ module Gem
|
|||
ask(question) if question
|
||||
end
|
||||
|
||||
# Terminate the application immediately without running any exit
|
||||
# handlers.
|
||||
def terminate_interaction!(status=-1)
|
||||
exit!(status)
|
||||
end
|
||||
|
||||
# Terminate the appliation normally, running any exit handlers
|
||||
# that might have been defined.
|
||||
def terminate_interaction(status=0)
|
||||
exit(status)
|
||||
def terminate_interaction(status = 0)
|
||||
raise Gem::SystemExitException, status
|
||||
end
|
||||
|
||||
# Return a progress reporter object
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -10,9 +10,10 @@ at_exit { $SAFE = 1 }
|
|||
require 'fileutils'
|
||||
require 'test/unit'
|
||||
require 'tmpdir'
|
||||
require 'tempfile'
|
||||
require 'uri'
|
||||
require 'rubygems/gem_open_uri'
|
||||
require 'rubygems/source_info_cache'
|
||||
require 'rubygems/package'
|
||||
|
||||
require File.join(File.expand_path(File.dirname(__FILE__)), 'mockgemui')
|
||||
|
||||
|
@ -56,6 +57,20 @@ class FakeFetcher
|
|||
data.respond_to?(:call) ? data.call : data.length
|
||||
end
|
||||
|
||||
def download spec, source_uri, install_dir = Gem.dir
|
||||
name = "#{spec.full_name}.gem"
|
||||
path = File.join(install_dir, 'cache', name)
|
||||
|
||||
if source_uri =~ /^http/ then
|
||||
File.open(path, "wb") do |f|
|
||||
f.write fetch_path(File.join(source_uri, "gems", name))
|
||||
end
|
||||
else
|
||||
FileUtils.cp source_uri, path
|
||||
end
|
||||
|
||||
path
|
||||
end
|
||||
end
|
||||
|
||||
class RubyGemTestCase < Test::Unit::TestCase
|
||||
|
@ -76,6 +91,7 @@ class RubyGemTestCase < Test::Unit::TestCase
|
|||
@gemhome = File.join @tempdir, "gemhome"
|
||||
@gemcache = File.join(@gemhome, "source_cache")
|
||||
@usrcache = File.join(@gemhome, ".gem", "user_cache")
|
||||
@latest_usrcache = File.join(@gemhome, ".gem", "latest_user_cache")
|
||||
|
||||
FileUtils.mkdir_p @gemhome
|
||||
|
||||
|
@ -101,6 +117,11 @@ class RubyGemTestCase < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
@marshal_version = "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
|
||||
|
||||
@private_key = File.expand_path File.join(File.dirname(__FILE__),
|
||||
'private_key.pem')
|
||||
@public_cert = File.expand_path File.join(File.dirname(__FILE__),
|
||||
'public_cert.pem')
|
||||
end
|
||||
|
||||
def teardown
|
||||
|
@ -135,25 +156,52 @@ class RubyGemTestCase < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def prep_cache_files(lc)
|
||||
[ [lc.system_cache_file, 'sys'],
|
||||
[lc.user_cache_file, 'usr'],
|
||||
].each do |fn, data|
|
||||
FileUtils.mkdir_p File.dirname(fn).untaint
|
||||
open(fn.dup.untaint, "wb") { |f| f.write(Marshal.dump({'key' => data})) }
|
||||
@usr_si ||= Gem::SourceIndex.new
|
||||
@usr_sice ||= Gem::SourceInfoCacheEntry.new @usr_si, 0
|
||||
|
||||
@sys_si ||= Gem::SourceIndex.new
|
||||
@sys_sice ||= Gem::SourceInfoCacheEntry.new @sys_si, 0
|
||||
|
||||
latest_si = Gem::SourceIndex.new
|
||||
latest_si.add_specs(*@sys_si.latest_specs)
|
||||
latest_sys_sice = Gem::SourceInfoCacheEntry.new latest_si, 0
|
||||
|
||||
latest_si = Gem::SourceIndex.new
|
||||
latest_si.add_specs(*@usr_si.latest_specs)
|
||||
latest_usr_sice = Gem::SourceInfoCacheEntry.new latest_si, 0
|
||||
|
||||
[ [lc.system_cache_file, @sys_sice],
|
||||
[lc.latest_system_cache_file, latest_sys_sice],
|
||||
[lc.user_cache_file, @usr_sice],
|
||||
[lc.latest_user_cache_file, latest_usr_sice],
|
||||
].each do |filename, data|
|
||||
FileUtils.mkdir_p File.dirname(filename).untaint
|
||||
|
||||
open filename.dup.untaint, 'wb' do |f|
|
||||
f.write Marshal.dump({ @gem_repo => data })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def read_cache(fn)
|
||||
open(fn.dup.untaint) { |f| Marshal.load f.read }
|
||||
def read_cache(path)
|
||||
open path.dup.untaint, 'rb' do |io|
|
||||
Marshal.load io.read
|
||||
end
|
||||
end
|
||||
|
||||
def read_binary(path)
|
||||
Gem.read_binary path
|
||||
end
|
||||
|
||||
def write_file(path)
|
||||
path = File.join(@gemhome, path)
|
||||
dir = File.dirname path
|
||||
FileUtils.mkdir_p dir
|
||||
File.open(path, "w") { |io|
|
||||
yield(io)
|
||||
}
|
||||
|
||||
open path, 'wb' do |io|
|
||||
yield io
|
||||
end
|
||||
|
||||
path
|
||||
end
|
||||
|
||||
|
@ -204,6 +252,23 @@ class RubyGemTestCase < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def util_gem(name, version, &block)
|
||||
spec = quick_gem(name, version, &block)
|
||||
|
||||
util_build_gem spec
|
||||
|
||||
cache_file = File.join @tempdir, 'gems', "#{spec.original_name}.gem"
|
||||
FileUtils.mv File.join(@gemhome, 'cache', "#{spec.original_name}.gem"),
|
||||
cache_file
|
||||
FileUtils.rm File.join(@gemhome, 'specifications',
|
||||
"#{spec.full_name}.gemspec")
|
||||
|
||||
spec.loaded_from = nil
|
||||
spec.loaded = false
|
||||
|
||||
[spec, cache_file]
|
||||
end
|
||||
|
||||
def util_make_gems
|
||||
init = proc do |s|
|
||||
s.files = %w[lib/code.rb]
|
||||
|
@ -212,6 +277,7 @@ class RubyGemTestCase < Test::Unit::TestCase
|
|||
|
||||
@a1 = quick_gem('a', '1', &init)
|
||||
@a2 = quick_gem('a', '2', &init)
|
||||
@a_evil9 = quick_gem('a_evil', '9', &init)
|
||||
@b2 = quick_gem('b', '2', &init)
|
||||
@c1_2 = quick_gem('c', '1.2', &init)
|
||||
@pl1 = quick_gem 'pl', '1' do |s| # l for legacy
|
||||
|
@ -227,7 +293,7 @@ class RubyGemTestCase < Test::Unit::TestCase
|
|||
write_file File.join(*%W[gems #{@c1_2.original_name} lib code.rb]) do end
|
||||
write_file File.join(*%W[gems #{@pl1.original_name} lib code.rb]) do end
|
||||
|
||||
[@a1, @a2, @b2, @c1_2, @pl1].each { |spec| util_build_gem spec }
|
||||
[@a1, @a2, @a_evil9, @b2, @c1_2, @pl1].each { |spec| util_build_gem spec }
|
||||
|
||||
FileUtils.rm_r File.join(@gemhome, 'gems', @pl1.original_name)
|
||||
|
||||
|
@ -256,32 +322,19 @@ class RubyGemTestCase < Test::Unit::TestCase
|
|||
@fetcher = FakeFetcher.new
|
||||
@fetcher.uri = @uri
|
||||
|
||||
@gem1 = quick_gem 'gem_one' do |gem|
|
||||
gem.files = %w[Rakefile lib/gem_one.rb]
|
||||
end
|
||||
util_make_gems
|
||||
|
||||
@gem2 = quick_gem 'gem_two' do |gem|
|
||||
gem.files = %w[Rakefile lib/gem_two.rb]
|
||||
end
|
||||
|
||||
@gem3 = quick_gem 'gem_three' do |gem| # missing gem
|
||||
gem.files = %w[Rakefile lib/gem_three.rb]
|
||||
end
|
||||
|
||||
# this gem has a higher version and longer name than the gem we want
|
||||
@gem4 = quick_gem 'gem_one_evil', '666' do |gem|
|
||||
gem.files = %w[Rakefile lib/gem_one.rb]
|
||||
end
|
||||
|
||||
@all_gems = [@gem1, @gem2, @gem3, @gem4].sort
|
||||
@all_gems = [@a1, @a2, @a_evil9, @b2, @c1_2].sort
|
||||
@all_gem_names = @all_gems.map { |gem| gem.full_name }
|
||||
|
||||
gem_names = [@gem1.full_name, @gem2.full_name, @gem4.full_name]
|
||||
gem_names = [@a1.full_name, @a2.full_name, @b2.full_name]
|
||||
@gem_names = gem_names.sort.join("\n")
|
||||
|
||||
@source_index = Gem::SourceIndex.new @gem1.full_name => @gem1,
|
||||
@gem2.full_name => @gem2,
|
||||
@gem4.full_name => @gem4
|
||||
@source_index = Gem::SourceIndex.new
|
||||
@source_index.add_spec @a1
|
||||
@source_index.add_spec @a2
|
||||
@source_index.add_spec @a_evil9
|
||||
@source_index.add_spec @c1_2
|
||||
|
||||
Gem::RemoteFetcher.instance_variable_set :@fetcher, @fetcher
|
||||
end
|
||||
|
@ -294,7 +347,12 @@ class RubyGemTestCase < Test::Unit::TestCase
|
|||
|
||||
sice = Gem::SourceInfoCacheEntry.new si, 0
|
||||
sic = Gem::SourceInfoCache.new
|
||||
|
||||
sic.set_cache_data( { @gem_repo => sice } )
|
||||
sic.update
|
||||
sic.write_cache
|
||||
sic.reset_cache_data
|
||||
|
||||
Gem::SourceInfoCache.instance_variable_set :@cache, sic
|
||||
si
|
||||
end
|
||||
|
@ -313,3 +371,30 @@ class RubyGemTestCase < Test::Unit::TestCase
|
|||
|
||||
end
|
||||
|
||||
class TempIO
|
||||
|
||||
@@count = 0
|
||||
|
||||
def initialize(string = '')
|
||||
@tempfile = Tempfile.new "TempIO-#{@@count ++ 1}"
|
||||
@tempfile.binmode
|
||||
@tempfile.write string
|
||||
@tempfile.rewind
|
||||
end
|
||||
|
||||
def method_missing(meth, *args, &block)
|
||||
@tempfile.send(meth, *args, &block)
|
||||
end
|
||||
|
||||
def respond_to?(meth)
|
||||
@tempfile.respond_to? meth
|
||||
end
|
||||
|
||||
def string
|
||||
@tempfile.flush
|
||||
|
||||
Gem.read_binary @tempfile.path
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -15,9 +15,8 @@ class MockGemUi < Gem::StreamUI
|
|||
def initialize(input="")
|
||||
super(StringIO.new(input), StringIO.new, StringIO.new)
|
||||
@terminated = false
|
||||
@banged = false
|
||||
end
|
||||
|
||||
|
||||
def input
|
||||
@ins.string
|
||||
end
|
||||
|
@ -30,22 +29,15 @@ class MockGemUi < Gem::StreamUI
|
|||
@errs.string
|
||||
end
|
||||
|
||||
def banged?
|
||||
@banged
|
||||
end
|
||||
|
||||
def terminated?
|
||||
@terminated
|
||||
end
|
||||
|
||||
def terminate_interaction!(status=1)
|
||||
@terminated = true
|
||||
@banged = true
|
||||
fail TermError
|
||||
end
|
||||
|
||||
def terminate_interaction(status=0)
|
||||
@terminated = true
|
||||
fail TermError
|
||||
|
||||
raise TermError
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -19,6 +19,7 @@ class TestGem < RubyGemTestCase
|
|||
expected = [
|
||||
File.join(@gemhome, *%W[gems #{@a1.full_name} lib]),
|
||||
File.join(@gemhome, *%W[gems #{@a2.full_name} lib]),
|
||||
File.join(@gemhome, *%W[gems #{@a_evil9.full_name} lib]),
|
||||
File.join(@gemhome, *%W[gems #{@b2.full_name} lib]),
|
||||
File.join(@gemhome, *%W[gems #{@c1_2.full_name} lib]),
|
||||
File.join(@gemhome, *%W[gems #{@pl1.full_name} lib]),
|
||||
|
@ -213,6 +214,7 @@ class TestGem < RubyGemTestCase
|
|||
|
||||
expected = [
|
||||
File.join(@gemhome, *%W[gems #{@a2.full_name} lib]),
|
||||
File.join(@gemhome, *%W[gems #{@a_evil9.full_name} lib]),
|
||||
File.join(@gemhome, *%W[gems #{@b2.full_name} lib]),
|
||||
File.join(@gemhome, *%W[gems #{@c1_2.full_name} lib]),
|
||||
File.join(@gemhome, *%W[gems #{@pl1.full_name} lib]),
|
||||
|
@ -226,7 +228,7 @@ class TestGem < RubyGemTestCase
|
|||
install_gem foo
|
||||
Gem.source_index = nil
|
||||
|
||||
Gem.activate 'foo', false
|
||||
Gem.activate 'foo'
|
||||
|
||||
assert_equal true, Gem.loaded_specs.keys.include?('foo')
|
||||
end
|
||||
|
@ -235,9 +237,29 @@ class TestGem < RubyGemTestCase
|
|||
assert_equal [Gem.dir], Gem.path
|
||||
end
|
||||
|
||||
def test_self_path_APPLE_GEM_HOME
|
||||
Gem.clear_paths
|
||||
Gem.const_set :APPLE_GEM_HOME, '/tmp/apple_gem_home'
|
||||
|
||||
assert Gem.path.include?('/tmp/apple_gem_home')
|
||||
ensure
|
||||
Gem.send :remove_const, :APPLE_GEM_HOME
|
||||
end
|
||||
|
||||
def test_self_path_APPLE_GEM_HOME_GEM_PATH
|
||||
Gem.clear_paths
|
||||
ENV['GEM_PATH'] = @gemhome
|
||||
Gem.const_set :APPLE_GEM_HOME, '/tmp/apple_gem_home'
|
||||
|
||||
assert !Gem.path.include?('/tmp/apple_gem_home')
|
||||
ensure
|
||||
Gem.send :remove_const, :APPLE_GEM_HOME
|
||||
end
|
||||
|
||||
def test_self_path_ENV_PATH
|
||||
Gem.clear_paths
|
||||
path_count = Gem.path.size
|
||||
path_count -= 1 if defined? APPLE_GEM_HOME
|
||||
Gem.clear_paths
|
||||
util_ensure_gem_dirs
|
||||
|
||||
|
@ -257,8 +279,8 @@ class TestGem < RubyGemTestCase
|
|||
ENV['GEM_PATH'] = dirs.join File::PATH_SEPARATOR
|
||||
|
||||
assert_equal @gemhome, Gem.dir
|
||||
|
||||
paths = [Gem.dir]
|
||||
paths << APPLE_GEM_HOME if defined? APPLE_GEM_HOME
|
||||
assert_equal @additional + paths, Gem.path
|
||||
end
|
||||
|
||||
|
@ -270,8 +292,8 @@ class TestGem < RubyGemTestCase
|
|||
ENV['GEM_PATH'] = @additional.join(File::PATH_SEPARATOR)
|
||||
|
||||
assert_equal @gemhome, Gem.dir
|
||||
|
||||
paths = [Gem.dir]
|
||||
paths.insert(0, APPLE_GEM_HOME) if defined? APPLE_GEM_HOME
|
||||
assert_equal @additional + paths, Gem.path
|
||||
end
|
||||
|
||||
|
@ -284,6 +306,18 @@ class TestGem < RubyGemTestCase
|
|||
assert_equal File.dirname(File.dirname(file_name)), Gem.prefix
|
||||
end
|
||||
|
||||
def test_self_prefix_odd
|
||||
orig_sitelibdir = Gem::ConfigMap[:sitelibdir]
|
||||
|
||||
file_name = File.expand_path __FILE__
|
||||
prefix = File.join File.dirname(File.dirname(file_name)), 'lib'
|
||||
Gem::ConfigMap[:sitelibdir] = prefix.sub(/[\w]\//, '\&/')
|
||||
|
||||
assert_nil Gem.prefix
|
||||
ensure
|
||||
Gem::ConfigMap[:sitelibdir] = orig_sitelibdir
|
||||
end
|
||||
|
||||
def test_self_required_location
|
||||
util_make_gems
|
||||
|
||||
|
@ -295,6 +329,13 @@ class TestGem < RubyGemTestCase
|
|||
Gem.required_location("a", "code.rb", "= 2")
|
||||
end
|
||||
|
||||
def test_self_ruby_version
|
||||
version = RUBY_VERSION.dup
|
||||
version << ".#{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
|
||||
|
||||
assert_equal Gem::Version.new(version), Gem.ruby_version
|
||||
end
|
||||
|
||||
def test_self_searcher
|
||||
assert_kind_of Gem::GemPathSearcher, Gem.searcher
|
||||
end
|
||||
|
|
|
@ -67,11 +67,12 @@ class TestGemCommandManager < RubyGemTestCase
|
|||
assert_equal true, check_options[:wrappers]
|
||||
assert_equal Gem::Requirement.default, check_options[:version]
|
||||
assert_equal Gem.dir, check_options[:install_dir]
|
||||
assert_equal nil, check_options[:bin_dir]
|
||||
|
||||
#check settings
|
||||
check_options = nil
|
||||
@command_manager.process_args(
|
||||
"install --force --test --local --rdoc --install-dir . --version 3.0 --no-wrapper")
|
||||
"install --force --test --local --rdoc --install-dir . --version 3.0 --no-wrapper --bindir . ")
|
||||
assert_equal true, check_options[:test]
|
||||
assert_equal true, check_options[:generate_rdoc]
|
||||
assert_equal true, check_options[:force]
|
||||
|
@ -79,6 +80,7 @@ class TestGemCommandManager < RubyGemTestCase
|
|||
assert_equal false, check_options[:wrappers]
|
||||
assert_equal Gem::Requirement.new('3.0'), check_options[:version]
|
||||
assert_equal Dir.pwd, check_options[:install_dir]
|
||||
assert_equal Dir.pwd, check_options[:bin_dir]
|
||||
|
||||
#check remote domain
|
||||
check_options = nil
|
||||
|
@ -164,7 +166,7 @@ class TestGemCommandManager < RubyGemTestCase
|
|||
|
||||
#check defaults
|
||||
@command_manager.process_args("query")
|
||||
assert_equal(/.*/, check_options[:name])
|
||||
assert_equal(//, check_options[:name])
|
||||
assert_equal :local, check_options[:domain]
|
||||
assert_equal false, check_options[:details]
|
||||
|
||||
|
|
|
@ -62,6 +62,21 @@ class TestGemCommandsEnvironmentCommand < RubyGemTestCase
|
|||
assert_equal '', @ui.error
|
||||
end
|
||||
|
||||
def test_execute_gempath_multiple
|
||||
Gem.clear_paths
|
||||
path = [@gemhome, "#{@gemhome}2"].join File::PATH_SEPARATOR
|
||||
ENV['GEM_PATH'] = path
|
||||
|
||||
@cmd.send :handle_options, %w[gempath]
|
||||
|
||||
use_ui @ui do
|
||||
@cmd.execute
|
||||
end
|
||||
|
||||
assert_equal "#{Gem.path.join File::PATH_SEPARATOR}\n", @ui.output
|
||||
assert_equal '', @ui.error
|
||||
end
|
||||
|
||||
def test_execute_packageversion
|
||||
@cmd.send :handle_options, %w[packageversion]
|
||||
|
||||
|
|
|
@ -15,13 +15,12 @@ class TestGemCommandsFetchCommand < RubyGemTestCase
|
|||
def test_execute
|
||||
util_setup_fake_fetcher
|
||||
|
||||
util_build_gem @gem1
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] =
|
||||
@source_index.dump
|
||||
@fetcher.data["#{@gem_repo}/gems/#{@gem1.full_name}.gem"] =
|
||||
File.read(File.join(@gemhome, 'cache', "#{@gem1.full_name}.gem"))
|
||||
@fetcher.data["#{@gem_repo}/gems/#{@a2.full_name}.gem"] =
|
||||
File.read(File.join(@gemhome, 'cache', "#{@a2.full_name}.gem"))
|
||||
|
||||
@cmd.options[:args] = [@gem1.name]
|
||||
@cmd.options[:args] = [@a2.name]
|
||||
|
||||
use_ui @ui do
|
||||
Dir.chdir @tempdir do
|
||||
|
@ -29,7 +28,7 @@ class TestGemCommandsFetchCommand < RubyGemTestCase
|
|||
end
|
||||
end
|
||||
|
||||
assert File.exist?(File.join(@tempdir, "#{@gem1.full_name}.gem"))
|
||||
assert File.exist?(File.join(@tempdir, "#{@a2.full_name}.gem"))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -34,25 +34,26 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
|
|||
util_setup_fake_fetcher
|
||||
@cmd.options[:domain] = :local
|
||||
|
||||
gem1 = quick_gem 'gem_one'
|
||||
util_build_gem gem1
|
||||
FileUtils.mv File.join(@gemhome, 'cache', "#{@gem1.full_name}.gem"),
|
||||
FileUtils.mv File.join(@gemhome, 'cache', "#{@a2.full_name}.gem"),
|
||||
File.join(@tempdir)
|
||||
|
||||
@cmd.options[:args] = [gem1.name]
|
||||
@cmd.options[:args] = [@a2.name]
|
||||
|
||||
use_ui @ui do
|
||||
orig_dir = Dir.pwd
|
||||
begin
|
||||
Dir.chdir @tempdir
|
||||
@cmd.execute
|
||||
e = assert_raises Gem::SystemExitException do
|
||||
@cmd.execute
|
||||
end
|
||||
assert_equal 0, e.exit_code
|
||||
ensure
|
||||
Dir.chdir orig_dir
|
||||
end
|
||||
end
|
||||
|
||||
out = @ui.output.split "\n"
|
||||
assert_equal "Successfully installed #{@gem1.full_name}", out.shift
|
||||
assert_equal "Successfully installed #{@a2.full_name}", out.shift
|
||||
assert_equal "1 gem installed", out.shift
|
||||
assert out.empty?, out.inspect
|
||||
end
|
||||
|
@ -61,14 +62,17 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
|
|||
util_setup_fake_fetcher
|
||||
@cmd.options[:domain] = :local
|
||||
|
||||
@cmd.options[:args] = %w[gem_one]
|
||||
@cmd.options[:args] = %w[no_such_gem]
|
||||
|
||||
use_ui @ui do
|
||||
@cmd.execute
|
||||
e = assert_raises Gem::SystemExitException do
|
||||
@cmd.execute
|
||||
end
|
||||
assert_equal 2, e.exit_code
|
||||
end
|
||||
|
||||
# HACK no repository was checked
|
||||
assert_equal "ERROR: could not find gem_one locally or in a repository\n",
|
||||
assert_equal "ERROR: could not find no_such_gem locally or in a repository\n",
|
||||
@ui.error
|
||||
end
|
||||
|
||||
|
@ -88,7 +92,10 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
|
|||
@cmd.options[:args] = %w[nonexistent]
|
||||
|
||||
use_ui @ui do
|
||||
@cmd.execute
|
||||
e = assert_raises Gem::SystemExitException do
|
||||
@cmd.execute
|
||||
end
|
||||
assert_equal 2, e.exit_code
|
||||
end
|
||||
|
||||
assert_equal "ERROR: could not find nonexistent locally or in a repository\n",
|
||||
|
@ -100,25 +107,27 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
|
|||
@cmd.options[:generate_ri] = true
|
||||
util_setup_fake_fetcher
|
||||
|
||||
util_build_gem @gem1
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] =
|
||||
@source_index.dump
|
||||
@fetcher.data["#{@gem_repo}/gems/gem_one-0.0.2.gem"] =
|
||||
File.read(File.join(@gemhome, 'cache', "#{@gem1.full_name}.gem"))
|
||||
@fetcher.data["#{@gem_repo}/gems/#{@a2.full_name}.gem"] =
|
||||
read_binary(File.join(@gemhome, 'cache', "#{@a2.full_name}.gem"))
|
||||
|
||||
@cmd.options[:args] = [@gem1.name]
|
||||
@cmd.options[:args] = [@a2.name]
|
||||
|
||||
use_ui @ui do
|
||||
@cmd.execute
|
||||
e = assert_raises Gem::SystemExitException do
|
||||
@cmd.execute
|
||||
end
|
||||
assert_equal 0, e.exit_code
|
||||
end
|
||||
|
||||
out = @ui.output.split "\n"
|
||||
assert_match %r|Bulk updating|, out.shift
|
||||
assert_equal "Successfully installed #{@gem1.full_name}", out.shift
|
||||
assert_equal "Successfully installed #{@a2.full_name}", out.shift
|
||||
assert_equal "1 gem installed", out.shift
|
||||
assert_equal "Installing ri documentation for #{@gem1.full_name}...",
|
||||
assert_equal "Installing ri documentation for #{@a2.full_name}...",
|
||||
out.shift
|
||||
assert_equal "Installing RDoc documentation for #{@gem1.full_name}...",
|
||||
assert_equal "Installing RDoc documentation for #{@a2.full_name}...",
|
||||
out.shift
|
||||
assert out.empty?, out.inspect
|
||||
end
|
||||
|
@ -127,31 +136,30 @@ class TestGemCommandsInstallCommand < RubyGemTestCase
|
|||
util_setup_fake_fetcher
|
||||
@cmd.options[:domain] = :local
|
||||
|
||||
gem1 = quick_gem 'gem_one'
|
||||
util_build_gem gem1
|
||||
FileUtils.mv File.join(@gemhome, 'cache', "#{@gem1.full_name}.gem"),
|
||||
FileUtils.mv File.join(@gemhome, 'cache', "#{@a2.full_name}.gem"),
|
||||
File.join(@tempdir)
|
||||
|
||||
gem2 = quick_gem 'gem_two'
|
||||
util_build_gem gem2
|
||||
FileUtils.mv File.join(@gemhome, 'cache', "#{@gem2.full_name}.gem"),
|
||||
FileUtils.mv File.join(@gemhome, 'cache', "#{@b2.full_name}.gem"),
|
||||
File.join(@tempdir)
|
||||
|
||||
@cmd.options[:args] = [gem1.name, gem2.name]
|
||||
@cmd.options[:args] = [@a2.name, @b2.name]
|
||||
|
||||
use_ui @ui do
|
||||
orig_dir = Dir.pwd
|
||||
begin
|
||||
Dir.chdir @tempdir
|
||||
@cmd.execute
|
||||
e = assert_raises Gem::SystemExitException do
|
||||
@cmd.execute
|
||||
end
|
||||
assert_equal 0, e.exit_code
|
||||
ensure
|
||||
Dir.chdir orig_dir
|
||||
end
|
||||
end
|
||||
|
||||
out = @ui.output.split "\n"
|
||||
assert_equal "Successfully installed #{@gem1.full_name}", out.shift
|
||||
assert_equal "Successfully installed #{@gem2.full_name}", out.shift
|
||||
assert_equal "Successfully installed #{@a2.full_name}", out.shift
|
||||
assert_equal "Successfully installed #{@b2.full_name}", out.shift
|
||||
assert_equal "2 gems installed", out.shift
|
||||
assert out.empty?, out.inspect
|
||||
end
|
||||
|
|
|
@ -7,20 +7,31 @@ class TestGemCommandsQueryCommand < RubyGemTestCase
|
|||
def setup
|
||||
super
|
||||
|
||||
@foo_gem = quick_gem 'foo' do |spec|
|
||||
spec.summary = 'This is a lot of text. ' * 5
|
||||
end
|
||||
@foo_gem_p = quick_gem 'foo' do |spec|
|
||||
spec.summary = 'This is a lot of text. ' * 5
|
||||
spec.platform = Gem::Platform::CURRENT
|
||||
end
|
||||
@bar_gem = quick_gem 'bar'
|
||||
util_make_gems
|
||||
|
||||
@a2.summary = 'This is a lot of text. ' * 4
|
||||
|
||||
@cmd = Gem::Commands::QueryCommand.new
|
||||
|
||||
@si = util_setup_source_info_cache @a1, @a2, @pl1
|
||||
util_setup_fake_fetcher
|
||||
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{Gem.marshal_version}"] = proc do
|
||||
raise Gem::RemoteFetcher::FetchError
|
||||
end
|
||||
end
|
||||
|
||||
def test_execute
|
||||
util_setup_source_info_cache @foo_gem, @foo_gem_p
|
||||
cache = Gem::SourceInfoCache.cache
|
||||
cache.update
|
||||
cache.write_cache
|
||||
cache.reset_cache_data
|
||||
|
||||
a2_name = @a2.full_name
|
||||
@fetcher.data["#{@gem_repo}/quick/latest_index.rz"] = util_zip a2_name
|
||||
@fetcher.data["#{@gem_repo}/quick/Marshal.#{Gem.marshal_version}/#{a2_name}.gemspec.rz"] = util_zip Marshal.dump(@a2)
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{Gem.marshal_version}"] =
|
||||
Marshal.dump @si
|
||||
|
||||
@cmd.handle_options %w[-r]
|
||||
|
||||
|
@ -32,7 +43,43 @@ class TestGemCommandsQueryCommand < RubyGemTestCase
|
|||
|
||||
*** 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
|
||||
|
||||
assert_equal expected, @ui.output
|
||||
|
@ -40,8 +87,6 @@ foo (2)
|
|||
end
|
||||
|
||||
def test_execute_details
|
||||
util_setup_source_info_cache @foo_gem
|
||||
|
||||
@cmd.handle_options %w[-r -d]
|
||||
|
||||
use_ui @ui do
|
||||
|
@ -52,18 +97,94 @@ foo (2)
|
|||
|
||||
*** REMOTE GEMS ***
|
||||
|
||||
foo (2)
|
||||
This is a lot of text. This is a lot of text. This is a lot of
|
||||
text. This is a lot of text. This is a lot of text.
|
||||
a (2, 1)
|
||||
This is a lot of text. This is a lot of text. This is a lot of text.
|
||||
This is a lot of text.
|
||||
|
||||
pl (1)
|
||||
this is a summary
|
||||
EOF
|
||||
|
||||
assert_equal expected, @ui.output
|
||||
assert_equal '', @ui.error
|
||||
end
|
||||
|
||||
def test_execute_no_versions
|
||||
util_setup_source_info_cache @foo_gem, @bar_gem
|
||||
def test_execute_installed
|
||||
@cmd.handle_options %w[-n c --installed]
|
||||
|
||||
e = assert_raise Gem::SystemExitException do
|
||||
use_ui @ui do
|
||||
@cmd.execute
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal 0, e.exit_code
|
||||
|
||||
assert_equal "true\n", @ui.output
|
||||
assert_equal '', @ui.error
|
||||
end
|
||||
|
||||
def test_execute_installed_no_name
|
||||
@cmd.handle_options %w[--installed]
|
||||
|
||||
e = assert_raise Gem::SystemExitException do
|
||||
use_ui @ui do
|
||||
@cmd.execute
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal '', @ui.output
|
||||
assert_equal "ERROR: You must specify a gem name\n", @ui.error
|
||||
|
||||
assert_equal 4, e.exit_code
|
||||
end
|
||||
|
||||
def test_execute_installed_not_installed
|
||||
@cmd.handle_options %w[-n not_installed --installed]
|
||||
|
||||
e = assert_raise Gem::SystemExitException do
|
||||
use_ui @ui do
|
||||
@cmd.execute
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal "false\n", @ui.output
|
||||
assert_equal '', @ui.error
|
||||
|
||||
assert_equal 1, e.exit_code
|
||||
end
|
||||
|
||||
def test_execute_installed_version
|
||||
@cmd.handle_options %w[-n c --installed --version 1.2]
|
||||
|
||||
e = assert_raise Gem::SystemExitException do
|
||||
use_ui @ui do
|
||||
@cmd.execute
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal "true\n", @ui.output
|
||||
assert_equal '', @ui.error
|
||||
|
||||
assert_equal 0, e.exit_code
|
||||
end
|
||||
|
||||
def test_execute_installed_version_not_installed
|
||||
@cmd.handle_options %w[-n c --installed --version 2]
|
||||
|
||||
e = assert_raise Gem::SystemExitException do
|
||||
use_ui @ui do
|
||||
@cmd.execute
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal "false\n", @ui.output
|
||||
assert_equal '', @ui.error
|
||||
|
||||
assert_equal 1, e.exit_code
|
||||
end
|
||||
|
||||
def test_execute_no_versions
|
||||
@cmd.handle_options %w[-r --no-versions]
|
||||
|
||||
use_ui @ui do
|
||||
|
@ -74,8 +195,8 @@ foo (2)
|
|||
|
||||
*** REMOTE GEMS ***
|
||||
|
||||
bar
|
||||
foo
|
||||
a
|
||||
pl
|
||||
EOF
|
||||
|
||||
assert_equal expected, @ui.output
|
||||
|
|
|
@ -20,7 +20,7 @@ class TestGemCommandsServerCommand < RubyGemTestCase
|
|||
@cmd.send :handle_options, %w[-p 9999 -d /nonexistent --daemon]
|
||||
|
||||
assert_equal true, @cmd.options[:daemon]
|
||||
assert_equal '/nonexistent', @cmd.options[:gemdir]
|
||||
assert_equal File.expand_path('/nonexistent'), @cmd.options[:gemdir]
|
||||
assert_equal 9999, @cmd.options[:port]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,10 +31,11 @@ class TestGemCommandsSourcesCommand < RubyGemTestCase
|
|||
def test_execute_add
|
||||
util_setup_fake_fetcher
|
||||
|
||||
@si = Gem::SourceIndex.new @gem1.full_name => @gem1.name
|
||||
si = Gem::SourceIndex.new
|
||||
si.add_spec @a1
|
||||
|
||||
@fetcher.data["http://beta-gems.example.com/Marshal.#{@marshal_version}"] =
|
||||
@si.dump
|
||||
si.dump
|
||||
|
||||
@cmd.handle_options %w[--add http://beta-gems.example.com]
|
||||
|
||||
|
@ -45,7 +46,7 @@ class TestGemCommandsSourcesCommand < RubyGemTestCase
|
|||
end
|
||||
|
||||
expected = <<-EOF
|
||||
Bulk updating Gem source index for: http://beta-gems.example.com
|
||||
Bulk updating Gem source index for: http://beta-gems.example.com/
|
||||
http://beta-gems.example.com added to sources
|
||||
EOF
|
||||
|
||||
|
@ -60,14 +61,11 @@ http://beta-gems.example.com added to sources
|
|||
def test_execute_add_nonexistent_source
|
||||
util_setup_fake_fetcher
|
||||
|
||||
@si = Gem::SourceIndex.new @gem1.full_name => @gem1.name
|
||||
|
||||
@fetcher.data["http://beta-gems.example.com/Marshal.#{@marshal_version}"] =
|
||||
proc do
|
||||
raise Gem::RemoteFetcher::FetchError, 'it died'
|
||||
end
|
||||
|
||||
|
||||
Gem::RemoteFetcher.instance_variable_set :@fetcher, @fetcher
|
||||
|
||||
@cmd.handle_options %w[--add http://beta-gems.example.com]
|
||||
|
@ -104,6 +102,41 @@ beta-gems.example.com is not a URI
|
|||
assert_equal '', @ui.error
|
||||
end
|
||||
|
||||
def test_execute_clear_all
|
||||
@cmd.handle_options %w[--clear-all]
|
||||
|
||||
util_setup_source_info_cache
|
||||
|
||||
cache = Gem::SourceInfoCache.cache
|
||||
cache.update
|
||||
cache.write_cache
|
||||
|
||||
assert File.exist?(cache.system_cache_file),
|
||||
'system cache file'
|
||||
assert File.exist?(cache.latest_system_cache_file),
|
||||
'latest system cache file'
|
||||
|
||||
use_ui @ui do
|
||||
@cmd.execute
|
||||
end
|
||||
|
||||
expected = <<-EOF
|
||||
*** Removed user source cache ***
|
||||
*** Removed latest user source cache ***
|
||||
*** Removed system source cache ***
|
||||
*** Removed latest system source cache ***
|
||||
EOF
|
||||
|
||||
assert_equal expected, @ui.output
|
||||
assert_equal '', @ui.error
|
||||
|
||||
assert !File.exist?(cache.system_cache_file),
|
||||
'system cache file'
|
||||
assert !File.exist?(cache.latest_system_cache_file),
|
||||
'latest system cache file'
|
||||
|
||||
end
|
||||
|
||||
def test_execute_remove
|
||||
@cmd.handle_options %W[--remove #{@gem_repo}]
|
||||
|
||||
|
@ -122,20 +155,43 @@ beta-gems.example.com is not a URI
|
|||
assert_equal [], Gem::SourceInfoCache.cache_data.keys
|
||||
end
|
||||
|
||||
def test_execute_remove_no_network
|
||||
@cmd.handle_options %W[--remove #{@gem_repo}]
|
||||
|
||||
util_setup_fake_fetcher
|
||||
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{Gem.marshal_version}"] = proc do
|
||||
raise Gem::RemoteFetcher::FetchError
|
||||
end
|
||||
|
||||
use_ui @ui do
|
||||
@cmd.execute
|
||||
end
|
||||
|
||||
expected = "#{@gem_repo} removed from sources\n"
|
||||
|
||||
assert_equal expected, @ui.output
|
||||
assert_equal '', @ui.error
|
||||
|
||||
Gem::SourceInfoCache.cache.flush
|
||||
assert_equal [], Gem::SourceInfoCache.cache_data.keys
|
||||
end
|
||||
|
||||
def test_execute_update
|
||||
@cmd.handle_options %w[--update]
|
||||
|
||||
util_setup_source_info_cache
|
||||
util_setup_fake_fetcher
|
||||
@si = Gem::SourceIndex.new @gem1.full_name => @gem1.name
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = @si.dump
|
||||
si = Gem::SourceIndex.new
|
||||
si.add_spec @a1
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump
|
||||
|
||||
use_ui @ui do
|
||||
@cmd.execute
|
||||
end
|
||||
|
||||
expected = <<-EOF
|
||||
Bulk updating Gem source index for: #{@gem_repo}
|
||||
Bulk updating Gem source index for: #{@gem_repo}/
|
||||
source cache successfully updated
|
||||
EOF
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ class TestGemCommandsSpecificationCommand < RubyGemTestCase
|
|||
|
||||
def test_execute
|
||||
foo = quick_gem 'foo'
|
||||
Gem.source_index.add_spec foo
|
||||
|
||||
@cmd.options[:args] = %w[foo]
|
||||
|
||||
|
@ -87,7 +88,6 @@ class TestGemCommandsSpecificationCommand < RubyGemTestCase
|
|||
|
||||
assert_match %r|\A--- !ruby/object:Gem::Specification|, @ui.output
|
||||
assert_match %r|name: foo|, @ui.output
|
||||
assert_equal "WARNING: Remote information is not complete\n\n", @ui.error
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -17,15 +17,57 @@ class TestGemCommandsUnpackCommand < RubyGemTestCase
|
|||
|
||||
@cmd.options[:args] = %w[a]
|
||||
|
||||
use_ui @ui do
|
||||
Dir.chdir @tempdir do
|
||||
@cmd.execute
|
||||
end
|
||||
use_ui @ui do
|
||||
Dir.chdir @tempdir do
|
||||
@cmd.execute
|
||||
end
|
||||
end
|
||||
|
||||
assert File.exist?(File.join(@tempdir, 'a-2'))
|
||||
end
|
||||
|
||||
def test_execute_gem_path
|
||||
util_make_gems
|
||||
|
||||
Gem.clear_paths
|
||||
|
||||
gemhome2 = File.join @tempdir, 'gemhome2'
|
||||
|
||||
Gem.send :set_paths, [gemhome2, @gemhome].join(File::PATH_SEPARATOR)
|
||||
Gem.send :set_home, gemhome2
|
||||
|
||||
@cmd.options[:args] = %w[a]
|
||||
|
||||
use_ui @ui do
|
||||
Dir.chdir @tempdir do
|
||||
@cmd.execute
|
||||
end
|
||||
end
|
||||
|
||||
assert File.exist?(File.join(@tempdir, 'a-2'))
|
||||
end
|
||||
|
||||
def test_execute_gem_path_missing
|
||||
util_make_gems
|
||||
|
||||
Gem.clear_paths
|
||||
|
||||
gemhome2 = File.join @tempdir, 'gemhome2'
|
||||
|
||||
Gem.send :set_paths, [gemhome2, @gemhome].join(File::PATH_SEPARATOR)
|
||||
Gem.send :set_home, gemhome2
|
||||
|
||||
@cmd.options[:args] = %w[z]
|
||||
|
||||
use_ui @ui do
|
||||
Dir.chdir @tempdir do
|
||||
@cmd.execute
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal '', @ui.output
|
||||
end
|
||||
|
||||
def test_execute_with_target_option
|
||||
util_make_gems
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -54,8 +54,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::DependencyInstaller.new 'a'
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new
|
||||
inst.install 'a'
|
||||
end
|
||||
|
||||
assert_equal Gem::SourceIndex.new(@a1.full_name => @a1),
|
||||
|
@ -70,8 +70,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::DependencyInstaller.new 'b'
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new
|
||||
inst.install 'b'
|
||||
end
|
||||
|
||||
assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
|
||||
|
@ -84,8 +84,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::DependencyInstaller.new 'b'
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new
|
||||
inst.install 'b'
|
||||
end
|
||||
|
||||
assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
|
||||
|
@ -102,8 +102,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::DependencyInstaller.new 'f'
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new
|
||||
inst.install 'f'
|
||||
end
|
||||
|
||||
assert_equal %w[f-2], inst.installed_gems.map { |s| s.full_name }
|
||||
|
@ -114,19 +114,49 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::DependencyInstaller.new 'a-1.gem'
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new :domain => :local
|
||||
inst.install 'a-1.gem'
|
||||
end
|
||||
|
||||
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
|
||||
end
|
||||
|
||||
def test_install_local_dependency
|
||||
FileUtils.mv @a1_gem, @tempdir
|
||||
FileUtils.mv @b1_gem, @tempdir
|
||||
|
||||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::DependencyInstaller.new :domain => :local
|
||||
inst.install 'b-1.gem'
|
||||
end
|
||||
|
||||
assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
|
||||
end
|
||||
|
||||
def test_install_local_dependency_installed
|
||||
FileUtils.mv @a1_gem, @tempdir
|
||||
FileUtils.mv @b1_gem, @tempdir
|
||||
|
||||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
Gem::Installer.new('a-1.gem').install
|
||||
|
||||
inst = Gem::DependencyInstaller.new :domain => :local
|
||||
inst.install 'b-1.gem'
|
||||
end
|
||||
|
||||
assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
|
||||
end
|
||||
|
||||
def test_install_local_subdir
|
||||
inst = nil
|
||||
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::DependencyInstaller.new 'gems/a-1.gem'
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new :domain => :local
|
||||
inst.install 'gems/a-1.gem'
|
||||
end
|
||||
|
||||
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
|
||||
|
@ -137,12 +167,11 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::DependencyInstaller.new 'a', nil, :env_shebang => true,
|
||||
:wrappers => true
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new :env_shebang => true, :wrappers => true
|
||||
inst.install 'a'
|
||||
end
|
||||
|
||||
assert_match %r|\A#!/usr/bin/env ruby\n|,
|
||||
assert_match %r|\A#!/usr/bin/env #{Gem::ConfigMap[:RUBY_INSTALL_NAME]}\n|,
|
||||
File.read(File.join(@gemhome, 'bin', 'a_bin'))
|
||||
end
|
||||
|
||||
|
@ -153,8 +182,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::DependencyInstaller.new 'b', nil, :force => true
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new :force => true
|
||||
inst.install 'b'
|
||||
end
|
||||
|
||||
assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
|
||||
|
@ -165,8 +194,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::DependencyInstaller.new 'b', nil, :ignore_dependencies => true
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new :ignore_dependencies => true
|
||||
inst.install 'b'
|
||||
end
|
||||
|
||||
assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name }
|
||||
|
@ -179,8 +208,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::DependencyInstaller.new 'a', nil, :install_dir => gemhome2
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new :install_dir => gemhome2
|
||||
inst.install 'a'
|
||||
end
|
||||
|
||||
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
|
||||
|
@ -201,8 +230,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::DependencyInstaller.new 'b', nil, :domain => :both
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new :domain => :both
|
||||
inst.install 'b'
|
||||
end
|
||||
|
||||
assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
|
||||
|
@ -217,14 +246,34 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
assert_equal b1_expected, b1.loaded_from
|
||||
end
|
||||
|
||||
def test_install_domain_both_no_network
|
||||
Gem::SourceInfoCache.instance_variable_set :@cache, nil
|
||||
|
||||
@fetcher.data["http://gems.example.com/gems/Marshal.#{@marshal_version}"] =
|
||||
proc do
|
||||
raise Gem::RemoteFetcher::FetchError
|
||||
end
|
||||
|
||||
FileUtils.mv @a1_gem, @tempdir
|
||||
FileUtils.mv @b1_gem, @tempdir
|
||||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::DependencyInstaller.new :domain => :both
|
||||
inst.install 'b'
|
||||
end
|
||||
|
||||
assert_equal %w[a-1 b-1], inst.installed_gems.map { |s| s.full_name }
|
||||
end
|
||||
|
||||
def test_install_domain_local
|
||||
FileUtils.mv @b1_gem, @tempdir
|
||||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
e = assert_raise Gem::InstallError do
|
||||
inst = Gem::DependencyInstaller.new 'b', nil, :domain => :local
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new :domain => :local
|
||||
inst.install 'b'
|
||||
end
|
||||
assert_equal 'b requires a (>= 0)', e.message
|
||||
end
|
||||
|
@ -240,8 +289,43 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
|
||||
@fetcher.data['http://gems.example.com/gems/a-1.gem'] = a1_data
|
||||
|
||||
inst = Gem::DependencyInstaller.new 'a', nil, :domain => :remote
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new :domain => :remote
|
||||
inst.install 'a'
|
||||
|
||||
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
|
||||
end
|
||||
|
||||
def test_install_remote
|
||||
a1_data = nil
|
||||
File.open @a1_gem, 'rb' do |fp|
|
||||
a1_data = fp.read
|
||||
end
|
||||
|
||||
@fetcher.data['http://gems.example.com/gems/a-1.gem'] = a1_data
|
||||
|
||||
inst = Gem::DependencyInstaller.new
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst.install 'a'
|
||||
end
|
||||
|
||||
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
|
||||
end
|
||||
|
||||
def test_install_remote_dep
|
||||
a1_data = nil
|
||||
File.open @a1_gem, 'rb' do |fp|
|
||||
a1_data = fp.read
|
||||
end
|
||||
|
||||
@fetcher.data['http://gems.example.com/gems/a-1.gem'] = a1_data
|
||||
|
||||
inst = Gem::DependencyInstaller.new
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
dep = Gem::Dependency.new @a1.name, @a1.version
|
||||
inst.install dep
|
||||
end
|
||||
|
||||
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
|
||||
end
|
||||
|
@ -266,8 +350,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
@fetcher.data["http://gems.example.com/gems/#{a2_o.full_name}.gem"] =
|
||||
a2_o_data
|
||||
|
||||
inst = Gem::DependencyInstaller.new 'a', nil, :domain => :remote
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new :domain => :remote
|
||||
inst.install 'a'
|
||||
|
||||
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
|
||||
end
|
||||
|
@ -278,8 +362,8 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::DependencyInstaller.new 'a'
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new
|
||||
inst.install 'a'
|
||||
end
|
||||
|
||||
assert_equal Gem::SourceIndex.new(@a1.full_name => @a1),
|
||||
|
@ -290,13 +374,17 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
|
||||
if defined? OpenSSL then
|
||||
def test_install_security_policy
|
||||
FileUtils.mv @a1_gem, @cache_dir
|
||||
FileUtils.mv @b1_gem, @cache_dir
|
||||
data = File.open(@a1_gem, 'rb') { |f| f.read }
|
||||
@fetcher.data['http://gems.example.com/gems/a-1.gem'] = data
|
||||
|
||||
data = File.open(@b1_gem, 'rb') { |f| f.read }
|
||||
@fetcher.data['http://gems.example.com/gems/b-1.gem'] = data
|
||||
|
||||
policy = Gem::Security::HighSecurity
|
||||
inst = Gem::DependencyInstaller.new 'b', nil, :security_policy => policy
|
||||
inst = Gem::DependencyInstaller.new :security_policy => policy
|
||||
|
||||
e = assert_raise Gem::Exception do
|
||||
inst.install
|
||||
inst.install 'b'
|
||||
end
|
||||
|
||||
assert_equal 'Unsigned gem', e.message
|
||||
|
@ -305,145 +393,48 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_install_wrappers
|
||||
FileUtils.mv @a1_gem, @cache_dir
|
||||
inst = Gem::DependencyInstaller.new 'a', :wrappers => true
|
||||
# Wrappers don't work on mswin
|
||||
unless win_platform? then
|
||||
def test_install_no_wrappers
|
||||
@fetcher.data['http://gems.example.com/gems/a-1.gem'] = read_binary(@a1_gem)
|
||||
|
||||
inst.install
|
||||
inst = Gem::DependencyInstaller.new :wrappers => false
|
||||
inst.install 'a'
|
||||
|
||||
assert_match %r|This file was generated by RubyGems.|,
|
||||
File.read(File.join(@gemhome, 'bin', 'a_bin'))
|
||||
assert_no_match(%r|This file was generated by RubyGems.|,
|
||||
File.read(File.join(@gemhome, 'bin', 'a_bin')))
|
||||
end
|
||||
end
|
||||
|
||||
def test_install_version
|
||||
FileUtils.mv @d1_gem, @cache_dir
|
||||
FileUtils.mv @d2_gem, @cache_dir
|
||||
inst = Gem::DependencyInstaller.new 'd', '= 1'
|
||||
data = File.open(@d2_gem, 'rb') { |f| f.read }
|
||||
@fetcher.data['http://gems.example.com/gems/d-2.gem'] = data
|
||||
|
||||
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 }
|
||||
end
|
||||
|
||||
def test_install_version_default
|
||||
FileUtils.mv @d1_gem, @cache_dir
|
||||
FileUtils.mv @d2_gem, @cache_dir
|
||||
inst = Gem::DependencyInstaller.new 'd'
|
||||
data = File.open(@d2_gem, 'rb') { |f| f.read }
|
||||
@fetcher.data['http://gems.example.com/gems/d-2.gem'] = data
|
||||
|
||||
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 }
|
||||
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
|
||||
inst = Gem::DependencyInstaller.new 'a'
|
||||
inst = Gem::DependencyInstaller.new
|
||||
dep = Gem::Dependency.new 'b', '>= 0'
|
||||
|
||||
assert_equal [[@b1, 'http://gems.example.com']],
|
||||
|
@ -452,7 +443,7 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
|
||||
def test_find_gems_with_sources_local
|
||||
FileUtils.mv @a1_gem, @tempdir
|
||||
inst = Gem::DependencyInstaller.new 'b'
|
||||
inst = Gem::DependencyInstaller.new
|
||||
dep = Gem::Dependency.new 'a', '>= 0'
|
||||
gems = nil
|
||||
|
||||
|
@ -462,7 +453,7 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
|
||||
assert_equal 2, gems.length
|
||||
remote = gems.first
|
||||
assert_equal @a1, remote.first, 'remote spec'
|
||||
assert_equal 'a-1', remote.first.full_name, 'remote spec'
|
||||
assert_equal 'http://gems.example.com', remote.last, 'remote path'
|
||||
|
||||
local = gems.last
|
||||
|
@ -472,7 +463,9 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
end
|
||||
|
||||
def test_gather_dependencies
|
||||
inst = Gem::DependencyInstaller.new 'b'
|
||||
inst = Gem::DependencyInstaller.new
|
||||
inst.find_spec_by_name_and_version 'b'
|
||||
inst.gather_dependencies
|
||||
|
||||
assert_equal %w[a-1 b-1], inst.gems_to_install.map { |s| s.full_name }
|
||||
end
|
||||
|
@ -488,7 +481,9 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
@fetcher.uri = URI.parse 'http://gems.example.com'
|
||||
@fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml
|
||||
|
||||
inst = Gem::DependencyInstaller.new 'c'
|
||||
inst = Gem::DependencyInstaller.new
|
||||
inst.find_spec_by_name_and_version 'c'
|
||||
inst.gather_dependencies
|
||||
|
||||
assert_equal %w[b-2 c-1], inst.gems_to_install.map { |s| s.full_name }
|
||||
end
|
||||
|
@ -496,14 +491,18 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
def test_gather_dependencies_platform_alternate
|
||||
util_set_arch 'cpu-my_platform1'
|
||||
|
||||
inst = Gem::DependencyInstaller.new 'w'
|
||||
inst = Gem::DependencyInstaller.new
|
||||
inst.find_spec_by_name_and_version 'w'
|
||||
inst.gather_dependencies
|
||||
|
||||
assert_equal %w[x-1-cpu-my_platform-1 w-1],
|
||||
inst.gems_to_install.map { |s| s.full_name }
|
||||
end
|
||||
|
||||
def test_gather_dependencies_platform_bump
|
||||
inst = Gem::DependencyInstaller.new 'z'
|
||||
inst = Gem::DependencyInstaller.new
|
||||
inst.find_spec_by_name_and_version 'z'
|
||||
inst.gather_dependencies
|
||||
|
||||
assert_equal %w[y-1 z-1], inst.gems_to_install.map { |s| s.full_name }
|
||||
end
|
||||
|
@ -518,27 +517,11 @@ class TestGemDependencyInstaller < RubyGemTestCase
|
|||
@fetcher.uri = URI.parse 'http://gems.example.com'
|
||||
@fetcher.data['http://gems.example.com/gems/yaml'] = si.to_yaml
|
||||
|
||||
inst = Gem::DependencyInstaller.new 'e'
|
||||
inst = Gem::DependencyInstaller.new
|
||||
inst.find_spec_by_name_and_version 'e'
|
||||
inst.gather_dependencies
|
||||
|
||||
assert_equal %w[d-1 e-1], inst.gems_to_install.map { |s| s.full_name }
|
||||
end
|
||||
|
||||
def util_gem(name, version, &block)
|
||||
spec = quick_gem(name, version, &block)
|
||||
|
||||
util_build_gem spec
|
||||
|
||||
cache_file = File.join @tempdir, 'gems', "#{spec.original_name}.gem"
|
||||
FileUtils.mv File.join(@gemhome, 'cache', "#{spec.original_name}.gem"),
|
||||
cache_file
|
||||
FileUtils.rm File.join(@gemhome, 'specifications',
|
||||
"#{spec.full_name}.gemspec")
|
||||
|
||||
spec.loaded_from = nil
|
||||
spec.loaded = false
|
||||
|
||||
[spec, cache_file]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -47,16 +47,19 @@ class TestGemExtConfigureBuilder < RubyGemTestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = %r|configure failed:
|
||||
shell_error_msg = %r{(\./configure: No such file or directory)|(Can't open \./configure)}
|
||||
sh_prefix_configure = "sh ./configure --prefix="
|
||||
|
||||
expected = %r(configure failed:
|
||||
|
||||
sh \./configure --prefix=#{Regexp.escape @dest_path}
|
||||
.*?: \./configure: No such file or directory
|
||||
|
|
||||
#{Regexp.escape sh_prefix_configure}#{Regexp.escape @dest_path}
|
||||
.*?: #{shell_error_msg}
|
||||
)
|
||||
|
||||
assert_match expected, error.message
|
||||
|
||||
assert_equal "sh ./configure --prefix=#{@dest_path}", output.shift
|
||||
assert_match %r|\./configure: No such file or directory\n|, output.shift
|
||||
assert_equal "#{sh_prefix_configure}#{@dest_path}", output.shift
|
||||
assert_match %r(#{shell_error_msg}\n), output.shift
|
||||
assert_equal true, output.empty?
|
||||
end
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ class TestGemFormat < RubyGemTestCase
|
|||
|
||||
gems = Dir[File.join(@gemhome, 'cache', '*.gem')]
|
||||
|
||||
names = [@a1, @a2, @b2, @c1_2, @pl1].map do |spec|
|
||||
names = [@a1, @a2, @a_evil9, @b2, @c1_2, @pl1].map do |spec|
|
||||
spec.original_name
|
||||
end
|
||||
|
||||
|
|
|
@ -53,6 +53,11 @@ class TestGemIndexer < RubyGemTestCase
|
|||
assert_indexed quickdir, "index"
|
||||
assert_indexed quickdir, "index.rz"
|
||||
|
||||
assert_indexed quickdir, "latest_index"
|
||||
assert_indexed quickdir, "latest_index.rz"
|
||||
|
||||
assert_no_match %r|a-1|, File.read(File.join(quickdir, 'latest_index'))
|
||||
|
||||
assert_indexed quickdir, "#{@a1.full_name}.gemspec.rz"
|
||||
assert_indexed quickdir, "#{@a2.full_name}.gemspec.rz"
|
||||
assert_indexed quickdir, "#{@b2.full_name}.gemspec.rz"
|
||||
|
@ -74,8 +79,8 @@ class TestGemIndexer < RubyGemTestCase
|
|||
end
|
||||
|
||||
expected = <<-EOF
|
||||
Generating index for 5 gems in #{@tempdir}
|
||||
.....
|
||||
Generating index for 6 gems in #{@tempdir}
|
||||
......
|
||||
Loaded all gems
|
||||
Generating master indexes (this may take a while)
|
||||
EOF
|
||||
|
|
|
@ -4,63 +4,10 @@
|
|||
# See LICENSE.txt for permissions.
|
||||
#++
|
||||
|
||||
require 'test/unit'
|
||||
require File.join(File.expand_path(File.dirname(__FILE__)), 'gemutilities')
|
||||
require 'rubygems/installer'
|
||||
require File.join(File.expand_path(File.dirname(__FILE__)),
|
||||
'gem_installer_test_case')
|
||||
|
||||
class Gem::Installer
|
||||
attr_accessor :gem_dir
|
||||
|
||||
attr_writer :format
|
||||
attr_writer :gem_home
|
||||
attr_writer :env_shebang
|
||||
attr_writer :ignore_dependencies
|
||||
attr_writer :format_executable
|
||||
attr_writer :security_policy
|
||||
attr_writer :spec
|
||||
attr_writer :wrappers
|
||||
end
|
||||
|
||||
class TestGemInstaller < RubyGemTestCase
|
||||
|
||||
def setup
|
||||
super
|
||||
|
||||
@spec = quick_gem "a"
|
||||
@gem = File.join @tempdir, "#{@spec.full_name}.gem"
|
||||
|
||||
util_build_gem @spec
|
||||
FileUtils.mv File.join(@gemhome, 'cache', "#{@spec.full_name}.gem"),
|
||||
@tempdir
|
||||
|
||||
@installer = Gem::Installer.new @gem
|
||||
@installer.gem_dir = util_gem_dir
|
||||
@installer.gem_home = @gemhome
|
||||
@installer.spec = @spec
|
||||
end
|
||||
|
||||
def util_gem_dir(version = '2')
|
||||
File.join @gemhome, "gems", "a-#{version}" # HACK
|
||||
end
|
||||
|
||||
def util_gem_bindir(version = '2')
|
||||
File.join util_gem_dir(version), "bin"
|
||||
end
|
||||
|
||||
def util_inst_bindir
|
||||
File.join @gemhome, "bin"
|
||||
end
|
||||
|
||||
def util_make_exec(version = '2', shebang = "#!/usr/bin/ruby")
|
||||
@spec.executables = ["my_exec"]
|
||||
|
||||
FileUtils.mkdir_p util_gem_bindir(version)
|
||||
exec_file = @installer.formatted_program_filename "my_exec"
|
||||
exec_path = File.join util_gem_bindir(version), exec_file
|
||||
File.open exec_path, 'w' do |f|
|
||||
f.puts shebang
|
||||
end
|
||||
end
|
||||
class TestGemInstaller < GemInstallerTestCase
|
||||
|
||||
def test_app_script_text
|
||||
util_make_exec '2', ''
|
||||
|
@ -162,7 +109,7 @@ load 'my_exec'
|
|||
@installer.gem_dir = '/nonexistent'
|
||||
expanded_gem_dir = @installer.send(:expand_and_validate_gem_dir)
|
||||
if win_platform?
|
||||
expected = File.join(Config::CONFIG['bindir'][0..2], 'nonexistent').downcase
|
||||
expected = File.expand_path('/nonexistent').downcase
|
||||
expanded_gem_dir = expanded_gem_dir.downcase
|
||||
else
|
||||
expected = '/nonexistent'
|
||||
|
@ -768,7 +715,7 @@ load 'my_exec'
|
|||
@installer.env_shebang = true
|
||||
|
||||
shebang = @installer.shebang 'my_exec'
|
||||
assert_equal "#!/usr/bin/env ruby", shebang
|
||||
assert_equal "#!/usr/bin/env #{Gem::ConfigMap[:RUBY_INSTALL_NAME]}", shebang
|
||||
end
|
||||
|
||||
def test_shebang_nested
|
||||
|
@ -855,31 +802,5 @@ load 'my_exec'
|
|||
File.join @gemhome, 'cache', "#{spec.full_name}.gem"
|
||||
end
|
||||
|
||||
def util_setup_gem
|
||||
@spec.files = File.join('lib', 'code.rb')
|
||||
@spec.executables << 'executable'
|
||||
@spec.extensions << File.join('ext', 'a', 'mkrf_conf.rb')
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
FileUtils.mkdir_p 'bin'
|
||||
FileUtils.mkdir_p 'lib'
|
||||
FileUtils.mkdir_p File.join('ext', 'a')
|
||||
File.open File.join('bin', 'executable'), 'w' do |f| f.puts '1' end
|
||||
File.open File.join('lib', 'code.rb'), 'w' do |f| f.puts '1' end
|
||||
File.open File.join('ext', 'a', 'mkrf_conf.rb'), 'w' do |f|
|
||||
f << <<-EOF
|
||||
File.open 'Rakefile', 'w' do |rf| rf.puts "task :default" end
|
||||
EOF
|
||||
end
|
||||
|
||||
use_ui @ui do
|
||||
FileUtils.rm @gem
|
||||
Gem::Builder.new(@spec).build
|
||||
end
|
||||
end
|
||||
|
||||
@installer = Gem::Installer.new @gem
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -97,6 +97,13 @@ gems:
|
|||
@server_uri = base_server_uri + "/yaml"
|
||||
@server_z_uri = base_server_uri + "/yaml.Z"
|
||||
|
||||
# REFACTOR: copied from test_gem_dependency_installer.rb
|
||||
@gems_dir = File.join @tempdir, 'gems'
|
||||
@cache_dir = File.join @gemhome, 'cache'
|
||||
FileUtils.mkdir @gems_dir
|
||||
|
||||
@a1, @a1_gem = util_gem 'a', '1' do |s| s.executables << 'a_bin' end
|
||||
|
||||
Gem::RemoteFetcher.instance_variable_set :@fetcher, nil
|
||||
end
|
||||
|
||||
|
@ -156,6 +163,140 @@ gems:
|
|||
end
|
||||
end
|
||||
|
||||
def util_fuck_with_fetcher data, blow = false
|
||||
fetcher = Gem::RemoteFetcher.fetcher
|
||||
fetcher.instance_variable_set :@test_data, data
|
||||
|
||||
unless blow then
|
||||
def fetcher.fetch_path arg
|
||||
@test_arg = arg
|
||||
@test_data
|
||||
end
|
||||
else
|
||||
def fetcher.fetch_path arg
|
||||
# OMG I'm such an ass
|
||||
class << self; remove_method :fetch_path; end
|
||||
def self.fetch_path arg
|
||||
@test_arg = arg
|
||||
@test_data
|
||||
end
|
||||
|
||||
raise Gem::RemoteFetcher::FetchError, "haha!"
|
||||
end
|
||||
end
|
||||
|
||||
fetcher
|
||||
end
|
||||
|
||||
def test_download
|
||||
a1_data = nil
|
||||
File.open @a1_gem, 'rb' do |fp|
|
||||
a1_data = fp.read
|
||||
end
|
||||
|
||||
fetcher = util_fuck_with_fetcher a1_data
|
||||
|
||||
a1_cache_gem = File.join(@gemhome, 'cache', "#{@a1.full_name}.gem")
|
||||
assert_equal a1_cache_gem, fetcher.download(@a1, 'http://gems.example.com')
|
||||
assert_equal("http://gems.example.com/gems/a-1.gem",
|
||||
fetcher.instance_variable_get(:@test_arg).to_s)
|
||||
assert File.exist?(a1_cache_gem)
|
||||
end
|
||||
|
||||
def test_download_cached
|
||||
FileUtils.mv @a1_gem, @cache_dir
|
||||
|
||||
inst = Gem::RemoteFetcher.fetcher
|
||||
|
||||
assert_equal File.join(@gemhome, 'cache', "#{@a1.full_name}.gem"),
|
||||
inst.download(@a1, 'http://gems.example.com')
|
||||
end
|
||||
|
||||
def test_download_local
|
||||
FileUtils.mv @a1_gem, @tempdir
|
||||
local_path = File.join @tempdir, "#{@a1.full_name}.gem"
|
||||
inst = nil
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::RemoteFetcher.fetcher
|
||||
end
|
||||
|
||||
assert_equal File.join(@gemhome, 'cache', "#{@a1.full_name}.gem"),
|
||||
inst.download(@a1, local_path)
|
||||
end
|
||||
|
||||
def test_download_install_dir
|
||||
a1_data = nil
|
||||
File.open @a1_gem, 'rb' do |fp|
|
||||
a1_data = fp.read
|
||||
end
|
||||
|
||||
fetcher = util_fuck_with_fetcher a1_data
|
||||
|
||||
install_dir = File.join @tempdir, 'more_gems'
|
||||
|
||||
a1_cache_gem = File.join install_dir, 'cache', "#{@a1.full_name}.gem"
|
||||
actual = fetcher.download(@a1, 'http://gems.example.com', install_dir)
|
||||
|
||||
assert_equal a1_cache_gem, actual
|
||||
assert_equal("http://gems.example.com/gems/a-1.gem",
|
||||
fetcher.instance_variable_get(:@test_arg).to_s)
|
||||
|
||||
assert File.exist?(a1_cache_gem)
|
||||
end
|
||||
|
||||
unless win_platform? then # File.chmod doesn't work
|
||||
def test_download_local_read_only
|
||||
FileUtils.mv @a1_gem, @tempdir
|
||||
local_path = File.join @tempdir, "#{@a1.full_name}.gem"
|
||||
inst = nil
|
||||
File.chmod 0555, File.join(@gemhome, 'cache')
|
||||
|
||||
Dir.chdir @tempdir do
|
||||
inst = Gem::RemoteFetcher.fetcher
|
||||
end
|
||||
|
||||
assert_equal File.join(@tempdir, "#{@a1.full_name}.gem"),
|
||||
inst.download(@a1, local_path)
|
||||
ensure
|
||||
File.chmod 0755, File.join(@gemhome, 'cache')
|
||||
end
|
||||
end
|
||||
|
||||
def test_download_platform_legacy
|
||||
original_platform = 'old-platform'
|
||||
|
||||
e1, e1_gem = util_gem 'e', '1' do |s|
|
||||
s.platform = Gem::Platform::CURRENT
|
||||
s.instance_variable_set :@original_platform, original_platform
|
||||
end
|
||||
|
||||
e1_data = nil
|
||||
File.open e1_gem, 'rb' do |fp|
|
||||
e1_data = fp.read
|
||||
end
|
||||
|
||||
fetcher = util_fuck_with_fetcher e1_data, :blow_chunks
|
||||
|
||||
e1_cache_gem = File.join(@gemhome, 'cache', "#{e1.full_name}.gem")
|
||||
|
||||
assert_equal e1_cache_gem, fetcher.download(e1, 'http://gems.example.com')
|
||||
|
||||
assert_equal("http://gems.example.com/gems/#{e1.original_name}.gem",
|
||||
fetcher.instance_variable_get(:@test_arg).to_s)
|
||||
assert File.exist?(e1_cache_gem)
|
||||
end
|
||||
|
||||
def test_download_unsupported
|
||||
inst = Gem::RemoteFetcher.fetcher
|
||||
|
||||
e = assert_raise Gem::InstallError do
|
||||
inst.download @a1, 'ftp://gems.rubyforge.org'
|
||||
end
|
||||
|
||||
assert_equal 'unsupported URI scheme ftp', e.message
|
||||
end
|
||||
|
||||
def test_explicit_proxy
|
||||
use_ui @ui do
|
||||
fetcher = Gem::RemoteFetcher.new @proxy_uri
|
||||
|
@ -232,22 +373,6 @@ gems:
|
|||
assert_equal 'EOFError: EOFError reading uri', e.message
|
||||
end
|
||||
|
||||
def test_fetch_path_open_uri_http_error
|
||||
fetcher = Gem::RemoteFetcher.new nil
|
||||
|
||||
def fetcher.open_uri_or_path(uri)
|
||||
io = StringIO.new 'went boom'
|
||||
err = OpenURI::HTTPError.new 'error', io
|
||||
raise err
|
||||
end
|
||||
|
||||
e = assert_raise Gem::RemoteFetcher::FetchError do
|
||||
fetcher.fetch_path 'uri'
|
||||
end
|
||||
|
||||
assert_equal "OpenURI::HTTPError: error reading uri\n\twent boom", e.message
|
||||
end
|
||||
|
||||
def test_fetch_path_socket_error
|
||||
fetcher = Gem::RemoteFetcher.new nil
|
||||
|
||||
|
@ -324,6 +449,53 @@ gems:
|
|||
end
|
||||
end
|
||||
|
||||
def test_open_uri_or_path
|
||||
fetcher = Gem::RemoteFetcher.new nil
|
||||
|
||||
conn = Object.new
|
||||
def conn.started?() true end
|
||||
def conn.request(req)
|
||||
unless defined? @requested then
|
||||
@requested = true
|
||||
res = Net::HTTPRedirection.new nil, 301, nil
|
||||
res.add_field 'Location', 'http://gems.example.com/real_path'
|
||||
res
|
||||
else
|
||||
res = Net::HTTPOK.new nil, 200, nil
|
||||
def res.body() 'real_path' end
|
||||
res
|
||||
end
|
||||
end
|
||||
|
||||
conn = { 'gems.example.com:80' => conn }
|
||||
fetcher.instance_variable_set :@connections, conn
|
||||
|
||||
fetcher.send :open_uri_or_path, 'http://gems.example.com/redirect' do |io|
|
||||
assert_equal 'real_path', io.read
|
||||
end
|
||||
end
|
||||
|
||||
def test_open_uri_or_path_limited_redirects
|
||||
fetcher = Gem::RemoteFetcher.new nil
|
||||
|
||||
conn = Object.new
|
||||
def conn.started?() true end
|
||||
def conn.request(req)
|
||||
res = Net::HTTPRedirection.new nil, 301, nil
|
||||
res.add_field 'Location', 'http://gems.example.com/redirect'
|
||||
res
|
||||
end
|
||||
|
||||
conn = { 'gems.example.com:80' => conn }
|
||||
fetcher.instance_variable_set :@connections, conn
|
||||
|
||||
e = assert_raise Gem::RemoteFetcher::FetchError do
|
||||
fetcher.send :open_uri_or_path, 'http://gems.example.com/redirect'
|
||||
end
|
||||
|
||||
assert_equal 'too many redirects', e.message
|
||||
end
|
||||
|
||||
def test_zip
|
||||
use_ui @ui do
|
||||
self.class.enable_zip = true
|
||||
|
|
|
@ -36,7 +36,8 @@ class TestGemSourceIndex < RubyGemTestCase
|
|||
|
||||
use_ui @ui do
|
||||
fetched_index = @source_index.fetch_bulk_index @uri
|
||||
assert_equal [@gem1.full_name, @gem4.full_name, @gem2.full_name].sort,
|
||||
assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
|
||||
@c1_2.full_name].sort,
|
||||
fetched_index.gems.map { |n,s| n }.sort
|
||||
end
|
||||
|
||||
|
@ -82,7 +83,8 @@ class TestGemSourceIndex < RubyGemTestCase
|
|||
|
||||
use_ui @ui do
|
||||
fetched_index = @source_index.fetch_bulk_index @uri
|
||||
assert_equal [@gem1.full_name, @gem4.full_name, @gem2.full_name].sort,
|
||||
assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
|
||||
@c1_2.full_name].sort,
|
||||
fetched_index.gems.map { |n,s| n }.sort
|
||||
end
|
||||
|
||||
|
@ -105,7 +107,8 @@ class TestGemSourceIndex < RubyGemTestCase
|
|||
|
||||
use_ui @ui do
|
||||
fetched_index = @source_index.fetch_bulk_index @uri
|
||||
assert_equal [@gem1.full_name, @gem4.full_name, @gem2.full_name].sort,
|
||||
assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
|
||||
@c1_2.full_name].sort,
|
||||
fetched_index.gems.map { |n,s| n }.sort
|
||||
end
|
||||
|
||||
|
@ -123,7 +126,8 @@ class TestGemSourceIndex < RubyGemTestCase
|
|||
util_setup_bulk_fetch false
|
||||
use_ui @ui do
|
||||
fetched_index = @source_index.fetch_bulk_index @uri
|
||||
assert_equal [@gem1.full_name, @gem4.full_name, @gem2.full_name].sort,
|
||||
assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
|
||||
@c1_2.full_name].sort,
|
||||
fetched_index.gems.map { |n,s| n }.sort
|
||||
end
|
||||
|
||||
|
@ -136,11 +140,32 @@ class TestGemSourceIndex < RubyGemTestCase
|
|||
end
|
||||
|
||||
def test_fetch_quick_index
|
||||
quick_index = util_zip @gem_names
|
||||
@fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index
|
||||
index = util_zip @gem_names
|
||||
latest_index = util_zip [@a2.full_name, @b2.full_name].join("\n")
|
||||
|
||||
quick_index = @source_index.fetch_quick_index @uri
|
||||
assert_equal [@gem1.full_name, @gem4.full_name, @gem2.full_name].sort,
|
||||
@fetcher.data["#{@gem_repo}/quick/index.rz"] = index
|
||||
@fetcher.data["#{@gem_repo}/quick/latest_index.rz"] = latest_index
|
||||
|
||||
quick_index = @source_index.fetch_quick_index @uri, false
|
||||
assert_equal [@a2.full_name, @b2.full_name].sort,
|
||||
quick_index.sort
|
||||
|
||||
paths = @fetcher.paths
|
||||
|
||||
assert_equal "#{@gem_repo}/quick/latest_index.rz", paths.shift
|
||||
|
||||
assert paths.empty?, paths.join(', ')
|
||||
end
|
||||
|
||||
def test_fetch_quick_index_all
|
||||
index = util_zip @gem_names
|
||||
latest_index = util_zip [@a2.full_name, @b2.full_name].join("\n")
|
||||
|
||||
@fetcher.data["#{@gem_repo}/quick/index.rz"] = index
|
||||
@fetcher.data["#{@gem_repo}/quick/latest_index.rz"] = latest_index
|
||||
|
||||
quick_index = @source_index.fetch_quick_index @uri, true
|
||||
assert_equal [@a1.full_name, @a2.full_name, @b2.full_name].sort,
|
||||
quick_index.sort
|
||||
|
||||
paths = @fetcher.paths
|
||||
|
@ -155,7 +180,7 @@ class TestGemSourceIndex < RubyGemTestCase
|
|||
proc { raise Exception }
|
||||
|
||||
e = assert_raise Gem::OperationNotSupportedError do
|
||||
@source_index.fetch_quick_index @uri
|
||||
@source_index.fetch_quick_index @uri, true
|
||||
end
|
||||
|
||||
assert_equal 'No quick index found: Exception', e.message
|
||||
|
@ -167,41 +192,201 @@ class TestGemSourceIndex < RubyGemTestCase
|
|||
assert paths.empty?, paths.join(', ')
|
||||
end
|
||||
|
||||
def test_fetch_quick_index_fallback
|
||||
index = util_zip @gem_names
|
||||
|
||||
@fetcher.data["#{@gem_repo}/quick/index.rz"] = index
|
||||
|
||||
quick_index = @source_index.fetch_quick_index @uri, false
|
||||
assert_equal @gem_names.split, quick_index.sort
|
||||
|
||||
paths = @fetcher.paths
|
||||
|
||||
assert_equal "#{@gem_repo}/quick/latest_index.rz", paths.shift
|
||||
assert_equal "#{@gem_repo}/quick/index.rz", paths.shift
|
||||
|
||||
assert paths.empty?, paths.join(', ')
|
||||
end
|
||||
|
||||
def test_fetch_quick_index_subdir
|
||||
latest_index = util_zip [@a2.full_name, @b2.full_name].join("\n")
|
||||
repo = URI.parse "#{@gem_repo}/~nobody/mirror/"
|
||||
|
||||
@fetcher.data["#{repo}quick/latest_index.rz"] = latest_index
|
||||
|
||||
quick_index = @source_index.fetch_quick_index repo, false
|
||||
assert_equal [@a2.full_name, @b2.full_name].sort,
|
||||
quick_index.sort
|
||||
|
||||
paths = @fetcher.paths
|
||||
|
||||
assert_equal "#{repo}quick/latest_index.rz", paths.shift
|
||||
|
||||
assert paths.empty?, paths.join(', ')
|
||||
end
|
||||
|
||||
def test_fetch_single_spec
|
||||
a1_spec_url = "#{@gem_repo}/quick/Marshal.#{Gem.marshal_version}/#{@a1.full_name}.gemspec.rz"
|
||||
@fetcher.data[a1_spec_url] = util_zip Marshal.dump(@a1)
|
||||
|
||||
spec = @source_index.send :fetch_single_spec, URI.parse(@gem_repo),
|
||||
@a1.full_name
|
||||
|
||||
assert_equal @a1.full_name, spec.full_name
|
||||
|
||||
paths = @fetcher.paths
|
||||
|
||||
assert_equal a1_spec_url, paths.shift
|
||||
|
||||
assert paths.empty?, paths.join(', ')
|
||||
end
|
||||
|
||||
def test_fetch_single_spec_subdir
|
||||
repo = URI.parse "#{@gem_repo}/~nobody/mirror/"
|
||||
|
||||
a1_spec_url = "#{repo}quick/Marshal.#{Gem.marshal_version}/#{@a1.full_name}.gemspec.rz"
|
||||
@fetcher.data[a1_spec_url] = util_zip Marshal.dump(@a1)
|
||||
|
||||
spec = @source_index.send :fetch_single_spec, repo, @a1.full_name
|
||||
|
||||
assert_equal @a1.full_name, spec.full_name
|
||||
|
||||
paths = @fetcher.paths
|
||||
|
||||
assert_equal a1_spec_url, paths.shift
|
||||
|
||||
assert paths.empty?, paths.join(', ')
|
||||
end
|
||||
|
||||
def test_fetch_single_spec_yaml
|
||||
a1_spec_url = "#{@gem_repo}/quick/#{@a1.full_name}.gemspec.rz"
|
||||
@fetcher.data[a1_spec_url] = util_zip @a1.to_yaml
|
||||
|
||||
repo = URI.parse @gem_repo
|
||||
|
||||
spec = @source_index.send :fetch_single_spec, repo, @a1.full_name
|
||||
|
||||
assert_equal @a1.full_name, spec.full_name
|
||||
|
||||
paths = @fetcher.paths
|
||||
|
||||
assert_equal "#{@gem_repo}/quick/Marshal.#{Gem.marshal_version}/#{@a1.full_name}.gemspec.rz", paths.shift
|
||||
assert_equal a1_spec_url, paths.shift
|
||||
|
||||
assert paths.empty?, paths.join(', ')
|
||||
end
|
||||
|
||||
def test_fetch_single_spec_yaml_subdir
|
||||
repo = URI.parse "#{@gem_repo}/~nobody/mirror/"
|
||||
|
||||
a1_spec_url = "#{repo}quick/#{@a1.full_name}.gemspec.rz"
|
||||
@fetcher.data[a1_spec_url] = util_zip @a1.to_yaml
|
||||
|
||||
spec = @source_index.send :fetch_single_spec, repo, @a1.full_name
|
||||
|
||||
assert_equal @a1.full_name, spec.full_name
|
||||
|
||||
paths = @fetcher.paths
|
||||
|
||||
assert_equal "#{repo}quick/Marshal.#{Gem.marshal_version}/#{@a1.full_name}.gemspec.rz", paths.shift
|
||||
assert_equal a1_spec_url, paths.shift
|
||||
|
||||
assert paths.empty?, paths.join(', ')
|
||||
end
|
||||
|
||||
def test_find_missing
|
||||
missing = @source_index.find_missing [@gem3.full_name]
|
||||
assert_equal [@gem3.full_name], missing
|
||||
missing = @source_index.find_missing [@b2.full_name]
|
||||
assert_equal [@b2.full_name], missing
|
||||
end
|
||||
|
||||
def test_find_missing_none_missing
|
||||
missing = @source_index.find_missing @gem_names.split
|
||||
missing = @source_index.find_missing [
|
||||
@a1.full_name, @a2.full_name, @c1_2.full_name
|
||||
]
|
||||
|
||||
assert_equal [], missing
|
||||
end
|
||||
|
||||
def test_latest_specs
|
||||
spec = quick_gem @gem1.name, '1'
|
||||
@source_index.add_spec spec
|
||||
p1_ruby = quick_gem 'p', '1'
|
||||
p1_platform = quick_gem 'p', '1' do |spec|
|
||||
spec.platform = Gem::Platform::CURRENT
|
||||
end
|
||||
|
||||
a1_platform = quick_gem @a1.name, (@a1.version) do |s|
|
||||
s.platform = Gem::Platform.new 'x86-my_platform1'
|
||||
end
|
||||
|
||||
a2_platform = quick_gem @a2.name, (@a2.version) do |s|
|
||||
s.platform = Gem::Platform.new 'x86-my_platform1'
|
||||
end
|
||||
|
||||
a2_platform_other = quick_gem @a2.name, (@a2.version) do |s|
|
||||
s.platform = Gem::Platform.new 'x86-other_platform1'
|
||||
end
|
||||
|
||||
a3_platform_other = quick_gem @a2.name, (@a2.version.bump) do |s|
|
||||
s.platform = Gem::Platform.new 'x86-other_platform1'
|
||||
end
|
||||
|
||||
@source_index.add_spec p1_ruby
|
||||
@source_index.add_spec p1_platform
|
||||
@source_index.add_spec a1_platform
|
||||
@source_index.add_spec a2_platform
|
||||
@source_index.add_spec a2_platform_other
|
||||
@source_index.add_spec a3_platform_other
|
||||
|
||||
expected = [
|
||||
@gem1.full_name,
|
||||
@gem2.full_name,
|
||||
@gem4.full_name,
|
||||
@a2.full_name,
|
||||
a2_platform.full_name,
|
||||
a3_platform_other.full_name,
|
||||
@c1_2.full_name,
|
||||
@a_evil9.full_name,
|
||||
p1_ruby.full_name,
|
||||
p1_platform.full_name,
|
||||
].sort
|
||||
|
||||
assert_equal expected, @source_index.latest_specs.map { |s| s.full_name }.sort
|
||||
latest_specs = @source_index.latest_specs.map { |s| s.full_name }.sort
|
||||
|
||||
assert_equal expected, latest_specs
|
||||
end
|
||||
|
||||
def test_load_gems_in
|
||||
spec_dir1 = File.join @gemhome, 'specifications'
|
||||
spec_dir2 = File.join @tempdir, 'gemhome2', 'specifications'
|
||||
|
||||
FileUtils.rm_r spec_dir1
|
||||
|
||||
FileUtils.mkdir_p spec_dir1
|
||||
FileUtils.mkdir_p spec_dir2
|
||||
|
||||
a1 = quick_gem 'a', '1' do |spec| spec.author = 'author 1' end
|
||||
a2 = quick_gem 'a', '1' do |spec| spec.author = 'author 2' end
|
||||
|
||||
File.open File.join(spec_dir1, "#{a1.full_name}.gemspec"), 'w' do |fp|
|
||||
fp.write a1.to_ruby
|
||||
end
|
||||
|
||||
File.open File.join(spec_dir2, "#{a2.full_name}.gemspec"), 'w' do |fp|
|
||||
fp.write a2.to_ruby
|
||||
end
|
||||
|
||||
@source_index.load_gems_in spec_dir1, spec_dir2
|
||||
|
||||
assert_equal a1.author, @source_index.specification(a1.full_name).author
|
||||
end
|
||||
|
||||
def test_outdated
|
||||
sic = Gem::SourceInfoCache.new
|
||||
Gem::SourceInfoCache.instance_variable_set :@cache, sic
|
||||
util_setup_source_info_cache
|
||||
|
||||
assert_equal [], @source_index.outdated
|
||||
|
||||
updated = quick_gem @gem1.name, (@gem1.version.bump)
|
||||
updated = quick_gem @a2.name, (@a2.version.bump)
|
||||
util_setup_source_info_cache updated
|
||||
|
||||
assert_equal [updated.name], @source_index.outdated
|
||||
|
||||
updated_platform = quick_gem @gem1.name, (updated.version.bump) do |s|
|
||||
updated_platform = quick_gem @a2.name, (updated.version.bump) do |s|
|
||||
s.platform = Gem::Platform.new 'x86-other_platform1'
|
||||
end
|
||||
|
||||
|
@ -211,28 +396,34 @@ class TestGemSourceIndex < RubyGemTestCase
|
|||
end
|
||||
|
||||
def test_remove_extra
|
||||
@source_index.remove_extra [@gem1.full_name]
|
||||
assert_equal [@gem1.full_name], @source_index.gems.map { |n,s| n }
|
||||
@source_index.add_spec @a1
|
||||
@source_index.add_spec @a2
|
||||
|
||||
@source_index.remove_extra [@a1.full_name]
|
||||
|
||||
assert_equal [@a1.full_name], @source_index.gems.map { |n,s| n }
|
||||
end
|
||||
|
||||
def test_remove_extra_no_changes
|
||||
gems = @gem_names.split.sort
|
||||
gems = [@a1.full_name, @a2.full_name]
|
||||
@source_index.add_spec @a1
|
||||
@source_index.add_spec @a2
|
||||
|
||||
@source_index.remove_extra gems
|
||||
|
||||
assert_equal gems, @source_index.gems.map { |n,s| n }.sort
|
||||
end
|
||||
|
||||
def test_search
|
||||
assert_equal [@gem1, @gem4], @source_index.search("gem_one")
|
||||
assert_equal [@gem1], @source_index.search("gem_one", "= 2")
|
||||
assert_equal [@a1, @a2, @a_evil9], @source_index.search('a')
|
||||
assert_equal [@a2], @source_index.search('a', '= 2')
|
||||
|
||||
assert_equal [], @source_index.search("bogusstring")
|
||||
assert_equal [], @source_index.search("gem_one", "= 3.2.1")
|
||||
assert_equal [], @source_index.search('bogusstring')
|
||||
assert_equal [], @source_index.search('a', '= 3')
|
||||
|
||||
@a1 = quick_gem 'a', '1'
|
||||
@a2 = quick_gem 'a', '2'
|
||||
|
||||
source_index = Gem::SourceIndex.new @a1.full_name => @a1,
|
||||
@a2.full_name => @a2
|
||||
source_index = Gem::SourceIndex.new
|
||||
source_index.add_spec @a1
|
||||
source_index.add_spec @a2
|
||||
|
||||
assert_equal [@a1], source_index.search(@a1.name, '= 1')
|
||||
|
||||
|
@ -276,7 +467,7 @@ class TestGemSourceIndex < RubyGemTestCase
|
|||
end
|
||||
|
||||
def test_specification
|
||||
assert_equal @gem1, @source_index.specification(@gem1.full_name)
|
||||
assert_equal @a1, @source_index.specification(@a1.full_name)
|
||||
|
||||
assert_nil @source_index.specification("foo-1.2.4")
|
||||
end
|
||||
|
@ -298,9 +489,11 @@ class TestGemSourceIndex < RubyGemTestCase
|
|||
assert_equal [], @source_index.gems.keys.sort
|
||||
|
||||
use_ui @ui do
|
||||
@source_index.update @uri
|
||||
@source_index.update @uri, true
|
||||
|
||||
assert_equal @gem_names.split, @source_index.gems.keys.sort
|
||||
assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
|
||||
@c1_2.full_name],
|
||||
@source_index.gems.keys.sort
|
||||
end
|
||||
|
||||
paths = @fetcher.paths
|
||||
|
@ -315,15 +508,42 @@ class TestGemSourceIndex < RubyGemTestCase
|
|||
old_gem_conf = Gem.configuration
|
||||
Gem.configuration = Gem::ConfigFile.new([])
|
||||
|
||||
latest_names = [@a2, @a_evil9, @b2, @c1_2].map { |s| s.full_name }
|
||||
latest_index = util_zip latest_names.join("\n")
|
||||
@fetcher.data["#{@gem_repo}/quick/latest_index.rz"] = latest_index
|
||||
|
||||
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
|
||||
"#{@b2.full_name}.gemspec.rz"
|
||||
@fetcher.data[marshal_uri] = util_zip Marshal.dump(@b2)
|
||||
|
||||
use_ui @ui do
|
||||
@source_index.update @uri, false
|
||||
|
||||
assert_equal latest_names, @source_index.gems.keys.sort
|
||||
end
|
||||
|
||||
paths = @fetcher.paths
|
||||
assert_equal "#{@gem_repo}/quick/latest_index.rz", paths.shift
|
||||
assert_equal marshal_uri, paths.shift
|
||||
|
||||
assert paths.empty?, paths.join(', ')
|
||||
ensure
|
||||
Gem.configuration = old_gem_conf
|
||||
end
|
||||
|
||||
def test_update_incremental_all
|
||||
old_gem_conf = Gem.configuration
|
||||
Gem.configuration = Gem::ConfigFile.new([])
|
||||
|
||||
quick_index = util_zip @all_gem_names.join("\n")
|
||||
@fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index
|
||||
|
||||
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
|
||||
"#{@gem3.full_name}.gemspec.rz"
|
||||
@fetcher.data[marshal_uri] = util_zip Marshal.dump(@gem3)
|
||||
"#{@b2.full_name}.gemspec.rz"
|
||||
@fetcher.data[marshal_uri] = util_zip Marshal.dump(@b2)
|
||||
|
||||
use_ui @ui do
|
||||
@source_index.update @uri
|
||||
@source_index.update @uri, true
|
||||
|
||||
assert_equal @all_gem_names, @source_index.gems.keys.sort
|
||||
end
|
||||
|
@ -345,13 +565,13 @@ class TestGemSourceIndex < RubyGemTestCase
|
|||
@fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index
|
||||
|
||||
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
|
||||
"#{@gem3.full_name}.gemspec.rz"
|
||||
"#{@b2.full_name}.gemspec.rz"
|
||||
|
||||
yaml_uri = "#{@gem_repo}/quick/#{@gem3.full_name}.gemspec.rz"
|
||||
@fetcher.data[yaml_uri] = util_zip @gem3.to_yaml
|
||||
yaml_uri = "#{@gem_repo}/quick/#{@b2.full_name}.gemspec.rz"
|
||||
@fetcher.data[yaml_uri] = util_zip @b2.to_yaml
|
||||
|
||||
use_ui @ui do
|
||||
@source_index.update @uri
|
||||
@source_index.update @uri, true
|
||||
|
||||
assert_equal @all_gem_names, @source_index.gems.keys.sort
|
||||
end
|
||||
|
@ -374,16 +594,16 @@ class TestGemSourceIndex < RubyGemTestCase
|
|||
@fetcher.data["#{@gem_repo}/quick/index.rz"] = quick_index
|
||||
|
||||
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
|
||||
"#{@gem3.full_name}.gemspec.rz"
|
||||
marshal_data = Marshal.dump(@gem3)
|
||||
"#{@b2.full_name}.gemspec.rz"
|
||||
marshal_data = Marshal.dump(@b2)
|
||||
marshal_data[0] = (Marshal::MAJOR_VERSION - 1).chr
|
||||
@fetcher.data[marshal_uri] = util_zip marshal_data
|
||||
|
||||
yaml_uri = "#{@gem_repo}/quick/#{@gem3.full_name}.gemspec.rz"
|
||||
@fetcher.data[yaml_uri] = util_zip @gem3.to_yaml
|
||||
yaml_uri = "#{@gem_repo}/quick/#{@b2.full_name}.gemspec.rz"
|
||||
@fetcher.data[yaml_uri] = util_zip @b2.to_yaml
|
||||
|
||||
use_ui @ui do
|
||||
@source_index.update @uri
|
||||
@source_index.update @uri, true
|
||||
|
||||
assert_equal @all_gem_names, @source_index.gems.keys.sort
|
||||
end
|
||||
|
@ -398,22 +618,48 @@ class TestGemSourceIndex < RubyGemTestCase
|
|||
Gem.configuration = old_gem_conf
|
||||
end
|
||||
|
||||
def test_update_subdir
|
||||
@gem_repo = @gem_repo + "/subdir"
|
||||
|
||||
util_setup_bulk_fetch true
|
||||
|
||||
@source_index.gems.replace({})
|
||||
assert_equal [], @source_index.gems.keys.sort
|
||||
|
||||
uri = @uri.to_s + "/subdir"
|
||||
|
||||
use_ui @ui do
|
||||
@source_index.update uri, true
|
||||
|
||||
assert_equal [@a1.full_name, @a2.full_name, @a_evil9.full_name,
|
||||
@c1_2.full_name],
|
||||
@source_index.gems.keys.sort
|
||||
end
|
||||
|
||||
paths = @fetcher.paths
|
||||
|
||||
assert_equal "#{@gem_repo}/quick/index.rz", paths.shift
|
||||
assert_equal "#{@gem_repo}/Marshal.#{@marshal_version}.Z", paths.shift
|
||||
|
||||
assert paths.empty?, paths.join(', ')
|
||||
end
|
||||
|
||||
def test_update_with_missing
|
||||
marshal_uri = File.join @gem_repo, "quick", "Marshal.#{@marshal_version}",
|
||||
"#{@gem3.full_name}.gemspec.rz"
|
||||
dumped = Marshal.dump @gem3
|
||||
"#{@c1_2.full_name}.gemspec.rz"
|
||||
dumped = Marshal.dump @c1_2
|
||||
@fetcher.data[marshal_uri] = util_zip(dumped)
|
||||
|
||||
use_ui @ui do
|
||||
@source_index.update_with_missing @uri, [@gem3.full_name]
|
||||
@source_index.update_with_missing @uri, [@c1_2.full_name]
|
||||
end
|
||||
|
||||
spec = @source_index.specification(@gem3.full_name)
|
||||
spec = @source_index.specification(@c1_2.full_name)
|
||||
# We don't care about the equality of undumped attributes
|
||||
@gem3.files = spec.files
|
||||
@gem3.loaded_from = spec.loaded_from
|
||||
@c1_2.files = spec.files
|
||||
@c1_2.loaded_from = spec.loaded_from
|
||||
|
||||
assert_equal @gem3, spec
|
||||
assert_equal @c1_2, spec
|
||||
end
|
||||
|
||||
def util_setup_bulk_fetch(compressed)
|
||||
|
@ -427,3 +673,4 @@ class TestGemSourceIndex < RubyGemTestCase
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -25,7 +25,12 @@ class TestGemSourceInfoCache < RubyGemTestCase
|
|||
@sic = Gem::SourceInfoCache.new
|
||||
@sic.instance_variable_set :@fetcher, @fetcher
|
||||
|
||||
@si_new = Gem::SourceIndex.new
|
||||
@sice_new = Gem::SourceInfoCacheEntry.new @si_new, 0
|
||||
|
||||
prep_cache_files @sic
|
||||
|
||||
@sic.reset_cache_data
|
||||
end
|
||||
|
||||
def teardown
|
||||
|
@ -35,8 +40,10 @@ class TestGemSourceInfoCache < RubyGemTestCase
|
|||
|
||||
def test_self_cache_refreshes
|
||||
Gem.configuration.update_sources = true #true by default
|
||||
source_index = Gem::SourceIndex.new 'key' => 'sys'
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = source_index.dump
|
||||
si = Gem::SourceIndex.new
|
||||
si.add_spec @a1
|
||||
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump
|
||||
|
||||
Gem.sources.replace %W[#{@gem_repo}]
|
||||
|
||||
|
@ -51,8 +58,10 @@ class TestGemSourceInfoCache < RubyGemTestCase
|
|||
|
||||
def test_self_cache_skips_refresh_based_on_configuration
|
||||
Gem.configuration.update_sources = false
|
||||
source_index = Gem::SourceIndex.new 'key' => 'sys'
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = source_index.dump
|
||||
si = Gem::SourceIndex.new
|
||||
si.add_spec @a1
|
||||
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump
|
||||
|
||||
Gem.sources.replace %w[#{@gem_repo}]
|
||||
|
||||
|
@ -66,20 +75,24 @@ class TestGemSourceInfoCache < RubyGemTestCase
|
|||
end
|
||||
|
||||
def test_self_cache_data
|
||||
source_index = Gem::SourceIndex.new 'key' => 'sys'
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = source_index.dump
|
||||
si = Gem::SourceIndex.new
|
||||
si.add_spec @a1
|
||||
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump
|
||||
|
||||
Gem::SourceInfoCache.instance_variable_set :@cache, nil
|
||||
sice = Gem::SourceInfoCacheEntry.new source_index, 0
|
||||
sice = Gem::SourceInfoCacheEntry.new si, 0
|
||||
|
||||
use_ui @ui do
|
||||
assert_equal source_index.gems,
|
||||
Gem::SourceInfoCache.cache_data[@gem_repo].source_index.gems
|
||||
gems = Gem::SourceInfoCache.cache_data[@gem_repo].source_index.gems
|
||||
gem_names = gems.map { |_, spec| spec.full_name }
|
||||
|
||||
assert_equal si.gems.map { |_,spec| spec.full_name }, gem_names
|
||||
end
|
||||
end
|
||||
|
||||
def test_cache_data
|
||||
assert_equal [['key','sys']], @sic.cache_data.to_a.sort
|
||||
assert_equal [[@gem_repo, @usr_sice]], @sic.cache_data.to_a.sort
|
||||
end
|
||||
|
||||
def test_cache_data_dirty
|
||||
|
@ -97,7 +110,14 @@ class TestGemSourceInfoCache < RubyGemTestCase
|
|||
|
||||
data = { @gem_repo => { 'totally' => 'borked' } }
|
||||
|
||||
[@sic.system_cache_file, @sic.user_cache_file].each do |fn|
|
||||
cache_files = [
|
||||
@sic.system_cache_file,
|
||||
@sic.latest_system_cache_file,
|
||||
@sic.user_cache_file,
|
||||
@sic.latest_user_cache_file
|
||||
]
|
||||
|
||||
cache_files.each do |fn|
|
||||
FileUtils.mkdir_p File.dirname(fn)
|
||||
open(fn, "wb") { |f| f.write Marshal.dump(data) }
|
||||
end
|
||||
|
@ -113,7 +133,9 @@ class TestGemSourceInfoCache < RubyGemTestCase
|
|||
|
||||
def test_cache_data_none_readable
|
||||
FileUtils.chmod 0222, @sic.system_cache_file
|
||||
FileUtils.chmod 0222, @sic.latest_system_cache_file
|
||||
FileUtils.chmod 0222, @sic.user_cache_file
|
||||
FileUtils.chmod 0222, @sic.latest_user_cache_file
|
||||
return if (File.stat(@sic.system_cache_file).mode & 0222) != 0222
|
||||
return if (File.stat(@sic.user_cache_file).mode & 0222) != 0222
|
||||
# HACK for systems that don't support chmod
|
||||
|
@ -129,6 +151,16 @@ class TestGemSourceInfoCache < RubyGemTestCase
|
|||
assert_equal 'unable to locate a writable cache file', e.message
|
||||
end
|
||||
|
||||
def test_cache_data_nonexistent
|
||||
FileUtils.rm @sic.system_cache_file
|
||||
FileUtils.rm @sic.latest_system_cache_file
|
||||
FileUtils.rm @sic.user_cache_file
|
||||
FileUtils.rm @sic.latest_user_cache_file
|
||||
|
||||
# TODO test verbose output
|
||||
assert_equal [], @sic.cache_data.to_a.sort
|
||||
end
|
||||
|
||||
def test_cache_data_repair
|
||||
data = {
|
||||
@gem_repo => {
|
||||
|
@ -152,7 +184,8 @@ class TestGemSourceInfoCache < RubyGemTestCase
|
|||
|
||||
def test_cache_data_user_fallback
|
||||
FileUtils.chmod 0444, @sic.system_cache_file
|
||||
assert_equal [['key','usr']], @sic.cache_data.to_a.sort
|
||||
|
||||
assert_equal [[@gem_repo, @usr_sice]], @sic.cache_data.to_a.sort
|
||||
end
|
||||
|
||||
def test_cache_file
|
||||
|
@ -174,60 +207,118 @@ class TestGemSourceInfoCache < RubyGemTestCase
|
|||
end
|
||||
|
||||
def test_flush
|
||||
@sic.cache_data['key'] = 'new'
|
||||
@sic.cache_data[@gem_repo] = @sice_new
|
||||
@sic.update
|
||||
@sic.flush
|
||||
|
||||
assert_equal [['key','new']], read_cache(@sic.system_cache_file).to_a.sort
|
||||
assert_equal [[@gem_repo, @sice_new]],
|
||||
read_cache(@sic.system_cache_file).to_a.sort
|
||||
end
|
||||
|
||||
def test_latest_cache_data
|
||||
util_make_gems
|
||||
|
||||
sice = Gem::SourceInfoCacheEntry.new @source_index, 0
|
||||
|
||||
@sic.set_cache_data @gem_repo => sice
|
||||
latest = @sic.latest_cache_data
|
||||
gems = latest[@gem_repo].source_index.search('a').map { |s| s.full_name }
|
||||
|
||||
assert_equal %w[a-2 a_evil-9], gems
|
||||
end
|
||||
|
||||
def test_latest_cache_file
|
||||
latest_cache_file = File.join File.dirname(@gemcache),
|
||||
"latest_#{File.basename @gemcache}"
|
||||
assert_equal latest_cache_file, @sic.latest_cache_file
|
||||
end
|
||||
|
||||
def test_latest_system_cache_file
|
||||
assert_equal File.join(Gem.dir, "latest_source_cache"),
|
||||
@sic.latest_system_cache_file
|
||||
end
|
||||
|
||||
def test_latest_user_cache_file
|
||||
assert_equal @latest_usrcache, @sic.latest_user_cache_file
|
||||
end
|
||||
|
||||
def test_read_system_cache
|
||||
assert_equal [['key','sys']], @sic.cache_data.to_a.sort
|
||||
assert_equal [[@gem_repo, @sys_sice]], @sic.cache_data.to_a.sort
|
||||
end
|
||||
|
||||
def test_read_user_cache
|
||||
FileUtils.chmod 0444, @sic.system_cache_file
|
||||
FileUtils.chmod 0444, @sic.user_cache_file
|
||||
FileUtils.chmod 0444, @sic.latest_user_cache_file
|
||||
|
||||
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
|
||||
|
||||
def test_search
|
||||
si = Gem::SourceIndex.new @gem1.full_name => @gem1
|
||||
cache_data = {
|
||||
@gem_repo => Gem::SourceInfoCacheEntry.new(si, nil)
|
||||
}
|
||||
si = Gem::SourceIndex.new
|
||||
si.add_spec @a1
|
||||
cache_data = { @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) }
|
||||
@sic.instance_variable_set :@cache_data, cache_data
|
||||
|
||||
assert_equal [@gem1], @sic.search(//)
|
||||
assert_equal [@a1], @sic.search(//)
|
||||
end
|
||||
|
||||
def test_search_all
|
||||
util_make_gems
|
||||
|
||||
sice = Gem::SourceInfoCacheEntry.new @source_index, 0
|
||||
|
||||
@sic.set_cache_data @gem_repo => sice
|
||||
@sic.update
|
||||
@sic.write_cache
|
||||
@sic.reset_cache_data
|
||||
|
||||
gem_names = @sic.search(//, false, true).map { |spec| spec.full_name }
|
||||
|
||||
assert_equal %w[a-1 a-2 a_evil-9 c-1.2], gem_names
|
||||
end
|
||||
|
||||
def test_search_dependency
|
||||
si = Gem::SourceIndex.new @gem1.full_name => @gem1
|
||||
cache_data = {
|
||||
@gem_repo => Gem::SourceInfoCacheEntry.new(si, nil)
|
||||
}
|
||||
si = Gem::SourceIndex.new
|
||||
si.add_spec @a1
|
||||
cache_data = { @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) }
|
||||
@sic.instance_variable_set :@cache_data, cache_data
|
||||
|
||||
dep = Gem::Dependency.new @gem1.name, @gem1.version
|
||||
dep = Gem::Dependency.new @a1.name, @a1.version
|
||||
|
||||
assert_equal [@gem1], @sic.search(dep)
|
||||
assert_equal [@a1], @sic.search(dep)
|
||||
end
|
||||
|
||||
def test_search_no_matches
|
||||
si = Gem::SourceIndex.new @gem1.full_name => @gem1
|
||||
cache_data = {
|
||||
@gem_repo => Gem::SourceInfoCacheEntry.new(si, nil)
|
||||
}
|
||||
si = Gem::SourceIndex.new
|
||||
si.add_spec @a1
|
||||
cache_data = { @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) }
|
||||
@sic.instance_variable_set :@cache_data, cache_data
|
||||
|
||||
assert_equal [], @sic.search(/nonexistent/)
|
||||
end
|
||||
|
||||
def test_search_no_matches_in_source
|
||||
si = Gem::SourceIndex.new @gem1.full_name => @gem1
|
||||
cache_data = {
|
||||
@gem_repo => Gem::SourceInfoCacheEntry.new(si, nil)
|
||||
}
|
||||
si = Gem::SourceIndex.new
|
||||
si.add_spec @a1
|
||||
cache_data = { @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) }
|
||||
@sic.instance_variable_set :@cache_data, cache_data
|
||||
Gem.sources.replace %w[more-gems.example.com]
|
||||
|
||||
|
@ -235,13 +326,12 @@ class TestGemSourceInfoCache < RubyGemTestCase
|
|||
end
|
||||
|
||||
def test_search_with_source
|
||||
si = Gem::SourceIndex.new @gem1.full_name => @gem1
|
||||
cache_data = {
|
||||
@gem_repo => Gem::SourceInfoCacheEntry.new(si, nil)
|
||||
}
|
||||
si = Gem::SourceIndex.new
|
||||
si.add_spec @a1
|
||||
cache_data = { @gem_repo => Gem::SourceInfoCacheEntry.new(si, nil) }
|
||||
@sic.instance_variable_set :@cache_data, cache_data
|
||||
|
||||
assert_equal [[@gem1, @gem_repo]],
|
||||
assert_equal [[@a1, @gem_repo]],
|
||||
@sic.search_with_source(//)
|
||||
end
|
||||
|
||||
|
@ -254,45 +344,81 @@ class TestGemSourceInfoCache < RubyGemTestCase
|
|||
end
|
||||
|
||||
def test_write_cache
|
||||
@sic.cache_data['key'] = 'new'
|
||||
@sic.cache_data[@gem_repo] = @sice_new
|
||||
@sic.write_cache
|
||||
|
||||
assert_equal [['key', 'new']],
|
||||
assert_equal [[@gem_repo, @sice_new]],
|
||||
read_cache(@sic.system_cache_file).to_a.sort
|
||||
assert_equal [['key', 'usr']],
|
||||
assert_equal [[@gem_repo, @usr_sice]],
|
||||
read_cache(@sic.user_cache_file).to_a.sort
|
||||
end
|
||||
|
||||
def test_write_cache_user
|
||||
FileUtils.chmod 0444, @sic.system_cache_file
|
||||
@sic.set_cache_data({'key' => 'new'})
|
||||
@sic.set_cache_data({@gem_repo => @sice_new})
|
||||
@sic.update
|
||||
@sic.write_cache
|
||||
|
||||
assert_equal [['key', 'sys']], read_cache(@sic.system_cache_file).to_a.sort
|
||||
assert_equal [['key', 'new']], read_cache(@sic.user_cache_file).to_a.sort
|
||||
assert File.exist?(@sic.user_cache_file), 'user_cache_file'
|
||||
assert File.exist?(@sic.latest_user_cache_file),
|
||||
'latest_user_cache_file exists'
|
||||
|
||||
assert_equal [[@gem_repo, @sys_sice]],
|
||||
read_cache(@sic.system_cache_file).to_a.sort
|
||||
assert_equal [[@gem_repo, @sice_new]],
|
||||
read_cache(@sic.user_cache_file).to_a.sort
|
||||
end
|
||||
|
||||
def test_write_cache_user_from_scratch
|
||||
FileUtils.rm_rf @sic.user_cache_file
|
||||
FileUtils.rm_rf @sic.latest_user_cache_file
|
||||
|
||||
FileUtils.chmod 0444, @sic.system_cache_file
|
||||
@sic.set_cache_data({'key' => 'new'})
|
||||
FileUtils.chmod 0444, @sic.latest_system_cache_file
|
||||
|
||||
@si = Gem::SourceIndex.new
|
||||
@si.add_specs @a1, @a2
|
||||
|
||||
@sice = Gem::SourceInfoCacheEntry.new @si, 0
|
||||
|
||||
@sic.set_cache_data({ @gem_repo => @sice })
|
||||
@sic.update
|
||||
@sic.write_cache
|
||||
|
||||
assert_equal [['key', 'sys']], read_cache(@sic.system_cache_file).to_a.sort
|
||||
assert_equal [['key', 'new']], read_cache(@sic.user_cache_file).to_a.sort
|
||||
assert File.exist?(@sic.user_cache_file), 'system_cache_file'
|
||||
assert File.exist?(@sic.latest_user_cache_file),
|
||||
'latest_system_cache_file'
|
||||
|
||||
user_cache_data = read_cache(@sic.user_cache_file).to_a.sort
|
||||
assert_equal 1, user_cache_data.length
|
||||
user_cache_data = user_cache_data.first
|
||||
|
||||
assert_equal @gem_repo, user_cache_data.first
|
||||
|
||||
gems = user_cache_data.last.source_index.map { |_,spec| spec.full_name }
|
||||
assert_equal [@a1.full_name, @a2.full_name], gems
|
||||
|
||||
user_cache_data = read_cache(@sic.latest_user_cache_file).to_a.sort
|
||||
assert_equal 1, user_cache_data.length
|
||||
user_cache_data = user_cache_data.first
|
||||
|
||||
assert_equal @gem_repo, user_cache_data.first
|
||||
|
||||
gems = user_cache_data.last.source_index.map { |_,spec| spec.full_name }
|
||||
assert_equal [@a2.full_name], gems
|
||||
end
|
||||
|
||||
def test_write_cache_user_no_directory
|
||||
FileUtils.rm_rf File.dirname(@sic.user_cache_file)
|
||||
FileUtils.chmod 0444, @sic.system_cache_file
|
||||
@sic.set_cache_data({'key' => 'new'})
|
||||
@sic.set_cache_data({ @gem_repo => @sice_new })
|
||||
@sic.update
|
||||
@sic.write_cache
|
||||
|
||||
assert_equal [['key','sys']], read_cache(@sic.system_cache_file).to_a.sort
|
||||
assert_equal [['key','new']], read_cache(@sic.user_cache_file).to_a.sort
|
||||
assert_equal [[@gem_repo, @sys_sice]],
|
||||
read_cache(@sic.system_cache_file).to_a.sort
|
||||
assert_equal [[@gem_repo, @sice_new]],
|
||||
read_cache(@sic.user_cache_file).to_a.sort
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -9,37 +9,68 @@ class TestGemSourceInfoCacheEntry < RubyGemTestCase
|
|||
|
||||
util_setup_fake_fetcher
|
||||
|
||||
@si = Gem::SourceIndex.new @gem1.full_name => @gem1.name
|
||||
@si = Gem::SourceIndex.new
|
||||
@si.add_spec @a1
|
||||
@sic_e = Gem::SourceInfoCacheEntry.new @si, @si.dump.size
|
||||
end
|
||||
|
||||
def test_refresh
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}.Z"] =
|
||||
proc { raise Exception }
|
||||
proc { raise }
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = @si.dump
|
||||
|
||||
assert_nothing_raised do
|
||||
@sic_e.refresh @gem_repo
|
||||
use_ui @ui do
|
||||
@sic_e.refresh @gem_repo, true
|
||||
end
|
||||
end
|
||||
|
||||
def test_refresh_all
|
||||
@si.add_spec @a2
|
||||
|
||||
a1_name = @a1.full_name
|
||||
a2_name = @a2.full_name
|
||||
|
||||
@fetcher.data["#{@gem_repo}/quick/index.rz"] =
|
||||
util_zip [a1_name, a2_name].join("\n")
|
||||
@fetcher.data["#{@gem_repo}/quick/latest_index.rz"] = util_zip a2_name
|
||||
@fetcher.data["#{@gem_repo}/quick/Marshal.#{Gem.marshal_version}/#{a1_name}.gemspec.rz"] = util_zip Marshal.dump(@a1)
|
||||
@fetcher.data["#{@gem_repo}/quick/Marshal.#{Gem.marshal_version}/#{a2_name}.gemspec.rz"] = util_zip Marshal.dump(@a2)
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{Gem.marshal_version}"] =
|
||||
Marshal.dump @si
|
||||
|
||||
sic_e = Gem::SourceInfoCacheEntry.new Gem::SourceIndex.new, 0
|
||||
|
||||
use_ui @ui do
|
||||
sic_e.refresh @gem_repo, false
|
||||
end
|
||||
|
||||
assert_equal [a2_name], sic_e.source_index.map { |n,| n }.sort
|
||||
|
||||
use_ui @ui do
|
||||
sic_e.refresh @gem_repo, true
|
||||
end
|
||||
|
||||
assert_equal [a1_name, a2_name], sic_e.source_index.map { |n,| n }.sort
|
||||
end
|
||||
|
||||
def test_refresh_bad_uri
|
||||
assert_raise URI::BadURIError do
|
||||
@sic_e.refresh 'gems.example.com'
|
||||
@sic_e.refresh 'gems.example.com', true
|
||||
end
|
||||
end
|
||||
|
||||
def test_refresh_update
|
||||
si = Gem::SourceIndex.new @gem1.full_name => @gem1,
|
||||
@gem2.full_name => @gem2
|
||||
si = Gem::SourceIndex.new
|
||||
si.add_spec @a1
|
||||
si.add_spec @b2
|
||||
@fetcher.data["#{@gem_repo}/Marshal.#{@marshal_version}"] = si.dump
|
||||
|
||||
use_ui @ui do
|
||||
@sic_e.refresh @gem_repo
|
||||
@sic_e.refresh @gem_repo, true
|
||||
end
|
||||
|
||||
new_gem = @sic_e.source_index.specification(@gem2.full_name)
|
||||
assert_equal @gem2.full_name, new_gem.full_name
|
||||
new_gem = @sic_e.source_index.specification(@b2.full_name)
|
||||
assert_equal @b2.full_name, new_gem.full_name
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
Загрузка…
Ссылка в новой задаче