Dealing with dependencies -- multiple versions of Ruby and multiple versions of Gems -- is a significant issue in Ruby. A project may need a Ruby version that differs from your default Ruby. Even if it requires the same version of Ruby, it may need a different version of a RubyGem.
This problem is not unique to Ruby; dependency issues arise in all languages. The techniques used to deal with the dilemma differ with each language. In Ruby, most developers use a Ruby version manager such as RVM or rbenv to manage multiple Ruby versions. You can also use your version manager to manage Gem dependencies, but the favored approach is to use a dependency manager.
The most widely used dependency manager in the Ruby community, by far, is the Bundler Gem. This Gem lets you configure which Ruby and which Gems each of your projects need.
In this chapter, we use the term app or application to refer to any Ruby code that you run or use with Bundler. Note that this includes Gems that you develop yourself, as well as full-blown applications.
You can skip this section if you're using Ruby version 2.5 or higher: recent versions of Ruby install Bundler automatically.
Bundler is a Gem, so you must use the
gem command to install it. If you use a Ruby version manager, you must install the Gem in each version of Ruby for which you wish to use Bundler. After switching to the appropriate Ruby you wish to use, use this command to install the Bundler Gem:
$ gem install bundler
That's all there is to it.
Bundler relies on a text file named
Gemfile to tell it which version of Ruby and its Gems it should use. This file is a simple Ruby program that uses a Domain Specific Language (DSL) to provide details about the Ruby and Gem versions. It's the configuration or instruction file for Bundler.
After you create
bundle install command scans it, downloads and installs all the dependencies listed, and produces a
Gemfile.lock shows all the dependencies for your program; this includes the Gems listed in
Gemfile, as well as the Gems they depend on (the dependencies), which may not be explicitly listed in the
Gemfile. It's very common for RubyGems you install for use in your project to rely on many other gems, creating a large dependency tree.
Lets examine a simple example. Suppose you are writing a program that requires Ruby 2.3.1 and the
rack Gems. Our
Gemfile incorporates these dependencies, and looks like this:
source 'https://rubygems.org' ruby '2.3.1' gem 'sinatra' gem 'erubis' gem 'rack' gem 'rake'
Furthermore, our ruby installation looks like Figure 5:
$ tree /usr/local/rvm # the following is partial output /usr/local/rvm # RVM path directory └── gems ├── ruby-2.2.2 └── ruby-2.3.1 ├── bin │ ├── bundle │ └── rubocop └── gems ├── erubis-2.7.0 ├── rack-1.6.4 ├── rack-protection-1.5.3 ├── rake-10.4.2 ├── rake-11.3.0 ├── sinatra-1.4.6 ├── sinatra-1.4.7 └── tilt-2.0.5
We now run:
$ bundle install
to install the specified Gems (if needed) and create a
Gemfile.lock, which looks like this:
GEM remote: https://RubyGems.org/ specs: erubis (2.7.0) rack (1.6.4) rack-protection (1.5.3) rack rake (10.4.2) sinatra (1.4.7) rack (~> 1.5) rack-protection (~> 1.4) tilt (>= 1.3, < 3) tilt (2.0.5) PLATFORMS ruby DEPENDENCIES erubis rack rake sinatra RUBY VERSION ruby 2.3.1p112 BUNDLED WITH 1.13.6
Note that the Gem is named Bundler, but the command you use is
bundle (without the
bundler are aliases: either works, but most documentation uses
specs section under the
GEM heading provides a list of the Gems (and their versions) that your app will load. Beneath each listed Gem is a list of the Gem's dependencies; that is, the Gems and versions it needs to work. Here, we can see that:
sinatraversion 1.4.7. Note that we chose 1.4.7 over 1.4.6. Bundler won't always choose the latest version like this. It will choose a version that works in conjunction with the other dependencies.
rack(version >= 1.5.0 and < 2.0.0); we have version 1.6.4.
rack-protection(version >= 1.4.0 and < 2.0.0); we have version 1.5.3.
tilt(versions >= 1.3.0 and < 3.0.0); we have version 2.0.5
We didn't have to provide any information about
tilt in our
Gemfile; Bundler found this information on its own by examining the
Gemfiles for those Gems -- that is, not our application's
Gemfile, but the
Gemfile that came with the Gems specified in our
Gemfile. It then added the information to our
If you're interested in even more details about how the
Gemfile works, see the Bundler documentation for more information on how to construct a
Once Bundler creates your
to the beginning of your app, before any other Gems. (This is unneeded if your app is a Rails app).
bundler/setup first removes all Gem directories from Ruby's
$LOAD_PATH global array. Ruby uses
$LOAD_PATH to list the directories that it searches when it needs to locate a required file. When
bundler/setup removes those directories from
$LOAD_PATH, Ruby can no longer find Gems.
To fix this,
Gemfile.lock; for each Gem listed, it adds the directory that contains that Gem back to
$LOAD_PATH. When finished,
require only finds the proper versions of each Gem. This ensures that the specific Gem and version your app depends on is loaded, and not a conflicting version of that Gem.
Now, all you need to do is run your app and the correct Gem will be loaded when you
You may see some advice to add:
to your apps. However, this is unnecessary. This statement is a holdover from the days before RubyGems became an official part of Ruby; Ruby now provides this functionality automatically.
Bundler does not interfere with your Rubies nor their Gems. They remain where they were before you installed Bundler, and will continue to use the same setup in the future. This means that you can still use
rvm info, and
rbenv version and other informational commands to find information you may need.
However, Bundler provides a feature called
binstubs; if you use this feature, you may have to add some directories to your
gem env and
rvm info commands will reflect this.
The Bundler development team has taken great care to work with Ruby version managers to ensure things still work as expected. Once in a while, you might experience some odd issue, especially after upgrading your Ruby version manager. These issues are usually resolved quickly by the Ruby community.
As we saw earlier, an app that relies on Bundler should
bundler/setup package before it loads any Gems. This package ensures that the app loads the desired Gems.
Unfortunately, you will surely encounter situations where you can't just add
require 'bundler/setup' to the code, or the program itself may run code that has conflicting needs. When this happens, you need the often mysterious
bundle exec command.
You can use
bundle exec to run most any command in an environment that conforms to the
Gemfile.lock versioning info. In fact, we can use this feature to see how
bundle exec modifies your environment:
# This command compares the output of 'bundle exec env' with the output of 'env' $ diff <(bundle exec env) <(env) < PATH=/usr/local/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bin:/usr/local/rbenv/versions/2.3.1/bin:/usr/local/Cellar/rbenv/1.0.0/libexec:/usr/local/rbenv/shims:... --- > PATH=/usr/local/rbenv/shims:... < RBENV_HOOK_PATH=/usr/local/rbenv/rbenv.d:/usr/local/Cellar/rbenv/1.0.0/rbenv.d:/usr/local/etc/rbenv.d:/etc/rbenv.d:/usr/lib/rbenv/hooks:/usr/local/rbenv/plugins/rbenv-default-gems/etc/rbenv.d < RBENV_DIR=/Users/wolfy/my_app < RUBYLIB=/usr/local/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/bundler-1.13.6/lib:/usr/local/Cellar/rbenv/1.0.0/rbenv.d/exec/gem-rehash < RBENV_VERSION=2.3.1 < BUNDLE_BIN_PATH=/usr/local/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/bundler-1.13.6/exe/bundle < BUNDLE_GEMFILE=/Users/wolfy/my_app/Gemfile < RUBYOPT=-rbundler/setup
(The above shows partial output only.) As you can see, the two commands produce different results; this output shows the modifications and additions that
bundle exec makes to your environment.
Of special importance is the
RUBYOPT value: this tells Ruby to
require 'bundler/setup' (recall that
'bundler' are aliases) before it starts running your code. This lets Bundler gain control in time to configure things so that the app loads the proper Gems, and only those Gems.
We use it above with
env primarily to demonstrate what
bundle exec does. Using
bundle exec with a non-Ruby command is rare, though. You usually use
bundle exec with commands written in Ruby and installed as Gems, e.g., Rake, Pry, and Rackup. But, exactly when would one need to use it?
We use it to resolve dependency conflicts when issuing shell commands. From time to time, you may encounter an error message that looks like this:
Gem::LoadError: You have already activated rake 11.3.0, but your Gemfile requires rake 10.4.2. Prepending `bundle exec` to your command may solve this.
This error usually appears when you use a Gem command whose version differs from the Gem version in your
Gemfile. For example, let's say your default version of
rake is version 11.3.0, but you're in a directory where the
Gemfile wants version 10.4.2. This is the situation in Figure 5 and our
When you run
rake from the command line, your system will find and execute
rake version 11.3.0; your shell doesn't know about
Gemfiles, so it just invokes the version of
rake it finds in the
rake sometimes runs code that Bundler manages but isn't part of
rake, and that's where things get ugly. When that code runs, it checks your
Gemfile.lock, and sees that it needs
rake 10.4.2, so it tries to load and run it. Unfortunately,
rake is already running, but it is version 11.3.0. Since you can't run two versions of
rake in the same process, the
require fails with a
Don't worry if your head is suddenly spinning; you don't need to understand why the error occurs. What's important is that you understand the fix, which we'll discuss next.
Fortunately, the solution is easy: the error message tells you what to do. All you have to do is run the command with
$ bundle exec rake
This changes the environment so that
rake 10.4.2 runs instead of your system default, 11.3.0; now, when
rake runs the external code, Bundler sees that you are already running 10.4.2, so everything is okay, and execution continues.
rake and other executables are the main reason to use
bundle exec; it's easy to find advice that says "always use
bundle exec rake", and this is good advice. However, this problem can happen with other commands as well. Any Gem command that requires other Gems may load a Gem that conflicts with your app's requirements.
bundle exec is the easiest way to fix this issue.
Earlier, we mentioned the
binstubs is an alternative to using
bundle exec. It sets up a directory of short Ruby scripts (wrappers) with the same names as executables installed by your Gems. By default,
binstubs names this directory as
bin, but you should override that if your app also needs a
bin directory of its own.
The scripts in the
binstubs-provided directory effectively replace
bundle exec; if you include the directory in your
PATH, you can avoid using
binstubs feature only installs wrappers for the Gems listed by
Gemfile.lock. It skips executables for unlisted Gems. This can be an issue with Gems that you don't require in your apps, but use externally. For example, Rubocop and Pry. For the time being, we recommend sticking with
bundle exec and not worrying about
binstubs; it's mostly mentioned here in case you're debugging a tricky issue and that's one place to double check.
In all likelihood, you will use Bundler to solve problems; it won't usually create problems. However, that doesn't mean that things won't go wrong.
We've already talked about one major problem that you will surely encounter:
Gem::LoadError: You have already activated ...
This error merely tells you that you need to use
bundle exec to run the command.
Another common issue occurs when you try to run your app, and you get something like this:
in `require': cannot load such file -- colorize (LoadError)
This message means that
bundler/setup can't find the named Gem (
colorize here). However, you've confirmed that the Gem is installed, has the proper permissions, and you're using the proper version of Ruby and the
gem command. The problem here is that the
Gemfile.lock file doesn't list the
bundler/setup insists that your
Gemfile.lock contains all needed Gems. To add this Gem to yours, add it to your
Gemfile, then run
bundle install again to generate a new
Another potential issue is that you may use the wrong version of the
bundler command. Remember that Bundler is a RubyGem, and every Ruby version on your system has its own Gems; this includes the
bundle command. If you use
bundle from version 2.2.2 of Ruby when you mean to use Ruby 2.3.1, you may end up with unexpected results. For instance, if your
Gemfile lists a specific version of a Gem that only runs under Ruby 2.3.0 or higher, the
bundle command will fail to find a Gem that meets that requirement. Make sure you use the correct version of
Here are some more things to try if problems continue:
Gemfile.lock and run
bundle install again. This creates a new
.bundle directory and its contents from your project directory and run
bundle install again.
If you're using the
binstubs feature, remove the directory used by
binstubs and run
bundle install --binstubs again. Don't do this if you aren't using
Remove and reinstall Bundler:
$ gem uninstall bundler $ gem install bundler
gem list shows that either
open_gem are installed, uninstall them. These old Gems are incompatible with Bundler. Repeat the above items if you remove either Gem.
Issue this command in the command line from your app's top-level directory:
$ rm Gemfile.lock ; DEBUG_RESOLVER=1 bundle install
This command removes the
Gemfile.lock file, then runs
bundle install while producing debug information. You can use the debug information to see how Bundler resolves each Gem. This can be valuable when you aren't sure if your app is loading the correct Gems. Note that you must include the
rm Gemfile.lock part; this mode only produces useful output when
Gemfile.lock doesn't exist. For additional information on how Bundler's "resolver" works, see How Does Bundler Bundle.
For a little bit of history about Ruby, RubyGems, and Bundler, check out this informative article.
Bundler lets you describe exactly which Ruby and Gems you want to use with your Ruby apps. Specifically, it lets you install multiple versions of each Gem under a specific version of Ruby and then use the proper version in your app.
Bundler is a RubyGem, so you must install it like a normal Gem:
gem install bundler.
To use Bundler, you provide a file named
Gemfile that describes the Ruby and Gem versions you want for your app. You use a DSL described on the Bundler website to provide this information.
Bundler uses the
Gemfile to generate a
Gemfile.lock file via the
bundle install command.
Gemfile.lock describes the actual versions of each Gem that your app needs, including any Gems that the Gems listed in
Gemfile depend on. The
bundler/setup package tells your Ruby program to use
Gemfile.lock to determine which Gem versions it should load.
bundle exec command ensures that executable programs installed by Gems don't interfere with your app's requirements. For instance, if your app needs a specific version of
rake but the default version of
bundle exec ensures that you can still run the specific
rake version compatible with your app.
In the next chapter, we'll take a look at Rake, Ruby's answer to the long time Unix development tool, Make. Rake lets you automate a lot of tasks common to many Ruby development projects.