Posted over 6 years ago. Visible to the public.

How to discard a surrounding Bundler environment

tl;dr: Ruby's Bundler environment is passed on to system calls, which may not be what you may want as it changes gem and binary lookup. For a bundler-free environment, use Bundler.with_clean_env. Do this whenever you want to execute shell commands inside other bundles.

Example outline

Consider this setup:

Copy
my_project/Gemfile # says: gem 'rails', '~> 3.0.0' my_project/foo/Gemfile # says: gem 'rails', '~> 3.2.0'

And, just to confirm this, these are the installed Rails versions for each of the bundles:

Copy
~/my_project$ bundle show rails .../gems/rails-3.0.20 ~/my_project$ cd foo && bundle show rails .../gems/rails-3.2.13

Now you will usually just use bundle exec to run stuff, which is good as it makes sure you are using the gems from the bundle. Here is how it would look like:

Copy
~/my_project$ bundle exec ruby -e "require %(rails) ; puts Rails.version" 3.0.20 ~/my_project$ cd foo && bundle exec ruby -e "require %(rails) ; puts Rails.version" 3.2.13

So far, so good. \
FYI, we're using %(...) instead of '...' here because we don't want to escape quotes for the example below.

Bundler + shell execution from Ruby = fun

Now let's see what happens when we do the above by executing the second command via Ruby:

Copy
~/my_project$ bundle exec ruby -e 'system %( cd foo && bundle exec ruby -e "require %(rails) ; puts Rails.version" )' 3.0.20

Well, that is not what we want. That should have been 3.2.13 since we're saying bundle exec from the 3.2 bundle.

Why it happens and how to fix it

To change some gem and binary lookup, Bundler defines these environment variables:

  • BUNDLE_BIN_PATH
  • BUNDLE_GEMFILE
  • RUBYOPT

Basically, that environment is being passed on the child calls, so when you say bundle exec and then inside make system calls.

So Bundler's environment still prevails when running shell commands from Ruby. On the one hand, this is good as you'd expect your bundle to deliver all you need. However, it will come bite you when you try to run code that has its own bundle.

If you need to do exactly that, Bundler supplies a method that will give you a bundler-free environment, which is just what we need: Bundler.with_clean_env.

Copy
~/my_project$ bundle exec ruby -e 'Bundler.with_clean_env { system %( cd foo && bundle exec ruby -e "require %(rails) ; puts Rails.version" ) }' 3.2.13
Growing Rails Applications in Practice
Check out our new e-book:
Learn to structure large Ruby on Rails codebases with the tools you already know and love.

Owner of this card:

Avatar
Arne Hartherz
Last edit:
over 6 years ago
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Arne Hartherz to makandra dev
This website uses cookies to improve usability and analyze traffic.
Accept or learn more