summaryrefslogtreecommitdiff
path: root/lib/rubygems/uninstaller.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rubygems/uninstaller.rb')
-rw-r--r--lib/rubygems/uninstaller.rb183
1 files changed, 183 insertions, 0 deletions
diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb
new file mode 100644
index 0000000000..0f7edb048c
--- /dev/null
+++ b/lib/rubygems/uninstaller.rb
@@ -0,0 +1,183 @@
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'fileutils'
+require 'rubygems'
+require 'rubygems/dependency_list'
+require 'rubygems/doc_manager'
+require 'rubygems/user_interaction'
+
+##
+# An Uninstaller.
+#
+class Gem::Uninstaller
+
+ include Gem::UserInteraction
+
+ ##
+ # Constructs an Uninstaller instance
+ #
+ # gem:: [String] The Gem name to uninstall
+ #
+ def initialize(gem, options)
+ @gem = gem
+ @version = options[:version] || Gem::Requirement.default
+ @force_executables = options[:executables]
+ @force_all = options[:all]
+ @force_ignore = options[:ignore]
+ end
+
+ ##
+ # Performs the uninstall of the Gem. This removes the spec, the
+ # Gem directory, and the cached .gem file,
+ #
+ def uninstall
+ list = Gem.source_index.search(/^#{@gem}$/, @version)
+
+ 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
+ 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)
+ else
+ say "Error: must enter a number [1-#{list.size+1}]"
+ end
+ else
+ remove(list[0], list.dup)
+ remove_executables(list.last)
+ end
+ end
+
+ ##
+ # Remove executables and batch files (windows only) for the gem as
+ # it is being installed
+ #
+ # gemspec::[Specification] the gem whose executables need to be removed.
+ #
+ def remove_executables(gemspec)
+ return if gemspec.nil?
+ if(gemspec.executables.size > 0)
+ raise Gem::FilePermissionError.new(Gem.bindir) unless
+ File.writable?(Gem.bindir)
+ list = Gem.source_index.search(gemspec.name).delete_if { |spec|
+ spec.version == gemspec.version
+ }
+ executables = gemspec.executables.clone
+ list.each do |spec|
+ spec.executables.each do |exe_name|
+ executables.delete(exe_name)
+ end
+ end
+ return if executables.size == 0
+ answer = @force_executables || ask_yes_no(
+ "Remove executables and scripts for\n" +
+ "'#{gemspec.executables.join(", ")}' in addition to the gem?",
+ true) # " # appease ruby-mode - don't ask
+ unless answer
+ say "Executables and scripts will remain installed."
+ return
+ else
+ gemspec.executables.each do |exe_name|
+ say "Removing #{exe_name}"
+ File.unlink File.join(Gem.bindir, exe_name) rescue nil
+ File.unlink File.join(Gem.bindir, exe_name + ".bat") rescue nil
+ end
+ end
+ end
+ end
+
+ #
+ # list:: the list of all gems to remove
+ #
+ # Warning: this method modifies the +list+ parameter. Once it has
+ # uninstalled a gem, it is removed from that list.
+ #
+ def remove_all(list)
+ list.dup.each { |gem| remove(gem, list) }
+ end
+
+ #
+ # spec:: the spec of the gem to be uninstalled
+ # list:: the list of all such gems
+ #
+ # Warning: this method modifies the +list+ parameter. Once it has
+ # uninstalled a gem, it is removed from that list.
+ #
+ def remove(spec, list)
+ unless ok_to_remove? spec then
+ raise Gem::DependencyRemovalException,
+ "Uninstallation aborted due to dependent gem(s)"
+ end
+
+ raise Gem::FilePermissionError, spec.installation_path unless
+ File.writable?(spec.installation_path)
+
+ FileUtils.rm_rf spec.full_gem_path
+
+ original_platform_name = [
+ spec.name, spec.version, spec.original_platform].join '-'
+
+ spec_dir = File.join spec.installation_path, 'specifications'
+ gemspec = File.join spec_dir, "#{spec.full_name}.gemspec"
+
+ unless File.exist? gemspec then
+ gemspec = File.join spec_dir, "#{original_platform_name}.gemspec"
+ end
+
+ FileUtils.rm_rf gemspec
+
+ cache_dir = File.join spec.installation_path, 'cache'
+ gem = File.join cache_dir, "#{spec.full_name}.gem"
+
+ unless File.exist? gemspec then
+ gem = File.join cache_dir, "#{original_platform_name}.gem"
+ end
+
+ FileUtils.rm_rf gem
+
+ Gem::DocManager.new(spec).uninstall_doc
+
+ say "Successfully uninstalled #{spec.full_name}"
+
+ list.delete spec
+ end
+
+ def ok_to_remove?(spec)
+ return true if @force_ignore
+
+ srcindex = Gem::SourceIndex.from_installed_gems
+ deplist = Gem::DependencyList.from_source_index srcindex
+ deplist.ok_to_remove?(spec.full_name) || ask_if_ok(spec)
+ end
+
+ def ask_if_ok(spec)
+ msg = ['']
+ msg << 'You have requested to uninstall the gem:'
+ msg << "\t#{spec.full_name}"
+ spec.dependent_gems.each do |gem,dep,satlist|
+ msg <<
+ ("#{gem.name}-#{gem.version} depends on " +
+ "[#{dep.name} (#{dep.version_requirements})]")
+ end
+ msg << 'If you remove this gems, one or more dependencies will not be met.'
+ msg << 'Continue with Uninstall?'
+ return ask_yes_no(msg.join("\n"), true)
+ end
+
+end
+