diff options
38 files changed, 1980 insertions, 698 deletions
@@ -67,11 +67,14 @@ module Gem :RUBY_SO_NAME => RbConfig::CONFIG["RUBY_SO_NAME"], :arch => RbConfig::CONFIG["arch"], :bindir => RbConfig::CONFIG["bindir"], :libdir => RbConfig::CONFIG["libdir"], :ruby_install_name => RbConfig::CONFIG["ruby_install_name"], :ruby_version => RbConfig::CONFIG["ruby_version"], :sitedir => RbConfig::CONFIG["sitedir"], - :sitelibdir => RbConfig::CONFIG["sitelibdir"] ) DIRECTORIES = %w[cache doc gems specifications] unless defined?(DIRECTORIES) @@ -137,7 +140,7 @@ module Gem unless matches.any? { |spec| spec.version == existing_spec.version } then raise Gem::Exception, - "can't activate #{gem}, already activated #{existing_spec.full_name}]" end return false @@ -151,7 +154,7 @@ module Gem @loaded_specs[spec.name] = spec # Load dependent gems first - spec.dependencies.each do |dep_gem| activate dep_gem end @@ -204,6 +207,19 @@ module Gem private_class_method :all_partials ## # The mode needed to read a file as straight binary. def self.binary_mode @@ -268,6 +284,13 @@ module Gem end ## # The path where gems are to be installed. def self.dir @@ -346,6 +369,33 @@ module Gem private_class_method :find_home ## # Return a list of all possible load paths for the latest version for all # gems in the Gem installation. @@ -438,7 +488,11 @@ module Gem @gem_path ||= nil unless @gem_path then - paths = [ENV['GEM_PATH']] || [default_path] if defined?(APPLE_GEM_HOME) and not ENV['GEM_PATH'] then paths << APPLE_GEM_HOME @@ -459,7 +513,7 @@ module Gem ## # Array of platforms this RubyGems supports. - def self.platforms @platforms ||= [] if @platforms.empty? @@ -586,13 +640,13 @@ module Gem def self.set_paths(gpaths) if gpaths @gem_path = gpaths.split(File::PATH_SEPARATOR) - if File::ALT_SEPARATOR then @gem_path.map! do |path| path.gsub File::ALT_SEPARATOR, File::SEPARATOR end end - @gem_path << Gem.dir else @gem_path = [Gem.dir] @@ -683,24 +737,25 @@ module Gem end -end -# Modify the non-gem version of datadir to handle gem package names. -require 'rbconfig/datadir' -module Config # :nodoc: class << self - alias gem_original_datadir datadir - # Return the path to the data directory associated with the named # package. If the package is loaded as a gem, return the gem # specific data directory. Otherwise return a path to the share # area as define by "#{ConfigMap[:datadir]}/#{package_name}". def datadir(package_name) - Gem.datadir(package_name) || Config.gem_original_datadir(package_name) end end end require 'rubygems/exceptions' @@ -712,6 +767,18 @@ require 'rubygems/source_index' # Needed for Kernel#gem require 'rubygems/platform' require 'rubygems/builder' # HACK: Needed for rake's package task. if RUBY_VERSION < '1.9' then require 'rubygems/custom_require' end @@ -46,6 +46,7 @@ module Gem register_command :server register_command :sources register_command :specification register_command :uninstall register_command :unpack register_command :update @@ -46,37 +46,67 @@ class Gem::Commands::DependencyCommand < Gem::Command options[:args] << '.' if options[:args].empty? specs = {} - source_indexes = [] - if local? then - source_indexes << Gem::SourceIndex.from_installed_gems end - if remote? then - Gem::SourceInfoCache.cache_data.map do |_, sice| - source_indexes << sice.source_index end end - options[:args].each do |name| - new_specs = nil - source_indexes.each do |source_index| - new_specs = find_gems(name, source_index) end - say "No match found for #{name} (#{options[:version]})" if - new_specs.empty? - specs = specs.merge new_specs end - terminate_interaction 1 if specs.empty? reverse = Hash.new { |h, k| h[k] = [] } if options[:reverse_dependencies] then - specs.values.each do |source_index, spec| - reverse[spec.full_name] = find_reverse_dependencies spec, source_index end end @@ -118,10 +148,10 @@ class Gem::Commands::DependencyCommand < Gem::Command end # Retuns list of [specification, dep] that are satisfied by spec. - def find_reverse_dependencies(spec, source_index) result = [] - source_index.each do |name, sp| sp.dependencies.each do |dep| dep = Gem::Dependency.new(*dep) unless Gem::Dependency === dep @@ -146,5 +176,6 @@ class Gem::Commands::DependencyCommand < Gem::Command specs end end @@ -51,6 +51,8 @@ class Gem::Commands::EnvironmentCommand < Gem::Command out << " - RUBY EXECUTABLE: #{Gem.ruby}\n" out << " - RUBYGEMS PLATFORMS:\n" Gem.platforms.each do |platform| out << " - #{platform}\n" @@ -33,12 +33,14 @@ class Gem::Commands::FetchCommand < Gem::Command def execute version = options[:version] || Gem::Requirement.default gem_names = get_all_gem_names gem_names.each do |gem_name| dep = Gem::Dependency.new gem_name, version - specs_and_sources = Gem::SourceInfoCache.search_with_source dep, true specs_and_sources.sort_by { |spec,| spec.version } @@ -16,7 +16,6 @@ class Gem::Commands::InstallCommand < Gem::Command defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({ :generate_rdoc => true, :generate_ri => true, - :install_dir => Gem.dir, :format_executable => false, :test => false, :version => Gem::Requirement.default, @@ -62,7 +61,8 @@ class Gem::Commands::InstallCommand < Gem::Command :install_dir => options[:install_dir], :security_policy => options[:security_policy], :wrappers => options[:wrappers], - :bin_dir => options[:bin_dir] } exit_code = 0 @@ -1,33 +1,35 @@ require 'rubygems/command' require 'rubygems/commands/query_command' -module Gem - module Commands - class ListCommand < QueryCommand - - def initialize - super 'list', 'Display gems whose name starts with STRING' - - remove_option('--name-matches') - end - - def arguments # :nodoc: - "STRING start of gem name to look for" - end - - def defaults_str # :nodoc: - "--local --no-details" - end - - def usage # :nodoc: - "#{program_name} [STRING]" - end - - def execute - string = get_one_optional_argument || '' - options[:name] = /^#{string}/i - super - end - end end end @@ -80,7 +80,7 @@ lock it down to the exact version. say "gem '#{spec.name}', '= #{spec.version}'" unless locked[spec.name] locked[spec.name] = true - spec.dependencies.each do |dep| next if locked[dep.name] candidates = Gem.source_index.search dep.name, dep.requirement_list @@ -1,6 +1,6 @@ require 'rubygems/command' require 'rubygems/local_remote_options' -require 'rubygems/source_info_cache' require 'rubygems/version_option' class Gem::Commands::OutdatedCommand < Gem::Command @@ -20,8 +20,11 @@ class Gem::Commands::OutdatedCommand < Gem::Command locals.outdated.sort.each do |name| local = locals.search(/^#{name}$/).last - remotes = Gem::SourceInfoCache.search_with_source(/^#{name}$/, true) remote = remotes.last.first say "#{local.name} (#{local.version} < #{remote.version})" end end @@ -82,51 +82,10 @@ revert the gem. end # TODO use installer options - installer = Gem::Installer.new gem, :wrappers => true - gem_file = File.join install_dir, "cache", "#{spec.full_name}.gem" - - security_policy = nil # TODO use installer option - - format = Gem::Format.from_file_by_path gem_file, security_policy - - target_directory = File.join(install_dir, "gems", format.spec.full_name) - target_directory.untaint - - pristine_files = format.file_entries.collect { |data| data[0]["path"] } - file_map = {} - - format.file_entries.each do |entry, file_data| - file_map[entry["path"]] = file_data - end - - Dir.chdir target_directory do - deployed_files = Dir.glob(File.join("**", "*")) + - Dir.glob(File.join("**", ".*")) - - pristine_files = pristine_files.map { |f| File.expand_path f } - deployed_files = deployed_files.map { |f| File.expand_path f } - - to_redeploy = (pristine_files - deployed_files) - to_redeploy = to_redeploy.map { |path| path.untaint} - - if to_redeploy.length > 0 then - say "Restoring #{to_redeploy.length} file#{to_redeploy.length == 1 ? "" : "s"} to #{spec.full_name}..." - - to_redeploy.each do |path| - say " #{path}" - FileUtils.mkdir_p File.dirname(path) - File.open(path, "wb") do |out| - out.write file_map[path] - end - end - else - say "#{spec.full_name} is in pristine condition" - end - end - - installer.generate_bin - installer.build_extensions end end @@ -1,6 +1,6 @@ require 'rubygems/command' require 'rubygems/local_remote_options' -require 'rubygems/source_info_cache' require 'rubygems/version_option' class Gem::Commands::QueryCommand < Gem::Command @@ -74,7 +74,13 @@ class Gem::Commands::QueryCommand < Gem::Command say "*** LOCAL GEMS ***" say - output_query_results Gem.source_index.search(name) end if remote? then @@ -84,13 +90,26 @@ class Gem::Commands::QueryCommand < Gem::Command all = options[:all] begin - Gem::SourceInfoCache.cache all - rescue Gem::RemoteFetcher::FetchError - # no network end - output_query_results Gem::SourceInfoCache.search(name, false, all) end end @@ -104,28 +123,30 @@ class Gem::Commands::QueryCommand < Gem::Command !Gem.source_index.search(dep).empty? end - def output_query_results(gemspecs) output = [] - gem_list_with_version = {} - gemspecs.flatten.each do |gemspec| - gem_list_with_version[gemspec.name] ||= [] - gem_list_with_version[gemspec.name] << gemspec end - gem_list_with_version = gem_list_with_version.sort_by do |name, spec| name.downcase end - gem_list_with_version.each do |gem_name, list_of_matching| - list_of_matching = list_of_matching.sort_by { |x| x.version.to_ints }.reverse - seen_versions = {} - list_of_matching.delete_if do |item| - if seen_versions[item.version] then true else - seen_versions[item.version] = true false end end @@ -133,12 +154,50 @@ class Gem::Commands::QueryCommand < Gem::Command entry = gem_name.dup if options[:versions] then - versions = list_of_matching.map { |s| s.version }.uniq entry << " (#{versions.join ', '})" end - entry << "\n" << format_text(list_of_matching[0].summary, 68, 4) if - options[:details] output << entry end @@ -1,7 +1,8 @@ require 'rubygems/command' require 'rubygems/remote_fetcher' require 'rubygems/source_info_cache' -require 'rubygems/source_info_cache_entry' class Gem::Commands::SourcesCommand < Gem::Command @@ -21,14 +22,14 @@ class Gem::Commands::SourcesCommand < Gem::Command options[:remove] = value end - add_option '-u', '--update', 'Update source cache' do |value, options| - options[:update] = value - end - add_option '-c', '--clear-all', 'Remove all sources (clear the cache)' do |value, options| options[:clear_all] = value end end def defaults_str @@ -36,9 +37,23 @@ class Gem::Commands::SourcesCommand < Gem::Command end def execute - options[:list] = !(options[:add] || options[:remove] || options[:clear_all] || options[:update]) if options[:clear_all] then sic = Gem::SourceInfoCache remove_cache_file 'user', sic.user_cache_file remove_cache_file 'latest user', sic.latest_user_cache_file @@ -48,15 +63,10 @@ class Gem::Commands::SourcesCommand < Gem::Command if options[:add] then source_uri = options[:add] - sice = Gem::SourceInfoCacheEntry.new nil, nil begin - sice.refresh source_uri, true - - Gem::SourceInfoCache.cache_data[source_uri] = sice - Gem::SourceInfoCache.cache.update - Gem::SourceInfoCache.cache.flush - Gem.sources << source_uri Gem.configuration.write @@ -64,15 +74,24 @@ class Gem::Commands::SourcesCommand < Gem::Command rescue URI::Error, ArgumentError say "#{source_uri} is not a URI" rescue Gem::RemoteFetcher::FetchError => e - say "Error fetching #{source_uri}:\n\t#{e.message}" - end - end - if options[:update] then - Gem::SourceInfoCache.cache true - Gem::SourceInfoCache.cache.flush - say "source cache successfully updated" end if options[:remove] then @@ -81,14 +100,6 @@ class Gem::Commands::SourcesCommand < Gem::Command unless Gem.sources.include? source_uri then say "source #{source_uri} not present in cache" else - begin # HACK figure out how to get the cache w/o update - Gem::SourceInfoCache.cache - rescue Gem::RemoteFetcher::FetchError - end - - Gem::SourceInfoCache.cache_data.delete source_uri - Gem::SourceInfoCache.cache.update - Gem::SourceInfoCache.cache.flush Gem.sources.delete source_uri Gem.configuration.write @@ -96,6 +107,23 @@ class Gem::Commands::SourcesCommand < Gem::Command end end if options[:list] then say "*** CURRENT SOURCES ***" say @@ -52,9 +52,10 @@ class Gem::Commands::SpecificationCommand < Gem::Command end if remote? then - Gem::SourceInfoCache.cache_data.each do |_,sice| - specs.push(*sice.source_index.search(gem, options[:version])) - end end if specs.empty? then @@ -0,0 +1,27 @@ @@ -2,7 +2,7 @@ require 'rubygems/command' require 'rubygems/command_manager' require 'rubygems/install_update_options' require 'rubygems/local_remote_options' -require 'rubygems/source_info_cache' require 'rubygems/version_option' require 'rubygems/commands/install_command' @@ -15,11 +15,10 @@ class Gem::Commands::UpdateCommand < Gem::Command def initialize super 'update', 'Update the named gems (or all installed gems) in the local repository', - :generate_rdoc => true, - :generate_ri => true, - :force => false, - :test => false, - :install_dir => Gem.dir add_install_update_options @@ -60,21 +59,13 @@ class Gem::Commands::UpdateCommand < Gem::Command hig = {} # highest installed gems - Gem::SourceIndex.from_installed_gems.each do |name, spec| if hig[spec.name].nil? or hig[spec.name].version < spec.version then hig[spec.name] = spec end end - pattern = if options[:args].empty? then - // - else - Regexp.union(*options[:args]) - end - - remote_gemspecs = Gem::SourceInfoCache.search pattern - - gems_to_update = which_to_update hig, remote_gemspecs updated = [] @@ -135,20 +126,42 @@ class Gem::Commands::UpdateCommand < Gem::Command end end - def which_to_update(highest_installed_gems, remote_gemspecs) result = [] highest_installed_gems.each do |l_name, l_spec| - matching_gems = remote_gemspecs.select do |spec| - spec.name == l_name and Gem.platforms.any? do |platform| - platform == spec.platform end end - highest_remote_gem = matching_gems.sort_by { |spec| spec.version }.last if highest_remote_gem and - l_spec.version < highest_remote_gem.version then result << l_name end end @@ -18,6 +18,22 @@ class Gem::ConfigFile DEFAULT_VERBOSITY = true DEFAULT_UPDATE_SOURCES = true # List of arguments supplied to the config file object. attr_reader :args @@ -81,18 +97,8 @@ class Gem::ConfigFile @verbose = DEFAULT_VERBOSITY @update_sources = DEFAULT_UPDATE_SOURCES - begin - # HACK $SAFE ok? - @hash = open(config_file_name.dup.untaint) {|f| YAML.load(f) } - rescue ArgumentError - warn "Failed to load #{config_file_name}" - rescue Errno::ENOENT - # Ignore missing config file error. - rescue Errno::EACCES - warn "Failed to load #{config_file_name} due to permissions problem." - end - - @hash ||= {} # HACK these override command-line args, which is bad @backtrace = @hash[:backtrace] if @hash.key? :backtrace @@ -105,6 +111,16 @@ class Gem::ConfigFile handle_arguments arg_list end # True if the backtrace option has been specified, or debug is on. def backtrace @backtrace or $DEBUG @@ -26,7 +26,7 @@ module Kernel def require(path) # :nodoc: gem_original_require path rescue LoadError => load_error - if load_error.message =~ /\A[Nn]o such file to load -- #{Regexp.escape path}\z/ and spec = Gem.searcher.find(path) then Gem.activate(spec.name, "= #{spec.version}") gem_original_require path @@ -2,7 +2,7 @@ module Gem # 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 @@ -8,24 +8,54 @@ require 'rubygems' ## # The Dependency class holds a Gem name and a Gem::Requirement class Gem::Dependency attr_accessor :name attr_writer :version_requirements def <=>(other) [@name] <=> [other.name] end ## - # Constructs the dependency - # - # name:: [String] name of the Gem - # version_requirements:: [String Array] version requirement (e.g. ["> 1.2"]) - # - def initialize(name, version_requirements) @name = name @version_requirements = Gem::Requirement.create version_requirements @version_requirement = nil # Avoid warnings. end @@ -48,17 +78,41 @@ class Gem::Dependency end def to_s # :nodoc: - "#{name} (#{version_requirements})" end def ==(other) # :nodoc: self.class === other && self.name == other.name && self.version_requirements == other.version_requirements end - def hash - name.hash + version_requirements.hash end end @@ -1,9 +1,12 @@ require 'rubygems' require 'rubygems/dependency_list' require 'rubygems/installer' -require 'rubygems/source_info_cache' require 'rubygems/user_interaction' class Gem::DependencyInstaller include Gem::UserInteraction @@ -25,36 +28,50 @@ class Gem::DependencyInstaller # Creates a new installer instance. # # Options are: - # :env_shebang:: See Gem::Installer::new. # :domain:: :local, :remote, or :both. :local only searches gems in the # current directory. :remote searches only gems in Gem::sources. # :both searches both. # :force:: See Gem::Installer#install. # :format_executable:: See Gem::Installer#initialize. - # :ignore_dependencies: Don't install any dependencies. - # :install_dir: See Gem::Installer#install. - # :security_policy: See Gem::Installer::new and Gem::Security. - # :wrappers: See Gem::Installer::new def initialize(options = {}) options = DEFAULT_OPTIONS.merge options - @env_shebang = options[:env_shebang] @domain = options[:domain] @force = options[:force] @format_executable = options[:format_executable] @ignore_dependencies = options[:ignore_dependencies] - @install_dir = options[:install_dir] || Gem.dir @security_policy = options[:security_policy] @wrappers = options[:wrappers] - @bin_dir = options[:bin_dir] @installed_gems = [] end ## # Returns a list of pairs of gemspecs and source_uris that match # Gem::Dependency +dep+ from both local (Dir.pwd) and remote (Gem.sources) - # sources. Gems are sorted with newer gems preferred over older gems, and # local gems preferred over remote gems. def find_gems_with_sources(dep) gems_and_sources = [] @@ -74,8 +91,7 @@ class Gem::DependencyInstaller all = requirements.length > 1 || (requirements.first != ">=" and requirements.first != ">") - found = Gem::SourceInfoCache.search_with_source dep, true, all - gems_and_sources.push(*found) rescue Gem::RemoteFetcher::FetchError => e @@ -95,6 +111,7 @@ class Gem::DependencyInstaller ## # Gathers all dependencies necessary for the installation from local and # remote sources unless the ignore_dependencies was given. def gather_dependencies specs = @specs_and_sources.map { |spec,_| spec } @@ -110,8 +127,18 @@ class Gem::DependencyInstaller next if spec.nil? or seen[spec.name] seen[spec.name] = true - spec.dependencies.each do |dep| - results = find_gems_with_sources(dep).reverse # local gems first results.each do |dep_spec, source_uri| next if seen[dep_spec.name] @@ -126,6 +153,11 @@ class Gem::DependencyInstaller @gems_to_install = dependency_list.dependency_order.reverse end def find_spec_by_name_and_version gem_name, version = Gem::Requirement.default spec_and_source = nil @@ -160,14 +192,16 @@ class Gem::DependencyInstaller if spec_and_source.nil? then raise Gem::GemNotFoundException, - "could not find #{gem_name} locally or in a repository" end @specs_and_sources = [spec_and_source] end ## - # Installs the gem and all its dependencies. def install dep_or_name, version = Gem::Requirement.default if String === dep_or_name then find_spec_by_name_and_version dep_or_name, version @@ -175,15 +209,14 @@ class Gem::DependencyInstaller @specs_and_sources = [find_gems_with_sources(dep_or_name).last] end - gather_dependencies - spec_dir = File.join @install_dir, 'specifications' - source_index = Gem::SourceIndex.from_gems_in spec_dir @gems_to_install.each do |spec| last = spec == @gems_to_install.last # HACK is this test for full_name acceptable? - next if source_index.any? { |n,_| n == spec.full_name } and not last # TODO: make this sorta_verbose so other users can benefit from it say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose @@ -191,7 +224,7 @@ class Gem::DependencyInstaller _, source_uri = @specs_and_sources.assoc spec begin local_gem_path = Gem::RemoteFetcher.fetcher.download spec, source_uri, - @install_dir rescue Gem::RemoteFetcher::FetchError next if @force raise @@ -205,12 +238,15 @@ class Gem::DependencyInstaller :install_dir => @install_dir, :security_policy => @security_policy, :wrappers => @wrappers, - :bin_dir => @bin_dir spec = inst.install @installed_gems << spec end end end @@ -69,7 +69,7 @@ class Gem::DependencyList # Are all the dependencies in the list satisfied? def ok? @specs.all? do |spec| - spec.dependencies.all? do |dep| @specs.find { |s| s.satisfies_requirement? dep } end end @@ -9,9 +9,9 @@ require 'fileutils' module Gem class DocManager - include UserInteraction - # Create a document manager for the given gem spec. # # spec:: The Gem::Specification object representing the gem. @@ -22,12 +22,12 @@ module Gem @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? 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 @@ -102,7 +102,7 @@ module Gem args << '--quiet' args << @spec.require_paths.clone args << @spec.extra_rdoc_files - args.flatten! r = RDoc::RDoc.new @@ -1,5 +1,6 @@ require 'fileutils' require 'tmpdir' require 'rubygems' require 'rubygems/format' @@ -40,116 +41,303 @@ class Gem::Indexer marshal_name = "Marshal.#{Gem.marshal_version}" - @master_index = Gem::Indexer::MasterIndexBuilder.new "yaml", @directory - @marshal_index = Gem::Indexer::MarshalIndexBuilder.new marshal_name, @directory - @quick_index = Gem::Indexer::QuickIndexBuilder.new 'index', @directory - quick_dir = File.join @directory, 'quick' - @latest_index = Gem::Indexer::LatestIndexBuilder.new 'latest_index', quick_dir end ## - # Build the index. - - def build_index - @master_index.build do - @quick_index.build do - @marshal_index.build do - @latest_index.build do - progress = ui.progress_reporter gem_file_list.size, - "Generating index for #{gem_file_list.size} gems in #{@dest_directory}", - "Loaded all gems" - - gem_file_list.each do |gemfile| - if File.size(gemfile.to_s) == 0 then - alert_warning "Skipping zero-length gem: #{gemfile}" - next - end - - begin - spec = Gem::Format.from_file_by_path(gemfile).spec - - unless gemfile =~ /\/#{Regexp.escape spec.original_name}.*\.gem\z/i then - alert_warning "Skipping misnamed gem: #{gemfile} => #{spec.full_name} (#{spec.original_name})" - next - end - - abbreviate spec - sanitize spec - - @master_index.add spec - @quick_index.add spec - @marshal_index.add spec - @latest_index.add spec - - progress.updated spec.original_name - - rescue SignalException => e - alert_error "Received signal, exiting" - raise - rescue Exception => e - alert_error "Unable to process #{gemfile}\n#{e.message} (#{e.class})\n\t#{e.backtrace.join "\n\t"}" - end - end - - progress.done - - say "Generating master indexes (this may take a while)" - end end end end end - def install_index - verbose = Gem.configuration.really_verbose - say "Moving index into production dir #{@dest_directory}" if verbose - files = @master_index.files + @quick_index.files + @marshal_index.files + - @latest_index.files - files.each do |file| - src_name = File.join @directory, file - dst_name = File.join @dest_directory, file - FileUtils.rm_rf dst_name, :verbose => verbose - FileUtils.mv src_name, @dest_directory, :verbose => verbose end end def generate_index FileUtils.rm_rf @directory FileUtils.mkdir_p @directory, :mode => 0700 - build_index - install_index rescue SignalException ensure FileUtils.rm_rf @directory end - # List of gem file names to index. - def gem_file_list - Dir.glob(File.join(@dest_directory, "gems", "*.gem")) end - # Abbreviate the spec for downloading. Abbreviated specs are only - # used for searching, downloading and related activities and do not - # need deployment specific information (e.g. list of files). So we - # abbreviate the spec, making it much smaller for quicker downloads. - def abbreviate(spec) - spec.files = [] - spec.test_files = [] - spec.rdoc_options = [] - spec.extra_rdoc_files = [] - spec.cert_chain = [] - spec end # Sanitize the descriptive fields in the spec. Sometimes non-ASCII # characters will garble the site index. Non-ASCII characters will # be replaced by their XML entity equivalent. def sanitize(spec) spec.summary = sanitize_string(spec.summary) spec.description = sanitize_string(spec.description) @@ -158,7 +346,9 @@ class Gem::Indexer spec end # Sanitize a single string. def sanitize_string(string) # HACK the #to_s is in here because RSpec has an Array of Arrays of # Strings for authors. Need a way to disallow bad values on gempsec @@ -168,9 +358,3 @@ class Gem::Indexer end -require 'rubygems/indexer/abstract_index_builder' -require 'rubygems/indexer/master_index_builder' -require 'rubygems/indexer/quick_index_builder' -require 'rubygems/indexer/marshal_index_builder' -require 'rubygems/indexer/latest_index_builder' - @@ -89,6 +89,12 @@ module Gem::InstallUpdateOptions 'foo_exec18') do |value, options| options[:format_executable] = value end end # Default options for the gem install command. @@ -56,6 +56,7 @@ class Gem::Installer # foo_exec18. # :security_policy:: Use the specified security policy. See Gem::Security # :wrappers:: Install wrappers if true, symlinks if false. def initialize(gem, options={}) @gem = gem @@ -76,6 +77,7 @@ class Gem::Installer @security_policy = options[:security_policy] @wrappers = options[:wrappers] @bin_dir = options[:bin_dir] begin @format = Gem::Format.from_file_by_path @gem, @security_policy @@ -98,6 +100,7 @@ class Gem::Installer # cache/<gem-version>.gem #=> a cached copy of the installed gem # gems/<gem-version>/... #=> extracted files # specifications/<gem-version>.gemspec #=> the Gem::Specification def install # If we're forcing the install then disable security unless the security # policy says that we only install singed gems. @@ -119,7 +122,10 @@ class Gem::Installer end unless @ignore_dependencies then - @spec.dependencies.each do |dep_gem| ensure_dependency @spec, dep_gem end end @@ -150,6 +156,8 @@ class Gem::Installer @spec.loaded_from = File.join(@gem_home, 'specifications', "#{@spec.full_name}.gemspec") return @spec rescue Zlib::GzipFile::Error raise Gem::InstallError, "gzip error installing #{@gem}" @@ -161,6 +169,7 @@ class Gem::Installer # # spec :: Gem::Specification # dependency :: Gem::Dependency def ensure_dependency(spec, dependency) unless installation_satisfies_dependency? dependency then raise Gem::InstallError, "#{spec.name} requires #{dependency}" @@ -170,17 +179,15 @@ class Gem::Installer end ## - # True if the current installed gems satisfy the given dependency. - # - # dependency :: Gem::Dependency def installation_satisfies_dependency?(dependency) - current_index = Gem::SourceIndex.from_installed_gems - current_index.find_name(dependency.name, dependency.version_requirements).size > 0 end ## # Unpacks the gem into the given directory. - # def unpack(directory) @gem_dir = directory @format = Gem::Format.from_file_by_path @gem, @security_policy @@ -193,7 +200,7 @@ class Gem::Installer # # spec:: [Gem::Specification] The Gem specification to output # spec_path:: [String] The location (path) to write the gemspec to - # def write_spec rubycode = @spec.to_ruby @@ -208,7 +215,7 @@ class Gem::Installer ## # Creates windows .bat files for easy running of commands - # def generate_windows_script(bindir, filename) if Gem.win_platform? then script_name = filename + ".bat" @@ -227,7 +234,7 @@ class Gem::Installer # If the user has asked for the gem to be installed in a directory that is # the system gem directory, then use the system bin directory, else create # (or use) a new bin dir under the gem_home. - bindir = @bin_dir ? @bin_dir : (Gem.bindir @gem_home) Dir.mkdir bindir unless File.exist? bindir raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir @@ -252,7 +259,7 @@ class Gem::Installer # The Windows script is generated in addition to the regular one due to a # bug or misfeature in the Windows shell's pipe. See # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/193379 - # def generate_bin_script(filename, bindir) bin_script_path = File.join bindir, formatted_program_filename(filename) @@ -260,6 +267,8 @@ class Gem::Installer # HACK some gems don't have #! in their executables, restore 2008/06 #if File.read(exec_path, 2) == '#!' then File.open bin_script_path, 'w', 0755 do |file| file.print app_script_text(filename) end @@ -277,7 +286,7 @@ class Gem::Installer ## # Creates the symlinks to run the applications in the gem. Moves # the symlink if the gem being installed has a newer version. - # def generate_bin_symlink(filename, bindir) if Gem.win_platform? then alert_warning "Unable to use symlinks on Windows, installing wrapper" @@ -303,6 +312,7 @@ class Gem::Installer ## # Generates a #! line for +bin_file_name+'s wrapper copying arguments if # necessary. def shebang(bin_file_name) if @env_shebang then "#!/usr/bin/env " + Gem::ConfigMap[:ruby_install_name] @@ -324,7 +334,9 @@ class Gem::Installer end end # Return the text for an application file. def app_script_text(bin_file_name) <<-TEXT #{shebang bin_file_name} @@ -349,7 +361,9 @@ load '#{bin_file_name}' TEXT end # return the stub script text used to launch the true ruby script def windows_stub_script(bindir, bin_file_name) <<-TEXT @ECHO OFF @@ -361,8 +375,10 @@ GOTO :EOF TEXT end # Builds extensions. Valid types of extensions are extconf.rb files, # configure scripts and rakefiles or mkrf_conf files. def build_extensions return if @spec.extensions.empty? say "Building native extensions. This could take a while..." @@ -418,6 +434,7 @@ Results logged to #{File.join(Dir.pwd, 'gem_make.out')} # Reads the file index and extracts each file into the gem directory. # # Ensures that files can't be installed outside the gem directory. def extract_files expand_and_validate_gem_dir @@ -445,11 +462,15 @@ Results logged to #{File.join(Dir.pwd, 'gem_make.out')} out.write file_data end say path if Gem.configuration.really_verbose end end # Prefix and suffix the program filename the same as ruby. def formatted_program_filename(filename) if @format_executable then self.class.exec_format % File.basename(filename) @@ -460,7 +481,9 @@ Results logged to #{File.join(Dir.pwd, 'gem_make.out')} private # HACK Pathname is broken on windows. def absolute_path? pathname pathname.absolute? or (Gem.win_platform? and pathname.to_s =~ /\A[a-z]:/i) end @@ -4,27 +4,34 @@ # See LICENSE.txt for permissions. #++ require 'rubygems' # Mixin methods for local and remote Gem::Command options. module Gem::LocalRemoteOptions # Allows OptionParser to handle HTTP URIs. def accept_uri_http OptionParser.accept URI::HTTP do |value| begin - value = URI.parse value rescue URI::InvalidURIError raise OptionParser::InvalidArgument, value end - raise OptionParser::InvalidArgument, value unless value.scheme == 'http' value end end # Add local/remote options to the command line parser. def add_local_remote_options add_option(:"Local/Remote", '-l', '--local', 'Restrict operations to the LOCAL domain') do |value, options| @@ -47,7 +54,9 @@ module Gem::LocalRemoteOptions add_update_sources_option end # Add the --bulk-threshold option def add_bulk_threshold_option add_option(:"Local/Remote", '-B', '--bulk-threshold COUNT', "Threshold for switching to bulk", @@ -57,7 +66,9 @@ module Gem::LocalRemoteOptions end end # Add the --http-proxy option def add_proxy_option accept_uri_http @@ -68,22 +79,28 @@ module Gem::LocalRemoteOptions end end # Add the --source option def add_source_option accept_uri_http add_option(:"Local/Remote", '--source URL', URI::HTTP, - 'Use URL as the remote source for gems') do |value, options| if options[:added_source] then - Gem.sources << value else options[:added_source] = true - Gem.sources.replace [value] end end end # Add the --source option def add_update_sources_option add_option(:"Local/Remote", '-u', '--[no-]update-sources', @@ -92,12 +109,16 @@ module Gem::LocalRemoteOptions end end # Is local fetching enabled? def local? options[:domain] == :local || options[:domain] == :both end # Is remote fetching enabled? def remote? options[:domain] == :remote || options[:domain] == :both end @@ -1,7 +1,8 @@ require 'rubygems' # Available list of platforms for targeting Gem installations. -# class Gem::Platform @local = nil @@ -122,11 +123,20 @@ class Gem::Platform to_a.compact.join '-' end def ==(other) self.class === other and @cpu == other.cpu and @os == other.os and @version == other.version end def ===(other) return nil unless Gem::Platform === other @@ -140,6 +150,10 @@ class Gem::Platform (@version.nil? or other.version.nil? or @version == other.version) end def =~(other) case other when Gem::Platform then # nop @@ -1,4 +1,5 @@ require 'net/http' require 'uri' require 'rubygems' @@ -11,15 +12,38 @@ class Gem::RemoteFetcher include Gem::UserInteraction - class FetchError < Gem::Exception; end @fetcher = nil # Cached RemoteFetcher instance. def self.fetcher @fetcher ||= self.new Gem.configuration[:http_proxy] end # Initialize a remote fetcher using the source URI and possible proxy # information. # @@ -29,6 +53,7 @@ class Gem::RemoteFetcher # * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER, # HTTP_PROXY_PASS) # * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy def initialize(proxy) Socket.do_not_reverse_lookup = true @@ -47,11 +72,13 @@ class Gem::RemoteFetcher # Moves the gem +spec+ from +source_uri+ to the cache dir unless it is # already there. If the source_uri is local the gem cache dir copy is # always replaced. def download(spec, source_uri, install_dir = Gem.dir) gem_file_name = "#{spec.full_name}.gem" - local_gem_path = File.join install_dir, 'cache', gem_file_name - Gem.ensure_gem_subdirectories install_dir source_uri = URI.parse source_uri unless URI::Generic === source_uri scheme = source_uri.scheme @@ -102,21 +129,26 @@ class Gem::RemoteFetcher local_gem_path end - # Downloads +uri+. def fetch_path(uri) open_uri_or_path(uri) do |input| input.read end rescue Timeout::Error - raise FetchError, "timed out fetching #{uri}" rescue IOError, SocketError, SystemCallError => e - raise FetchError, "#{e.class}: #{e} reading #{uri}" rescue => e - message = "#{e.class}: #{e} reading #{uri}" - raise FetchError, message end # Returns the size of +uri+ in bytes. def fetch_size(uri) return File.size(get_file_uri_path(uri)) if file_uri? uri @@ -124,30 +156,21 @@ class Gem::RemoteFetcher raise ArgumentError, 'uri is not an HTTP URI' unless URI::HTTP === uri - http = connect_to uri.host, uri.port - - request = Net::HTTP::Head.new uri.request_uri - - request.basic_auth unescape(uri.user), unescape(uri.password) unless - uri.user.nil? or uri.user.empty? - resp = http.request request - - if resp.code !~ /^2/ then - raise Gem::RemoteSourceException, - "HTTP Response #{resp.code} fetching #{uri}" end - if resp['content-length'] then - return resp['content-length'].to_i else - resp = http.get uri.request_uri - return resp.body.size end rescue SocketError, SystemCallError, Timeout::Error => e - raise Gem::RemoteFetcher::FetchError, - "#{e.message} (#{e.class})\n\tgetting size of #{uri}" end private @@ -162,7 +185,9 @@ class Gem::RemoteFetcher URI.unescape(str) end # Returns an HTTP proxy URI if one is set in the environment variables. def get_proxy_from_env env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY'] @@ -179,104 +204,129 @@ class Gem::RemoteFetcher uri end # Normalize the URI by adding "http://" if it is missing. def normalize_uri(uri) (uri =~ /^(https?|ftp|file):/) ? uri : "http://#{uri}" end - # Connect to the source host/port, using a proxy if needed. - def connect_to(host, port) - if @proxy_uri - Net::HTTP::Proxy(@proxy_uri.host, @proxy_uri.port, unescape(@proxy_uri.user), unescape(@proxy_uri.password)).new(host, port) - else - Net::HTTP.new(host, port) end end # Read the data from the (source based) URI, but if it is a file:// URI, # read from the filesystem instead. def open_uri_or_path(uri, depth = 0, &block) if file_uri?(uri) open(get_file_uri_path(uri), &block) else uri = URI.parse uri unless URI::Generic === uri - net_http_args = [uri.host, uri.port] - - if @proxy_uri then - net_http_args += [ @proxy_uri.host, - @proxy_uri.port, - @proxy_uri.user, - @proxy_uri.password - ] - end - - connection_id = net_http_args.join ':' - @connections[connection_id] ||= Net::HTTP.new(*net_http_args) - connection = @connections[connection_id] - if uri.scheme == 'https' && ! connection.started? - http_obj.use_ssl = true - http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE - end - - connection.start unless connection.started? - - request = Net::HTTP::Get.new(uri.request_uri) - unless uri.nil? || uri.user.nil? || uri.user.empty? then - request.basic_auth(uri.user, uri.password) - end - - ua = "RubyGems/#{Gem::RubyGemsVersion} #{Gem::Platform.local}" - ua << " Ruby/#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}" - ua << " level #{RUBY_LEVEL}" if defined? RUBY_LEVEL - ua << ")" - - request.add_field 'User-Agent', ua - request.add_field 'Connection', 'keep-alive' - request.add_field 'Keep-Alive', '30' - - # HACK work around EOFError bug in Net::HTTP - # NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible - # to install gems. - retried = false - begin - @requests[connection_id] += 1 - response = connection.request(request) - rescue EOFError, Errno::ECONNABORTED - requests = @requests[connection_id] - say "connection reset after #{requests} requests, retrying" if - Gem.configuration.really_verbose - - raise Gem::RemoteFetcher::FetchError, 'too many connection resets' if - retried - - @requests[connection_id] = 0 - - connection.finish - connection.start - retried = true - retry - end case response when Net::HTTPOK then block.call(StringIO.new(response.body)) if block when Net::HTTPRedirection then - raise Gem::RemoteFetcher::FetchError, "too many redirects" if depth > 10 open_uri_or_path(response['Location'], depth + 1, &block) else - raise Gem::RemoteFetcher::FetchError, - "bad response #{response.message} #{response.code}" end end end # Checks if the provided string is a file:// URI. def file_uri?(uri) uri =~ %r{\Afile://} end # Given a file:// URI, returns its local path. def get_file_uri_path(uri) uri.sub(%r{\Afile://}, '') end @@ -12,6 +12,7 @@ require 'rubygems/version' # # A Requirement object can actually contain multiple, er, # requirements, as in (> 1.2, < 2.0). class Gem::Requirement include Comparable @@ -35,7 +36,7 @@ class Gem::Requirement # Version, a String, or nil. Intended to simplify client code. # # If the input is "weird", the default version requirement is returned. - # def self.create(input) case input when Gem::Requirement then @@ -57,6 +58,7 @@ class Gem::Requirement # This comment once said: # # "A default "version requirement" can surely _only_ be '> 0'." def self.default self.new ['>= 0'] end @@ -65,6 +67,7 @@ class Gem::Requirement # Constructs a Requirement from +requirements+ which can be a String, a # Gem::Version, or an Array of those. See parse for details on the # formatting of requirement strings. def initialize(requirements) @requirements = case requirements when Array then @@ -77,13 +80,17 @@ class Gem::Requirement @version = nil # Avoid warnings. end # Marshal raw requirements, rather than the full object - def marshal_dump [@requirements] end # Load custom marshal format - def marshal_load(array) @requirements = array[0] @version = nil end @@ -108,20 +115,16 @@ class Gem::Requirement end ## - # Is the requirement satisfied by +version+. - # - # version:: [Gem::Version] the version to compare against - # return:: [Boolean] true if this requirement is satisfied by - # the version, otherwise false - # def satisfied_by?(version) normalize @requirements.all? { |op, rv| satisfy?(op, version, rv) } end ## - # Is "version op required_version" satisfied? - # def satisfy?(op, version, required_version) OPS[op].call(version, required_version) end @@ -132,6 +135,7 @@ class Gem::Requirement # The requirement can be a String or a Gem::Version. A String can be an # operator (<, <=, =, =>, >, !=, ~>), a version number, or both, operator # first. def parse(obj) case obj when /^\s*(#{OP_RE})\s*([0-9.]+)\s*$/o then @@ -147,7 +151,7 @@ class Gem::Requirement end end - def <=>(other) to_s <=> other.to_s end @@ -2,5 +2,5 @@ # This file is auto-generated by build scripts. # See: rake update_version module Gem - RubyGemsVersion = '1.1.1' end @@ -4,6 +4,7 @@ require 'zlib' require 'erb' require 'rubygems' ## # Gem::Server and allows users to serve gems for consumption by @@ -11,18 +12,24 @@ require 'rubygems' # # gem_server starts an HTTP server on the given port and serves the following: # * "/" - Browsing of gem spec files for installed gems -# * "/Marshal" - Full SourceIndex dump of metadata for installed gems -# * "/yaml" - YAML dump of metadata for installed gems - deprecated # * "/gems" - Direct access to download the installable gems # # == Usage # -# gem server [-p portnum] [-d gem_path] # -# port_num:: The TCP port the HTTP server will bind to -# gem_path:: -# Root gem directory containing both "cache" and "specifications" -# subdirectories. class Gem::Server include Gem::UserInteraction @@ -36,7 +43,6 @@ class Gem::Server <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>RubyGems Documentation Index</title> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" /> </head> <body> @@ -325,32 +331,99 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } new(options[:gemdir], options[:port], options[:daemon]).run end - def initialize(gemdir, port, daemon) Socket.do_not_reverse_lookup = true - @gemdir = gemdir @port = port @daemon = daemon logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL @server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger - @spec_dir = File.join @gemdir, "specifications" @source_index = Gem::SourceIndex.from_gems_in @spec_dir end def quick(req, res) res['content-type'] = 'text/plain' res['date'] = File.stat(@spec_dir).mtime - case req.request_uri.request_uri when '/quick/index' then - res.body << @source_index.map { |name,_| name }.join("\n") when '/quick/index.rz' then - index = @source_index.map { |name,_| name }.join("\n") - res.body << Zlib::Deflate.deflate(index) when %r|^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)-([0-9.]+)(-.*?)?\.gemspec\.rz$| then dep = Gem::Dependency.new $2, $3 specs = @source_index.search dep selector = [$2, $3, $4].map { |s| s.inspect }.join ' ' @@ -368,17 +441,98 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } elsif specs.length > 1 then res.status = 500 res.body = "Multiple gems found matching #{selector}" - elsif $1 then # marshal quickindex instead of YAML - res.body << Zlib::Deflate.deflate(Marshal.dump(specs.first)) else # deprecated YAML format - res.body << Zlib::Deflate.deflate(specs.first.to_yaml) end else - res.status = 404 - res.body = "#{req.request_uri} not found" end end def run @server.listen nil, @port @@ -386,27 +540,21 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } WEBrick::Daemon.start if @daemon - @server.mount_proc("/yaml") do |req, res| - res['content-type'] = 'text/plain' - res['date'] = File.stat(@spec_dir).mtime - if req.request_method == 'HEAD' then - res['content-length'] = @source_index.to_yaml.length - else - res.body << @source_index.to_yaml - end - end - @server.mount_proc("/Marshal") do |req, res| - res['content-type'] = 'text/plain' - res['date'] = File.stat(@spec_dir).mtime - if req.request_method == 'HEAD' then - res['content-length'] = Marshal.dump(@source_index).length - else - res.body << Marshal.dump(@source_index) - end - end - @server.mount_proc("/quick/", &method(:quick)) @server.mount_proc("/gem-server-rdoc-style.css") do |req, res| res['content-type'] = 'text/css' @@ -414,80 +562,12 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } res.body << RDOC_CSS end - @server.mount_proc("/") do |req, res| - specs = [] - total_file_count = 0 - - @source_index.each do |path, spec| - total_file_count += spec.files.size - deps = spec.dependencies.collect { |dep| - { "name" => dep.name, - "version" => dep.version_requirements.to_s, } - } - deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] } - deps.last["is_last"] = true unless deps.empty? - - # executables - executables = spec.executables.sort.collect { |exec| {"executable" => exec} } - executables = nil if executables.empty? - executables.last["is_last"] = true if executables - - specs << { - "authors" => spec.authors.sort.join(", "), - "date" => spec.date.to_s, - "dependencies" => deps, - "doc_path" => ('/doc_root/' + spec.full_name + '/rdoc/index.html'), - "executables" => executables, - "only_one_executable" => (executables && executables.size==1), - "full_name" => spec.full_name, - "has_deps" => !deps.empty?, - "homepage" => spec.homepage, - "name" => spec.name, - "rdoc_installed" => Gem::DocManager.new(spec).rdoc_installed?, - "summary" => spec.summary, - "version" => spec.version.to_s, - } - end - - specs << { - "authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others", - "dependencies" => [], - "doc_path" => "/doc_root/rubygems-#{Gem::RubyGemsVersion}/rdoc/index.html", - "executables" => [{"executable" => 'gem', "is_last" => true}], - "only_one_executable" => true, - "full_name" => "rubygems-#{Gem::RubyGemsVersion}", - "has_deps" => false, - "homepage" => "http://rubygems.org/", - "name" => 'rubygems', - "rdoc_installed" => true, - "summary" => "RubyGems itself", - "version" => Gem::RubyGemsVersion, - } - - specs = specs.sort_by { |spec| [spec["name"].downcase, spec["version"]] } - specs.last["is_last"] = true - - # tag all specs with first_name_entry - last_spec = nil - specs.each do |spec| - is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase) - spec["first_name_entry"] = is_first - last_spec = spec - end - - # create page from template - template = ERB.new(DOC_TEMPLATE) - res['content-type'] = 'text/html' - values = { "gem_count" => specs.size.to_s, "specs" => specs, - "total_file_count" => total_file_count.to_s } - result = template.result binding - res.body = result - end paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" } paths.each do |mount_point, mount_dir| @server.mount(mount_point, WEBrick::HTTPServlet::FileHandler, - File.join(@gemdir, mount_dir), true) end trap("INT") { @server.shutdown; exit! } @@ -496,5 +576,54 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; } @server.start end end @@ -7,6 +7,7 @@ require 'rubygems' require 'rubygems/user_interaction' require 'rubygems/specification' ## # The SourceIndex object indexes all the gems available from a @@ -27,6 +28,11 @@ class Gem::SourceIndex attr_reader :gems # :nodoc: class << self include Gem::UserInteraction @@ -39,7 +45,7 @@ class Gem::SourceIndex # +from_gems_in+. This argument is deprecated and is provided # just for backwards compatibility, and should not generally # be used. - # # return:: # SourceIndex instance @@ -63,7 +69,9 @@ class Gem::SourceIndex # +spec_dirs+. def from_gems_in(*spec_dirs) - self.new.load_gems_in(*spec_dirs) end ## @@ -79,6 +87,8 @@ class Gem::SourceIndex return gemspec end alert_warning "File '#{file_name}' does not evaluate to a gem specification" rescue SyntaxError => e alert_warning e alert_warning spec_code @@ -100,6 +110,7 @@ class Gem::SourceIndex def initialize(specifications={}) @gems = specifications end ## @@ -121,8 +132,8 @@ class Gem::SourceIndex end ## - # Returns a Hash of name => Specification of the latest versions of each - # gem in this index. def latest_specs result = Hash.new { |h,k| h[k] = [] } @@ -241,7 +252,9 @@ class Gem::SourceIndex when Gem::Dependency then only_platform = platform_only version_requirement = gem_pattern.version_requirements - gem_pattern = if gem_pattern.name.empty? then // else /^#{Regexp.escape gem_pattern.name}$/ @@ -271,29 +284,43 @@ class Gem::SourceIndex ## # Replaces the gems in the source index from specifications in the - # installed_spec_directories, def refresh! - load_gems_in(*self.class.installed_spec_directories) end ## # Returns an Array of Gem::Specifications that are not up to date. def outdated - dep = Gem::Dependency.new '', Gem::Requirement.default - - remotes = Gem::SourceInfoCache.search dep, true - outdateds = [] latest_specs.each do |local| name = local.name - remote = remotes.select { |spec| spec.name == name }. - sort_by { |spec| spec.version.to_ints }. - last - outdateds << name if remote and local.version < remote.version end outdateds @@ -387,7 +414,8 @@ class Gem::SourceIndex end def fetch_bulk_index(source_uri) - say "Bulk updating Gem source index for: #{source_uri}" index = fetch_index_from(source_uri) if index.nil? then @@ -447,7 +475,7 @@ class Gem::SourceIndex def unzip(string) require 'zlib' - Zlib::Inflate.inflate(string) end ## @@ -0,0 +1,251 @@ @@ -6,6 +6,7 @@ require 'rubygems' require 'rubygems/version' require 'rubygems/platform' # :stopdoc: @@ -16,6 +17,9 @@ if RUBY_VERSION < '1.9' then t - ((t.to_f + t.gmt_offset) % 86400) end unless defined? Time.today end # :startdoc: module Gem @@ -37,22 +41,32 @@ module Gem # class Specification # Allows deinstallation of gems with legacy platforms. 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). NONEXISTENT_SPECIFICATION_VERSION = -1 # The specification version applied to any new Specification instances # created. This should be bumped whenever something in the spec format # changes. - CURRENT_SPECIFICATION_VERSION = 2 # An informal list of changes to the specification. The highest-valued # key should be equal to the CURRENT_SPECIFICATION_VERSION. SPECIFICATION_VERSION_HISTORY = { -1 => ['(RubyGems versions up to and including 0.7 did not have versioned specifications)'], 1 => [ @@ -63,10 +77,13 @@ module Gem 'Added "required_rubygems_version"', 'Now forward-compatible with future versions', ], } # :stopdoc: - MARSHAL_FIELDS = { -1 => 16, 1 => 16, 2 => 16 } now = Time.at(Time.now.to_i) TODAY = now - ((now.to_i + now.gmt_offset) % 86400) @@ -335,6 +352,14 @@ module Gem read_only :dependencies # ALIASED gemspec attributes ------------------------------------- attribute_alias_singular :executable, :executables @@ -629,27 +654,31 @@ module Gem end end - # Adds a dependency to this Gem. For example, # - # spec.add_dependency('jabber4r', '> 0.1', '<= 0.5') # # gem:: [String or Gem::Dependency] The Gem name/dependency. # requirements:: [default=">= 0"] The version requirements. # - def add_dependency(gem, *requirements) - requirements = if requirements.empty? then - Gem::Requirement.default - else - requirements.flatten - end - unless gem.respond_to?(:name) && gem.respond_to?(:version_requirements) - gem = Dependency.new(gem, requirements) - end - dependencies << gem - end - # 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). @@ -673,30 +702,31 @@ module Gem end end # The full path to the gem (install path + full name). - # - # return:: [String] the full gem path - # 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 root directory that the gem was installed into. - # - # return:: [String] the installation path - # def installation_path - (File.dirname(@loaded_from).split(File::SEPARATOR)[0..-2]). - join(File::SEPARATOR) end - # Checks if this Specification meets the requirement of the supplied # dependency. # @@ -778,9 +808,11 @@ module Gem 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 = [] @@ -792,8 +824,6 @@ module Gem result << " s.platform = #{ruby_code original_platform}" end result << "" - result << " s.specification_version = #{specification_version} if s.respond_to? :specification_version=" - result << "" result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version=" handled = [ @@ -816,15 +846,42 @@ module Gem end end - result << "" unless dependencies.empty? - 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 << "" result.join "\n" end @@ -940,6 +997,22 @@ module Gem private def find_all_satisfiers(dep) Gem.source_index.each do |name,gem| if(gem.satisfies_requirement?(dep)) then @@ -0,0 +1,120 @@ @@ -176,9 +176,10 @@ class Gem::Uninstaller end def path_ok?(spec) - match_path = File.join @gem_home, 'gems', spec.full_name - match_path == spec.full_gem_path end def dependencies_ok?(spec) @@ -6,54 +6,71 @@ module Gem - #################################################################### - # Module that defines the default UserInteraction. Any class - # including this module will have access to the +ui+ method that - # returns the default UI. module DefaultUserInteraction # Return the default UI. def ui DefaultUserInteraction.ui end - # Set the default UI. If the default UI is never explicitly set, a - # simple console based UserInteraction will be used automatically. def ui=(new_ui) DefaultUserInteraction.ui = new_ui end def use_ui(new_ui, &block) DefaultUserInteraction.use_ui(new_ui, &block) end - # The default UI is a class variable of the singleton class for - # this module. - - @ui = nil - - class << self - def ui - @ui ||= Gem::ConsoleUI.new - end - def ui=(new_ui) - @ui = new_ui - end - def use_ui(new_ui) - old_ui = @ui - @ui = new_ui - yield - ensure - @ui = old_ui - end - end end - #################################################################### # Make the default UI accessable without the "ui." prefix. Classes - # including this module may use the interaction methods on the - # default UI directly. Classes may also reference the +ui+ and - # <tt>ui=</tt> methods. # # Example: # @@ -64,22 +81,30 @@ module Gem # n = ask("What is the meaning of life?") # end # end module UserInteraction include DefaultUserInteraction - [ - :choose_from_list, :ask, :ask_yes_no, :say, :alert, :alert_warning, - :alert_error, :terminate_interaction - ].each do |methname| class_eval %{ def #{methname}(*args) ui.#{methname}(*args) end - } end end - #################################################################### # StreamUI implements a simple stream based user interface. class StreamUI attr_reader :ins, :outs, :errs @@ -89,15 +114,19 @@ module Gem @outs = out_stream @errs = err_stream end - - # Choose from a list of options. +question+ is a prompt displayed - # above the list. +list+ is a list of option strings. Returns - # the pair [option_name, option_index]. def choose_from_list(question, list) @outs.puts question list.each_with_index do |item, index| @outs.puts " #{index+1}. #{item}" end @outs.print "> " @outs.flush @@ -109,28 +138,32 @@ module Gem return list[result], result end - # Ask a question. Returns a true for yes, false for no. If not - # connected to a tty, raises an exception if default is nil, - # otherwise returns default. def ask_yes_no(question, default=nil) - if not @ins.tty? then if default.nil? then - raise( - Gem::OperationNotSupportedError, - "Not connected to a tty and no default specified") else return default end end qstr = case default - when nil - 'yn' - when true - 'Yn' - else - 'yN' - end result = nil while result.nil? result = ask("#{question} [#{qstr}]") result = case result @@ -144,51 +177,68 @@ module Gem nil end end return result end - - # Ask a question. Returns an answer if connected to a tty, nil - # otherwise. def ask(question) return nil if not @ins.tty? @outs.print(question + " ") @outs.flush result = @ins.gets result.chomp! if result result end - # Display a statement. def say(statement="") @outs.puts statement end - - # Display an informational alert. def alert(statement, question=nil) @outs.puts "INFO: #{statement}" - return ask(question) if question end - - # Display a warning in a location expected to get error messages. def alert_warning(statement, question=nil) @errs.puts "WARNING: #{statement}" - ask(question) if question end - - # Display an error message in a location expected to get error - # messages. def alert_error(statement, question=nil) @errs.puts "ERROR: #{statement}" ask(question) if question end - # Terminate the application normally, running any exit handlers - # that might have been defined. def terminate_interaction(status = 0) raise Gem::SystemExitException, status end - # Return a progress reporter object def progress_reporter(*args) case Gem.configuration.verbose when nil, false @@ -200,6 +250,9 @@ module Gem end end class SilentProgressReporter attr_reader :count @@ -213,6 +266,9 @@ module Gem end end class SimpleProgressReporter include DefaultUserInteraction @@ -228,17 +284,27 @@ module Gem @out.puts initial_message end def updated(message) @count += 1 @out.print "." @out.flush end def done @out.puts "\n#{@terminal_message}" end end class VerboseProgressReporter include DefaultUserInteraction @@ -254,32 +320,41 @@ module Gem @out.puts initial_message end def updated(message) @count += 1 @out.puts "#{@count}/#{@total}: #{message}" end def done @out.puts @terminal_message end end end - #################################################################### - # Subclass of StreamUI that instantiates the user interaction using - # standard in, out and error. class ConsoleUI < StreamUI def initialize super(STDIN, STDOUT, STDERR) end end - #################################################################### # SilentUI is a UI choice that is absolutely silent. class SilentUI def method_missing(sym, *args, &block) self end end end @@ -8,6 +8,7 @@ require 'rubygems' ## # The Version class processes string versions into comparable values class Gem::Version include Comparable @@ -17,11 +18,8 @@ class Gem::Version attr_reader :version ## - # Checks if version string is valid format - # - # str:: [String] the version string - # return:: [Boolean] true if the string format is correct, otherwise false - # def self.correct?(version) case version when Integer, /\A\s*(\d+(\.\d+)*)*\s*\z/ then true @@ -36,7 +34,7 @@ class Gem::Version # ver1 = Version.create('1.3.17') # -> (Version object) # ver2 = Version.create(ver1) # -> (ver1) # ver3 = Version.create(nil) # -> nil - # def self.create(input) if input.respond_to? :version then input @@ -48,10 +46,9 @@ class Gem::Version end ## - # Constructs a version from the supplied string - # - # version:: [String] The version string. Format is digit.digit... - # def initialize(version) raise ArgumentError, "Malformed version number string #{version}" unless self.class.correct?(version) @@ -73,7 +70,9 @@ class Gem::Version self.version = array[0] end # Strip ignored trailing zeros. def normalize @ints = build_array_from_version_string @@ -94,10 +93,8 @@ class Gem::Version end ## - # Convert version to integer array - # - # return:: [Array] list of integers - # def to_ints normalize unless @ints @ints @@ -117,20 +114,25 @@ class Gem::Version end ## - # Compares two versions - # - # other:: [Version or .ints] other version to compare to - # return:: [Fixnum] -1, 0, 1 - # def <=>(other) return 1 unless other @ints <=> other.ints end - alias eql? == # :nodoc: def hash # :nodoc: - to_ints.inject { |hash_code, n| hash_code + n } end # Return a new version object where the next to the last revision |