# File lib/automateit/package_manager/gem.rb, line 71
  def install(*packages)
    return _install_helper(*packages) do |list, opts|
      gem = opts[:gem] || self.gem

      # Why is the "gem" utility such a steaming pile of offal? Lameness include:
      # - Requires interactive input to install a package, with no way to prevent this
      # - Repeatedly updates indexes even when there's no reason to, and can't be told to stop
      # - Doesn't cache packages, insists on downloading them again
      # - Installs broken packages, often without giving any indication of failure
      # - Installs broken packages and leaves you to deal with the jagged pieces
      # - Sometimes fails through exit status, sometimes through output, but not both and not consistently
      # - Lacks a proper "is this package installed?" feature
      # - A nightmare to deal with if you want to install your own GEMHOME/GEMPATH

      # Example of an invalid gem that'll cause the failure I'm trying to avoid below:
      #   package_manager.install("sys-cpu", :with => :gem)

      # gem options:
      # -y : Include dependencies,
      # -E : use /usr/bin/env for installed executables; but only with >= 0.9.4
      cmd = "#{gem} install -y"
      cmd << " --no-ri" if opts[:ri] == false or opts[:docs] == false
      cmd << " --no-rdoc" if opts[:rdoc] == false or opts[:docs] == false
      cmd << " --source #{opts[:source]}" if opts[:source]
      cmd << " "+list.join(" ")
      cmd << " " << opts[:args] if opts[:args]
      cmd << " 2>&1"

      # XXX Try to warn the user that they won't see any output for a while
      log.info(PNOTE+"Installing Gems, this will take a while...") if writing? and not opts[:quiet]
      log.info(PEXEC+cmd)
      return true if preview?

      uninstall_needed = false
      begin
        require 'expect'
        require 'open4'
        exitstruct = Open4::popen4(cmd) do |pid, sin, sout, serr|
          $expect_verbose = opts[:quiet] ? false : true

          re_missing=/Could not find.+in any repository/m
          re_select=/Select which gem to install.+>/m
          re_failed=/Gem files will remain.+for inspection/m
          re_refused=/Errno::ECONNREFUSED reading .+?\.gem/m
          re_all=/#{re_missing}|#{re_select}|#{re_failed}|#{re_refused}/m

          while true
            begin
              captureded = sout.expect(re_all)
            rescue NoMethodError
              log.debug(PNOTE+"Gem seems to be done")
              break
            end
            ### puts "Captureded %s" % captureded.inspect
            captured = captureded.first
            if captured.match(re_failed)
              log.warn(PERROR+"Gem install failed mid-process")
              uninstall_needed = true
              break
            elsif captured.match(re_refused)
              log.warn(PERROR+"Gem install refused by server!\n#{captured}")
              break
            elsif captured.match(re_select)
              choice = captured.match(/^ (\d+)\. .+?\(ruby\)\s*$/)[1]
              log.info(PNOTE+"Guessing: #{choice}")
              sin.puts(choice)
            end
          end
        end
      rescue Errno::ENOENT => e
        raise NotImplementedError.new("can't find gem command: #{e}")
      end

      if uninstall_needed or not exitstruct.exitstatus.zero?
        log.error(PERROR+"Gem install failed, trying to uninstall broken pieces: #{list.inspect}")
        uninstall(list, opts)

        raise ArgumentError.new("Gem install failed because it's invalid, missing a dependency, or can't talk with Gem server: #{list.inspect}")
      end
    end
  end