class Isolate::Sandbox

An isolated environment.

Public Class Methods

new(options = {}) click to toggle source

Create a new Isolate::Sandbox instance. See Isolate.now! for the most common use of the API. You probably don’t want to use this constructor directly.

# File lib/isolate/sandbox.rb, line 33
def initialize options = {}, &block
  @enabled      = false
  @entries      = []
  @environments = []
  @files        = []
  @options      = options

  path, name = options.values_at :path, :name

  raise ArgumentError, "can't specify both name and path!" if name && path
  options[:path] = File.expand_path("~/.gem/repos/#{name}") if name

  user = File.expand_path "~/.isolate/user.rb"
  load user if File.exist? user

  file, local = nil

  unless FalseClass === options[:file]
    file  = options[:file] || Dir["{Isolate,config/isolate.rb}"].first
    local = "#{file}.local" if file
  end

  load file if file

  if block_given?
    files << (block.to_s[/ (\/.+):\d+/, 1] || "inline block")
    instance_eval(&block)
  end

  load local if local && File.exist?(local)
end

Public Instance Methods

activate(environment = nil) click to toggle source

Activate this set of isolated entries, respecting an optional environment. Points RubyGems to a separate repository, messes with paths, auto-installs gems (if necessary), activates everything, and removes any superfluous gem (again, if necessary). If environment isn’t specified, ISOLATE_ENV, RAILS_ENV, and RACK_ENV are checked before falling back to "development".

# File lib/isolate/sandbox.rb, line 73
def activate environment = nil
  enable unless enabled?

  env = (environment || Isolate.env).to_s

  install env if install?

  entries.each do |e|
    e.activate if e.matches? env
  end

  cleanup if cleanup?

  self
end
cleanup?() click to toggle source
# File lib/isolate/sandbox.rb, line 99
def cleanup?
  install? and @options.fetch(:cleanup, true)
end
disable() { || ... } click to toggle source
# File lib/isolate/sandbox.rb, line 103
def disable
  return self unless enabled?

  ENV.replace @old_env
  $LOAD_PATH.replace @old_load_path

  @enabled = false

  Isolate.refresh

  if block_given? then
    begin
      return yield
    ensure
      enable
    end
  end

  self
end
enabled?() click to toggle source
# File lib/isolate/sandbox.rb, line 177
def enabled?
  @enabled
end
env(*environments, &block)
Alias for: environment
environment(*environments, &block) click to toggle source

Restricts gem calls inside block to a set of environments.

# File lib/isolate/sandbox.rb, line 183
def environment *environments, &block
  old = @environments
  @environments = @environments.dup.concat environments.map { |e| e.to_s }

  instance_eval(&block)
ensure
  @environments = old
end
Also aliased as: env
gem(name, *requirements) click to toggle source

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

# File lib/isolate/sandbox.rb, line 198
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
install_missing(environment) click to toggle source
# File lib/isolate/sandbox.rb, line 213
def install_missing environment
  installable = entries.select do |e|
    !e.specification && 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

    Gem::Specification.reset
  end
end
multiruby?() click to toggle source
# File lib/isolate/sandbox.rb, line 265
def multiruby?
  @options.fetch :multiruby, false
end
options(options = nil) click to toggle source
# File lib/isolate/sandbox.rb, line 269
def options options = nil
  @options.merge! options if options
  @options
end
path() click to toggle source
# File lib/isolate/sandbox.rb, line 274
def path
  base = @options.fetch :path, DEFAULT_PATH

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

  File.expand_path base
end
rebuild_extensions() click to toggle source
# File lib/isolate/sandbox.rb, line 231
def rebuild_extensions
  broken = entries.find_all { |e|
    e.specification && e.specification.missing_extensions?
  }

  unless broken.empty?
    padding = Math.log10(broken.size).to_i + 1
    format  = "[%0#{padding}d/%d] Building extensions for %s (ruby v%s)."

    broken.each_with_index do |e, i|
      spec = e.specification
      log format % [i + 1, broken.size, e.name, RUBY_VERSION]

      builder = Gem::Ext::Builder.new spec
      builder.build_extensions
    end

    Gem::Specification.reset
  end
end
remove(*extra) click to toggle source
# File lib/isolate/sandbox.rb, line 285
def remove(*extra)
  unless extra.empty?
    padding = Math.log10(extra.size).to_i + 1
    format  = "[%0#{padding}d/%s] Nuking %s."

    extra.each_with_index do |e, i|
      log format % [i + 1, extra.size, e.full_name]

      Gem::DefaultUserInteraction.use_ui Gem::SilentUI.new do
        uninstaller =
          Gem::Uninstaller.new(e.name,
                               :version     => e.version,
                               :ignore      => true,
                               :executables => true,
                               :install_dir => e.base_dir)
        uninstaller.uninstall
      end
    end
  end
end
system?() click to toggle source
# File lib/isolate/sandbox.rb, line 306
def system?
  @options.fetch :system, true
end
verbose?() click to toggle source
# File lib/isolate/sandbox.rb, line 310
def verbose?
  @options.fetch :verbose, true
end

Private Instance Methods

legitimize!(deps = entries) click to toggle source

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.

# File lib/isolate/sandbox.rb, line 321
def legitimize! deps = entries
  specs = []

  deps.flatten.each do |dep|
    spec = case dep
           when Gem::Dependency then
             begin
               dep.to_spec
             rescue Gem::LoadError
               nil
             end
           when Isolate::Entry then
             dep.specification
           else
             raise "unknown dep: #{dep.inspect}"
           end

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

  specs.uniq
end