summaryrefslogtreecommitdiff
path: root/lib/rubygems/commands/unpack_command.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rubygems/commands/unpack_command.rb')
-rw-r--r--lib/rubygems/commands/unpack_command.rb76
1 files changed, 76 insertions, 0 deletions
diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb
new file mode 100644
index 0000000000..ece24745a2
--- /dev/null
+++ b/lib/rubygems/commands/unpack_command.rb
@@ -0,0 +1,76 @@
+require 'fileutils'
+require 'rubygems/command'
+require 'rubygems/installer'
+require 'rubygems/version_option'
+
+class Gem::Commands::UnpackCommand < Gem::Command
+
+ include Gem::VersionOption
+
+ def initialize
+ super 'unpack', 'Unpack an installed gem to the current directory',
+ :version => Gem::Requirement.default
+ add_version_option
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of gem to unpack"
+ end
+
+ def defaults_str # :nodoc:
+ "--version '#{Gem::Requirement.default}'"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME"
+ end
+
+ #--
+ # TODO: allow, e.g., 'gem unpack rake-0.3.1'. Find a general solution for
+ # this, so that it works for uninstall as well. (And check other commands
+ # at the same time.)
+ def execute
+ gemname = get_one_gem_name
+ path = get_path(gemname, options[:version])
+ if path
+ target_dir = File.basename(path).sub(/\.gem$/, '')
+ FileUtils.mkdir_p target_dir
+ Gem::Installer.new(path).unpack(File.expand_path(target_dir))
+ say "Unpacked gem: '#{target_dir}'"
+ else
+ alert_error "Gem '#{gemname}' not installed."
+ end
+ end
+
+ # Return the full path to the cached gem file matching the given
+ # name and version requirement. Returns 'nil' if no match.
+ #
+ # Example:
+ #
+ # get_path('rake', '> 0.4') # -> '/usr/lib/ruby/gems/1.8/cache/rake-0.4.2.gem'
+ # get_path('rake', '< 0.1') # -> nil
+ # get_path('rak') # -> nil (exact name required)
+ #--
+ # TODO: This should be refactored so that it's a general service. I don't
+ # think any of our existing classes are the right place though. Just maybe
+ # 'Cache'?
+ #
+ # TODO: It just uses Gem.dir for now. What's an easy way to get the list of
+ # source directories?
+ def get_path(gemname, version_req)
+ return gemname if gemname =~ /\.gem$/i
+ specs = Gem::SourceIndex.from_installed_gems.search(/\A#{gemname}\z/, version_req)
+ selected = specs.sort_by { |s| s.version }.last
+ return nil if selected.nil?
+ # We expect to find (basename).gem in the 'cache' directory.
+ # Furthermore, the name match must be exact (ignoring case).
+ if gemname =~ /^#{selected.name}$/i
+ filename = selected.full_name + '.gem'
+ return File.join(Gem.dir, 'cache', filename)
+ else
+ return nil
+ end
+ end
+
+end
+