From d478c7a7342478847cc1148f4134b5f0db04e1d9 Mon Sep 17 00:00:00 2001 From: drbrain Date: Thu, 25 Sep 2008 10:13:50 +0000 Subject: Update to RubyGems 1.3.0 r1891 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@19547 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- 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 | 23 +- lib/rubygems/commands/rdoc_command.rb | 6 +- lib/rubygems/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 | 53 +- lib/rubygems/dependency_installer.rb | 34 +- lib/rubygems/doc_manager.rb | 277 +++++---- 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 | 795 ++++++++++++++++--------- lib/rubygems/test_utilities.rb | 20 +- lib/rubygems/uninstaller.rb | 86 ++- 30 files changed, 1101 insertions(+), 596 deletions(-) (limited to 'lib/rubygems') 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 + dep = Gem::Dependency.new name, Gem::Requirement.default + if local? then - say - say "*** LOCAL GEMS ***" - say + if ui.outs.tty? or both? then + say + say "*** LOCAL GEMS ***" + say + end - specs = Gem.source_index.search name + 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! + + update_gems = Gem.source_index.search 'rubygems-update' - latest_ruby_gem = latest_ruby_gem.sort_by { |s| s.version }.last + latest_update_gem = update_gems.sort_by { |s| s.version }.last - say "Updating version of RubyGems to #{latest_ruby_gem.version}" - installed = do_rubygems_update latest_ruby_gem.version + 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. + ## + # 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 + + ## + # Default gem load path + def self.default_path - [File.join(ENV['HOME'], '.gem'), default_dir] + [user_dir, default_dir] end - # Deduce Ruby's --program-prefix and --program-suffix from its install name. + ## + # 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 - # Is the RDoc documentation installed? - def rdoc_installed? - return File.exist?(File.join(@doc_dir, "rdoc")) + def self.load_rdoc + begin + gem 'rdoc' + rescue Gem::LoadError + # use built-in 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 + begin + require 'rdoc/rdoc' + rescue LoadError => e + raise Gem::DocumentError, + "ERROR: RDoc documentation generator not installed!" + 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 - FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir) + 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 - # 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 - FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_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). + + def generate_rdoc + if @spec.has_rdoc then + setup_rdoc + install_rdoc 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) + end - FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir) + ## + # Generate and install RDoc into the documentation directory - begin - gem 'rdoc' - rescue Gem::LoadError - # use built-in RDoc - end + def install_rdoc + rdoc_dir = File.join @doc_dir, 'rdoc' - begin - require 'rdoc/rdoc' - rescue LoadError => e - raise Gem::DocumentError, - "ERROR: RDoc documentation generator not installed!" - end - end + FileUtils.rm_rf rdoc_dir - def install_rdoc - rdoc_dir = File.join @doc_dir, 'rdoc' + say "Installing RDoc documentation for #{@spec.full_name}..." + run_rdoc '--op', rdoc_dir + end - FileUtils.rm_rf rdoc_dir + ## + # Generate and install RI into the documentation directory - say "Installing RDoc documentation for #{@spec.full_name}..." - run_rdoc '--op', rdoc_dir - end + def install_ri + ri_dir = File.join @doc_dir, 'ri' - def install_ri - ri_dir = File.join @doc_dir, 'ri' + FileUtils.rm_rf ri_dir - FileUtils.rm_rf ri_dir + say "Installing ri documentation for #{@spec.full_name}..." + run_rdoc '--ri', '--op', ri_dir + end - say "Installing ri documentation for #{@spec.full_name}..." - run_rdoc '--ri', '--op', ri_dir + ## + # 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 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 - - 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 + def setup_rdoc + if File.exist?(@doc_dir) && !File.writable?(@doc_dir) then + raise Gem::FilePermissionError.new(@doc_dir) end - def uninstall_doc - raise Gem::FilePermissionError.new(@spec.installation_path) unless - File.writable? @spec.installation_path + FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir) - original_name = [ - @spec.name, @spec.version, @spec.original_platform].join '-' + self.class.load_rdoc + end + + ## + # Remove RDoc and RI documentation + + 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 '-' 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,189 +351,45 @@ 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) -------------- - - overwrite_accessor :version= do |version| - @version = Version.create(version) - end - - overwrite_accessor :platform do - @new_platform - end - - overwrite_accessor :platform= do |platform| - if @original_platform.nil? or - @original_platform == Gem::Platform::RUBY then - @original_platform = platform - end - - case platform - when Gem::Platform::CURRENT then - @new_platform = Gem::Platform.local - @original_platform = @new_platform.to_s - - when Gem::Platform then - @new_platform = platform - - # legacy constants - when nil, Gem::Platform::RUBY then - @new_platform = Gem::Platform::RUBY - when 'mswin32' then # was Gem::Platform::WIN32 - @new_platform = Gem::Platform.new 'x86-mswin32' - when 'i586-linux' then # was Gem::Platform::LINUX_586 - @new_platform = Gem::Platform.new 'x86-linux' - when 'powerpc-darwin' then # was Gem::Platform::DARWIN - @new_platform = Gem::Platform.new 'ppc-darwin' - else - @new_platform = Gem::Platform.new platform - end - - @platform = @new_platform.to_s - - @new_platform - end - - overwrite_accessor :required_ruby_version= do |value| - @required_ruby_version = Gem::Requirement.create(value) - end - - overwrite_accessor :required_rubygems_version= do |value| - @required_rubygems_version = Gem::Requirement.create(value) - end - - overwrite_accessor :date= do |date| - # We want to end up with a Time object with one-day resolution. - # This is the cleanest, most-readable, faster-than-using-Date - # way to do it. - case date - when String then - @date = if /\A(\d{4})-(\d{2})-(\d{2})\Z/ =~ date then - Time.local($1.to_i, $2.to_i, $3.to_i) - else - require 'time' - Time.parse date - end - when Time then - @date = Time.local(date.year, date.month, date.day) - when Date then - @date = Time.local(date.year, date.month, date.day) - else - @date = TODAY - end - end - - overwrite_accessor :date do - self.date = nil if @date.nil? # HACK Sets the default value for date - @date - end - - overwrite_accessor :summary= do |str| - @summary = if str then - str.strip. - gsub(/(\w-)\n[ \t]*(\w)/, '\1\2'). - gsub(/\n[ \t]*/, " ") - end - end - overwrite_accessor :description= do |str| - @description = if str then - str.strip. - gsub(/(\w-)\n[ \t]*(\w)/, '\1\2'). - gsub(/\n[ \t]*/, " ") - end - end + attr_accessor :loaded_from - overwrite_accessor :default_executable do - begin - if defined?(@default_executable) and @default_executable - result = @default_executable - elsif @executables and @executables.size == 1 - result = Array(@executables).first - else - result = nil - end - result - rescue - nil - end - end + ## + # Returns an array with bindir attached to each executable in the + # executables list def add_bindir(executables) return nil if executables.nil? @@ -516,17 +403,9 @@ module Gem 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| @@ -535,34 +414,25 @@ module Gem 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 - # YAML. - if defined? @test_suite_file then - @test_files = [@test_suite_file].flatten - @test_suite_file = nil - end - if defined?(@test_files) and @test_files then - @test_files - else - @test_files = [] - 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 - # 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 --------------------------------------------------- + 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 @@ -575,11 +445,13 @@ module Gem @@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. + ## + # 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 @@ -598,13 +470,14 @@ module Gem 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. + ## + # 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 @@ -627,6 +500,9 @@ module Gem spec end + ## + # Loads ruby format gemspec from +filename+ + def self.load(filename) gemspec = nil fail "NESTED Specification.load calls not allowed!" if @@gather @@ -638,22 +514,25 @@ module Gem @@gather = nil end - # Make sure the yaml specification is properly formatted with dashes. + ## + # 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. - # + ## + # Sets the rubygems_version to the current RubyGems version + def mark_version @rubygems_version = RubyGemsVersion end - # Ignore unknown attributes if the + ## + # Ignore unknown attributes while loading + def method_missing(sym, *a, &b) # :nodoc: if @specification_version > CURRENT_SPECIFICATION_VERSION and sym.to_s =~ /=$/ then @@ -663,35 +542,39 @@ module Gem end end - # Adds a development dependency to this Gem. For example, - # - # spec.add_development_dependency('jabber4r', '> 0.1', '<= 0.5') + ## + # Adds a development dependency named +gem+ with +requirements+ to this + # Gem. For example: # - # Development dependencies aren't installed by default, and - # aren't activated when a gem is required. + # spec.add_development_dependency 'jabber4r', '> 0.1', '<= 0.5' # - # gem:: [String or Gem::Dependency] The Gem name/dependency. - # requirements:: [default=">= 0"] The version requirements. + # 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 to this Gem. For example, - # - # spec.add_runtime_dependency('jabber4r', '> 0.1', '<= 0.5') + ## + # Adds a runtime dependency named +gem+ with +requirements+ to this Gem. + # For example: # - # gem:: [String or Gem::Dependency] The Gem name/dependency. - # requirements:: [default=">= 0"] The version requirements. + # 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). - # + # 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}" @@ -700,9 +583,10 @@ module Gem end end + ## # Returns the full name (name-version) of this gemspec using the original - # platform. - # + # platform. For use with legacy gems. + def original_name # :nodoc: if platform == Gem::Platform::RUBY or platform.nil? then "#{@name}-#{@version}" @@ -736,18 +620,16 @@ module Gem 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 - # + ## + # Checks if this specification meets the requirement of +dependency+. + def satisfies_requirement?(dependency) return @name == dependency.name && dependency.version_requirements.satisfied_by?(@version) end - # Comparison methods --------------------------------------------- + ## + # 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] @@ -757,19 +639,25 @@ module Gem 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: @@ -779,8 +667,6 @@ module Gem } end - # Export methods (YAML and Ruby code) ---------------------------- - def to_yaml(opts = {}) # :nodoc: mark_version @@ -825,6 +711,8 @@ module Gem def to_ruby mark_version result = [] + result << "# -*- encoding: utf-8 -*-" + result << nil result << "Gem::Specification.new do |s|" result << " s.name = #{ruby_code name}" @@ -861,7 +749,7 @@ module Gem result << " s.specification_version = #{specification_version}" result << nil - result << " if current_version >= 3 then" + result << " if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then" unless dependencies.empty? then dependencies.each do |dep| @@ -895,16 +783,15 @@ module Gem result.join "\n" end - # Validation and normalization methods --------------------------- - - # Checks that the specification contains all required fields, and - # does a very basic sanity check. + ## + # 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.. + # Raises InvalidSpecificationException if the spec does not pass the + # checks.. + def validate extend Gem::UserInteraction - normalize if rubygems_version != RubyGemsVersion then @@ -959,13 +846,14 @@ module Gem 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. + # * 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. + # 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! @@ -975,15 +863,12 @@ module Gem @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: + ## + # 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| @@ -1004,8 +889,6 @@ module Gem "#" end - private - def add_dependency_with_type(dependency, type, *requirements) requirements = if requirements.empty? then Gem::Requirement.default @@ -1022,6 +905,8 @@ module Gem 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 @@ -1030,8 +915,12 @@ module Gem end end - # Return a string containing a Ruby code representation of the - # given object. + 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 + '}' @@ -1046,6 +935,326 @@ module Gem 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 + + overwrite_accessor :platform do + @new_platform + end + + overwrite_accessor :platform= do |platform| + if @original_platform.nil? or + @original_platform == Gem::Platform::RUBY then + @original_platform = platform + end + + case platform + when Gem::Platform::CURRENT then + @new_platform = Gem::Platform.local + @original_platform = @new_platform.to_s + + when Gem::Platform then + @new_platform = platform + + # legacy constants + when nil, Gem::Platform::RUBY then + @new_platform = Gem::Platform::RUBY + when 'mswin32' then # was Gem::Platform::WIN32 + @new_platform = Gem::Platform.new 'x86-mswin32' + when 'i586-linux' then # was Gem::Platform::LINUX_586 + @new_platform = Gem::Platform.new 'x86-linux' + when 'powerpc-darwin' then # was Gem::Platform::DARWIN + @new_platform = Gem::Platform.new 'ppc-darwin' + else + @new_platform = Gem::Platform.new platform + end + + @platform = @new_platform.to_s + + @new_platform + end + + overwrite_accessor :required_ruby_version= do |value| + @required_ruby_version = Gem::Requirement.create(value) + end + + overwrite_accessor :required_rubygems_version= do |value| + @required_rubygems_version = Gem::Requirement.create(value) + end + + overwrite_accessor :date= do |date| + # We want to end up with a Time object with one-day resolution. + # This is the cleanest, most-readable, faster-than-using-Date + # way to do it. + case date + when String then + @date = if /\A(\d{4})-(\d{2})-(\d{2})\Z/ =~ date then + Time.local($1.to_i, $2.to_i, $3.to_i) + else + require 'time' + Time.parse date + end + when Time then + @date = Time.local(date.year, date.month, date.day) + when Date then + @date = Time.local(date.year, date.month, date.day) + else + @date = TODAY + end + end + + overwrite_accessor :date do + self.date = nil if @date.nil? # HACK Sets the default value for date + @date + end + + overwrite_accessor :summary= do |str| + @summary = if str then + str.strip. + gsub(/(\w-)\n[ \t]*(\w)/, '\1\2'). + gsub(/\n[ \t]*/, " ") + end + end + + overwrite_accessor :description= do |str| + @description = if str then + str.strip. + gsub(/(\w-)\n[ \t]*(\w)/, '\1\2'). + gsub(/\n[ \t]*/, " ") + end + end + + overwrite_accessor :default_executable do + begin + if defined?(@default_executable) and @default_executable + result = @default_executable + elsif @executables and @executables.size == 1 + result = Array(@executables).first + else + result = nil + end + result + rescue + nil + 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 + # YAML. + if defined? @test_suite_file then + @test_files = [@test_suite_file].flatten + @test_suite_file = nil + end + if defined?(@test_files) and @test_files then + @test_files + else + @test_files = [] + end + 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 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) + 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 -- cgit v1.2.3