# File lib/isolate/sandbox.rb, line 114
    def disable &block
      return self if not enabled?
      fire :disabling

      ENV.replace @old_env
      $LOAD_PATH.replace @old_load_path

      @enabled = false

      Isolate.refresh
      fire :disabled

      begin; return yield ensure enable end if block_given?

      self
    end

    def enable # :nodoc:
      return self if enabled?
      fire :enabling

      @old_env       = ENV.to_hash
      @old_load_path = $LOAD_PATH.dup

      FileUtils.mkdir_p path
      ENV["GEM_HOME"] = path

      lib = File.expand_path "../..", __FILE__

      unless system?
        $LOAD_PATH.reject! do |p|
          p != lib && Gem.path.any? { |gp| p.include?(gp) }
        end

        # HACK: Gotta keep isolate explicitly in the LOAD_PATH in
        # subshells, and the only way I can think of to do that is by
        # abusing RUBYOPT.

        dirname = Regexp.escape lib

        unless ENV["RUBYOPT"] =~ /\s+-I\s*#{lib}\b/
          ENV["RUBYOPT"] = "#{ENV['RUBYOPT']} -I#{lib}"
        end

        ENV["GEM_PATH"] = path
      end

      bin = File.join path, "bin"

      unless ENV["PATH"].split(File::PATH_SEPARATOR).include? bin
        ENV["PATH"] = [bin, ENV["PATH"]].join File::PATH_SEPARATOR
      end

      ENV["ISOLATED"] = path

      Isolate.refresh
      Gem.path.unshift path if system?

      @enabled = true
      fire :enabled

      self
    end

    def enabled?
      @enabled
    end

    # Restricts +gem+ calls inside +block+ to a set of +environments+.

    def environment *environments, &block
      old = @environments
      @environments = @environments.dup.concat environments.map { |e| e.to_s }

      instance_eval(&block)
    ensure
      @environments = old
    end

    alias_method :env, :environment

    # Express a gem dependency. Works pretty much like RubyGems' +gem+
    # method, but respects +environment+ and doesn't activate 'til
    # later.

    def gem name, *requirements
      entry = entries.find { |e| e.name == name }
      return entry.update(*requirements) if entry

      entries << entry = Entry.new(self, name, *requirements)
      entry
    end

    # A source index representing only isolated gems.

    def index
      @index ||= Gem::SourceIndex.from_gems_in File.join(path, "specifications")
    end

    def install environment # :nodoc:
      fire :installing

      installable = entries.select do |e|
        !Gem.available?(e.name, *e.requirement.as_list) &&
          e.matches?(environment)
      end

      unless installable.empty?
        padding = Math.log10(installable.size).to_i + 1
        format  = "[%0#{padding}d/%s] Isolating %s (%s)."

        installable.each_with_index do |entry, i|
          log format % [i + 1, installable.size, entry.name, entry.requirement]
          entry.install
        end

        index.refresh!
        Gem.source_index.refresh!
      end

      fire :installed

      self
    end

    def install? # :nodoc:
      @options.fetch :install, true
    end

    def load file # :nodoc:
      files << file
      instance_eval IO.read(file), file, 1
    end

    def log s # :nodoc:
      $stderr.puts s if verbose?
    end


    def multiruby?
      @options.fetch :multiruby, true
    end

    def options options = nil
      @options.merge! options if options
      @options
    end

    def path
      base = @options.fetch :path, "tmp/isolate"

      unless @options.key?(:multiruby) && @options[:multiruby] == false
        suffix = "#{Gem.ruby_engine}-#{RbConfig::CONFIG['ruby_version']}"
        base   = File.join(base, suffix) unless base =~ /#{suffix}/
      end

      File.expand_path base
    end

    def system?
      @options.fetch :system, true
    end

    def verbose?
      @options.fetch :verbose, true
    end

    private

    # Returns a list of Gem::Specification instances that 1. exist in
    # the isolated gem path, and 2. are allowed to be there. Used in
    # cleanup. It's only an external method 'cause recursion is
    # easier.

    def legitimize! deps = entries
      specs = []

      deps.flatten.each do |dep|
        spec = index.find_name(dep.name, dep.requirement).last

        if spec
          specs.concat legitimize!(spec.runtime_dependencies)
          specs << spec
        end
      end

      specs.uniq
    end
  end