aboutsummaryrefslogtreecommitdiffstats
path: root/lib/rubygems/doctor.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rubygems/doctor.rb')
-rw-r--r--lib/rubygems/doctor.rb124
1 files changed, 124 insertions, 0 deletions
diff --git a/lib/rubygems/doctor.rb b/lib/rubygems/doctor.rb
new file mode 100644
index 0000000000..198c758b00
--- /dev/null
+++ b/lib/rubygems/doctor.rb
@@ -0,0 +1,124 @@
+require 'rubygems'
+require 'rubygems/user_interaction'
+require 'pathname'
+
+##
+# Cleans up after a partially-failed uninstall or for an invalid
+# Gem::Specification.
+#
+# If a specification was removed by hand this will remove any remaining files.
+#
+# If a corrupt specification was installed this will clean up warnings by
+# removing the bogus specification.
+
+class Gem::Doctor
+
+ include Gem::UserInteraction
+
+ ##
+ # Maps a gem subdirectory to the files that are expected to exist in the
+ # subdirectory.
+
+ REPOSITORY_EXTENSION_MAP = { # :nodoc:
+ 'build_info' => '.info',
+ 'cache' => '.gem',
+ 'doc' => '',
+ 'gems' => '',
+ 'specifications' => '.gemspec'
+ }
+
+ raise 'Update REPOSITORY_EXTENSION_MAP' unless
+ Gem::REPOSITORY_SUBDIRECTORIES == REPOSITORY_EXTENSION_MAP.keys.sort
+
+ ##
+ # Creates a new Gem::Doctor that will clean up +gem_repository+. Only one
+ # gem repository may be cleaned at a time.
+ #
+ # If +dry_run+ is true no files or directories will be removed.
+
+ def initialize gem_repository, dry_run = false
+ @gem_repository = Pathname(gem_repository)
+ @dry_run = dry_run
+
+ @installed_specs = nil
+ end
+
+ ##
+ # Specs installed in this gem repository
+
+ def installed_specs # :nodoc:
+ @installed_specs ||= Gem::Specification.map { |s| s.full_name }
+ end
+
+ ##
+ # Are we doctoring a gem repository?
+
+ def gem_repository?
+ not installed_specs.empty?
+ end
+
+ ##
+ # Cleans up uninstalled files and invalid gem specifications
+
+ def doctor
+ @orig_home = Gem.dir
+ @orig_path = Gem.path
+
+ say "Checking #{@gem_repository}"
+
+ Gem.use_paths @gem_repository.to_s
+
+ unless gem_repository? then
+ say 'This directory does not appear to be a RubyGems repository, ' +
+ 'skipping'
+ say
+ return
+ end
+
+ doctor_children
+
+ say
+ ensure
+ Gem.use_paths @orig_home, *@orig_path
+ end
+
+ ##
+ # Cleans up children of this gem repository
+
+ def doctor_children # :nodoc:
+ REPOSITORY_EXTENSION_MAP.each do |sub_directory, extension|
+ doctor_child sub_directory, extension
+ end
+ end
+
+ ##
+ # Removes files in +sub_directory+ with +extension+
+
+ def doctor_child sub_directory, extension # :nodoc:
+ directory = @gem_repository + sub_directory
+
+ directory.each_child do |child|
+ next unless child.exist?
+
+ basename = child.basename(extension).to_s
+ next if installed_specs.include? basename
+ next if /^rubygems-\d/ =~ basename
+ next if 'specifications' == sub_directory and 'default' == basename
+
+ type = child.directory? ? 'directory' : 'file'
+
+ action = if @dry_run then
+ 'Extra'
+ else
+ child.rmtree
+ 'Removed'
+ end
+
+ say "#{action} #{type} #{sub_directory}/#{child.basename}"
+ end
+ rescue Errno::ENOENT
+ # ignore
+ end
+
+end
+