# module Bundler

`Bundler` provides a consistent environment for Ruby projects by tracking and installing the exact gems and versions that are needed.

`Bundler` is a part of Ruby’s standard library.

`Bundler` is used by creating gemfiles listing all the project dependencies and (optionally) their versions and then using

    require 'bundler/setup'

or `Bundler.setup` to setup environment where only specified gems and their specified versions could be used.

See Bundler website for extensive documentation on gemfiles creation and `Bundler` usage.

As a standard library inside project, `Bundler` could be used for introspection of loaded and required modules.

### Constants

ORIGINAL_ENV
SUDO_MUTEX

### Public Class Methods

app_cache (custom_path = nil)
Source

    # File lib/bundler.rb, line 345
    def app_cache(custom_path = nil)
      path = custom_path || root
      Pathname.new(path).join(settings.app_cache_path)
    end

app_config_path ()
Source

    # File lib/bundler.rb, line 331
    def app_config_path
      if app_config = ENV["BUNDLE_APP_CONFIG"]
        app_config_pathname = Pathname.new(app_config)

        if app_config_pathname.absolute?
          app_config_pathname
        else
          app_config_pathname.expand_path(root)
        end
      else
        root.join(".bundle")
      end
    end

auto_install ()
Source

    # File lib/bundler.rb, line 183
    def auto_install
      return unless settings[:auto_install]

      begin
        definition.specs
      rescue GemNotFound, GitError
        ui.info "Automatically installing missing gems."
        reset!
        CLI::Install.new({}).run
        reset!
      end
    end

Automatically install dependencies if `Bundler.settings[:auto_install]` exists. This is set through config cmd ‘bundle config set –global `auto_install` 1`.

Note that this method ‘nil`s out the global Definition object, so it should be called first, before you instantiate anything like an `Installer` that’ll keep a reference to the old one instead.

auto_switch ()
Source

    # File lib/bundler.rb, line 173
    def auto_switch
      self_manager.restart_with_locked_bundler_if_needed
    end

bin_path ()
Source

    # File lib/bundler.rb, line 121
    def bin_path
      @bin_path ||= begin
        path = settings[:bin] || "bin"
        path = Pathname.new(path).expand_path(root).expand_path
        mkdir_p(path)
        path
      end
    end

Returns absolute location of where binstubs are installed to.

bundle_path ()
Source

    # File lib/bundler.rb, line 103
    def bundle_path
      @bundle_path ||= Pathname.new(configured_bundle_path.path).expand_path(root)
    end

Returns absolute path of where gems are installed on the filesystem.

clean_env ()
Source

    # File lib/bundler.rb, line 371
    def clean_env
      message =
        "`Bundler.clean_env` has been deprecated in favor of `Bundler.unbundled_env`. " \
        "If you instead want the environment before bundler was originally loaded, use `Bundler.original_env`"
      removed_message =
        "`Bundler.clean_env` has been removed in favor of `Bundler.unbundled_env`. " \
        "If you instead want the environment before bundler was originally loaded, use `Bundler.original_env`"
      Bundler::SharedHelpers.major_deprecation(2, message, removed_message: removed_message, print_caller_location: true)
      unbundled_env
    end

@deprecated Use ‘unbundled_env` instead

clean_exec (*args)
Source

    # File lib/bundler.rb, line 442
    def clean_exec(*args)
      message =
        "`Bundler.clean_exec` has been deprecated in favor of `Bundler.unbundled_exec`. " \
        "If you instead want to exec to a command in the environment before bundler was originally loaded, use `Bundler.original_exec`"
      removed_message =
        "`Bundler.clean_exec` has been removed in favor of `Bundler.unbundled_exec`. " \
        "If you instead want to exec to a command in the environment before bundler was originally loaded, use `Bundler.original_exec`"
      Bundler::SharedHelpers.major_deprecation(2, message, removed_message: removed_message, print_caller_location: true)
      with_env(unbundled_env) { Kernel.exec(*args) }
    end

@deprecated Use ‘unbundled_exec` instead

clean_system (*args)
Source

    # File lib/bundler.rb, line 420
    def clean_system(*args)
      message =
        "`Bundler.clean_system` has been deprecated in favor of `Bundler.unbundled_system`. " \
        "If you instead want to run the command in the environment before bundler was originally loaded, use `Bundler.original_system`"
      removed_message =
        "`Bundler.clean_system` has been removed in favor of `Bundler.unbundled_system`. " \
        "If you instead want to run the command in the environment before bundler was originally loaded, use `Bundler.original_system`"
      Bundler::SharedHelpers.major_deprecation(2, message, removed_message: removed_message, print_caller_location: true)
      with_env(unbundled_env) { Kernel.system(*args) }
    end

@deprecated Use ‘unbundled_system` instead

clear_gemspec_cache ()
Source

    # File lib/bundler.rb, line 566
    def clear_gemspec_cache
      @gemspec_cache = {}
    end

configure ()
Source

    # File lib/bundler.rb, line 89
    def configure
      @configure ||= configure_gem_home_and_path
    end

configure_gem_home_and_path (path = bundle_path)
Source

    # File lib/bundler.rb, line 629
    def configure_gem_home_and_path(path = bundle_path)
      configure_gem_path
      configure_gem_home(path)
      Bundler.rubygems.clear_paths
    end

configured_bundle_path ()
Source

    # File lib/bundler.rb, line 116
    def configured_bundle_path
      @configured_bundle_path ||= settings.path.tap(&:validate!)
    end

create_bundle_path ()
Source

    # File lib/bundler.rb, line 107
    def create_bundle_path
      mkdir_p(bundle_path) unless bundle_path.exist?

      @bundle_path = bundle_path.realpath
    rescue Errno::EEXIST
      raise PathError, "Could not install to path `#{bundle_path}` " \
        "because a file already exists at that path. Either remove or rename the file so the directory can be created."
    end

default_bundle_dir ()
Source

    # File lib/bundler.rb, line 471
    def default_bundle_dir
      SharedHelpers.default_bundle_dir
    end

default_gemfile ()
Source

    # File lib/bundler.rb, line 463
    def default_gemfile
      SharedHelpers.default_gemfile
    end

default_lockfile ()
Source

    # File lib/bundler.rb, line 467
    def default_lockfile
      SharedHelpers.default_lockfile
    end

definition (unlock = nil, lockfile = default_lockfile)
Source

    # File lib/bundler.rb, line 234
    def definition(unlock = nil, lockfile = default_lockfile)
      @definition = nil if unlock
      @definition ||= begin
        configure
        Definition.build(default_gemfile, lockfile, unlock)
      end
    end

Returns an instance of Bundler::Definition for given Gemfile and lockfile

@param unlock [Hash, Boolean, nil] Gems that have been requested

    to be updated or true if all gems should be updated

@param lockfile [Pathname] Path to Gemfile.lock @return [Bundler::Definition]

environment ()
Source

    # File lib/bundler.rb, line 223
    def environment
      SharedHelpers.major_deprecation 2, "Bundler.environment has been removed in favor of Bundler.load", print_caller_location: true
      load
    end

feature_flag ()
Source

    # File lib/bundler.rb, line 575
    def feature_flag
      @feature_flag ||= FeatureFlag.new(VERSION)
    end

find_executable (path)
Source

    # File lib/bundler.rb, line 512
    def find_executable(path)
      extensions = RbConfig::CONFIG["EXECUTABLE_EXTS"]&.split
      extensions = [RbConfig::CONFIG["EXEEXT"]] unless extensions&.any?
      candidates = extensions.map {|ext| "#{path}#{ext}" }

      candidates.find {|candidate| File.file?(candidate) && File.executable?(candidate) }
    end

frozen_bundle? ()
Source

    # File lib/bundler.rb, line 242
    def frozen_bundle?
      frozen = settings[:frozen]
      return frozen unless frozen.nil?

      settings[:deployment]
    end

git_present? ()
Source

    # File lib/bundler.rb, line 570
    def git_present?
      return @git_present if defined?(@git_present)
      @git_present = Bundler.which("git")
    end

home ()
Source

    # File lib/bundler.rb, line 309
    def home
      bundle_path.join("bundler")
    end

install_path ()
Source

    # File lib/bundler.rb, line 313
    def install_path
      home.join("gems")
    end

load ()
Source

    # File lib/bundler.rb, line 219
    def load
      @load ||= Runtime.new(root, definition)
    end

load_gemspec (file, validate = false)
Source

    # File lib/bundler.rb, line 539
    def load_gemspec(file, validate = false)
      @gemspec_cache ||= {}
      key = File.expand_path(file)
      @gemspec_cache[key] ||= load_gemspec_uncached(file, validate)
      # Protect against caching side-effected gemspecs by returning a
      # new instance each time.
      @gemspec_cache[key]&.dup
    end

load_gemspec_uncached (file, validate = false)
Source

    # File lib/bundler.rb, line 548
    def load_gemspec_uncached(file, validate = false)
      path = Pathname.new(file)
      contents = read_file(file)
      spec = if contents.start_with?("---") # YAML header
        eval_yaml_gemspec(path, contents)
      else
        # Eval the gemspec from its parent directory, because some gemspecs
        # depend on "./" relative paths.
        SharedHelpers.chdir(path.dirname.to_s) do
          eval_gemspec(path, contents)
        end
      end
      return unless spec
      spec.loaded_from = path.expand_path.to_s
      Bundler.rubygems.validate(spec) if validate
      spec
    end

load_plugins (definition = Bundler.definition)
Source

    # File lib/bundler.rb, line 579
    def load_plugins(definition = Bundler.definition)
      return if defined?(@load_plugins_ran)

      Bundler.rubygems.load_plugins

      requested_path_gems = definition.requested_specs.select {|s| s.source.is_a?(Source::Path) }
      path_plugin_files = requested_path_gems.flat_map do |spec|
        spec.matches_for_glob("rubygems_plugin#{Bundler.rubygems.suffix_pattern}")
      rescue TypeError
        error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
        raise Gem::InvalidSpecificationException, error_message
      end
      Bundler.rubygems.load_plugin_files(path_plugin_files)
      Bundler.rubygems.load_env_plugins
      @load_plugins_ran = true
    end

local_platform ()
Source

    # File lib/bundler.rb, line 458
    def local_platform
      return Gem::Platform::RUBY if settings[:force_ruby_platform]
      Gem::Platform.local
    end

locked_gems ()
Source

    # File lib/bundler.rb, line 249
    def locked_gems
      @locked_gems ||=
        if defined?(@definition) && @definition
          definition.locked_gems
        elsif Bundler.default_lockfile.file?
          lock = Bundler.read_file(Bundler.default_lockfile)
          LockfileParser.new(lock)
        end
    end

mkdir_p (path)
Source

    # File lib/bundler.rb, line 492
    def mkdir_p(path)
      SharedHelpers.filesystem_access(path, :create) do |p|
        FileUtils.mkdir_p(p)
      end
    end

original_env ()
Source

    # File lib/bundler.rb, line 366
    def original_env
      ORIGINAL_ENV.clone
    end

@return [Hash] Environment present before `Bundler` was activated

original_exec (*args)
Source

    # File lib/bundler.rb, line 437
    def original_exec(*args)
      with_original_env { Kernel.exec(*args) }
    end

Run a ‘Kernel.exec` to a subcommand with the environment present before `Bundler` was activated

original_system (*args)
Source

    # File lib/bundler.rb, line 415
    def original_system(*args)
      with_original_env { Kernel.system(*args) }
    end

Run subcommand with the environment present before `Bundler` was activated

preferred_gemfile_name ()
Source

    # File lib/bundler.rb, line 484
    def preferred_gemfile_name
      Bundler.settings[:init_gems_rb] ? "gems.rb" : "Gemfile"
    end

read_file (file)
Source

    # File lib/bundler.rb, line 520
    def read_file(file)
      SharedHelpers.filesystem_access(file, :read) do
        File.open(file, "r:UTF-8", &:read)
      end
    end

require (*groups)
Source

    # File lib/bundler.rb, line 214
    def require(*groups)
      load_plugins
      setup(*groups).require(*groups)
    end

Setups `Bundler` environment (see `Bundler.setup`) if it is not already set, and loads all gems from groups specified. Unlike `::setup`, can be called multiple times with different groups (if they were allowed by setup).

Assuming Gemfile

    gem 'first_gem', '= 1.0'
    group :test do
      gem 'second_gem', '= 1.0'
    end

The code will work as follows:

    Bundler.setup # allow all groups
    Bundler.require(:default) # requires only first_gem
    # ...later
    Bundler.require(:test)   # requires second_gem

reset! ()
Source

    # File lib/bundler.rb, line 596
    def reset!
      reset_paths!
      Plugin.reset!
      reset_rubygems!
    end

reset_paths! ()
Source

    # File lib/bundler.rb, line 607
    def reset_paths!
      @bin_path = nil
      @bundler_major_version = nil
      @bundle_path = nil
      @configure = nil
      @configured_bundle_path = nil
      @definition = nil
      @load = nil
      @locked_gems = nil
      @root = nil
      @settings = nil
      @setup = nil
      @user_home = nil
    end

reset_rubygems! ()
Source

    # File lib/bundler.rb, line 622
    def reset_rubygems!
      return unless defined?(@rubygems) && @rubygems
      rubygems.undo_replacements
      rubygems.reset
      @rubygems = nil
    end

reset_settings_and_root! ()
Source

    # File lib/bundler.rb, line 602
    def reset_settings_and_root!
      @settings = nil
      @root = nil
    end

rm_rf (path)
Source

    # File lib/bundler.rb, line 355
    def rm_rf(path)
      FileUtils.remove_entry_secure(path) if path && File.exist?(path)
    end

root ()
Source

    # File lib/bundler.rb, line 321
    def root
      @root ||= begin
                  SharedHelpers.root
                rescue GemfileNotFound
                  bundle_dir = default_bundle_dir
                  raise GemfileNotFound, "Could not locate Gemfile or .bundle/ directory" unless bundle_dir
                  Pathname.new(File.expand_path("..", bundle_dir))
                end
    end

ruby_scope ()
Source

    # File lib/bundler.rb, line 259
    def ruby_scope
      "#{Bundler.rubygems.ruby_engine}/#{RbConfig::CONFIG["ruby_version"]}"
    end

safe_load_marshal (data)
Source

    # File lib/bundler.rb, line 526
    def safe_load_marshal(data)
      if Gem.respond_to?(:load_safe_marshal)
        Gem.load_safe_marshal
        begin
          Gem::SafeMarshal.safe_load(data)
        rescue Gem::SafeMarshal::Reader::Error, Gem::SafeMarshal::Visitors::ToRuby::Error => e
          raise MarshalError, "#{e.class}: #{e.message}"
        end
      else
        load_marshal(data, marshal_proc: SafeMarshal.proc)
      end
    end

self_manager ()
Source

    # File lib/bundler.rb, line 635
    def self_manager
      @self_manager ||= begin
                          require_relative "bundler/self_manager"
                          Bundler::SelfManager.new
                        end
    end

settings ()
Source

    # File lib/bundler.rb, line 359
    def settings
      @settings ||= Settings.new(app_config_path)
    rescue GemfileNotFound
      @settings = Settings.new
    end

setup (*groups)
Source

    # File lib/bundler.rb, line 157
    def setup(*groups)
      # Return if all groups are already loaded
      return @setup if defined?(@setup) && @setup

      definition.validate_runtime!

      SharedHelpers.print_major_deprecations!

      if groups.empty?
        # Load all groups, but only once
        @setup = load.setup
      else
        load.setup(*groups)
      end
    end

Turns on the `Bundler` runtime. After `Bundler.setup` call, all `load` or `require` of the gems would be allowed only if they are part of the Gemfile or Ruby’s standard library. If the versions specified in Gemfile, only those versions would be loaded.

Assuming Gemfile

    gem 'first_gem', '= 1.0'
    group :test do
      gem 'second_gem', '= 1.0'
    end

The code using `Bundler.setup` works as follows:

    require 'third_gem' # allowed, required from global gems
    require 'first_gem' # allowed, loads the last installed version
    Bundler.setup
    require 'fourth_gem' # fails with LoadError
    require 'second_gem' # loads exactly version 1.0

`Bundler.setup` can be called only once, all subsequent calls are no-op.

If groups list is provided, only gems from specified groups would be allowed (gems specified outside groups belong to special `:default` group).

To require all gems from Gemfile (or only some groups), see `Bundler.require`.

specs_path ()
Source

    # File lib/bundler.rb, line 317
    def specs_path
      bundle_path.join("specifications")
    end

system_bindir ()
Source

    # File lib/bundler.rb, line 475
    def system_bindir
      # Gem.bindir doesn't always return the location that RubyGems will install
      # system binaries. If you put '-n foo' in your .gemrc, RubyGems will
      # install binstubs there instead. Unfortunately, RubyGems doesn't expose
      # that directory at all, so rather than parse .gemrc ourselves, we allow
      # the directory to be set as well, via `bundle config set --local bindir foo`.
      Bundler.settings[:system_bindir] || Bundler.rubygems.gem_bindir
    end

tmp (name = Process.pid.to_s)
Source

    # File lib/bundler.rb, line 350
    def tmp(name = Process.pid.to_s)
      Kernel.send(:require, "tmpdir")
      Pathname.new(Dir.mktmpdir(["bundler", name]))
    end

ui ()
Source

    # File lib/bundler.rb, line 93
    def ui
      (defined?(@ui) && @ui) || (self.ui = UI::Shell.new)
    end

ui= (ui)
Source

    # File lib/bundler.rb, line 97
    def ui=(ui)
      Bundler.rubygems.ui = UI::RGProxy.new(ui)
      @ui = ui
    end

unbundle_env! ()
Source

    # File lib/bundler.rb, line 388
    def unbundle_env!
      ENV.replace(unbundle_env(ENV))
    end

Remove all bundler-related variables from `ENV`

unbundled_env ()
Source

    # File lib/bundler.rb, line 383
    def unbundled_env
      unbundle_env(original_env)
    end

@return [Hash] Environment with all bundler-related variables removed

unbundled_exec (*args)
Source

    # File lib/bundler.rb, line 454
    def unbundled_exec(*args)
      with_env(unbundled_env) { Kernel.exec(*args) }
    end

Run a ‘Kernel.exec` to a subcommand in an environment with all bundler related variables removed

unbundled_system (*args)
Source

    # File lib/bundler.rb, line 432
    def unbundled_system(*args)
      with_unbundled_env { Kernel.system(*args) }
    end

Run subcommand in an environment with all bundler related variables removed

use_system_gems? ()
Source

    # File lib/bundler.rb, line 488
    def use_system_gems?
      configured_bundle_path.use_system_gems?
    end

user_bundle_path (dir = "home")
Source

    # File lib/bundler.rb, line 287
    def user_bundle_path(dir = "home")
      env_var, fallback = case dir
                          when "home"
                            ["BUNDLE_USER_HOME", proc { Pathname.new(user_home).join(".bundle") }]
                          when "cache"
                            ["BUNDLE_USER_CACHE", proc { user_bundle_path.join("cache") }]
                          when "config"
                            ["BUNDLE_USER_CONFIG", proc { user_bundle_path.join("config") }]
                          when "plugin"
                            ["BUNDLE_USER_PLUGIN", proc { user_bundle_path.join("plugin") }]
                          else
                            raise BundlerError, "Unknown user path requested: #{dir}"
      end
      # `fallback` will already be a Pathname, but Pathname.new() is
      # idempotent so it's OK
      Pathname.new(ENV.fetch(env_var, &fallback))
    end

user_cache ()
Source

    # File lib/bundler.rb, line 305
    def user_cache
      user_bundle_path("cache")
    end

user_home ()
Source

    # File lib/bundler.rb, line 263
    def user_home
      @user_home ||= begin
        home = Bundler.rubygems.user_home
        bundle_home = home ? File.join(home, ".bundle") : nil

        warning = if home.nil?
          "Your home directory is not set."
        elsif !File.directory?(home)
          "`#{home}` is not a directory."
        elsif !File.writable?(home) && (!File.directory?(bundle_home) || !File.writable?(bundle_home))
          "`#{home}` is not writable."
        end

        if warning
          Bundler.ui.warn "#{warning}\n"
          user_home = tmp_home_path
          Bundler.ui.warn "Bundler will use `#{user_home}' as your home directory temporarily.\n"
          user_home
        else
          Pathname.new(home)
        end
      end
    end

which (executable)
Source

    # File lib/bundler.rb, line 498
    def which(executable)
      executable_path = find_executable(executable)
      return executable_path if executable_path

      if (paths = ENV["PATH"])
        quote = '"'
        paths.split(File::PATH_SEPARATOR).find do |path|
          path = path[1..-2] if path.start_with?(quote) && path.end_with?(quote)
          executable_path = find_executable(File.expand_path(executable, path))
          return executable_path if executable_path
        end
      end
    end

with_clean_env () { || ... }
Source

    # File lib/bundler.rb, line 398
    def with_clean_env
      message =
        "`Bundler.with_clean_env` has been deprecated in favor of `Bundler.with_unbundled_env`. " \
        "If you instead want the environment before bundler was originally loaded, use `Bundler.with_original_env`"
      removed_message =
        "`Bundler.with_clean_env` has been removed in favor of `Bundler.with_unbundled_env`. " \
        "If you instead want the environment before bundler was originally loaded, use `Bundler.with_original_env`"
      Bundler::SharedHelpers.major_deprecation(2, message, removed_message: removed_message, print_caller_location: true)
      with_env(unbundled_env) { yield }
    end

@deprecated Use ‘with_unbundled_env` instead

with_original_env () { || ... }
Source

    # File lib/bundler.rb, line 393
    def with_original_env
      with_env(original_env) { yield }
    end

Run block with environment present before `Bundler` was activated

with_unbundled_env () { || ... }
Source

    # File lib/bundler.rb, line 410
    def with_unbundled_env
      with_env(unbundled_env) { yield }
    end

Run block with all bundler-related variables removed

### Private Class Methods

configure_gem_home (path)
Source

    # File lib/bundler.rb, line 699
    def configure_gem_home(path)
      Bundler::SharedHelpers.set_env "GEM_HOME", path.to_s
    end

configure_gem_path ()
Source

    # File lib/bundler.rb, line 690
    def configure_gem_path
      unless use_system_gems?
        # this needs to be empty string to cause
        # PathSupport.split_gem_path to only load up the
        # Bundler --path setting as the GEM_PATH.
        Bundler::SharedHelpers.set_env "GEM_PATH", ""
      end
    end

eval_gemspec (path, contents)
Source

    # File lib/bundler.rb, line 682
    def eval_gemspec(path, contents)
      eval(contents, TOPLEVEL_BINDING.dup, path.expand_path.to_s)
    rescue ScriptError, StandardError => e
      msg = "There was an error while loading `#{path.basename}`: #{e.message}"

      raise GemspecError, Dsl::DSLError.new(msg, path.to_s, e.backtrace, contents)
    end

eval_yaml_gemspec (path, contents)
Source

    # File lib/bundler.rb, line 674
    def eval_yaml_gemspec(path, contents)
      Kernel.require "psych"

      Gem::Specification.from_yaml(contents)
    rescue ::Psych::SyntaxError, ArgumentError, Gem::EndOfYAMLException, Gem::Exception
      eval_gemspec(path, contents)
    end

load_marshal (data, marshal_proc: nil)
Source

    # File lib/bundler.rb, line 668
    def load_marshal(data, marshal_proc: nil)
      Marshal.load(data, marshal_proc)
    rescue TypeError => e
      raise MarshalError, "#{e.class}: #{e.message}"
    end

tmp_home_path ()
Source

    # File lib/bundler.rb, line 703
    def tmp_home_path
      Kernel.send(:require, "tmpdir")
      SharedHelpers.filesystem_access(Dir.tmpdir) do
        path = Bundler.tmp
        at_exit { Bundler.rm_rf(path) }
        path
      end
    end

unbundle_env (env)
Source

    # File lib/bundler.rb, line 644
    def unbundle_env(env)
      if env.key?("BUNDLER_ORIG_MANPATH")
        env["MANPATH"] = env["BUNDLER_ORIG_MANPATH"]
      end

      env.delete_if {|k, _| k[0, 7] == "BUNDLE_" }
      env.delete("BUNDLER_SETUP")

      if env.key?("RUBYOPT")
        rubyopt = env["RUBYOPT"].split(" ")
        rubyopt.delete("-r#{File.expand_path("bundler/setup", __dir__)}")
        rubyopt.delete("-rbundler/setup")
        env["RUBYOPT"] = rubyopt.join(" ")
      end

      if env.key?("RUBYLIB")
        rubylib = env["RUBYLIB"].split(File::PATH_SEPARATOR)
        rubylib.delete(__dir__)
        env["RUBYLIB"] = rubylib.join(File::PATH_SEPARATOR)
      end

      env
    end

with_env (env) { || ... }
Source

    # File lib/bundler.rb, line 713
    def with_env(env)
      backup = ENV.to_hash
      ENV.replace(env)
      yield
    ensure
      ENV.replace(backup)
    end

@param env [Hash]