From d478c7a7342478847cc1148f4134b5f0db04e1d9 Mon Sep 17 00:00:00 2001 From: drbrain Date: Thu, 25 Sep 2008 10:13:50 +0000 Subject: [PATCH] Update to RubyGems 1.3.0 r1891 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@19547 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 4 + lib/rubygems.rb | 121 +- lib/rubygems/commands/contents_command.rb | 2 +- lib/rubygems/commands/environment_command.rb | 40 + lib/rubygems/commands/help_command.rb | 4 +- lib/rubygems/commands/install_command.rb | 15 + lib/rubygems/commands/lock_command.rb | 21 +- lib/rubygems/commands/outdated_command.rb | 2 +- lib/rubygems/commands/pristine_command.rb | 2 +- lib/rubygems/commands/query_command.rb | 25 +- lib/rubygems/commands/rdoc_command.rb | 6 +- .../commands/specification_command.rb | 4 +- lib/rubygems/commands/unpack_command.rb | 2 +- lib/rubygems/commands/update_command.rb | 37 +- lib/rubygems/commands/which_command.rb | 7 +- lib/rubygems/config_file.rb | 6 +- lib/rubygems/defaults.rb | 55 +- lib/rubygems/dependency_installer.rb | 34 +- lib/rubygems/doc_manager.rb | 291 ++-- lib/rubygems/gem_path_searcher.rb | 54 +- lib/rubygems/installer.rb | 94 +- lib/rubygems/local_remote_options.rb | 7 + lib/rubygems/package/tar_reader.rb | 14 +- lib/rubygems/platform.rb | 19 +- lib/rubygems/remote_fetcher.rb | 25 +- lib/rubygems/rubygems_version.rb | 2 +- lib/rubygems/source_index.rb | 29 +- lib/rubygems/source_info_cache.rb | 9 + lib/rubygems/spec_fetcher.rb | 8 +- lib/rubygems/specification.rb | 1441 ++++++++++------- lib/rubygems/test_utilities.rb | 22 +- lib/rubygems/uninstaller.rb | 86 +- test/rubygems/gemutilities.rb | 37 +- test/rubygems/mockgemui.rb | 24 +- test/rubygems/test_gem.rb | 121 +- test/rubygems/test_gem_builder.rb | 2 +- test/rubygems/test_gem_command_manager.rb | 4 +- .../test_gem_commands_dependency_command.rb | 2 +- .../test_gem_commands_install_command.rb | 10 +- .../test_gem_commands_list_command.rb | 37 + .../test_gem_commands_lock_command.rb | 69 + .../test_gem_commands_query_command.rb | 41 +- .../test_gem_commands_uninstall_command.rb | 60 + test/rubygems/test_gem_config_file.rb | 5 + .../test_gem_ext_configure_builder.rb | 5 +- test/rubygems/test_gem_ext_rake_builder.rb | 10 +- test/rubygems/test_gem_gem_path_searcher.rb | 22 +- .../test_gem_install_update_options.rb | 6 +- test/rubygems/test_gem_installer.rb | 60 +- .../rubygems/test_gem_local_remote_options.rb | 12 + test/rubygems/test_gem_package_tar_header.rb | 8 +- test/rubygems/test_gem_platform.rb | 19 +- test/rubygems/test_gem_remote_fetcher.rb | 24 +- test/rubygems/test_gem_source_index.rb | 95 +- test/rubygems/test_gem_source_info_cache.rb | 3 +- test/rubygems/test_gem_spec_fetcher.rb | 14 +- test/rubygems/test_gem_specification.rb | 20 +- test/rubygems/test_gem_stream_ui.rb | 2 +- test/rubygems/test_gem_uninstaller.rb | 21 + test/rubygems/test_gem_version.rb | 2 +- 60 files changed, 2174 insertions(+), 1049 deletions(-) create mode 100644 test/rubygems/test_gem_commands_list_command.rb create mode 100644 test/rubygems/test_gem_commands_lock_command.rb create mode 100644 test/rubygems/test_gem_commands_uninstall_command.rb diff --git a/ChangeLog b/ChangeLog index 3c9f11be70..f76610b794 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Thu Sep 25 19:13:12 2008 + + * lib/rubygems*: Update to RubyGems 1.3.0 r1891. + Thu Sep 25 18:40:42 2008 Yuki Sonoda (Yugui) * class.c (rb_make_metaclass): Made class of class of class diff --git a/lib/rubygems.rb b/lib/rubygems.rb index fde91bfd4b..40c6defb66 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -104,6 +104,11 @@ module Gem @ruby = nil @sources = [] + @post_install_hooks = [] + @post_uninstall_hooks = [] + @pre_uninstall_hooks = [] + @pre_install_hooks = [] + ## # Activates an installed gem matching +gem+. The gem must satisfy # +version_requirements+. @@ -244,7 +249,10 @@ module Gem def self.clear_paths @gem_home = nil @gem_path = nil + @user_home = nil + @@source_index = nil + MUTEX.synchronize do @searcher = nil end @@ -261,9 +269,7 @@ module Gem # The standard configuration object for gems. def self.configuration - return @configuration if @configuration - require 'rubygems/config_file' - @configuration = Gem::ConfigFile.new [] + @configuration ||= Gem::ConfigFile.new [] end ## @@ -336,6 +342,22 @@ module Gem end end + ## + # Returns a list of paths matching +file+ that can be used by a gem to pick + # up features from other gems. For example: + # + # Gem.find_files('rdoc/discover').each do |path| load path end + # + # find_files does not search $LOAD_PATH for files, only gems. + + def self.find_files(path) + specs = searcher.find_all path + + specs.map do |spec| + searcher.matching_files spec, path + end.flatten + end + ## # Finds the user's home directory. #-- @@ -353,7 +375,7 @@ module Gem end if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then - return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}" + return "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}" end begin @@ -466,13 +488,11 @@ module Gem ## # manage_gems is useless and deprecated. Don't call it anymore. - #-- - # TODO warn w/ RubyGems 1.2.x release. - def self.manage_gems - #file, lineno = location_of_caller + def self.manage_gems # :nodoc: + file, lineno = location_of_caller - #warn "#{file}:#{lineno}:Warning: Gem#manage_gems is deprecated and will be removed on or after September 2008." + warn "#{file}:#{lineno}:Warning: Gem::manage_gems is deprecated and will be removed on or after March 2009." end ## @@ -489,11 +509,7 @@ module Gem @gem_path ||= nil unless @gem_path then - paths = if ENV['GEM_PATH'] then - [ENV['GEM_PATH']] - else - [default_path] - end + paths = [ENV['GEM_PATH'] || Gem.configuration.path || default_path] if defined?(APPLE_GEM_HOME) and not ENV['GEM_PATH'] then paths << APPLE_GEM_HOME @@ -523,6 +539,40 @@ module Gem @platforms end + ## + # Adds a post-install hook that will be passed an Gem::Installer instance + # when Gem::Installer#install is called + + def self.post_install(&hook) + @post_install_hooks << hook + end + + ## + # Adds a post-uninstall hook that will be passed a Gem::Uninstaller instance + # and the spec that was uninstalled when Gem::Uninstaller#uninstall is + # called + + def self.post_uninstall(&hook) + @post_uninstall_hooks << hook + end + + ## + # Adds a pre-install hook that will be passed an Gem::Installer instance + # when Gem::Installer#install is called + + def self.pre_install(&hook) + @pre_install_hooks << hook + end + + ## + # Adds a pre-uninstall hook that will be passed an Gem::Uninstaller instance + # and the spec that will be uninstalled when Gem::Uninstaller#uninstall is + # called + + def self.pre_uninstall(&hook) + @pre_uninstall_hooks << hook + end + ## # The directory prefix this RubyGems was installed at. @@ -600,6 +650,9 @@ module Gem @ruby = File.join(ConfigMap[:bindir], ConfigMap[:ruby_install_name]) @ruby << ConfigMap[:EXEEXT] + + # escape string in case path to ruby executable contain spaces. + @ruby.sub!(/.*\s.*/m, '"\&"') end @ruby @@ -655,7 +708,13 @@ module Gem end @gem_path.uniq! - @gem_path.each do |gp| ensure_gem_subdirectories(gp) end + @gem_path.each do |path| + if 0 == File.expand_path(path).index(Gem.user_home) and + Etc.getpwuid.uid != File::Stat.new(Gem.user_home).uid then + next # only create by matching user + end + ensure_gem_subdirectories path + end end private_class_method :set_paths @@ -685,6 +744,14 @@ module Gem @sources end + ## + # Need to be able to set the sources without calling + # Gem.sources.replace since that would cause an infinite loop. + + def self.sources=(new_sources) + @sources = new_sources + end + ## # Glob pattern for require-able path suffixes. @@ -731,6 +798,27 @@ module Gem attr_reader :loaded_specs + ## + # The list of hooks to be run before Gem::Install#install does any work + + attr_reader :post_install_hooks + + ## + # The list of hooks to be run before Gem::Uninstall#uninstall does any + # work + + attr_reader :post_uninstall_hooks + + ## + # The list of hooks to be run after Gem::Install#install is finished + + attr_reader :pre_install_hooks + + ## + # The list of hooks to be run after Gem::Uninstall#uninstall is finished + + attr_reader :pre_uninstall_hooks + # :stopdoc: alias cache source_index # an alias for the old name @@ -781,7 +869,10 @@ if defined?(RUBY_ENGINE) then end end +require 'rubygems/config_file' + if RUBY_VERSION < '1.9' then require 'rubygems/custom_require' end +Gem.clear_paths diff --git a/lib/rubygems/commands/contents_command.rb b/lib/rubygems/commands/contents_command.rb index 5060403fd8..bc75fb5c03 100644 --- a/lib/rubygems/commands/contents_command.rb +++ b/lib/rubygems/commands/contents_command.rb @@ -51,7 +51,7 @@ class Gem::Commands::ContentsCommand < Gem::Command si = Gem::SourceIndex.from_gems_in(*s) - gem_spec = si.search(/\A#{gem}\z/, version).last + gem_spec = si.find_name(gem, version).last unless gem_spec then say "Unable to find gem '#{gem}' in #{path_kind}" diff --git a/lib/rubygems/commands/environment_command.rb b/lib/rubygems/commands/environment_command.rb index a67c00bfd6..e672da54f0 100644 --- a/lib/rubygems/commands/environment_command.rb +++ b/lib/rubygems/commands/environment_command.rb @@ -18,6 +18,46 @@ class Gem::Commands::EnvironmentCommand < Gem::Command return args.gsub(/^\s+/, '') end + def description # :nodoc: + <<-EOF +The RubyGems environment can be controlled through command line arguments, +gemrc files, environment variables and built-in defaults. + +Command line argument defaults and some RubyGems defaults can be set in +~/.gemrc file for individual users and a /etc/gemrc for all users. A gemrc +is a YAML file with the following YAML keys: + + :sources: A YAML array of remote gem repositories to install gems from + :verbose: Verbosity of the gem command. false, true, and :really are the + levels + :update_sources: Enable/disable automatic updating of repository metadata + :backtrace: Print backtrace when RubyGems encounters an error + :bulk_threshold: Switch to a bulk update when this many sources are out of + date (legacy setting) + :gempath: The paths in which to look for gems + gem_command: A string containing arguments for the specified gem command + +Example: + + :verbose: false + install: --no-wrappers + update: --no-wrappers + +RubyGems' default local repository can be overriden with the GEM_PATH and +GEM_HOME environment variables. GEM_HOME sets the default repository to +install into. GEM_PATH allows multiple local repositories to be searched for +gems. + +If you are behind a proxy server, RubyGems uses the HTTP_PROXY, +HTTP_PROXY_USER and HTTP_PROXY_PASS environment variables to discover the +proxy server. + +If you are packaging RubyGems all of RubyGems' defaults are in +lib/rubygems/defaults.rb. You may override these in +lib/rubygems/defaults/operating_system.rb + EOF + end + def usage # :nodoc: "#{program_name} [arg]" end diff --git a/lib/rubygems/commands/help_command.rb b/lib/rubygems/commands/help_command.rb index 05ea3f7a71..0c4a4ec16f 100644 --- a/lib/rubygems/commands/help_command.rb +++ b/lib/rubygems/commands/help_command.rb @@ -20,9 +20,9 @@ Some examples of 'gem' usage. gem install --remote rake --test --rdoc --ri * Install 'rake', but only version 0.3.1, even if dependencies - are not met, and into a specific directory: + are not met, and into a user-specific directory: - gem install rake --version 0.3.1 --force --install-dir $HOME/.gems + gem install rake --version 0.3.1 --force --user-install * List local gems whose name begins with 'D': diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb index 923d578a15..1a6eb68a8b 100644 --- a/lib/rubygems/commands/install_command.rb +++ b/lib/rubygems/commands/install_command.rb @@ -38,6 +38,19 @@ class Gem::Commands::InstallCommand < Gem::Command "--no-test --install-dir #{Gem.dir}" end + def description # :nodoc: + <<-EOF +The install command installs local or remote gem into a gem repository. + +For gems with executables ruby installs a wrapper file into the executable +directory by deault. This can be overridden with the --no-wrappers option. +The wrapper allows you to choose among alternate gem versions using _version_. + +For example `rake _0.7.3_ --version` will run rake version 0.7.3 if a newer +version is also installed. + EOF + end + def usage # :nodoc: "#{program_name} GEMNAME [GEMNAME ...] [options] -- --build-flags" end @@ -106,6 +119,8 @@ class Gem::Commands::InstallCommand < Gem::Command installed_gems.each do |gem| Gem::DocManager.new(gem, options[:rdoc_args]).generate_ri end + + Gem::DocManager.update_ri_cache end if options[:generate_rdoc] then diff --git a/lib/rubygems/commands/lock_command.rb b/lib/rubygems/commands/lock_command.rb index 6be2774e92..5a43978dd9 100644 --- a/lib/rubygems/commands/lock_command.rb +++ b/lib/rubygems/commands/lock_command.rb @@ -58,15 +58,15 @@ lock it down to the exact version. end def complain(message) - if options.strict then - raise message + if options[:strict] then + raise Gem::Exception, message else say "# #{message}" end end def execute - say 'require "rubygems"' + say "require 'rubygems'" locked = {} @@ -77,15 +77,20 @@ lock it down to the exact version. spec = Gem::SourceIndex.load_specification spec_path(full_name) + if spec.nil? then + complain "Could not find gem #{full_name}, try using the full name" + next + end + say "gem '#{spec.name}', '= #{spec.version}'" unless locked[spec.name] locked[spec.name] = true spec.runtime_dependencies.each do |dep| next if locked[dep.name] - candidates = Gem.source_index.search dep.name, dep.requirement_list + candidates = Gem.source_index.search dep if candidates.empty? then - complain "Unable to satisfy '#{dep}' from currently installed gems." + complain "Unable to satisfy '#{dep}' from currently installed gems" else pending << candidates.last.full_name end @@ -94,7 +99,11 @@ lock it down to the exact version. end def spec_path(gem_full_name) - File.join Gem.path, "specifications", "#{gem_full_name }.gemspec" + gemspecs = Gem.path.map do |path| + File.join path, "specifications", "#{gem_full_name}.gemspec" + end + + gemspecs.find { |gemspec| File.exist? gemspec } end end diff --git a/lib/rubygems/commands/outdated_command.rb b/lib/rubygems/commands/outdated_command.rb index 1cd1087dd1..9e054f988c 100644 --- a/lib/rubygems/commands/outdated_command.rb +++ b/lib/rubygems/commands/outdated_command.rb @@ -19,7 +19,7 @@ class Gem::Commands::OutdatedCommand < Gem::Command locals = Gem::SourceIndex.from_installed_gems locals.outdated.sort.each do |name| - local = locals.search(/^#{name}$/).last + local = locals.find_name(name).last dep = Gem::Dependency.new local.name, ">= #{local.version}" remotes = Gem::SpecFetcher.fetcher.fetch dep diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb index 3e55a1bb30..d47fe54edd 100644 --- a/lib/rubygems/commands/pristine_command.rb +++ b/lib/rubygems/commands/pristine_command.rb @@ -57,7 +57,7 @@ revert the gem. end else gem_name = get_one_gem_name - Gem::SourceIndex.from_installed_gems.search(gem_name, + Gem::SourceIndex.from_installed_gems.find_name(gem_name, options[:version]) end diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb index f4d6120bcd..29fe8acb79 100644 --- a/lib/rubygems/commands/query_command.rb +++ b/lib/rubygems/commands/query_command.rb @@ -59,7 +59,7 @@ class Gem::Commands::QueryCommand < Gem::Command if name.source.empty? then alert_error "You must specify a gem name" exit_code |= 4 - elsif installed? name.source, options[:version] then + elsif installed? name, options[:version] then say "true" else say "false" @@ -69,12 +69,16 @@ class Gem::Commands::QueryCommand < Gem::Command raise Gem::SystemExitException, exit_code end - if local? then - say - say "*** LOCAL GEMS ***" - say + dep = Gem::Dependency.new name, Gem::Requirement.default - specs = Gem.source_index.search name + if local? then + if ui.outs.tty? or both? then + say + say "*** LOCAL GEMS ***" + say + end + + specs = Gem.source_index.search dep spec_tuples = specs.map do |spec| [[spec.name, spec.version, spec.original_platform, spec], :local] @@ -84,13 +88,14 @@ class Gem::Commands::QueryCommand < Gem::Command end if remote? then - say - say "*** REMOTE GEMS ***" - say + if ui.outs.tty? or both? then + say + say "*** REMOTE GEMS ***" + say + end all = options[:all] - dep = Gem::Dependency.new name, Gem::Requirement.default begin fetcher = Gem::SpecFetcher.fetcher spec_tuples = fetcher.find_matching dep, all, false diff --git a/lib/rubygems/commands/rdoc_command.rb b/lib/rubygems/commands/rdoc_command.rb index f2e677c115..82180d485c 100644 --- a/lib/rubygems/commands/rdoc_command.rb +++ b/lib/rubygems/commands/rdoc_command.rb @@ -59,11 +59,15 @@ module Gem if specs.empty? fail "Failed to find gem #{gem_name} to generate RDoc for #{options[:version]}" end + if options[:include_ri] specs.each do |spec| Gem::DocManager.new(spec).generate_ri end + + Gem::DocManager.update_ri_cache end + if options[:include_rdoc] specs.each do |spec| Gem::DocManager.new(spec).generate_rdoc @@ -73,6 +77,6 @@ module Gem true end end - + end end diff --git a/lib/rubygems/commands/specification_command.rb b/lib/rubygems/commands/specification_command.rb index 689f2560c9..5aaf6d1797 100644 --- a/lib/rubygems/commands/specification_command.rb +++ b/lib/rubygems/commands/specification_command.rb @@ -40,6 +40,7 @@ class Gem::Commands::SpecificationCommand < Gem::Command def execute specs = [] gem = get_one_gem_name + dep = Gem::Dependency.new gem, options[:version] if local? then if File.exist? gem then @@ -47,12 +48,11 @@ class Gem::Commands::SpecificationCommand < Gem::Command end if specs.empty? then - specs.push(*Gem.source_index.search(/\A#{gem}\z/, options[:version])) + specs.push(*Gem.source_index.search(dep)) end end if remote? then - dep = Gem::Dependency.new gem, options[:version] found = Gem::SpecFetcher.fetcher.fetch dep specs.push(*found.map { |spec,| spec }) diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb index d187f8a9ea..ef9436ae34 100644 --- a/lib/rubygems/commands/unpack_command.rb +++ b/lib/rubygems/commands/unpack_command.rb @@ -68,7 +68,7 @@ class Gem::Commands::UnpackCommand < Gem::Command def get_path(gemname, version_req) return gemname if gemname =~ /\.gem$/i - specs = Gem::source_index.search(/\A#{gemname}\z/, version_req) + specs = Gem::source_index.find_name gemname, version_req selected = specs.sort_by { |s| s.version }.last diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb index 78baa8ba56..4490f385dc 100644 --- a/lib/rubygems/commands/update_command.rb +++ b/lib/rubygems/commands/update_command.rb @@ -45,6 +45,8 @@ class Gem::Commands::UpdateCommand < Gem::Command end def execute + hig = {} + if options[:system] then say "Updating RubyGems" @@ -52,16 +54,22 @@ class Gem::Commands::UpdateCommand < Gem::Command fail "No gem names are allowed with the --system option" end - options[:args] = ["rubygems-update"] + spec = Gem::Specification.new + spec.name = 'rubygems-update' + spec.version = Gem::Version.new Gem::RubyGemsVersion + spec.version = Gem::Version.new '1.1.1' + hig['rubygems-update'] = spec + + options[:user_install] = false else say "Updating installed gems" - end - hig = {} # highest installed gems + hig = {} # highest installed gems - Gem.source_index.each do |name, spec| - if hig[spec.name].nil? or hig[spec.name].version < spec.version then - hig[spec.name] = spec + Gem.source_index.each do |name, spec| + if hig[spec.name].nil? or hig[spec.name].version < spec.version then + hig[spec.name] = spec + end end end @@ -84,14 +92,14 @@ class Gem::Commands::UpdateCommand < Gem::Command end if gems_to_update.include? "rubygems-update" then - latest_ruby_gem = remote_gemspecs.select do |s| - s.name == 'rubygems-update' - end + Gem.source_index.refresh! - latest_ruby_gem = latest_ruby_gem.sort_by { |s| s.version }.last + update_gems = Gem.source_index.search 'rubygems-update' - say "Updating version of RubyGems to #{latest_ruby_gem.version}" - installed = do_rubygems_update latest_ruby_gem.version + latest_update_gem = update_gems.sort_by { |s| s.version }.last + + say "Updating RubyGems to #{latest_update_gem.version}" + installed = do_rubygems_update latest_update_gem.version say "RubyGems system software updated" if installed else @@ -103,6 +111,9 @@ class Gem::Commands::UpdateCommand < Gem::Command end end + ## + # Update the RubyGems software to +version+. + def do_rubygems_update(version) args = [] args.push '--prefix', Gem.prefix unless Gem.prefix.nil? @@ -112,8 +123,6 @@ class Gem::Commands::UpdateCommand < Gem::Command update_dir = File.join Gem.dir, 'gems', "rubygems-update-#{version}" - success = false - Dir.chdir update_dir do say "Installing RubyGems #{version}" setup_cmd = "#{Gem.ruby} setup.rb #{args.join ' '}" diff --git a/lib/rubygems/commands/which_command.rb b/lib/rubygems/commands/which_command.rb index b42244ce7d..2267e44b11 100644 --- a/lib/rubygems/commands/which_command.rb +++ b/lib/rubygems/commands/which_command.rb @@ -3,10 +3,10 @@ require 'rubygems/gem_path_searcher' class Gem::Commands::WhichCommand < Gem::Command - EXT = %w[.rb .rbw .so .dll] # HACK + EXT = %w[.rb .rbw .so .dll .bundle] # HACK def initialize - super 'which', 'Find the location of a library', + super 'which', 'Find the location of a library file you can require', :search_gems_first => false, :show_all => false add_option '-a', '--[no-]all', 'show all matching files' do |show_all, options| @@ -52,7 +52,7 @@ class Gem::Commands::WhichCommand < Gem::Command paths = find_paths arg, dirs if paths.empty? then - say "Can't find #{arg}" + say "Can't find ruby library file or shared library #{arg}" else say paths end @@ -84,3 +84,4 @@ class Gem::Commands::WhichCommand < Gem::Command end end + diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb index c093c31a1b..0a35ca9417 100644 --- a/lib/rubygems/config_file.rb +++ b/lib/rubygems/config_file.rb @@ -49,6 +49,9 @@ class Gem::ConfigFile # List of arguments supplied to the config file object. attr_reader :args + # Where to look for gems + attr_accessor :path + # True if we print backtraces on errors. attr_writer :backtrace @@ -123,9 +126,10 @@ class Gem::ConfigFile @backtrace = @hash[:backtrace] if @hash.key? :backtrace @benchmark = @hash[:benchmark] if @hash.key? :benchmark @bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold - Gem.sources.replace @hash[:sources] if @hash.key? :sources + Gem.sources = @hash[:sources] if @hash.key? :sources @verbose = @hash[:verbose] if @hash.key? :verbose @update_sources = @hash[:update_sources] if @hash.key? :update_sources + @path = @hash[:gempath] handle_arguments arg_list end diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb index 8bb9776575..7eba0cb049 100644 --- a/lib/rubygems/defaults.rb +++ b/lib/rubygems/defaults.rb @@ -1,36 +1,52 @@ module Gem - # An Array of the default sources that come with RubyGems. + ## + # An Array of the default sources that come with RubyGems + def self.default_sources %w[http://gems.rubyforge.org/] end + ## # Default home directory path to be used if an alternate value is not - # specified in the environment. + # specified in the environment + def self.default_dir 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] + File.join(ConfigMap[:libdir], ruby_engine, 'gems', + ConfigMap[:ruby_version]) end end - # Default gem load path. - def self.default_path - [File.join(ENV['HOME'], '.gem'), default_dir] + ## + # Path for gems in the user's home directory + + def self.user_dir + File.join(Gem.user_home, '.gem', ruby_engine, + ConfigMap[:ruby_version]) end - # Deduce Ruby's --program-prefix and --program-suffix from its install name. + ## + # Default gem load path + + def self.default_path + [user_dir, default_dir] + end + + ## + # Deduce Ruby's --program-prefix and --program-suffix from its install name + def self.default_exec_format baseruby = ConfigMap[:BASERUBY] || 'ruby' ConfigMap[:RUBY_INSTALL_NAME].sub(baseruby, '%s') rescue '%s' end + ## # The default directory for binaries + def self.default_bindir if defined? RUBY_FRAMEWORK_VERSION then # mac framework support '/usr/bin' @@ -39,15 +55,30 @@ module Gem end end - # The default system-wide source info cache directory. + ## + # The default system-wide source info cache directory + def self.default_system_source_cache_dir File.join Gem.dir, 'source_cache' end - # The default user-specific source info cache directory. + ## + # The default user-specific source info cache directory + def self.default_user_source_cache_dir File.join Gem.user_home, '.gem', 'source_cache' end + ## + # A wrapper around RUBY_ENGINE const that may not be defined + + def self.ruby_engine + if defined? RUBY_ENGINE then + RUBY_ENGINE + else + 'ruby' + end + end + end diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb index 8406844e79..9ae2659536 100644 --- a/lib/rubygems/dependency_installer.rb +++ b/lib/rubygems/dependency_installer.rb @@ -38,9 +38,17 @@ class Gem::DependencyInstaller # :ignore_dependencies:: Don't install any dependencies. # :install_dir:: See Gem::Installer#install. # :security_policy:: See Gem::Installer::new and Gem::Security. + # :user_install:: See Gem::Installer.new # :wrappers:: See Gem::Installer::new def initialize(options = {}) + if options[:install_dir] then + spec_dir = options[:install_dir], 'specifications' + @source_index = Gem::SourceIndex.from_gems_in spec_dir + else + @source_index = Gem.source_index + end + options = DEFAULT_OPTIONS.merge options @bin_dir = options[:bin_dir] @@ -51,19 +59,13 @@ class Gem::DependencyInstaller @format_executable = options[:format_executable] @ignore_dependencies = options[:ignore_dependencies] @security_policy = options[:security_policy] + @user_install = options[:user_install] @wrappers = options[:wrappers] @installed_gems = [] @install_dir = options[:install_dir] || Gem.dir @cache_dir = options[:cache_dir] || @install_dir - - if options[:install_dir] then - spec_dir = File.join @install_dir, 'specifications' - @source_index = Gem::SourceIndex.from_gems_in spec_dir - else - @source_index = Gem.source_index - end end ## @@ -232,15 +234,17 @@ class Gem::DependencyInstaller end inst = Gem::Installer.new local_gem_path, - :env_shebang => @env_shebang, - :force => @force, - :format_executable => @format_executable, + :bin_dir => @bin_dir, + :development => @development, + :env_shebang => @env_shebang, + :force => @force, + :format_executable => @format_executable, :ignore_dependencies => @ignore_dependencies, - :install_dir => @install_dir, - :security_policy => @security_policy, - :wrappers => @wrappers, - :bin_dir => @bin_dir, - :development => @development + :install_dir => @install_dir, + :security_policy => @security_policy, + :source_index => @source_index, + :user_install => @user_install, + :wrappers => @wrappers spec = inst.install diff --git a/lib/rubygems/doc_manager.rb b/lib/rubygems/doc_manager.rb index 88d7964d85..00ef4c51e3 100644 --- a/lib/rubygems/doc_manager.rb +++ b/lib/rubygems/doc_manager.rb @@ -5,132 +5,194 @@ #++ require 'fileutils' +require 'rubygems' -module Gem +## +# The documentation manager generates RDoc and RI for RubyGems. - class DocManager +class Gem::DocManager - include UserInteraction + include Gem::UserInteraction - # Create a document manager for the given gem spec. - # - # spec:: The Gem::Specification object representing the gem. - # rdoc_args:: Optional arguments for RDoc (template etc.) as a String. - # - def initialize(spec, rdoc_args="") - @spec = spec - @doc_dir = File.join(spec.installation_path, "doc", spec.full_name) - @rdoc_args = rdoc_args.nil? ? [] : rdoc_args.split + @configured_args = [] + + def self.configured_args + @configured_args ||= [] + end + + def self.configured_args=(args) + case args + when Array + @configured_args = args + when String + @configured_args = args.split + end + end + + ## + # Load RDoc from a gem if it is available, otherwise from Ruby's stdlib + + def self.load_rdoc + begin + gem 'rdoc' + rescue Gem::LoadError + # use built-in RDoc end - # Is the RDoc documentation installed? - def rdoc_installed? - return File.exist?(File.join(@doc_dir, "rdoc")) - end - - # Generate the RI documents for this gem spec. - # - # Note that if both RI and RDoc documents are generated from the - # same process, the RI docs should be done first (a likely bug in - # RDoc will cause RI docs generation to fail if run after RDoc). - def generate_ri - if @spec.has_rdoc then - load_rdoc - install_ri # RDoc bug, ri goes first - end - - FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir) - end - - # Generate the RDoc documents for this gem spec. - # - # Note that if both RI and RDoc documents are generated from the - # same process, the RI docs should be done first (a likely bug in - # RDoc will cause RI docs generation to fail if run after RDoc). - def generate_rdoc - if @spec.has_rdoc then - load_rdoc - install_rdoc - end - - FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir) - end - - # Load the RDoc documentation generator library. - def load_rdoc - if File.exist?(@doc_dir) && !File.writable?(@doc_dir) then - raise Gem::FilePermissionError.new(@doc_dir) - end - - FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir) - - begin - gem 'rdoc' - rescue Gem::LoadError - # use built-in RDoc - end - - begin - require 'rdoc/rdoc' - rescue LoadError => e - raise Gem::DocumentError, + begin + require 'rdoc/rdoc' + rescue LoadError => e + raise Gem::DocumentError, "ERROR: RDoc documentation generator not installed!" - end + end + end + + ## + # Updates the RI cache for RDoc 2 if it is installed + + def self.update_ri_cache + load_rdoc rescue return + + return unless defined? RDoc::VERSION # RDoc 1 does not have VERSION + + require 'rdoc/ri/driver' + + options = { + :use_cache => true, + :use_system => true, + :use_site => true, + :use_home => true, + :use_gems => true, + :formatter => RDoc::RI::Formatter, + } + + driver = RDoc::RI::Driver.new(options).class_cache + end + + ## + # Create a document manager for +spec+. +rdoc_args+ contains arguments for + # RDoc (template etc.) as a String. + + def initialize(spec, rdoc_args="") + @spec = spec + @doc_dir = File.join(spec.installation_path, "doc", spec.full_name) + @rdoc_args = rdoc_args.nil? ? [] : rdoc_args.split + end + + ## + # Is the RDoc documentation installed? + + def rdoc_installed? + File.exist?(File.join(@doc_dir, "rdoc")) + end + + ## + # Generate the RI documents for this gem spec. + # + # Note that if both RI and RDoc documents are generated from the same + # process, the RI docs should be done first (a likely bug in RDoc will cause + # RI docs generation to fail if run after RDoc). + + def generate_ri + if @spec.has_rdoc then + setup_rdoc + install_ri # RDoc bug, ri goes first end - def install_rdoc - rdoc_dir = File.join @doc_dir, 'rdoc' + FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir) + end - FileUtils.rm_rf rdoc_dir + ## + # Generate the RDoc documents for this gem spec. + # + # Note that if both RI and RDoc documents are generated from the same + # process, the RI docs should be done first (a likely bug in RDoc will cause + # RI docs generation to fail if run after RDoc). - say "Installing RDoc documentation for #{@spec.full_name}..." - run_rdoc '--op', rdoc_dir + def generate_rdoc + if @spec.has_rdoc then + setup_rdoc + install_rdoc end - def install_ri - ri_dir = File.join @doc_dir, 'ri' + FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir) + end - FileUtils.rm_rf ri_dir + ## + # Generate and install RDoc into the documentation directory - say "Installing ri documentation for #{@spec.full_name}..." - run_rdoc '--ri', '--op', ri_dir + def install_rdoc + rdoc_dir = File.join @doc_dir, 'rdoc' + + FileUtils.rm_rf rdoc_dir + + say "Installing RDoc documentation for #{@spec.full_name}..." + run_rdoc '--op', rdoc_dir + end + + ## + # Generate and install RI into the documentation directory + + def install_ri + ri_dir = File.join @doc_dir, 'ri' + + FileUtils.rm_rf ri_dir + + say "Installing ri documentation for #{@spec.full_name}..." + run_rdoc '--ri', '--op', ri_dir + end + + ## + # Run RDoc with +args+, which is an ARGV style argument list + + def run_rdoc(*args) + args << @spec.rdoc_options + args << self.class.configured_args + args << '--quiet' + args << @spec.require_paths.clone + args << @spec.extra_rdoc_files + args = args.flatten.map do |arg| arg.to_s end + + r = RDoc::RDoc.new + + old_pwd = Dir.pwd + Dir.chdir(@spec.full_gem_path) + begin + r.document args + rescue Errno::EACCES => e + dirname = File.dirname e.message.split("-")[1].strip + raise Gem::FilePermissionError.new(dirname) + rescue RuntimeError => ex + alert_error "While generating documentation for #{@spec.full_name}" + ui.errs.puts "... MESSAGE: #{ex}" + ui.errs.puts "... RDOC args: #{args.join(' ')}" + ui.errs.puts "\t#{ex.backtrace.join "\n\t"}" if + Gem.configuration.backtrace + ui.errs.puts "(continuing with the rest of the installation)" + ensure + Dir.chdir(old_pwd) + end + end + + def setup_rdoc + if File.exist?(@doc_dir) && !File.writable?(@doc_dir) then + raise Gem::FilePermissionError.new(@doc_dir) end - def run_rdoc(*args) - args << @spec.rdoc_options - args << DocManager.configured_args - args << '--quiet' - args << @spec.require_paths.clone - args << @spec.extra_rdoc_files - args = args.flatten.map do |arg| arg.to_s end + FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir) - r = RDoc::RDoc.new + self.class.load_rdoc + end - old_pwd = Dir.pwd - Dir.chdir(@spec.full_gem_path) - begin - r.document args - rescue Errno::EACCES => e - dirname = File.dirname e.message.split("-")[1].strip - raise Gem::FilePermissionError.new(dirname) - rescue RuntimeError => ex - alert_error "While generating documentation for #{@spec.full_name}" - ui.errs.puts "... MESSAGE: #{ex}" - ui.errs.puts "... RDOC args: #{args.join(' ')}" - ui.errs.puts "\t#{ex.backtrace.join "\n\t"}" if - Gem.configuration.backtrace - ui.errs.puts "(continuing with the rest of the installation)" - ensure - Dir.chdir(old_pwd) - end - end + ## + # Remove RDoc and RI documentation - def uninstall_doc - raise Gem::FilePermissionError.new(@spec.installation_path) unless - File.writable? @spec.installation_path + def uninstall_doc + raise Gem::FilePermissionError.new(@spec.installation_path) unless + File.writable? @spec.installation_path - original_name = [ - @spec.name, @spec.version, @spec.original_platform].join '-' + original_name = [ + @spec.name, @spec.version, @spec.original_platform].join '-' doc_dir = File.join @spec.installation_path, 'doc', @spec.full_name unless File.directory? doc_dir then @@ -146,22 +208,7 @@ module Gem end FileUtils.rm_rf ri_dir - end - - class << self - def configured_args - @configured_args ||= [] - end - - def configured_args=(args) - case args - when Array - @configured_args = args - when String - @configured_args = args.split - end - end - end - end + end + diff --git a/lib/rubygems/gem_path_searcher.rb b/lib/rubygems/gem_path_searcher.rb index dadad66289..e2b8543bb0 100644 --- a/lib/rubygems/gem_path_searcher.rb +++ b/lib/rubygems/gem_path_searcher.rb @@ -6,15 +6,15 @@ require 'rubygems' -# +## # GemPathSearcher has the capability to find loadable files inside # gems. It generates data up front to speed up searches later. -# + class Gem::GemPathSearcher - # + ## # Initialise the data we need to make searches later. - # + def initialize # We want a record of all the installed gemspecs, in the order # we wish to examine them. @@ -27,7 +27,7 @@ class Gem::GemPathSearcher end end - # + ## # Look in all the installed gems until a matching _path_ is found. # Return the _gemspec_ of the gem where it was found. If no match # is found, return nil. @@ -46,36 +46,52 @@ class Gem::GemPathSearcher # others), which may or may not already be attached to _file_. # This method doesn't care about the full filename that matches; # only that there is a match. - # + def find(path) - @gemspecs.each do |spec| - return spec if matching_file(spec, path) + @gemspecs.find do |spec| matching_file? spec, path end + end + + ## + # Works like #find, but finds all gemspecs matching +path+. + + def find_all(path) + @gemspecs.select do |spec| + matching_file? spec, path end - nil end - private + ## + # Attempts to find a matching path using the require_paths of the given + # +spec+. - # Attempts to find a matching path using the require_paths of the - # given _spec_. - # - # Some of the intermediate results are cached in @lib_dirs for - # speed. - def matching_file(spec, path) # :doc: + def matching_file?(spec, path) + !matching_files(spec, path).empty? + end + + ## + # Returns files matching +path+ in +spec+. + #-- + # Some of the intermediate results are cached in @lib_dirs for speed. + + def matching_files(spec, path) glob = File.join @lib_dirs[spec.object_id], "#{path}#{Gem.suffix_pattern}" - return true unless Dir[glob].select { |f| File.file?(f.untaint) }.empty? + Dir[glob].select { |f| File.file? f.untaint } end - # Return a list of all installed gemspecs, sorted by alphabetical - # order and in reverse version order. + ## + # Return a list of all installed gemspecs, sorted by alphabetical order and + # in reverse version order. + def init_gemspecs Gem.source_index.map { |_, spec| spec }.sort { |a,b| (a.name <=> b.name).nonzero? || (b.version <=> a.version) } end + ## # Returns library directories glob for a gemspec. For example, # '/usr/local/lib/ruby/gems/1.8/gems/foobar-1.0/{lib,ext}' + def lib_dirs_for(spec) "#{spec.full_gem_path}/{#{spec.require_paths.join(',')}}" end diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index 5e2e8e0f42..d9006f9fc7 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -20,6 +20,7 @@ require 'rubygems/require_paths_builder' # filesystem including unpacking the gem into its gem dir, installing the # gemspec in the specifications dir, storing the cached gem in the cache dir, # and installing either wrappers or symlinks for executables. + class Gem::Installer ## @@ -31,8 +32,36 @@ class Gem::Installer include Gem::RequirePathsBuilder + ## + # The directory a gem's executables will be installed into + + attr_reader :bin_dir + + ## + # The gem repository the gem will be installed into + + attr_reader :gem_home + + ## + # The Gem::Specification for the gem being installed + + attr_reader :spec + + @home_install_warning = false + @path_warning = false + class << self + ## + # True if we've warned about ~/.gems install + + attr_accessor :home_install_warning + + ## + # True if we've warned about PATH not including Gem.bindir + + attr_accessor :path_warning + attr_writer :exec_format # Defaults to use Ruby's program prefix and suffix. @@ -61,11 +90,12 @@ class Gem::Installer @gem = gem options = { - :force => false, - :install_dir => Gem.dir, - :exec_format => false, - :env_shebang => false, - :bin_dir => nil + :bin_dir => nil, + :env_shebang => false, + :exec_format => false, + :force => false, + :install_dir => Gem.dir, + :source_index => Gem.source_index, }.merge options @env_shebang = options[:env_shebang] @@ -78,6 +108,7 @@ class Gem::Installer @wrappers = options[:wrappers] @bin_dir = options[:bin_dir] @development = options[:development] + @source_index = options[:source_index] begin @format = Gem::Format.from_file_by_path @gem, @security_policy @@ -85,30 +116,41 @@ class Gem::Installer raise Gem::InstallError, "invalid gem format for #{@gem}" end + begin + FileUtils.mkdir_p @gem_home + rescue Errno::EACCESS, Errno::ENOTDIR + # We'll divert to ~/.gems below + end + if not File.writable? @gem_home or # TODO: Shouldn't have to test for existence of bindir; tests need it. - (@gem_home.to_s == Gem.dir and File.exist? Gem.bindir and - not File.writable? Gem.bindir) - if options[:user_install] == false # You explicitly don't want to use ~ + (@gem_home.to_s == Gem.dir and File.exist? Gem.bindir and + not File.writable? Gem.bindir) then + if options[:user_install] == false then # You don't want to use ~ raise Gem::FilePermissionError, @gem_home - elsif options[:user_install].nil? - say "Warning: falling back to user-level install since #{@gem_home} and #{@bin_dir} aren't both writable." + elsif options[:user_install].nil? then + unless self.class.home_install_warning then + alert_warning "Installing to ~/.gem since #{@gem_home} and\n\t #{Gem.bindir} aren't both writable." + self.class.home_install_warning = true + end end options[:user_install] = true end - if options[:user_install] - @gem_home = File.join(ENV['HOME'], '.gem') + if options[:user_install] then + @gem_home = Gem.user_dir - user_bin_dir = File.join(@gem_home, 'gems', 'bin') - if !ENV['PATH'].split(':').include?(user_bin_dir) - say "You don't have #{user_bin_dir} in your PATH." - say "You won't be able to run gem-installed executables until you add it." + user_bin_dir = File.join(@gem_home, 'bin') + unless ENV['PATH'].split(File::PATH_SEPARATOR).include? user_bin_dir then + unless self.class.path_warning then + alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run." + self.class.path_warning = true + end end - - Dir.mkdir @gem_home if ! File.directory? @gem_home + + FileUtils.mkdir_p @gem_home unless File.directory? @gem_home # If it's still not writable, you've got issues. - raise Gem::FilePermissionError, @gem_home if ! File.writable? @gem_home + raise Gem::FilePermissionError, @gem_home unless File.writable? @gem_home end @spec = @format.spec @@ -157,6 +199,10 @@ class Gem::Installer end end + Gem.pre_install_hooks.each do |hook| + hook.call self + end + FileUtils.mkdir_p @gem_home unless File.directory? @gem_home Gem.ensure_gem_subdirectories @gem_home @@ -181,7 +227,11 @@ class Gem::Installer @spec.loaded_from = File.join(@gem_home, 'specifications', "#{@spec.full_name}.gemspec") - Gem.source_index.add_spec @spec + @source_index.add_spec @spec + + Gem.post_install_hooks.each do |hook| + hook.call self + end return @spec rescue Zlib::GzipFile::Error @@ -204,10 +254,10 @@ class Gem::Installer end ## - # True if the gems in Gem.source_index satisfy +dependency+. + # True if the gems in the source_index satisfy +dependency+. def installation_satisfies_dependency?(dependency) - Gem.source_index.find_name(dependency.name, dependency.version_requirements).size > 0 + @source_index.find_name(dependency.name, dependency.version_requirements).size > 0 end ## diff --git a/lib/rubygems/local_remote_options.rb b/lib/rubygems/local_remote_options.rb index 799b9d5893..d77d600ec6 100644 --- a/lib/rubygems/local_remote_options.rb +++ b/lib/rubygems/local_remote_options.rb @@ -109,6 +109,13 @@ module Gem::LocalRemoteOptions end end + ## + # Is fetching of local and remote information enabled? + + def both? + options[:domain] == :both + end + ## # Is local fetching enabled? diff --git a/lib/rubygems/package/tar_reader.rb b/lib/rubygems/package/tar_reader.rb index 8359399207..4aa9c26cc9 100644 --- a/lib/rubygems/package/tar_reader.rb +++ b/lib/rubygems/package/tar_reader.rb @@ -46,17 +46,17 @@ class Gem::Package::TarReader yield entry skip = (512 - (size % 512)) % 512 + pending = size - entry.bytes_read - if @io.respond_to? :seek then + begin # avoid reading... - @io.seek(size - entry.bytes_read, IO::SEEK_CUR) - else - pending = size - entry.bytes_read - + @io.seek pending, IO::SEEK_CUR + pending = 0 + rescue Errno::EINVAL, NameError while pending > 0 do - bread = @io.read([pending, 4096].min).size + bytes_read = @io.read([pending, 4096].min).size raise UnexpectedEOF if @io.eof? - pending -= bread + pending -= bytes_read end end diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb index 5e932cd592..3e5b5cde66 100644 --- a/lib/rubygems/platform.rb +++ b/lib/rubygems/platform.rb @@ -13,23 +13,6 @@ class Gem::Platform attr_accessor :version - DEPRECATED_CONSTS = [ - :DARWIN, - :LINUX_586, - :MSWIN32, - :PPC_DARWIN, - :WIN32, - :X86_LINUX - ] - - def self.const_missing(name) # TODO remove six months from 2007/12 - if DEPRECATED_CONSTS.include? name then - raise NameError, "#{name} has been removed, use CURRENT instead" - else - super - end - end - def self.local arch = Gem::ConfigMap[:arch] arch = "#{arch}_60" if arch =~ /mswin32$/ @@ -73,7 +56,7 @@ class Gem::Platform else cpu end - if arch.length == 2 and arch.last =~ /^\d+$/ then # for command-line + if arch.length == 2 and arch.last =~ /^\d+(\.\d+)?$/ then # for command-line @os, @version = arch return end diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb index 3c747f1d65..1570740163 100644 --- a/lib/rubygems/remote_fetcher.rb +++ b/lib/rubygems/remote_fetcher.rb @@ -78,7 +78,7 @@ class Gem::RemoteFetcher if File.writable?(install_dir) cache_dir = File.join install_dir, 'cache' else - cache_dir = File.join(ENV['HOME'], '.gem', 'cache') + cache_dir = File.join(Gem.user_dir, 'cache') end gem_file_name = "#{spec.full_name}.gem" @@ -93,7 +93,7 @@ class Gem::RemoteFetcher scheme = nil if scheme =~ /^[a-z]$/i case scheme - when 'http' then + when 'http', 'https' then unless File.exist? local_gem_path then begin say "Downloading gem #{gem_file_name}" if @@ -139,8 +139,8 @@ class Gem::RemoteFetcher # Downloads +uri+ and returns it as a String. def fetch_path(uri, mtime = nil, head = false) - data = open_uri_or_path(uri, mtime, head) - data = Gem.gunzip data if uri.to_s =~ /gz$/ and not head + data = open_uri_or_path uri, mtime, head + data = Gem.gunzip data if data and not head and uri.to_s =~ /gz$/ data rescue FetchError raise @@ -216,8 +216,9 @@ class Gem::RemoteFetcher connection = @connections[connection_id] if uri.scheme == 'https' and not connection.started? then - http_obj.use_ssl = true - http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE + require 'net/https' + connection.use_ssl = true + connection.verify_mode = OpenSSL::SSL::VERIFY_NONE end connection.start unless connection.started? @@ -241,9 +242,10 @@ class Gem::RemoteFetcher response = request uri, fetch_type, last_modified case response - when Net::HTTPOK then + when Net::HTTPOK, Net::HTTPNotModified then head ? response : response.body - when Net::HTTPRedirection then + when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther, + Net::HTTPTemporaryRedirect then raise FetchError.new('too many redirects', uri) if depth > 10 open_uri_or_path(response['Location'], last_modified, head, depth + 1) @@ -274,6 +276,7 @@ class Gem::RemoteFetcher request.add_field 'Keep-Alive', '30' if last_modified then + last_modified = last_modified.utc request.add_field 'If-Modified-Since', last_modified.rfc2822 end @@ -282,9 +285,6 @@ class Gem::RemoteFetcher retried = false bad_response = false - # HACK work around EOFError bug in Net::HTTP - # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible - # to install gems. begin @requests[connection.object_id] += 1 response = connection.request request @@ -297,6 +297,9 @@ class Gem::RemoteFetcher bad_response = true retry + # HACK work around EOFError bug in Net::HTTP + # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible + # to install gems. rescue EOFError, Errno::ECONNABORTED, Errno::ECONNRESET requests = @requests[connection.object_id] say "connection reset after #{requests} requests, retrying" if diff --git a/lib/rubygems/rubygems_version.rb b/lib/rubygems/rubygems_version.rb index d393e57a09..06e2104715 100644 --- a/lib/rubygems/rubygems_version.rb +++ b/lib/rubygems/rubygems_version.rb @@ -2,5 +2,5 @@ # This file is auto-generated by build scripts. # See: rake update_version module Gem - RubyGemsVersion = '1.2.0.1824' + RubyGemsVersion = '1.3.0' end diff --git a/lib/rubygems/source_index.rb b/lib/rubygems/source_index.rb index 1eefd8c149..c6499ed2a8 100644 --- a/lib/rubygems/source_index.rb +++ b/lib/rubygems/source_index.rb @@ -7,7 +7,9 @@ require 'rubygems' require 'rubygems/user_interaction' require 'rubygems/specification' -require 'rubygems/spec_fetcher' +module Gem + autoload(:SpecFetcher, 'rubygems/spec_fetcher') +end ## # The SourceIndex object indexes all the gems available from a @@ -80,8 +82,14 @@ class Gem::SourceIndex def load_specification(file_name) begin - spec_code = File.read(file_name).untaint + spec_code = if RUBY_VERSION < '1.9' then + File.read file_name + else + File.read file_name, :encoding => 'UTF-8' + end.untaint + gemspec = eval spec_code, binding, file_name + if gemspec.is_a?(Gem::Specification) gemspec.loaded_from = file_name return gemspec @@ -93,7 +101,7 @@ class Gem::SourceIndex alert_warning e alert_warning spec_code rescue Exception => e - alert_warning(e.inspect.to_s + "\n" + spec_code) + alert_warning "#{e.inspect}\n#{spec_code}" alert_warning "Invalid .gemspec format in '#{file_name}'" end return nil @@ -230,7 +238,8 @@ class Gem::SourceIndex # 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) + dep = Gem::Dependency.new(/^#{gem_name}$/, version_requirement) + search dep end ## @@ -246,7 +255,13 @@ class Gem::SourceIndex version_requirement = nil only_platform = false - case gem_pattern # TODO warn after 2008/03, remove three months after + # TODO - Remove support and warning for legacy arguments after 2008/11 + unless Gem::Dependency === gem_pattern + warn "Gem::SourceIndex#search support for #{gem_pattern.class} patterns is deprecated" + warn "#{caller[0]} is outdated" + end + + case gem_pattern when Regexp then version_requirement = platform_only || Gem::Requirement.default when Gem::Dependency then @@ -270,7 +285,7 @@ class Gem::SourceIndex specs = @gems.values.select do |spec| spec.name =~ gem_pattern and - version_requirement.satisfied_by? spec.version + version_requirement.satisfied_by? spec.version end if only_platform then @@ -539,7 +554,7 @@ module Gem # objects to load properly. Cache = SourceIndex - # :starddoc: + # :startdoc: end diff --git a/lib/rubygems/source_info_cache.rb b/lib/rubygems/source_info_cache.rb index ec6928c00d..fdb30ad8d3 100644 --- a/lib/rubygems/source_info_cache.rb +++ b/lib/rubygems/source_info_cache.rb @@ -284,6 +284,10 @@ class Gem::SourceInfoCache cache_data.map do |source_uri, sic_entry| next unless Gem.sources.include? source_uri + # TODO - Remove this gunk after 2008/11 + unless pattern.kind_of?(Gem::Dependency) + pattern = Gem::Dependency.new(pattern, Gem::Requirement.default) + end sic_entry.source_index.search pattern, platform_only end.flatten.compact end @@ -300,6 +304,11 @@ class Gem::SourceInfoCache cache_data.map do |source_uri, sic_entry| next unless Gem.sources.include? source_uri + # TODO - Remove this gunk after 2008/11 + unless pattern.kind_of?(Gem::Dependency) + pattern = Gem::Dependency.new(pattern, Gem::Requirement.default) + end + sic_entry.source_index.search(pattern, only_platform).each do |spec| results << [spec, source_uri] end diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb index 66b4ee36c7..a1fc82ed4f 100644 --- a/lib/rubygems/spec_fetcher.rb +++ b/lib/rubygems/spec_fetcher.rb @@ -167,7 +167,7 @@ class Gem::SpecFetcher if all and @specs.include? source_uri then list[source_uri] = @specs[source_uri] - elsif @latest_specs.include? source_uri then + elsif not all and @latest_specs.include? source_uri then list[source_uri] = @latest_specs[source_uri] else specs = load_specs source_uri, file @@ -182,6 +182,10 @@ class Gem::SpecFetcher list end + ## + # Loads specs in +file+, fetching from +source_uri+ if the on-disk cache is + # out of date. + def load_specs(source_uri, file) file_name = "#{file}.#{Gem.marshal_version}" spec_path = source_uri + "#{file_name}.gz" @@ -192,7 +196,7 @@ class Gem::SpecFetcher if File.exist? local_file then spec_dump = @fetcher.fetch_path spec_path, File.mtime(local_file) - if spec_dump.empty? then + if spec_dump.nil? then spec_dump = Gem.read_binary local_file else loaded = true diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index f3f38e5033..2e6cdc1b04 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -24,6 +24,7 @@ class Date; end # for ruby_code if date.rb wasn't required module Gem + ## # == Gem::Specification # # The Specification class contains the metadata for a Gem. Typically @@ -38,7 +39,7 @@ module Gem # # There are many gemspec attributes, and the best place to learn # about them in the "Gemspec Reference" linked from the RubyGems wiki. - # + class Specification ## @@ -46,8 +47,6 @@ module Gem attr_accessor :original_platform # :nodoc: - # ------------------------- Specification version constants. - ## # The the version number of a specification that does not specify one # (i.e. RubyGems 0.7 or earlier). @@ -88,77 +87,98 @@ module Gem TODAY = now - ((now.to_i + now.gmt_offset) % 86400) # :startdoc: - # ------------------------- Class variables. - + ## # List of Specification instances. + @@list = [] + ## # Optional block used to gather newly defined instances. + @@gather = nil + ## # List of attribute names: [:name, :version, ...] @@required_attributes = [] - # List of _all_ attributes and default values: [[:name, nil], [:bindir, 'bin'], ...] + ## + # List of _all_ attributes and default values: + # + # [[:name, nil], + # [:bindir, 'bin'], + # ...] + @@attributes = [] @@nil_attributes = [] @@non_nil_attributes = [:@original_platform] + ## # List of array attributes + @@array_attributes = [] + ## # Map of attribute names to default values. + @@default_value = {} - # ------------------------- Convenience class methods. + ## + # Names of all specification attributes def self.attribute_names @@attributes.map { |name, default| name } end + ## + # Default values for specification attributes + def self.attribute_defaults @@attributes.dup end + ## + # The default value for specification attribute +name+ + def self.default_value(name) @@default_value[name] end + ## + # Required specification attributes + def self.required_attributes @@required_attributes.dup end + ## + # Is +name+ a required attribute? + def self.required_attribute?(name) @@required_attributes.include? name.to_sym end + ## + # Specification attributes that are arrays (appendable and so-forth) + def self.array_attributes @@array_attributes.dup end - # ------------------------- Infrastructure class methods. + ## + # A list of Specification instances that have been defined in this Ruby + # instance. - # A list of Specification instances that have been defined in this Ruby instance. def self.list @@list end - # Used to specify the name and default value of a specification - # attribute. The side effects are: - # * the name and default value are added to the @@attributes list - # and @@default_value map - # * a standard _writer_ method (attribute=) is created - # * a non-standard _reader method (attribute) is created - # - # The reader method behaves like this: - # def attribute - # @attribute ||= (copy of default value) - # end - # - # This allows lazy initialization of attributes to their default - # values. + ## + # Specifies the +name+ and +default+ for a specification attribute, and + # creates a reader and writer method like Module#attr_accessor. # + # The reader method returns the default if the value hasn't been set. + def self.attribute(name, default=nil) ivar_name = "@#{name}".intern if default.nil? then @@ -172,8 +192,10 @@ module Gem attr_accessor(name) end - # Same as :attribute, but ensures that values assigned to the - # attribute are array values by applying :to_a to the value. + ## + # Same as :attribute, but ensures that values assigned to the attribute + # are array values by applying :to_a to the value. + def self.array_attribute(name) @@non_nil_attributes << ["@#{name}".intern, []] @@ -192,51 +214,60 @@ module Gem module_eval code, __FILE__, __LINE__ - 9 end + ## # Same as attribute above, but also records this attribute as mandatory. + def self.required_attribute(*args) @@required_attributes << args.first attribute(*args) end - # Sometimes we don't want the world to use a setter method for a particular attribute. + ## + # Sometimes we don't want the world to use a setter method for a + # particular attribute. + # # +read_only+ makes it private so we can still use it internally. + def self.read_only(*names) names.each do |name| private "#{name}=" end end - # Shortcut for creating several attributes at once (each with a default value of - # +nil+). + # Shortcut for creating several attributes at once (each with a default + # value of +nil+). + def self.attributes(*args) args.each do |arg| attribute(arg, nil) end end - # Some attributes require special behaviour when they are accessed. This allows for - # that. + ## + # Some attributes require special behaviour when they are accessed. This + # allows for that. + def self.overwrite_accessor(name, &block) remove_method name define_method(name, &block) end - # Defines a _singular_ version of an existing _plural_ attribute - # (i.e. one whose value is expected to be an array). This means - # just creating a helper method that takes a single value and - # appends it to the array. These are created for convenience, so - # that in a spec, one can write + ## + # Defines a _singular_ version of an existing _plural_ attribute (i.e. one + # whose value is expected to be an array). This means just creating a + # helper method that takes a single value and appends it to the array. + # These are created for convenience, so that in a spec, one can write # # s.require_path = 'mylib' # - # instead of + # instead of: # # s.require_paths = ['mylib'] # - # That above convenience is available courtesy of + # That above convenience is available courtesy of: # # attribute_alias_singular :require_path, :require_paths - # + def self.attribute_alias_singular(singular, plural) define_method("#{singular}=") { |val| send("#{plural}=", [val]) @@ -320,85 +351,781 @@ module Gem spec end - # REQUIRED gemspec attributes ------------------------------------ - - required_attribute :rubygems_version, Gem::RubyGemsVersion - required_attribute :specification_version, CURRENT_SPECIFICATION_VERSION - required_attribute :name - required_attribute :version - required_attribute :date, TODAY - required_attribute :summary - required_attribute :require_paths, ['lib'] - - # OPTIONAL gemspec attributes ------------------------------------ - - attributes :email, :homepage, :rubyforge_project, :description - attributes :autorequire, :default_executable - - attribute :bindir, 'bin' - attribute :has_rdoc, false - attribute :required_ruby_version, Gem::Requirement.default - attribute :required_rubygems_version, Gem::Requirement.default - attribute :platform, Gem::Platform::RUBY - - attribute :signing_key, nil - attribute :cert_chain, [] - attribute :post_install_message, nil - - array_attribute :authors - array_attribute :files - array_attribute :test_files - array_attribute :rdoc_options - array_attribute :extra_rdoc_files - array_attribute :executables - - # Array of extensions to build. See Gem::Installer#build_extensions for - # valid values. - - array_attribute :extensions - array_attribute :requirements - array_attribute :dependencies - - read_only :dependencies + ## + # List of depedencies that will automatically be activated at runtime. def runtime_dependencies dependencies.select { |d| d.type == :runtime || d.type == nil } end + ## + # List of dependencies that are used for development + def development_dependencies dependencies.select { |d| d.type == :development } end - # ALIASED gemspec attributes ------------------------------------- - - attribute_alias_singular :executable, :executables - attribute_alias_singular :author, :authors - attribute_alias_singular :require_path, :require_paths - attribute_alias_singular :test_file, :test_files - - # DEPRECATED gemspec attributes ---------------------------------- - - def test_suite_file + def test_suite_file # :nodoc: warn 'test_suite_file deprecated, use test_files' test_files.first end - def test_suite_file=(val) + def test_suite_file=(val) # :nodoc: warn 'test_suite_file= deprecated, use test_files=' @test_files = [] unless defined? @test_files @test_files << val end + ## # true when this gemspec has been loaded from a specifications directory. # This attribute is not persisted. - attr_writer :loaded + attr_accessor :loaded + ## # Path this gemspec was loaded from. This attribute is not persisted. + attr_accessor :loaded_from - # Special accessor behaviours (overwriting default) -------------- + ## + # Returns an array with bindir attached to each executable in the + # executables list + + def add_bindir(executables) + return nil if executables.nil? + + if @bindir then + Array(executables).map { |e| File.join(@bindir, e) } + else + executables + end + rescue + return nil + end + + ## + # Files in the Gem under one of the require_paths + + def lib_files + @files.select do |file| + require_paths.any? do |path| + file.index(path) == 0 + end + end + end + + ## + # True if this gem was loaded from disk + + alias :loaded? :loaded + + ## + # True if this gem has files in test_files + + def has_unit_tests? + not test_files.empty? + end + + alias has_test_suite? has_unit_tests? # :nodoc: deprecated + ## + # Specification constructor. Assigns the default values to the + # attributes, adds this spec to the list of loaded specs (see + # Specification.list), and yields itself for further initialization. + + def initialize + @new_platform = nil + assign_defaults + @loaded = false + @loaded_from = nil + @@list << self + + yield self if block_given? + + @@gather.call(self) if @@gather + end + + ## + # Each attribute has a default value (possibly nil). Here, we initialize + # all attributes to their default value. This is done through the + # accessor methods, so special behaviours will be honored. Furthermore, + # we take a _copy_ of the default so each specification instance has its + # own empty arrays, etc. + + def assign_defaults + @@nil_attributes.each do |name| + instance_variable_set name, nil + end + + @@non_nil_attributes.each do |name, default| + value = case default + when Time, Numeric, Symbol, true, false, nil then default + else default.dup + end + + instance_variable_set name, value + end + + # HACK + instance_variable_set :@new_platform, Gem::Platform::RUBY + end + + ## + # Special loader for YAML files. When a Specification object is loaded + # from a YAML file, it bypasses the normal Ruby object initialization + # routine (#initialize). This method makes up for that and deals with + # gems of different ages. + # + # 'input' can be anything that YAML.load() accepts: String or IO. + + def self.from_yaml(input) + input = normalize_yaml_input input + spec = YAML.load input + + if spec && spec.class == FalseClass then + raise Gem::EndOfYAMLException + end + + unless Gem::Specification === spec then + raise Gem::Exception, "YAML data doesn't evaluate to gem specification" + end + + unless (spec.instance_variables.include? '@specification_version' or + spec.instance_variables.include? :@specification_version) and + spec.instance_variable_get :@specification_version + spec.instance_variable_set :@specification_version, + NONEXISTENT_SPECIFICATION_VERSION + end + + spec + end + + ## + # Loads ruby format gemspec from +filename+ + + def self.load(filename) + gemspec = nil + fail "NESTED Specification.load calls not allowed!" if @@gather + @@gather = proc { |gs| gemspec = gs } + data = File.read(filename) + eval(data) + gemspec + ensure + @@gather = nil + end + + ## + # Make sure the YAML specification is properly formatted with dashes + + def self.normalize_yaml_input(input) + result = input.respond_to?(:read) ? input.read : input + result = "--- " + result unless result =~ /^--- / + result + end + + ## + # Sets the rubygems_version to the current RubyGems version + + def mark_version + @rubygems_version = RubyGemsVersion + end + + ## + # Ignore unknown attributes while loading + + def method_missing(sym, *a, &b) # :nodoc: + if @specification_version > CURRENT_SPECIFICATION_VERSION and + sym.to_s =~ /=$/ then + warn "ignoring #{sym} loading #{full_name}" if $DEBUG + else + super + end + end + + ## + # Adds a development dependency named +gem+ with +requirements+ to this + # Gem. For example: + # + # spec.add_development_dependency 'jabber4r', '> 0.1', '<= 0.5' + # + # Development dependencies aren't installed by default and aren't + # activated when a gem is required. + + def add_development_dependency(gem, *requirements) + add_dependency_with_type(gem, :development, *requirements) + end + + ## + # Adds a runtime dependency named +gem+ with +requirements+ to this Gem. + # For example: + # + # spec.add_runtime_dependency 'jabber4r', '> 0.1', '<= 0.5' + + def add_runtime_dependency(gem, *requirements) + add_dependency_with_type(gem, :runtime, *requirements) + end + + ## + # Adds a runtime dependency + + alias add_dependency add_runtime_dependency + + ## + # Returns the full name (name-version) of this Gem. Platform information + # is included (name-version-platform) if it is specified and not the + # default Ruby platform. + + def full_name + if platform == Gem::Platform::RUBY or platform.nil? then + "#{@name}-#{@version}" + else + "#{@name}-#{@version}-#{platform}" + end + end + + ## + # Returns the full name (name-version) of this gemspec using the original + # platform. For use with legacy gems. + + def original_name # :nodoc: + if platform == Gem::Platform::RUBY or platform.nil? then + "#{@name}-#{@version}" + else + "#{@name}-#{@version}-#{@original_platform}" + end + end + + ## + # The full path to the gem (install path + full name). + + def full_gem_path + path = File.join installation_path, 'gems', full_name + return path if File.directory? path + File.join installation_path, 'gems', original_name + end + + ## + # The default (generated) file name of the gem. + + def file_name + full_name + ".gem" + end + + ## + # The directory that this gem was installed into. + + def installation_path + path = File.dirname(@loaded_from).split(File::SEPARATOR)[0..-2] + path = path.join File::SEPARATOR + File.expand_path path + end + + ## + # Checks if this specification meets the requirement of +dependency+. + + def satisfies_requirement?(dependency) + return @name == dependency.name && + dependency.version_requirements.satisfied_by?(@version) + end + + ## + # Returns an object you can use to sort specifications in #sort_by. + + def sort_obj + [@name, @version.to_ints, @new_platform == Gem::Platform::RUBY ? -1 : 1] + end + + def <=>(other) # :nodoc: + sort_obj <=> other.sort_obj + end + + ## + # Tests specs for equality (across all attributes). + + def ==(other) # :nodoc: + self.class === other && same_attributes?(other) + end + + alias eql? == # :nodoc: + + ## + # True if this gem has the same attributes as +other+. + + def same_attributes?(other) + @@attributes.each do |name, default| + return false unless self.send(name) == other.send(name) + end + true + end + + private :same_attributes? + + def hash # :nodoc: + @@attributes.inject(0) { |hash_code, (name, default_value)| + n = self.send(name).hash + hash_code + n + } + end + + def to_yaml(opts = {}) # :nodoc: + mark_version + + attributes = @@attributes.map { |name,| name.to_s }.sort + attributes = attributes - %w[name version platform] + + yaml = YAML.quick_emit object_id, opts do |out| + out.map taguri, to_yaml_style do |map| + map.add 'name', @name + map.add 'version', @version + platform = case @original_platform + when nil, '' then + 'ruby' + when String then + @original_platform + else + @original_platform.to_s + end + map.add 'platform', platform + + attributes.each do |name| + map.add name, instance_variable_get("@#{name}") + end + end + end + end + + def yaml_initialize(tag, vals) # :nodoc: + vals.each do |ivar, val| + instance_variable_set "@#{ivar}", val + end + + @original_platform = @platform # for backwards compatibility + self.platform = Gem::Platform.new @platform + end + + ## + # Returns a Ruby code representation of this specification, such that it + # can be eval'ed and reconstruct the same specification later. Attributes + # that still have their default values are omitted. + + def to_ruby + mark_version + result = [] + result << "# -*- encoding: utf-8 -*-" + result << nil + result << "Gem::Specification.new do |s|" + + result << " s.name = #{ruby_code name}" + result << " s.version = #{ruby_code version}" + unless platform.nil? or platform == Gem::Platform::RUBY then + result << " s.platform = #{ruby_code original_platform}" + end + result << "" + result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version=" + + handled = [ + :dependencies, + :name, + :platform, + :required_rubygems_version, + :specification_version, + :version, + ] + + attributes = @@attributes.sort_by { |attr_name,| attr_name.to_s } + + attributes.each do |attr_name, default| + next if handled.include? attr_name + current_value = self.send(attr_name) + if current_value != default or + self.class.required_attribute? attr_name then + result << " s.#{attr_name} = #{ruby_code current_value}" + end + end + + result << nil + result << " if s.respond_to? :specification_version then" + result << " current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION" + result << " s.specification_version = #{specification_version}" + result << nil + + result << " if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then" + + unless dependencies.empty? then + dependencies.each do |dep| + version_reqs_param = dep.requirements_list.inspect + dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK + result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>, #{version_reqs_param})" + end + end + + result << " else" + + unless dependencies.empty? then + dependencies.each do |dep| + version_reqs_param = dep.requirements_list.inspect + result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})" + end + end + + result << ' end' + + result << " else" + dependencies.each do |dep| + version_reqs_param = dep.requirements_list.inspect + result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})" + end + result << " end" + + result << "end" + result << nil + + result.join "\n" + end + + ## + # Checks that the specification contains all required fields, and does a + # very basic sanity check. + # + # Raises InvalidSpecificationException if the spec does not pass the + # checks.. + + def validate + extend Gem::UserInteraction + normalize + + if rubygems_version != RubyGemsVersion then + raise Gem::InvalidSpecificationException, + "expected RubyGems version #{RubyGemsVersion}, was #{rubygems_version}" + end + + @@required_attributes.each do |symbol| + unless self.send symbol then + raise Gem::InvalidSpecificationException, + "missing value for attribute #{symbol}" + end + end + + if require_paths.empty? then + raise Gem::InvalidSpecificationException, + "specification must have at least one require_path" + end + + case platform + when Gem::Platform, Platform::RUBY then # ok + else + raise Gem::InvalidSpecificationException, + "invalid platform #{platform.inspect}, see Gem::Platform" + end + + unless Array === authors and + authors.all? { |author| String === author } then + raise Gem::InvalidSpecificationException, + 'authors must be Array of Strings' + end + + # Warnings + + %w[author email homepage rubyforge_project summary].each do |attribute| + value = self.send attribute + alert_warning "no #{attribute} specified" if value.nil? or value.empty? + end + + alert_warning "RDoc will not be generated (has_rdoc == false)" unless + has_rdoc + + alert_warning "deprecated autorequire specified" if autorequire + + executables.each do |executable| + executable_path = File.join bindir, executable + shebang = File.read(executable_path, 2) == '#!' + + alert_warning "#{executable_path} is missing #! line" unless shebang + end + + true + end + + ## + # Normalize the list of files so that: + # * All file lists have redundancies removed. + # * Files referenced in the extra_rdoc_files are included in the package + # file list. + # + # Also, the summary and description are converted to a normal format. + + def normalize + if defined?(@extra_rdoc_files) and @extra_rdoc_files then + @extra_rdoc_files.uniq! + @files ||= [] + @files.concat(@extra_rdoc_files) + end + @files.uniq! if @files + end + + ## + # Return a list of all gems that have a dependency on this gemspec. The + # list is structured with entries that conform to: + # + # [depending_gem, dependency, [list_of_gems_that_satisfy_dependency]] + + def dependent_gems + out = [] + Gem.source_index.each do |name,gem| + gem.dependencies.each do |dep| + if self.satisfies_requirement?(dep) then + sats = [] + find_all_satisfiers(dep) do |sat| + sats << sat + end + out << [gem, dep, sats] + end + end + end + out + end + + def to_s + "#" + end + + def add_dependency_with_type(dependency, type, *requirements) + requirements = if requirements.empty? then + Gem::Requirement.default + else + requirements.flatten + end + + unless dependency.respond_to?(:name) && + dependency.respond_to?(:version_requirements) + + dependency = Dependency.new(dependency, requirements, type) + end + + dependencies << dependency + end + + private :add_dependency_with_type + + def find_all_satisfiers(dep) + Gem.source_index.each do |name,gem| + if(gem.satisfies_requirement?(dep)) then + yield gem + end + end + end + + private :find_all_satisfiers + + ## + # Return a string containing a Ruby code representation of the given + # object. + + def ruby_code(obj) + case obj + when String then '%q{' + obj + '}' + when Array then obj.inspect + when Gem::Version then obj.to_s.inspect + when Date then '%q{' + obj.strftime('%Y-%m-%d') + '}' + when Time then '%q{' + obj.strftime('%Y-%m-%d') + '}' + when Numeric then obj.inspect + when true, false, nil then obj.inspect + when Gem::Platform then "Gem::Platform.new(#{obj.to_a.inspect})" + when Gem::Requirement then "Gem::Requirement.new(#{obj.to_s.inspect})" + else raise Exception, "ruby_code case not handled: #{obj.class}" + end + end + + private :ruby_code + + # :section: Required gemspec attributes + + ## + # The version of RubyGems used to create this gem + + required_attribute :rubygems_version, Gem::RubyGemsVersion + + ## + # The Gem::Specification version of this gemspec + + required_attribute :specification_version, CURRENT_SPECIFICATION_VERSION + + ## + # This gem's name + + required_attribute :name + + ## + # This gem's version + + required_attribute :version + + ## + # The date this gem was created + + required_attribute :date, TODAY + + ## + # A short summary of this gem's description. Displayed in `gem list -d`. + + required_attribute :summary + + ## + # Paths in the gem to add to $LOAD_PATH when this gem is activated + + required_attribute :require_paths, ['lib'] + + # :section: Optional gemspec attributes + + ## + # A contact email for this gem + + attribute :email + + ## + # The URL of this gem's home page + + attribute :homepage + + ## + # The rubyforge project this gem lives under. i.e. RubyGems' + # rubyforge_project is "rubygems". + + attribute :rubyforge_project + + ## + # A long description of this gem + + attribute :description + + ## + # Autorequire was used by old RubyGems to automatically require a file. + # It no longer is supported. + + attribute :autorequire + + ## + # The default executable for this gem. + + attribute :default_executable + + ## + # The path in the gem for executable scripts + + attribute :bindir, 'bin' + + ## + # True if this gem is RDoc-compliant + + attribute :has_rdoc, false + + ## + # True if this gem supports RDoc + + alias :has_rdoc? :has_rdoc + + ## + # The ruby of version required by this gem + + attribute :required_ruby_version, Gem::Requirement.default + + ## + # The RubyGems version required by this gem + + attribute :required_rubygems_version, Gem::Requirement.default + + ## + # The platform this gem runs on. See Gem::Platform for details. + + attribute :platform, Gem::Platform::RUBY + + ## + # The key used to sign this gem. See Gem::Security for details. + + attribute :signing_key, nil + + ## + # The certificate chain used to sign this gem. See Gem::Security for + # details. + + attribute :cert_chain, [] + + ## + # A message that gets displayed after the gem is installed + + attribute :post_install_message, nil + + ## + # The list of authors who wrote this gem + + array_attribute :authors + + ## + # Files included in this gem + + array_attribute :files + + ## + # Test files included in this gem + + array_attribute :test_files + + ## + # An ARGV-style array of options to RDoc + + array_attribute :rdoc_options + + ## + # Extra files to add to RDoc + + array_attribute :extra_rdoc_files + + ## + # Executables included in the gem + + array_attribute :executables + + ## + # Extensions to build when installing the gem. See + # Gem::Installer#build_extensions for valid values. + + array_attribute :extensions + + ## + # An array or things required by this gem. Not used by anything + # presently. + + array_attribute :requirements + + ## + # A list of Gem::Dependency objects this gem depends on. Only appendable. + + array_attribute :dependencies + + read_only :dependencies + + # :section: Aliased gemspec attributes + + ## + # Singular accessor for executables + + attribute_alias_singular :executable, :executables + + ## + # Singular accessor for authors + + attribute_alias_singular :author, :authors + + ## + # Singular accessor for require_paths + + attribute_alias_singular :require_path, :require_paths + + ## + # Singular accessor for test_files + + attribute_alias_singular :test_file, :test_files + overwrite_accessor :version= do |version| @version = Version.create(version) end @@ -504,37 +1231,6 @@ module Gem end end - def add_bindir(executables) - return nil if executables.nil? - - if @bindir then - Array(executables).map { |e| File.join(@bindir, e) } - else - executables - end - rescue - return nil - end - - overwrite_accessor :files do - result = [] - result.push(*@files) if defined?(@files) - result.push(*@test_files) if defined?(@test_files) - result.push(*(add_bindir(@executables))) - result.push(*@extra_rdoc_files) if defined?(@extra_rdoc_files) - result.push(*@extensions) if defined?(@extensions) - result.uniq.compact - end - - # Files in the Gem under one of the require_paths - def lib_files - @files.select do |file| - require_paths.any? do |path| - file.index(path) == 0 - end - end - end - overwrite_accessor :test_files do # Handle the possibility that we have @test_suite_file but not # @test_files. This will happen when an old gem is loaded via @@ -550,501 +1246,14 @@ module Gem end end - # Predicates ----------------------------------------------------- - - def loaded?; @loaded ? true : false ; end - def has_rdoc?; has_rdoc ? true : false ; end - def has_unit_tests?; not test_files.empty?; end - alias has_test_suite? has_unit_tests? # (deprecated) - - # Constructors --------------------------------------------------- - - # Specification constructor. Assigns the default values to the - # attributes, adds this spec to the list of loaded specs (see - # Specification.list), and yields itself for further initialization. - # - def initialize - @new_platform = nil - assign_defaults - @loaded = false - @loaded_from = nil - @@list << self - - yield self if block_given? - - @@gather.call(self) if @@gather - end - - # Each attribute has a default value (possibly nil). Here, we - # initialize all attributes to their default value. This is - # done through the accessor methods, so special behaviours will - # be honored. Furthermore, we take a _copy_ of the default so - # each specification instance has its own empty arrays, etc. - def assign_defaults - @@nil_attributes.each do |name| - instance_variable_set name, nil - end - - @@non_nil_attributes.each do |name, default| - value = case default - when Time, Numeric, Symbol, true, false, nil then default - else default.dup - end - - instance_variable_set name, value - end - - # HACK - instance_variable_set :@new_platform, Gem::Platform::RUBY - end - - # Special loader for YAML files. When a Specification object is - # loaded from a YAML file, it bypasses the normal Ruby object - # initialization routine (#initialize). This method makes up for - # that and deals with gems of different ages. - # - # 'input' can be anything that YAML.load() accepts: String or IO. - # - def self.from_yaml(input) - input = normalize_yaml_input input - spec = YAML.load input - - if spec && spec.class == FalseClass then - raise Gem::EndOfYAMLException - end - - unless Gem::Specification === spec then - raise Gem::Exception, "YAML data doesn't evaluate to gem specification" - end - - unless (spec.instance_variables.include? '@specification_version' or - spec.instance_variables.include? :@specification_version) and - spec.instance_variable_get :@specification_version - spec.instance_variable_set :@specification_version, - NONEXISTENT_SPECIFICATION_VERSION - end - - spec - end - - def self.load(filename) - gemspec = nil - fail "NESTED Specification.load calls not allowed!" if @@gather - @@gather = proc { |gs| gemspec = gs } - data = File.read(filename) - eval(data) - gemspec - ensure - @@gather = nil - end - - # Make sure the yaml specification is properly formatted with dashes. - def self.normalize_yaml_input(input) - result = input.respond_to?(:read) ? input.read : input - result = "--- " + result unless result =~ /^--- / - result - end - - # Instance methods ----------------------------------------------- - - # Sets the rubygems_version to Gem::RubyGemsVersion. - # - def mark_version - @rubygems_version = RubyGemsVersion - end - - # Ignore unknown attributes if the - def method_missing(sym, *a, &b) # :nodoc: - if @specification_version > CURRENT_SPECIFICATION_VERSION and - sym.to_s =~ /=$/ then - warn "ignoring #{sym} loading #{full_name}" if $DEBUG - else - super - end - end - - # Adds a development dependency to this Gem. For example, - # - # spec.add_development_dependency('jabber4r', '> 0.1', '<= 0.5') - # - # Development dependencies aren't installed by default, and - # aren't activated when a gem is required. - # - # gem:: [String or Gem::Dependency] The Gem name/dependency. - # requirements:: [default=">= 0"] The version requirements. - def add_development_dependency(gem, *requirements) - add_dependency_with_type(gem, :development, *requirements) - end - - # Adds a runtime dependency to this Gem. For example, - # - # spec.add_runtime_dependency('jabber4r', '> 0.1', '<= 0.5') - # - # gem:: [String or Gem::Dependency] The Gem name/dependency. - # requirements:: [default=">= 0"] The version requirements. - def add_runtime_dependency(gem, *requirements) - add_dependency_with_type(gem, :runtime, *requirements) - end - - alias add_dependency add_runtime_dependency - - # Returns the full name (name-version) of this Gem. Platform information - # is included (name-version-platform) if it is specified (and not the - # default Ruby platform). - # - def full_name - if platform == Gem::Platform::RUBY or platform.nil? then - "#{@name}-#{@version}" - else - "#{@name}-#{@version}-#{platform}" - end - end - - # Returns the full name (name-version) of this gemspec using the original - # platform. - # - def original_name # :nodoc: - if platform == Gem::Platform::RUBY or platform.nil? then - "#{@name}-#{@version}" - else - "#{@name}-#{@version}-#{@original_platform}" - end - end - - ## - # The full path to the gem (install path + full name). - - def full_gem_path - path = File.join installation_path, 'gems', full_name - return path if File.directory? path - File.join installation_path, 'gems', original_name - end - - ## - # The default (generated) file name of the gem. - - def file_name - full_name + ".gem" - end - - ## - # The directory that this gem was installed into. - - def installation_path - path = File.dirname(@loaded_from).split(File::SEPARATOR)[0..-2] - path = path.join File::SEPARATOR - File.expand_path path - end - - # Checks if this Specification meets the requirement of the supplied - # dependency. - # - # dependency:: [Gem::Dependency] the dependency to check - # return:: [Boolean] true if dependency is met, otherwise false - # - def satisfies_requirement?(dependency) - return @name == dependency.name && - dependency.version_requirements.satisfied_by?(@version) - end - - # Comparison methods --------------------------------------------- - - def sort_obj - [@name, @version.to_ints, @new_platform == Gem::Platform::RUBY ? -1 : 1] - end - - def <=>(other) # :nodoc: - sort_obj <=> other.sort_obj - end - - # Tests specs for equality (across all attributes). - def ==(other) # :nodoc: - self.class === other && same_attributes?(other) - end - - alias eql? == # :nodoc: - - def same_attributes?(other) - @@attributes.each do |name, default| - return false unless self.send(name) == other.send(name) - end - true - end - private :same_attributes? - - def hash # :nodoc: - @@attributes.inject(0) { |hash_code, (name, default_value)| - n = self.send(name).hash - hash_code + n - } - end - - # Export methods (YAML and Ruby code) ---------------------------- - - def to_yaml(opts = {}) # :nodoc: - mark_version - - attributes = @@attributes.map { |name,| name.to_s }.sort - attributes = attributes - %w[name version platform] - - yaml = YAML.quick_emit object_id, opts do |out| - out.map taguri, to_yaml_style do |map| - map.add 'name', @name - map.add 'version', @version - platform = case @original_platform - when nil, '' then - 'ruby' - when String then - @original_platform - else - @original_platform.to_s - end - map.add 'platform', platform - - attributes.each do |name| - map.add name, instance_variable_get("@#{name}") - end - end - end - end - - def yaml_initialize(tag, vals) # :nodoc: - vals.each do |ivar, val| - instance_variable_set "@#{ivar}", val - end - - @original_platform = @platform # for backwards compatibility - self.platform = Gem::Platform.new @platform - end - - ## - # Returns a Ruby code representation of this specification, such that it - # can be eval'ed and reconstruct the same specification later. Attributes - # that still have their default values are omitted. - - def to_ruby - mark_version + overwrite_accessor :files do result = [] - result << "Gem::Specification.new do |s|" - - result << " s.name = #{ruby_code name}" - result << " s.version = #{ruby_code version}" - unless platform.nil? or platform == Gem::Platform::RUBY then - result << " s.platform = #{ruby_code original_platform}" - end - result << "" - result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version=" - - handled = [ - :dependencies, - :name, - :platform, - :required_rubygems_version, - :specification_version, - :version, - ] - - attributes = @@attributes.sort_by { |attr_name,| attr_name.to_s } - - attributes.each do |attr_name, default| - next if handled.include? attr_name - current_value = self.send(attr_name) - if current_value != default or - self.class.required_attribute? attr_name then - result << " s.#{attr_name} = #{ruby_code current_value}" - end - end - - result << nil - result << " if s.respond_to? :specification_version then" - result << " current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION" - result << " s.specification_version = #{specification_version}" - result << nil - - result << " if current_version >= 3 then" - - unless dependencies.empty? then - dependencies.each do |dep| - version_reqs_param = dep.requirements_list.inspect - dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK - result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>, #{version_reqs_param})" - end - end - - result << " else" - - unless dependencies.empty? then - dependencies.each do |dep| - version_reqs_param = dep.requirements_list.inspect - result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})" - end - end - - result << ' end' - - result << " else" - dependencies.each do |dep| - version_reqs_param = dep.requirements_list.inspect - result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})" - end - result << " end" - - result << "end" - result << nil - - result.join "\n" - end - - # Validation and normalization methods --------------------------- - - # Checks that the specification contains all required fields, and - # does a very basic sanity check. - # - # Raises InvalidSpecificationException if the spec does not pass - # the checks.. - def validate - extend Gem::UserInteraction - - normalize - - if rubygems_version != RubyGemsVersion then - raise Gem::InvalidSpecificationException, - "expected RubyGems version #{RubyGemsVersion}, was #{rubygems_version}" - end - - @@required_attributes.each do |symbol| - unless self.send symbol then - raise Gem::InvalidSpecificationException, - "missing value for attribute #{symbol}" - end - end - - if require_paths.empty? then - raise Gem::InvalidSpecificationException, - "specification must have at least one require_path" - end - - case platform - when Gem::Platform, Platform::RUBY then # ok - else - raise Gem::InvalidSpecificationException, - "invalid platform #{platform.inspect}, see Gem::Platform" - end - - unless Array === authors and - authors.all? { |author| String === author } then - raise Gem::InvalidSpecificationException, - 'authors must be Array of Strings' - end - - # Warnings - - %w[author email homepage rubyforge_project summary].each do |attribute| - value = self.send attribute - alert_warning "no #{attribute} specified" if value.nil? or value.empty? - end - - alert_warning "RDoc will not be generated (has_rdoc == false)" unless - has_rdoc - - alert_warning "deprecated autorequire specified" if autorequire - - executables.each do |executable| - executable_path = File.join bindir, executable - shebang = File.read(executable_path, 2) == '#!' - - alert_warning "#{executable_path} is missing #! line" unless shebang - end - - true - end - - # Normalize the list of files so that: - # * All file lists have redundancies removed. - # * Files referenced in the extra_rdoc_files are included in the - # package file list. - # - # Also, the summary and description are converted to a normal - # format. - def normalize - if defined?(@extra_rdoc_files) and @extra_rdoc_files then - @extra_rdoc_files.uniq! - @files ||= [] - @files.concat(@extra_rdoc_files) - end - @files.uniq! if @files - end - - # Dependency methods --------------------------------------------- - - # Return a list of all gems that have a dependency on this - # gemspec. The list is structured with entries that conform to: - # - # [depending_gem, dependency, [list_of_gems_that_satisfy_dependency]] - # - # return:: [Array] [[dependent_gem, dependency, [list_of_satisfiers]]] - # - def dependent_gems - out = [] - Gem.source_index.each do |name,gem| - gem.dependencies.each do |dep| - if self.satisfies_requirement?(dep) then - sats = [] - find_all_satisfiers(dep) do |sat| - sats << sat - end - out << [gem, dep, sats] - end - end - end - out - end - - def to_s - "#" - end - - private - - def add_dependency_with_type(dependency, type, *requirements) - requirements = if requirements.empty? then - Gem::Requirement.default - else - requirements.flatten - end - - unless dependency.respond_to?(:name) && - dependency.respond_to?(:version_requirements) - - dependency = Dependency.new(dependency, requirements, type) - end - - dependencies << dependency - end - - def find_all_satisfiers(dep) - Gem.source_index.each do |name,gem| - if(gem.satisfies_requirement?(dep)) then - yield gem - end - end - end - - # Return a string containing a Ruby code representation of the - # given object. - def ruby_code(obj) - case obj - when String then '%q{' + obj + '}' - when Array then obj.inspect - when Gem::Version then obj.to_s.inspect - when Date then '%q{' + obj.strftime('%Y-%m-%d') + '}' - when Time then '%q{' + obj.strftime('%Y-%m-%d') + '}' - when Numeric then obj.inspect - when true, false, nil then obj.inspect - when Gem::Platform then "Gem::Platform.new(#{obj.to_a.inspect})" - when Gem::Requirement then "Gem::Requirement.new(#{obj.to_s.inspect})" - else raise Exception, "ruby_code case not handled: #{obj.class}" - end + result.push(*@files) if defined?(@files) + result.push(*@test_files) if defined?(@test_files) + result.push(*(add_bindir(@executables))) + result.push(*@extra_rdoc_files) if defined?(@extra_rdoc_files) + result.push(*@extensions) if defined?(@extensions) + result.uniq.compact end end diff --git a/lib/rubygems/test_utilities.rb b/lib/rubygems/test_utilities.rb index e8709b9be3..8b23d3236e 100644 --- a/lib/rubygems/test_utilities.rb +++ b/lib/rubygems/test_utilities.rb @@ -34,16 +34,20 @@ class Gem::FakeFetcher path = path.to_s @paths << path raise ArgumentError, 'need full URI' unless path =~ %r'^http://' - data = @data[path] - if data.nil? then - raise Gem::RemoteFetcher::FetchError.new('no data', path) + unless @data.key? path then + raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path) end + data = @data[path] + if data.respond_to?(:call) then data.call else - data = Gem.gunzip data if path.to_s =~ /gz$/ unless data.empty? + if path.to_s =~ /gz$/ and not data.nil? and not data.empty? then + data = Gem.gunzip data + end + data end end @@ -51,13 +55,15 @@ class Gem::FakeFetcher def fetch_size(path) path = path.to_s @paths << path - raise ArgumentError, 'need full URI' unless path =~ %r'^http://' - data = @data[path] - if data.nil? then - raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", nil) + raise ArgumentError, 'need full URI' unless path =~ %r'^http://' + + unless @data.key? path then + raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path) end + data = @data[path] + data.respond_to?(:call) ? data.call : data.length end diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb index 2ad961972b..5f19da5e82 100644 --- a/lib/rubygems/uninstaller.rb +++ b/lib/rubygems/uninstaller.rb @@ -18,9 +18,23 @@ class Gem::Uninstaller include Gem::UserInteraction ## - # Constructs an Uninstaller instance - # - # gem:: [String] The Gem name to uninstall + # The directory a gem's executables will be installed into + + attr_reader :bin_dir + + ## + # The gem repository the gem will be installed into + + attr_reader :gem_home + + ## + # The Gem::Specification for the gem being uninstalled, only set during + # #uninstall_gem + + attr_reader :spec + + ## + # Constructs an uninstaller that will uninstall +gem+ def initialize(gem, options = {}) @gem = gem @@ -31,41 +45,62 @@ class Gem::Uninstaller @force_all = options[:all] @force_ignore = options[:ignore] @bin_dir = options[:bin_dir] + + spec_dir = File.join @gem_home, 'specifications' + @source_index = Gem::SourceIndex.from_gems_in spec_dir end ## - # Performs the uninstall of the Gem. This removes the spec, the - # Gem directory, and the cached .gem file, + # 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) + list = @source_index.find_name @gem, @version if list.empty? then raise Gem::InstallError, "Unknown gem #{@gem} #{@version}" - elsif list.size > 1 && @force_all - remove_all(list.dup) - remove_executables(list.last) - elsif list.size > 1 - say + + elsif list.size > 1 and @force_all then + remove_all list.dup + + elsif list.size > 1 then gem_names = list.collect {|gem| gem.full_name} + ["All versions"] - gem_name, index = - choose_from_list("Select gem to uninstall:", gem_names) - if index == list.size - remove_all(list.dup) - remove_executables(list.last) - elsif index >= 0 && index < list.size - to_remove = list[index] - remove(to_remove, list) - remove_executables(to_remove) + + say + gem_name, index = choose_from_list "Select gem to uninstall:", gem_names + + if index == list.size then + remove_all list.dup + elsif index >= 0 && index < list.size then + uninstall_gem list[index], list.dup else say "Error: must enter a number [1-#{list.size+1}]" end else - remove(list[0], list.dup) - remove_executables(list.last) + uninstall_gem list.first, list.dup end end + ## + # Uninstalls gem +spec+ + + def uninstall_gem(spec, specs) + @spec = spec + + Gem.pre_uninstall_hooks.each do |hook| + hook.call self + end + + specs.each { |s| remove_executables s } + remove spec, specs + + Gem.post_uninstall_hooks.each do |hook| + hook.call self + end + + @spec = nil + end + ## # Removes installed executables and batch files (windows only) for # +gemspec+. @@ -76,7 +111,7 @@ class Gem::Uninstaller if gemspec.executables.size > 0 then bindir = @bin_dir ? @bin_dir : (Gem.bindir @gem_home) - list = Gem.source_index.search(gemspec.name).delete_if { |spec| + list = @source_index.find_name(gemspec.name).delete_if { |spec| spec.version == gemspec.version } @@ -118,7 +153,7 @@ class Gem::Uninstaller # NOTE: removes uninstalled gems from +list+. def remove_all(list) - list.dup.each { |spec| remove spec, list } + list.dup.each { |spec| uninstall_gem spec, list } end ## @@ -185,8 +220,7 @@ class Gem::Uninstaller def dependencies_ok?(spec) return true if @force_ignore - source_index = Gem::SourceIndex.from_installed_gems - deplist = Gem::DependencyList.from_source_index source_index + deplist = Gem::DependencyList.from_source_index @source_index deplist.ok_to_remove?(spec.full_name) || ask_if_ok(spec) end diff --git a/test/rubygems/gemutilities.rb b/test/rubygems/gemutilities.rb index 0ab2c5272c..d1e50676d5 100644 --- a/test/rubygems/gemutilities.rb +++ b/test/rubygems/gemutilities.rb @@ -19,6 +19,10 @@ require 'rubygems/test_utilities' require File.join(File.expand_path(File.dirname(__FILE__)), 'mockgemui') module Gem + def self.searcher=(searcher) + MUTEX.synchronize do @searcher = searcher end + end + def self.source_index=(si) @@source_index = si end @@ -26,7 +30,7 @@ module Gem def self.win_platform=(val) @@win_platform = val end - + module DefaultUserInteraction @ui = MockGemUi.new end @@ -89,6 +93,27 @@ class RubyGemTestCase < Test::Unit::TestCase 'private_key.pem') @public_cert = File.expand_path File.join(File.dirname(__FILE__), 'public_cert.pem') + + Gem.post_install_hooks.clear + Gem.post_uninstall_hooks.clear + Gem.pre_install_hooks.clear + Gem.pre_uninstall_hooks.clear + + Gem.post_install do |installer| + @post_install_hook_arg = installer + end + + Gem.post_uninstall do |uninstaller| + @post_uninstall_hook_arg = uninstaller + end + + Gem.pre_install do |installer| + @pre_install_hook_arg = installer + end + + Gem.pre_uninstall do |uninstaller| + @pre_uninstall_hook_arg = uninstaller + end end def teardown @@ -435,7 +460,15 @@ class RubyGemTestCase < Test::Unit::TestCase end @@ruby = rubybin - @@rake = ENV["rake"] || (@@ruby + " " + File.expand_path("../../../bin/rake", __FILE__)) + env_rake = ENV['rake'] + ruby19_rake = @@ruby + " " + File.expand_path("../../../bin/rake", __FILE__) + @@rake = if env_rake then + ENV["rake"] + elsif File.exist? ruby19_rake then + ruby19_rake + else + 'rake' + end end diff --git a/test/rubygems/mockgemui.rb b/test/rubygems/mockgemui.rb index 95a95fbf98..57582ef57d 100644 --- a/test/rubygems/mockgemui.rb +++ b/test/rubygems/mockgemui.rb @@ -12,8 +12,28 @@ require 'rubygems/user_interaction' class MockGemUi < Gem::StreamUI class TermError < RuntimeError; end - def initialize(input="") - super(StringIO.new(input), StringIO.new, StringIO.new) + module TTY + + attr_accessor :tty + + def tty?() + @tty = true unless defined?(@tty) + @tty + end + + end + + def initialize(input = "") + ins = StringIO.new input + outs = StringIO.new + errs = StringIO.new + + ins.extend TTY + outs.extend TTY + errs.extend TTY + + super ins, outs, errs + @terminated = false end diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb index c26eeb573d..6735f2f02a 100644 --- a/test/rubygems/test_gem.rb +++ b/test/rubygems/test_gem.rb @@ -45,7 +45,12 @@ class TestGem < RubyGemTestCase def test_self_bindir_default_dir default = Gem.default_dir - bindir = (defined? RUBY_FRAMEWORK_VERSION) ? '/usr/bin' : Config::CONFIG['bindir'] + bindir = if defined?(RUBY_FRAMEWORK_VERSION) then + '/usr/bin' + else + Config::CONFIG['bindir'] + end + assert_equal bindir, Gem.bindir(default) assert_equal bindir, Gem.bindir(Pathname.new(default)) end @@ -218,6 +223,36 @@ class TestGem < RubyGemTestCase Gem.ssl_available = orig_Gem_ssl_available end + def test_self_find_files + foo1 = quick_gem 'foo', '1' do |s| + s.files << 'lib/foo/discover.rb' + end + + foo2 = quick_gem 'foo', '2' do |s| + s.files << 'lib/foo/discover.rb' + end + + path = File.join 'gems', foo1.full_name, 'lib', 'foo', 'discover.rb' + write_file(path) { |fp| fp.puts "# #{path}" } + + path = File.join 'gems', foo2.full_name, 'lib', 'foo', 'discover.rb' + write_file(path) { |fp| fp.puts "# #{path}" } + + @fetcher = Gem::FakeFetcher.new + Gem::RemoteFetcher.fetcher = @fetcher + + Gem.source_index = util_setup_spec_fetcher foo1, foo2 + + Gem.searcher = nil + + expected = [ + File.join(foo1.full_gem_path, 'lib', 'foo', 'discover.rb'), + File.join(foo2.full_gem_path, 'lib', 'foo', 'discover.rb'), + ] + + assert_equal expected, Gem.find_files('foo/discover').sort + end + def test_self_latest_load_paths util_make_gems @@ -261,20 +296,21 @@ class TestGem < RubyGemTestCase unless win_platform? def test_self_path_APPLE_GEM_HOME Gem.clear_paths - Dir.mktmpdir("apple_gem_home") {|d| - Gem.const_set :APPLE_GEM_HOME, d - assert Gem.path.include?(d) - } + apple_gem_home = File.join @tempdir, 'apple_gem_home' + Gem.const_set :APPLE_GEM_HOME, apple_gem_home + + assert Gem.path.include?(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') + apple_gem_home = File.join @tempdir, 'apple_gem_home' + Gem.const_set :APPLE_GEM_HOME, apple_gem_home + + assert !Gem.path.include?(apple_gem_home) ensure Gem.send :remove_const, :APPLE_GEM_HOME end @@ -291,7 +327,7 @@ class TestGem < RubyGemTestCase assert_equal path_count + @additional.size, Gem.path.size, "extra path components: #{Gem.path[2..-1].inspect}" - assert_match Gem.dir, Gem.path.last + assert_equal Gem.dir, Gem.path.last end def test_self_path_duplicate @@ -390,6 +426,44 @@ class TestGem < RubyGemTestCase Gem.required_location("a", "code.rb", "= 2") end + def test_self_ruby_escaping_spaces_in_path + orig_ruby = Gem.ruby + orig_bindir = Gem::ConfigMap[:bindir] + orig_ruby_install_name = Gem::ConfigMap[:ruby_install_name] + orig_exe_ext = Gem::ConfigMap[:EXEEXT] + + Gem::ConfigMap[:bindir] = "C:/Ruby 1.8/bin" + Gem::ConfigMap[:ruby_install_name] = "ruby" + Gem::ConfigMap[:EXEEXT] = ".exe" + Gem.instance_variable_set("@ruby", nil) + + assert_equal "\"C:/Ruby 1.8/bin/ruby.exe\"", Gem.ruby + ensure + Gem.instance_variable_set("@ruby", orig_ruby) + Gem::ConfigMap[:bindir] = orig_bindir + Gem::ConfigMap[:ruby_install_name] = orig_ruby_install_name + Gem::ConfigMap[:EXEEXT] = orig_exe_ext + end + + def test_self_ruby_path_without_spaces + orig_ruby = Gem.ruby + orig_bindir = Gem::ConfigMap[:bindir] + orig_ruby_install_name = Gem::ConfigMap[:ruby_install_name] + orig_exe_ext = Gem::ConfigMap[:EXEEXT] + + Gem::ConfigMap[:bindir] = "C:/Ruby18/bin" + Gem::ConfigMap[:ruby_install_name] = "ruby" + Gem::ConfigMap[:EXEEXT] = ".exe" + Gem.instance_variable_set("@ruby", nil) + + assert_equal "C:/Ruby18/bin/ruby.exe", Gem.ruby + ensure + Gem.instance_variable_set("@ruby", orig_ruby) + Gem::ConfigMap[:bindir] = orig_bindir + Gem::ConfigMap[:ruby_install_name] = orig_ruby_install_name + Gem::ConfigMap[:EXEEXT] = orig_exe_ext + end + def test_self_ruby_version version = RUBY_VERSION.dup version << ".#{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL @@ -430,6 +504,11 @@ class TestGem < RubyGemTestCase assert_equal @additional + [Gem.dir], Gem.path end + def test_self_user_dir + assert_equal File.join(@userhome, '.gem', Gem.ruby_engine, + Gem::ConfigMap[:ruby_version]), Gem.user_dir + end + def test_self_user_home if ENV['HOME'] then assert_equal ENV['HOME'], Gem.user_home @@ -438,6 +517,28 @@ class TestGem < RubyGemTestCase end end + def test_self_user_home_user_drive_and_path + Gem.clear_paths + + # safe-keep env variables + orig_home, orig_user_profile = ENV['HOME'], ENV['USERPROFILE'] + orig_user_drive, orig_user_path = ENV['HOMEDRIVE'], ENV['HOMEPATH'] + + # prepare the environment + ENV.delete('HOME') + ENV.delete('USERPROFILE') + ENV['HOMEDRIVE'] = 'Z:' + ENV['HOMEPATH'] = '\\Users\\RubyUser' + + assert_equal "Z:\\Users\\RubyUser", Gem.user_home + + ensure + ENV['HOME'] = orig_home + ENV['USERPROFILE'] = orig_user_profile + ENV['USERDRIVE'] = orig_user_drive + ENV['USERPATH'] = orig_user_path + end + def util_ensure_gem_dirs Gem.ensure_gem_subdirectories @gemhome @additional.each do |dir| diff --git a/test/rubygems/test_gem_builder.rb b/test/rubygems/test_gem_builder.rb index 3463b4d403..31a0d71880 100644 --- a/test/rubygems/test_gem_builder.rb +++ b/test/rubygems/test_gem_builder.rb @@ -25,7 +25,7 @@ class TestGemBuilder < RubyGemTestCase def test_build_validates builder = Gem::Builder.new Gem::Specification.new - assert_raise Gem::InvalidSpecificationException do + assert_raises Gem::InvalidSpecificationException do builder.build end end diff --git a/test/rubygems/test_gem_command_manager.rb b/test/rubygems/test_gem_command_manager.rb index 59d3b5cb8e..ee58e89844 100644 --- a/test/rubygems/test_gem_command_manager.rb +++ b/test/rubygems/test_gem_command_manager.rb @@ -31,7 +31,7 @@ class TestGemCommandManager < RubyGemTestCase def test_run_interrupt use_ui @ui do @command_manager.register_command :interrupt - assert_raise MockGemUi::TermError do + assert_raises MockGemUi::TermError do @command_manager.run 'interrupt' end assert_equal '', ui.output @@ -41,7 +41,7 @@ class TestGemCommandManager < RubyGemTestCase def test_process_args_bad_arg use_ui @ui do - assert_raise(MockGemUi::TermError) { + assert_raises(MockGemUi::TermError) { @command_manager.process_args("--bad-arg") } end diff --git a/test/rubygems/test_gem_commands_dependency_command.rb b/test/rubygems/test_gem_commands_dependency_command.rb index e143110a71..0acf2a0ddb 100644 --- a/test/rubygems/test_gem_commands_dependency_command.rb +++ b/test/rubygems/test_gem_commands_dependency_command.rb @@ -199,7 +199,7 @@ ERROR: Only reverse dependencies for local gems are supported. @fetcher.data["#{@gem_repo}Marshal.#{Gem.marshal_version}"] = si.dump - @fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz"] = nil + @fetcher.data.delete "#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz" FileUtils.rm File.join(@gemhome, 'specifications', "#{foo.full_name}.gemspec") diff --git a/test/rubygems/test_gem_commands_install_command.rb b/test/rubygems/test_gem_commands_install_command.rb index e972429ce1..ef04072b93 100644 --- a/test/rubygems/test_gem_commands_install_command.rb +++ b/test/rubygems/test_gem_commands_install_command.rb @@ -43,7 +43,7 @@ class TestGemCommandsInstallCommand < RubyGemTestCase orig_dir = Dir.pwd begin Dir.chdir @tempdir - e = assert_raise Gem::SystemExitException do + e = assert_raises Gem::SystemExitException do @cmd.execute end assert_equal 0, e.exit_code @@ -65,7 +65,7 @@ class TestGemCommandsInstallCommand < RubyGemTestCase @cmd.options[:args] = %w[no_such_gem] use_ui @ui do - e = assert_raise Gem::SystemExitException do + e = assert_raises Gem::SystemExitException do @cmd.execute end assert_equal 2, e.exit_code @@ -91,7 +91,7 @@ class TestGemCommandsInstallCommand < RubyGemTestCase @cmd.options[:args] = %w[nonexistent] use_ui @ui do - e = assert_raise Gem::SystemExitException do + e = assert_raises Gem::SystemExitException do @cmd.execute end assert_equal 2, e.exit_code @@ -114,7 +114,7 @@ class TestGemCommandsInstallCommand < RubyGemTestCase @cmd.options[:args] = [@a2.name] use_ui @ui do - e = assert_raise Gem::SystemExitException do + e = assert_raises Gem::SystemExitException do @cmd.execute end assert_equal 0, e.exit_code @@ -146,7 +146,7 @@ class TestGemCommandsInstallCommand < RubyGemTestCase orig_dir = Dir.pwd begin Dir.chdir @tempdir - e = assert_raise Gem::SystemExitException do + e = assert_raises Gem::SystemExitException do @cmd.execute end assert_equal 0, e.exit_code diff --git a/test/rubygems/test_gem_commands_list_command.rb b/test/rubygems/test_gem_commands_list_command.rb new file mode 100644 index 0000000000..bac9cca03f --- /dev/null +++ b/test/rubygems/test_gem_commands_list_command.rb @@ -0,0 +1,37 @@ +require 'test/unit' +require File.join(File.expand_path(File.dirname(__FILE__)), 'gemutilities') +require 'rubygems/commands/list_command' + +class TestGemCommandsListCommand < RubyGemTestCase + + def setup + super + + @cmd = Gem::Commands::ListCommand.new + + util_setup_fake_fetcher + + @si = util_setup_spec_fetcher @a1, @a2, @pl1 + + @fetcher.data["#{@gem_repo}Marshal.#{Gem.marshal_version}"] = proc do + raise Gem::RemoteFetcher::FetchError + end + end + + def test_execute_installed + @cmd.handle_options %w[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 + +end diff --git a/test/rubygems/test_gem_commands_lock_command.rb b/test/rubygems/test_gem_commands_lock_command.rb new file mode 100644 index 0000000000..089fed6e9a --- /dev/null +++ b/test/rubygems/test_gem_commands_lock_command.rb @@ -0,0 +1,69 @@ +require 'test/unit' +require File.join(File.expand_path(File.dirname(__FILE__)), 'gemutilities') +require 'rubygems/commands/lock_command' + +class TestGemCommandsLockCommand < RubyGemTestCase + + def setup + super + + @a1 = quick_gem 'a', '1' + @b1 = quick_gem 'b', '1' do |s| + s.add_runtime_dependency 'a' + end + + @d1 = quick_gem 'd', '1' do |s| + s.add_runtime_dependency 'z' + end + + @cmd = Gem::Commands::LockCommand.new + end + + def test_execute + @cmd.handle_options %w[b-1] + + use_ui @ui do + @cmd.execute + end + + expected = <<-EXPECTED +require 'rubygems' +gem 'b', '= 1' +gem 'a', '= 1' + EXPECTED + + assert_equal expected, @ui.output + assert_equal '', @ui.error + end + + def test_execute_missing_dependency + @cmd.handle_options %w[d-1] + + use_ui @ui do + @cmd.execute + end + + expected = <<-EXPECTED +require 'rubygems' +gem 'd', '= 1' +# Unable to satisfy 'z (>= 0, runtime)' from currently installed gems + EXPECTED + + assert_equal expected, @ui.output + assert_equal '', @ui.error + end + + def test_execute_strict + @cmd.handle_options %w[c-1 --strict] + + e = assert_raise Gem::Exception do + use_ui @ui do + @cmd.execute + end + end + + assert_equal 'Could not find gem c-1, try using the full name', e.message + end + +end + diff --git a/test/rubygems/test_gem_commands_query_command.rb b/test/rubygems/test_gem_commands_query_command.rb index 1b65fc7633..b17e2a3ad5 100644 --- a/test/rubygems/test_gem_commands_query_command.rb +++ b/test/rubygems/test_gem_commands_query_command.rb @@ -180,7 +180,7 @@ pl (1) @fetcher.data["#{@gem_repo}Marshal.#{Gem.marshal_version}"] = si.dump - @fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz"] = nil + @fetcher.data.delete "#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz" @cmd.handle_options %w[-r] @@ -265,6 +265,27 @@ pl (1) assert_equal '', @ui.error end + def test_execute_local_notty + @cmd.handle_options %w[] + + @ui.outs.tty = false + + use_ui @ui do + @cmd.execute + end + + expected = <<-EOF +a (2, 1) +a_evil (9) +b (2) +c (1.2) +pl (1) + EOF + + assert_equal expected, @ui.output + assert_equal '', @ui.error + end + def test_execute_no_versions @cmd.handle_options %w[-r --no-versions] @@ -284,5 +305,23 @@ pl assert_equal '', @ui.error end + def test_execute_notty + @cmd.handle_options %w[-r] + + @ui.outs.tty = false + + use_ui @ui do + @cmd.execute + end + + expected = <<-EOF +a (2) +pl (1) + EOF + + assert_equal expected, @ui.output + assert_equal '', @ui.error + end + end diff --git a/test/rubygems/test_gem_commands_uninstall_command.rb b/test/rubygems/test_gem_commands_uninstall_command.rb new file mode 100644 index 0000000000..dfe84dc348 --- /dev/null +++ b/test/rubygems/test_gem_commands_uninstall_command.rb @@ -0,0 +1,60 @@ +require 'test/unit' +require File.join(File.expand_path(File.dirname(__FILE__)), 'gemutilities') +require File.join(File.expand_path(File.dirname(__FILE__)), + 'gem_installer_test_case') +require 'rubygems/commands/uninstall_command' + +class TestGemCommandsUninstallCommand < GemInstallerTestCase + + def setup + super + + ui = MockGemUi.new + util_setup_gem ui + + use_ui ui do + @installer.install + end + + @cmd = Gem::Commands::UninstallCommand.new + @cmd.options[:executables] = true + @executable = File.join(@gemhome, 'bin', 'executable') + end + + def test_execute_removes_executable + if win_platform? + assert_equal true, File.exist?(@executable) + else + assert_equal true, File.symlink?(@executable) + end + + # Evil hack to prevent false removal success + FileUtils.rm_f @executable + File.open(@executable, "wb+") {|f| f.puts "binary"} + + @cmd.options[:args] = Array(@spec.name) + use_ui @ui do + @cmd.execute + end + + output = @ui.output.split "\n" + assert_match(/Removing executable/, output.shift) + assert_match(/Successfully uninstalled/, output.shift) + assert_equal false, File.exist?(@executable) + assert_nil output.shift, "UI output should have contained only two lines" + end + + def test_execute_not_installed + @cmd.options[:args] = ["foo"] + e = assert_raise(Gem::InstallError) do + use_ui @ui do + @cmd.execute + end + end + + assert_match(/\AUnknown gem foo >= 0$/, e.message) + output = @ui.output.split "\n" + assert output.empty?, "UI output should be empty after an uninstall error" + end +end + diff --git a/test/rubygems/test_gem_config_file.rb b/test/rubygems/test_gem_config_file.rb index 06321d4e7c..17071ef65f 100644 --- a/test/rubygems/test_gem_config_file.rb +++ b/test/rubygems/test_gem_config_file.rb @@ -57,6 +57,9 @@ class TestGemConfigFile < RubyGemTestCase fp.puts ":sources:" fp.puts " - http://more-gems.example.com" fp.puts "install: --wrappers" + fp.puts ":gempath:" + fp.puts "- /usr/ruby/1.8/lib/ruby/gems/1.8" + fp.puts "- /var/ruby/1.8/gem_home" end util_config_file @@ -68,6 +71,8 @@ class TestGemConfigFile < RubyGemTestCase assert_equal false, @cfg.update_sources assert_equal %w[http://more-gems.example.com], Gem.sources assert_equal '--wrappers', @cfg[:install] + assert_equal(['/usr/ruby/1.8/lib/ruby/gems/1.8', '/var/ruby/1.8/gem_home'], + @cfg.path) end def test_initialize_handle_arguments_config_file diff --git a/test/rubygems/test_gem_ext_configure_builder.rb b/test/rubygems/test_gem_ext_configure_builder.rb index 9ce17075bc..a1b656353a 100644 --- a/test/rubygems/test_gem_ext_configure_builder.rb +++ b/test/rubygems/test_gem_ext_configure_builder.rb @@ -53,12 +53,13 @@ class TestGemExtConfigureBuilder < RubyGemTestCase expected = %r(configure failed: #{Regexp.escape sh_prefix_configure}#{Regexp.escape @dest_path} -.*?: #{shell_error_msg}) +.*?: #{shell_error_msg} +) assert_match expected, error.message assert_equal "#{sh_prefix_configure}#{@dest_path}", output.shift - assert_match %r(#{shell_error_msg}\n), output.shift + assert_match %r(#{shell_error_msg}), output.shift assert_equal true, output.empty? end diff --git a/test/rubygems/test_gem_ext_rake_builder.rb b/test/rubygems/test_gem_ext_rake_builder.rb index 6d9184e804..b1e7783d54 100644 --- a/test/rubygems/test_gem_ext_rake_builder.rb +++ b/test/rubygems/test_gem_ext_rake_builder.rb @@ -32,6 +32,8 @@ class TestGemExtRakeBuilder < RubyGemTestCase end end + output = output.join "\n" + expected = [ "#{@@ruby} mkrf_conf.rb", "", @@ -39,7 +41,9 @@ class TestGemExtRakeBuilder < RubyGemTestCase "(in #{realdir})\n" ] - assert_equal expected, output + assert_no_match %r%^rake failed:%, output + assert_match %r%^#{Regexp.escape @@ruby} mkrf_conf\.rb%, output + assert_match %r%^#{Regexp.escape @@rake} RUBYARCHDIR=#{Regexp.escape @dest_path} RUBYLIBDIR=#{Regexp.escape @dest_path}%, output end def test_class_build_fail @@ -69,7 +73,9 @@ rake failed: #{@@rake} RUBYARCHDIR=#{@dest_path} RUBYLIBDIR=#{@dest_path} EOF - assert_equal expected, error.message.split("\n")[0..4].join("\n") + assert_match %r%^rake failed:%, error.message + assert_match %r%^#{Regexp.escape @@ruby} mkrf_conf\.rb%, error.message + assert_match %r%^#{Regexp.escape @@rake} RUBYARCHDIR=#{Regexp.escape @dest_path} RUBYLIBDIR=#{Regexp.escape @dest_path}%, error.message end end diff --git a/test/rubygems/test_gem_gem_path_searcher.rb b/test/rubygems/test_gem_gem_path_searcher.rb index c9da4d2b05..a6124df84d 100644 --- a/test/rubygems/test_gem_gem_path_searcher.rb +++ b/test/rubygems/test_gem_gem_path_searcher.rb @@ -5,10 +5,6 @@ require 'rubygems/gem_path_searcher' class Gem::GemPathSearcher attr_accessor :gemspecs attr_accessor :lib_dirs - - public :init_gemspecs - public :matching_file - public :lib_dirs_for end class TestGemGemPathSearcher < RubyGemTestCase @@ -40,6 +36,10 @@ class TestGemGemPathSearcher < RubyGemTestCase assert_equal @foo1, @gps.find('foo') end + def test_find_all + assert_equal [@foo1], @gps.find_all('foo') + end + def test_init_gemspecs assert_equal [@bar2, @bar1, @foo2, @foo1], @gps.init_gemspecs end @@ -51,9 +51,17 @@ class TestGemGemPathSearcher < RubyGemTestCase assert_equal expected, lib_dirs end - def test_matching_file - assert !@gps.matching_file(@foo1, 'bar') - assert @gps.matching_file(@foo1, 'foo') + def test_matching_file_eh + assert !@gps.matching_file?(@foo1, 'bar') + assert @gps.matching_file?(@foo1, 'foo') + end + + def test_matching_files + assert_equal [], @gps.matching_files(@foo1, 'bar') + + expected = File.join @foo1.full_gem_path, 'lib', 'foo.rb' + + assert_equal [expected], @gps.matching_files(@foo1, 'foo') end end diff --git a/test/rubygems/test_gem_install_update_options.rb b/test/rubygems/test_gem_install_update_options.rb index 9a017384a5..cccbb74314 100644 --- a/test/rubygems/test_gem_install_update_options.rb +++ b/test/rubygems/test_gem_install_update_options.rb @@ -41,8 +41,8 @@ class TestGemInstallUpdateOptions < GemInstallerTestCase @installer = Gem::Installer.new @gem, @cmd.options @installer.install - assert File.exist?(File.join(@userhome, '.gem', 'gems')) - assert File.exist?(File.join(@userhome, '.gem', 'gems', + assert File.exist?(File.join(Gem.user_dir, 'gems')) + assert File.exist?(File.join(Gem.user_dir, 'gems', @spec.full_name)) end @@ -52,7 +52,7 @@ class TestGemInstallUpdateOptions < GemInstallerTestCase File.chmod 0755, @userhome FileUtils.chmod 0000, @gemhome - assert_raise(Gem::FilePermissionError) do + assert_raises(Gem::FilePermissionError) do @installer = Gem::Installer.new @gem, @cmd.options end ensure diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb index e57461f53a..4dc31efd1b 100644 --- a/test/rubygems/test_gem_installer.rb +++ b/test/rubygems/test_gem_installer.rb @@ -70,7 +70,10 @@ load 'my_exec' #{Gem.ruby}: No such file or directory -- extconf.rb (LoadError) EOF - assert_equal expected, File.read(gem_make_out) + assert_match %r%#{Regexp.escape Gem.ruby} extconf.rb%, + File.read(gem_make_out) + assert_match %r%#{Regexp.escape Gem.ruby}: No such file%, + File.read(gem_make_out) end def test_build_extensions_unsupported @@ -287,7 +290,7 @@ load 'my_exec' Dir.mkdir util_inst_bindir File.chmod 0000, util_inst_bindir - assert_raise Gem::FilePermissionError do + assert_raises Gem::FilePermissionError do @installer.generate_bin end @@ -372,7 +375,7 @@ load 'my_exec' Dir.mkdir util_inst_bindir File.chmod 0000, util_inst_bindir - assert_raise Gem::FilePermissionError do + assert_raises Gem::FilePermissionError do @installer.generate_bin end @@ -529,6 +532,16 @@ load 'my_exec' Dir.mkdir util_inst_bindir util_setup_gem + cache_file = File.join @gemhome, 'cache', "#{@spec.full_name}.gem" + + Gem.pre_install do |installer| + assert !File.exist?(cache_file), 'cache file should not exist yet' + end + + Gem.post_install do |installer| + assert File.exist?(cache_file), 'cache file should exist' + end + build_rake_in do use_ui @ui do assert_equal @spec, @installer.install @@ -552,6 +565,9 @@ load 'my_exec' assert_equal spec_file, @spec.loaded_from assert File.exist?(spec_file) + + assert_same @installer, @pre_install_hook_arg + assert_same @installer, @post_install_hook_arg end def test_install_bad_gem @@ -586,6 +602,29 @@ load 'my_exec' end end + def test_install_check_dependencies_install_dir + gemhome2 = "#{@gemhome}2" + @spec.add_dependency 'b' + + b2 = quick_gem 'b', 2 + + FileUtils.mv @gemhome, gemhome2 + Gem.source_index.gems.delete b2.full_name + source_index = Gem::SourceIndex.from_gems_in File.join(gemhome2, + 'specifications') + + util_setup_gem + + @installer = Gem::Installer.new @gem, :install_dir => gemhome2, + :source_index => source_index + + use_ui @ui do + @installer.install + end + + assert File.exist?(File.join(gemhome2, 'gems', @spec.full_name)) + end + def test_install_force use_ui @ui do installer = Gem::Installer.new old_ruby_required, :force => true @@ -641,13 +680,13 @@ load 'my_exec' assert File.exist?(File.join(@gemhome, 'specifications', "#{@spec.full_name}.gemspec")) end + unless win_platform? # File.chmod doesn't work def test_install_user_local_fallback Dir.mkdir util_inst_bindir File.chmod 0755, @userhome File.chmod 0000, util_inst_bindir File.chmod 0000, Gem.dir - install_dir = File.join @userhome, '.gem', 'gems', @spec.full_name @spec.executables = ["executable"] build_rake_in do @@ -656,9 +695,10 @@ load 'my_exec' @installer.install end end - - assert File.exist?(File.join(install_dir, 'lib', 'code.rb')) - assert File.exist?(File.join(@userhome, '.gem', 'bin', 'executable')) + + assert File.exist?(File.join(Gem.user_dir, 'gems', + @spec.full_name, 'lib', 'code.rb')) + assert File.exist?(File.join(Gem.user_dir, 'bin', 'executable')) ensure File.chmod 0755, Gem.dir File.chmod 0755, util_inst_bindir @@ -676,13 +716,13 @@ load 'my_exec' @installer.install end end - - assert File.exist?(File.join(@userhome, '.gem', 'bin', 'executable')) + + assert File.exist?(File.join(Gem.user_dir, 'bin', 'executable')) ensure File.chmod 0755, util_inst_bindir end end - + def test_install_with_message @spec.post_install_message = 'I am a shiny gem!' diff --git a/test/rubygems/test_gem_local_remote_options.rb b/test/rubygems/test_gem_local_remote_options.rb index e676c94f21..5f2a1d3d86 100644 --- a/test/rubygems/test_gem_local_remote_options.rb +++ b/test/rubygems/test_gem_local_remote_options.rb @@ -19,6 +19,18 @@ class TestGemLocalRemoteOptions < RubyGemTestCase assert @cmd.handles?(args) end + def test_both_eh + assert_equal false, @cmd.both? + + @cmd.options[:domain] = :local + + assert_equal false, @cmd.both? + + @cmd.options[:domain] = :both + + assert_equal true, @cmd.both? + end + def test_local_eh assert_equal false, @cmd.local? diff --git a/test/rubygems/test_gem_package_tar_header.rb b/test/rubygems/test_gem_package_tar_header.rb index 9158061cf6..9b7708dca8 100644 --- a/test/rubygems/test_gem_package_tar_header.rb +++ b/test/rubygems/test_gem_package_tar_header.rb @@ -62,19 +62,19 @@ class TestGemPackageTarHeader < TarTestCase end def test_initialize_bad - assert_raise ArgumentError do + assert_raises ArgumentError do Gem::Package::TarHeader.new :name => '', :size => '', :mode => '' end - assert_raise ArgumentError do + assert_raises ArgumentError do Gem::Package::TarHeader.new :name => '', :size => '', :prefix => '' end - assert_raise ArgumentError do + assert_raises ArgumentError do Gem::Package::TarHeader.new :name => '', :prefix => '', :mode => '' end - assert_raise ArgumentError do + assert_raises ArgumentError do Gem::Package::TarHeader.new :prefix => '', :size => '', :mode => '' end end diff --git a/test/rubygems/test_gem_platform.rb b/test/rubygems/test_gem_platform.rb index 648c2a9f36..362a366266 100644 --- a/test/rubygems/test_gem_platform.rb +++ b/test/rubygems/test_gem_platform.rb @@ -5,19 +5,6 @@ require 'rbconfig' class TestGemPlatform < RubyGemTestCase - def test_self_const_missing - consts = [:DARWIN, :LINUX_586, :MSWIN32, :PPC_DARWIN, :WIN32, :X86_LINUX] - - consts.each do |const| - e = assert_raise NameError do - Gem::Platform.const_missing const - end - - assert_equal "#{const} has been removed, use CURRENT instead", - e.message - end - end - def test_self_local util_set_arch 'i686-darwin8.10.1' @@ -105,6 +92,12 @@ class TestGemPlatform < RubyGemTestCase platform = Gem::Platform.new 'i386-mswin32-80' assert_equal expected, platform.to_a, 'i386-mswin32-80' + + expected = ['x86', 'solaris', '2.10'] + + platform = Gem::Platform.new 'i386-solaris-2.10' + + assert_equal expected, platform.to_a, 'i386-solaris-2.10' end def test_initialize_mswin32_vc6 diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb index 484104dd8f..103cb38898 100644 --- a/test/rubygems/test_gem_remote_fetcher.rb +++ b/test/rubygems/test_gem_remote_fetcher.rb @@ -268,15 +268,15 @@ gems: ensure File.chmod 0755, File.join(@gemhome, 'cache') end - + def test_download_read_only File.chmod 0555, File.join(@gemhome, 'cache') File.chmod 0555, File.join(@gemhome) fetcher = util_fuck_with_fetcher File.read(@a1_gem) fetcher.download(@a1, 'http://gems.example.com') - assert File.exist?(File.join(@userhome, '.gem', - 'cache', "#{@a1.full_name}.gem")) + assert File.exist?(File.join(Gem.user_dir, 'cache', + "#{@a1.full_name}.gem")) ensure File.chmod 0755, File.join(@gemhome) File.chmod 0755, File.join(@gemhome, 'cache') @@ -391,6 +391,16 @@ gems: assert_equal 'foo', fetcher.fetch_path(@uri + 'foo.gz') end + def test_fetch_path_gzip_unmodified + fetcher = Gem::RemoteFetcher.new nil + + def fetcher.open_uri_or_path(uri, mtime, head = nil) + nil + end + + assert_equal nil, fetcher.fetch_path(@uri + 'foo.gz', Time.at(0)) + end + def test_fetch_path_io_error fetcher = Gem::RemoteFetcher.new nil @@ -441,10 +451,10 @@ gems: fetcher = Gem::RemoteFetcher.new nil def fetcher.open_uri_or_path(uri, mtime, head = nil) - '' + nil end - assert_equal '', fetcher.fetch_path(URI.parse(@gem_repo), Time.at(0)) + assert_equal nil, fetcher.fetch_path(URI.parse(@gem_repo), Time.at(0)) end def test_get_proxy_from_env_empty @@ -504,7 +514,7 @@ gems: def conn.request(req) unless defined? @requested then @requested = true - res = Net::HTTPRedirection.new nil, 301, nil + res = Net::HTTPMovedPermanently.new nil, 301, nil res.add_field 'Location', 'http://gems.example.com/real_path' res else @@ -528,7 +538,7 @@ gems: conn = Object.new def conn.started?() true end def conn.request(req) - res = Net::HTTPRedirection.new nil, 301, nil + res = Net::HTTPMovedPermanently.new nil, 301, nil res.add_field 'Location', 'http://gems.example.com/redirect' res end diff --git a/test/rubygems/test_gem_source_index.rb b/test/rubygems/test_gem_source_index.rb index 263826759a..7f379ef19a 100644 --- a/test/rubygems/test_gem_source_index.rb +++ b/test/rubygems/test_gem_source_index.rb @@ -64,6 +64,54 @@ class TestGemSourceIndex < RubyGemTestCase assert_equal a1.author, spec.author end + def test_self_load_specification_utf_8 + spec_dir = File.join @gemhome, 'specifications' + + FileUtils.rm_r spec_dir + + FileUtils.mkdir_p spec_dir + + spec_file = File.join spec_dir, "utf-8.gemspec" + spec_data = <<-SPEC +Gem::Specification.new do |s| + s.name = %q{utf} + s.version = "8" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") + s.authors = ["\317\200"] + s.date = %q{2008-09-10} + s.description = %q{This is a test description} + s.email = %q{example@example.com} + s.has_rdoc = true + s.homepage = %q{http://example.com} + s.require_paths = ["lib"] + s.rubygems_version = %q{1.2.0} + s.summary = %q{this is a summary} + + if s.respond_to? :specification_version then + current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION + s.specification_version = 2 + + if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then + else + end + else + end +end + SPEC + + spec_data.force_encoding 'UTF-8' + + File.open spec_file, 'w' do |io| io.write spec_data end + + spec = Gem::SourceIndex.load_specification spec_file + + pi = "\317\200" + pi.force_encoding 'UTF-8' if pi.respond_to? :force_encoding + + assert_equal pi, spec.author + end if Gem.ruby_version > Gem::Version.new('1.9') + def test_self_load_specification_exception spec_dir = File.join @gemhome, 'specifications' @@ -437,6 +485,27 @@ WARNING: Invalid .gemspec format in '#{spec_file}' assert_equal [], missing end + def test_find_name + assert_equal [@a1, @a2], @source_index.find_name('a') + assert_equal [@a2], @source_index.find_name('a', '= 2') + assert_equal [], @source_index.find_name('bogusstring') + assert_equal [], @source_index.find_name('a', '= 3') + + source_index = Gem::SourceIndex.new + source_index.add_spec @a1 + source_index.add_spec @a2 + + assert_equal [@a1], source_index.find_name(@a1.name, '= 1') + + r1 = Gem::Requirement.create '= 1' + assert_equal [@a1], source_index.find_name(@a1.name, r1) + end + + def test_find_name_empty_cache + empty_source_index = Gem::SourceIndex.new({}) + assert_equal [], empty_source_index.find_name("foo") + end + def test_latest_specs p1_ruby = quick_gem 'p', '1' p1_platform = quick_gem 'p', '1' do |spec| @@ -573,28 +642,12 @@ WARNING: Invalid .gemspec format in '#{spec_file}' end def test_search - assert_equal [@a1, @a2, @a_evil9], @source_index.search('a') - assert_equal [@a2], @source_index.search('a', '= 2') + requirement = Gem::Requirement.create '= 9' + with_version = Gem::Dependency.new(/^a/, requirement) + assert_equal [@a_evil9], @source_index.search(with_version) - assert_equal [], @source_index.search('bogusstring') - assert_equal [], @source_index.search('a', '= 3') - - source_index = Gem::SourceIndex.new - source_index.add_spec @a1 - source_index.add_spec @a2 - - assert_equal [@a1], source_index.search(@a1.name, '= 1') - - r1 = Gem::Requirement.create '= 1' - assert_equal [@a1], source_index.search(@a1.name, r1) - - dep = Gem::Dependency.new @a1.name, r1 - assert_equal [@a1], source_index.search(dep) - end - - def test_search_empty_cache - empty_source_index = Gem::SourceIndex.new({}) - assert_equal [], empty_source_index.search("foo") + with_default = Gem::Dependency.new(/^a/, Gem::Requirement.default) + assert_equal [@a1, @a2, @a_evil9], @source_index.search(with_default) end def test_search_platform diff --git a/test/rubygems/test_gem_source_info_cache.rb b/test/rubygems/test_gem_source_info_cache.rb index 86866a8012..744a51c154 100644 --- a/test/rubygems/test_gem_source_info_cache.rb +++ b/test/rubygems/test_gem_source_info_cache.rb @@ -224,7 +224,8 @@ class TestGemSourceInfoCache < RubyGemTestCase @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 } + beginning_with_a = Gem::Dependency.new(/^a/, Gem::Requirement.default) + gems = latest[@gem_repo].source_index.search(beginning_with_a).map { |s| s.full_name } assert_equal %w[a-2 a_evil-9], gems end diff --git a/test/rubygems/test_gem_spec_fetcher.rb b/test/rubygems/test_gem_spec_fetcher.rb index 2e9ce1b7eb..8f3ab77bb8 100644 --- a/test/rubygems/test_gem_spec_fetcher.rb +++ b/test/rubygems/test_gem_spec_fetcher.rb @@ -67,7 +67,7 @@ class TestGemSpecFetcher < RubyGemTestCase end def test_fetch_legacy_repo - @fetcher.data["#{@gem_repo}specs.#{Gem.marshal_version}.gz"] = nil + @fetcher.data.delete "#{@gem_repo}specs.#{Gem.marshal_version}.gz" @fetcher.data["#{@gem_repo}yaml"] = '' util_setup_source_info_cache @a1, @a2 @@ -259,6 +259,16 @@ RubyGems will revert to legacy indexes degrading performance. assert_equal specs, cached_specs end + def test_list_latest_all + specs = @sf.list false + + assert_equal [@latest_specs], specs.values + + specs = @sf.list true + + assert_equal [@specs], specs.values, 'specs file not loaded' + end + def test_load_specs specs = @sf.load_specs @uri, 'specs' @@ -280,7 +290,7 @@ RubyGems will revert to legacy indexes degrading performance. end def test_load_specs_cached - @fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz"] = '' + @fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz"] = nil @fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}"] = ' ' * Marshal.dump(@latest_specs).length diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb index 57c3fdc158..9c3b585c91 100644 --- a/test/rubygems/test_gem_specification.rb +++ b/test/rubygems/test_gem_specification.rb @@ -500,7 +500,7 @@ end end def test_has_rdoc_eh - assert_equal true, @a1.has_rdoc? + assert @a1.has_rdoc? end def test_hash @@ -634,7 +634,10 @@ end ruby_code = @a2.to_ruby - expected = "Gem::Specification.new do |s| + expected = <<-SPEC +# -*- encoding: utf-8 -*- + +Gem::Specification.new do |s| s.name = %q{a} s.version = \"2\" @@ -654,7 +657,7 @@ end current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION s.specification_version = #{Gem::Specification::CURRENT_SPECIFICATION_VERSION} - if current_version >= 3 then + if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then s.add_runtime_dependency(%q, [\"= 1\"]) else s.add_dependency(%q, [\"= 1\"]) @@ -663,7 +666,7 @@ end s.add_dependency(%q, [\"= 1\"]) end end -" + SPEC assert_equal expected, ruby_code @@ -679,7 +682,10 @@ end local = Gem::Platform.local expected_platform = "[#{local.cpu.inspect}, #{local.os.inspect}, #{local.version.inspect}]" - expected = "Gem::Specification.new do |s| + expected = <<-SPEC +# -*- encoding: utf-8 -*- + +Gem::Specification.new do |s| s.name = %q{a} s.version = \"1\" s.platform = Gem::Platform.new(#{expected_platform}) @@ -706,7 +712,7 @@ end current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION s.specification_version = 2 - if current_version >= 3 then + if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then s.add_runtime_dependency(%q, [\"> 0.4\"]) s.add_runtime_dependency(%q, [\"> 0.0.0\"]) s.add_runtime_dependency(%q, [\"> 0.4\", \"<= 0.6\"]) @@ -721,7 +727,7 @@ end s.add_dependency(%q, [\"> 0.4\", \"<= 0.6\"]) end end -" + SPEC assert_equal expected, ruby_code diff --git a/test/rubygems/test_gem_stream_ui.rb b/test/rubygems/test_gem_stream_ui.rb index caf620ff00..a8564ba94f 100644 --- a/test/rubygems/test_gem_stream_ui.rb +++ b/test/rubygems/test_gem_stream_ui.rb @@ -63,7 +63,7 @@ class TestGemStreamUI < RubyGemTestCase @in.tty = false timeout(0.1) do - assert_raise(Gem::OperationNotSupportedError) do + assert_raises(Gem::OperationNotSupportedError) do @sui.ask_yes_no("do coconuts migrate?") end end diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb index 7d7890c3ea..bb90f5cc5f 100644 --- a/test/rubygems/test_gem_uninstaller.rb +++ b/test/rubygems/test_gem_uninstaller.rb @@ -62,5 +62,26 @@ class TestGemUninstaller < GemInstallerTestCase assert_equal true, uninstaller.path_ok?(@spec) end + def test_uninstall + uninstaller = Gem::Uninstaller.new @spec.name, :executables => true + + gem_dir = File.join @gemhome, 'gems', @spec.full_name + + Gem.pre_uninstall do + assert File.exist?(gem_dir), 'gem_dir should exist' + end + + Gem.post_uninstall do + assert !File.exist?(gem_dir), 'gem_dir should not exist' + end + + uninstaller.uninstall + + assert !File.exist?(gem_dir) + + assert_same uninstaller, @pre_uninstall_hook_arg + assert_same uninstaller, @post_uninstall_hook_arg + end + end diff --git a/test/rubygems/test_gem_version.rb b/test/rubygems/test_gem_version.rb index 5c04502d48..8d10700490 100644 --- a/test/rubygems/test_gem_version.rb +++ b/test/rubygems/test_gem_version.rb @@ -107,7 +107,7 @@ class TestGemVersion < RubyGemTestCase def test_illformed_requirements [ ">>> 1.3.5", "> blah" ].each do |rq| - assert_raise(ArgumentError, "req [#{rq}] should fail") { + assert_raises(ArgumentError, "req [#{rq}] should fail") { Gem::Version::Requirement.new(rq) } end