# class Gem::Commands::UnpackCommand

### Public Class Methods

new() click to toggle source
Calls superclass method Gem::Command.new

    # File lib/rubygems/commands/unpack_command.rb, line 19
    def initialize
      require 'fileutils'

      super 'unpack', 'Unpack an installed gem to the current directory',
            :version => Gem::Requirement.default,
            :target  => Dir.pwd

      add_option('--target=DIR',
                 'target directory for unpacking') do |value, options|
        options[:target] = value
      end

      add_option('--spec', 'unpack the gem specification') do |value, options|
        options[:spec] = true
      end

      add_security_option
      add_version_option
    end

### Public Instance Methods

description() click to toggle source

    # File lib/rubygems/commands/unpack_command.rb, line 47
      def description
        <<-EOF
    The unpack command allows you to examine the contents of a gem or modify
    them to help diagnose a bug.

    You can add the contents of the unpacked gem to the load path using the
    RUBYLIB environment variable or -I:

      $ gem unpack my_gem
      Unpacked gem: '.../my_gem-1.0'
      [edit my_gem-1.0/lib/my_gem.rb]
      $ ruby -Imy_gem-1.0/lib -S other_program

    You can repackage an unpacked gem using the build command.  See the build
    command help for an example.
        EOF
      end

execute() click to toggle source

    # File lib/rubygems/commands/unpack_command.rb, line 74
    def execute
      security_policy = options[:security_policy]

      get_all_gem_names.each do |name|
        dependency = Gem::Dependency.new name, options[:version]
        path = get_path dependency

        unless path then
          alert_error "Gem '#{name}' not installed nor fetchable."
          next
        end

        if @options[:spec] then
          spec, metadata = get_metadata path, security_policy

          if metadata.nil? then
            alert_error "--spec is unsupported on '#{name}' (old format gem)"
            next
          end

          spec_file = File.basename spec.spec_file

          File.open spec_file, 'w' do |io|
            io.write metadata
          end
        else
          basename = File.basename path, '.gem'
          target_dir = File.expand_path basename, options[:target]

          package = Gem::Package.new path, security_policy
          package.extract_files target_dir

          say "Unpacked gem: '#{target_dir}'"
        end
      end
    end

find_in_cache(filename) click to toggle source

Find cached filename in Gem.path. Returns nil if the file cannot be found.

    # File lib/rubygems/commands/unpack_command.rb, line 118
    def find_in_cache(filename)
      Gem.path.each do |path|
        this_path = File.join(path, "cache", filename)
        return this_path if File.exist? this_path
      end

      return nil
    end

get_metadata(path, security_policy = nil) click to toggle source

Extracts the Gem::Specification and raw metadata from the .gem file at `path`.

    # File lib/rubygems/commands/unpack_command.rb, line 172
    def get_metadata path, security_policy = nil
      format = Gem::Package.new path, security_policy
      spec = format.spec

      metadata = nil

      File.open path, Gem.binary_mode do |io|
        tar = Gem::Package::TarReader.new io
        tar.each_entry do |entry|
          case entry.full_name
          when 'metadata' then
            metadata = entry.read
          when 'metadata.gz' then
            metadata = Gem.gunzip entry.read
          end
        end
      end

      return spec, metadata
    end

get_path(dependency) click to toggle source

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)

    # File lib/rubygems/commands/unpack_command.rb, line 144
    def get_path dependency
      return dependency.name if dependency.name =~ /\.gem$/i

      specs = dependency.matching_specs

      selected = specs.max_by { |s| s.version }

      return Gem::RemoteFetcher.fetcher.download_to_cache(dependency) unless
        selected

      return unless dependency.name =~ /^#{selected.name}$/i

      # We expect to find (basename).gem in the 'cache' directory.  Furthermore,
      # the name match must be exact (ignoring case).

      path = find_in_cache File.basename selected.cache_file

      return Gem::RemoteFetcher.fetcher.download_to_cache(dependency) unless path

      path
    end