Automating bundle exec

Bundler is a program for installing the libraries (i.e. gems) that a Ruby application depends on, in a sandbox. By specifying an application's dependencies with Bundler developers can guarantee that only specific versions of the dependencies are used when the application is running on a user's computer, or when deployed to a web server.

Quick instructions

Before I get into the nitty gritty of how it works, here are the quick installation instructions:

$ curl -L https://github.com/gma/bundler-exec/raw/master/bundler-exec.sh > ~/.bundler-exec.sh
$ echo "[ -f ~/.bundler-exec.sh ] && source ~/.bundler-exec.sh" >> ~/.bashrc

Read more on the GitHub page.

How bundler-exec works

Once bundler-exec is configured, it replaces a bunch of common Ruby commands (e.g. rake, ruby, rails, rspec) with a shell function that will:

  1. Check whether your current directory is within a bundled project (it checks for a Gemfile in your current directory, or one of its parents), and
  2. Run the command you entered, prefixing it with bundle exec if appropriate.

It does this with the magic of shell aliases (and therefore only works with Bourne style shells, or shells that support the alias command). Browse the code on GitHub; it's very simple stuff. The full list of commands that it looks after for you is currently:

  • cap
  • capify
  • cucumber
  • heroku
  • rackup
  • rails
  • rake
  • rspec
  • ruby
  • shotgun
  • spec
  • spork
  • thin
  • unicorn
  • unicorn_rails

You can easily override that list by setting BUNDLED_COMMANDS in your ~/.bashrc file before ~/.bundler-exec.sh gets loaded, like this:

BUNDLED_COMMANDS="ruby rails"
[ -f ~/.bundler-exec.sh ] && source ~/.bundler-exec.sh

If you need to add commands that are in common use, please fork the project, add them to the script, and send me a pull request. Cheers!

To check whether bundler-exec is configured, check what your shell will do if you run Ruby. It should tell you that Ruby has been aliased, like this:

$ type ruby
ruby is aliased to `run-with-bundler ruby'

What can go wrong if you forget bundle exec?

Remembering to run bundle exec is a pain, can be easy to forget, and no end of subtle bugs can occur if you forget it.

Imagine that your project requires version 1.0.0 of a gem, but you've got both 1.0.0 and 1.0.1 installed. It all goes south like this:

  • Your shell will find the command that shipped with 1.0.1, and run it.
  • The command will load your application code, which calls Bundler.require or Bundler.setup.
  • Bundler (quite rightly) complains when it tries to load 1.0.0 and finds that 1.0.1 is "already activated").

Avoid the ball ache, get bundler-exec!

I love feedback and questions — please feel free to get in touch on Mastodon or Twitter, or leave a comment.