Using Bundler

Imagine you’d want, for some reason, use an older version of a particular gem, rather than the latest one.

Maybe the authors of this gem have made a change that breaks your application. Of course they’d normally try to avoid this at all costs, but sometimes it still happens. (Another, maybe more common, case is that you are working on a Rails application that has been started a year ago, or is even older than that. Now a new major version of Rails comes out, and ships lots of changes that would break this application. So you want to stick to the older version of Rails until you get around making it ready for the newer one.)

One way to use an older version of a particular gem is to simply uninstall any newer versions from your system. E.g. you could run:

gem uninstall rails
gem install rails --version '~> 3'

This would uninstall all rails gems, and then install the latest version of Rails that still starts with a 3 (i.e. ignoring all the newer versions that, as time of this writing, start with a 4).

However, this is quite cumbersome. You’d need to figure out what versions of all the gems that you want to use are compatible with each other. And if you ever switch from one application to another you’d have to uninstall and reinstall all those gems over and over.

Instead, Bundler provides a much better way of picking certain gem versions out of the plethora of gems that might be installed on your computer.

In this example we are going to use the Chronic gem, which provides a great way to convert natural language date formats to Ruby Time objects.

Inside a new directory chronic create a file Gemfile, and add the following lines:

source "https://rubygems.org"

gem "chronic", "~> 0.9.0"

The source directive says that we’d like to use the standard RubyGems.org as a source for our gems. There are other sources, such as private Rubygems servers run by companies to host their own, internal gems, or Rubygems servers that people run in order to sell paid versions of their gems.

The gem directive declares that we’d like to use the gem chronic, and we’d like to use any version that starts with 0.9 (for whatever reason).

Now, make sure you have cded to that directory, and run:

bundle install

You’ll see that Bundler first fetches some meta data from RubyGems.org. This means it checks what versions of the Chronic gem are available, what dependencies it has, and what dependencies these might have.

In our case Chronic doesn’t have any other dependencies, and so Bundler just installs it. You will see that it installs a version starting with 0.9 (as time of this writing that would be 0.9.1).

In order to check what gems are part of your bundle you can run bundle show. You can also open the file Gemfile.lock that Bundler has created.

With this we’ve created a little gems sandbox that makes, no matter what other gems are available on your system, just two gems available to our Ruby code: Chronic, and Bundler itself.

Lets have a look at the load path to confirm that:

bundle exec ruby -e 'puts $LOAD_PATH'

You’ll se a bunch of directories that belong to your Ruby installation, and make its standard library available. But you’ll also see two directories that end with something similar to this:

gems/chronic-0.9.1/lib
gems/bundler-1.9.6/lib

This means that any code that we run using our bundle (i.e. standing in this directory, and using bundle exec as a prefix) will be able to require, and use the Chronic library.

Let’s try that. Create a new file 19-bundler.rb in this directory, and add the following lines:

require "chronic"

time = Chronic.parse('tomorrow')
p time.class
p time

This will output something like this:

Time
2015-05-15 12:00:00 +0200

You can see that the method parse takes a natural language string, and converts it into an instance of the Ruby class Time (see here for the documentation), so we can now use this object’s methods to do more interesting stuff. E.g. let’s look up which month that is:

require "chronic"

time = Chronic.parse('tomorrow')
puts time.month

Let’s say for some reason we’d now want to upgrade the Chronic gem, and use the latest version in the newer 0.10 series. Maybe the authors have added some new functionality that we’d like to use, or they’ve fixed a certain bug.

In order to do this we need to allow Bundler using the 0.10 series of versions. So in your Gemfile change ~> 0.9.0 to ~> 0.10.0.

If you now run:

bundle update chronic

You’ll see that it now installs a newer version of this gem. Also, it has changed the version number in the Gemfile.lock file.

Bundler is a really cool piece of software. It is one of those things that, once you’ve used it for a while, wonder how life was possible before it existed (“How on earth did people meet before there were cellphones?!?”). If you ask any seasoned Ruby developer how their life was before Bundler, they’ll probably respond “Quite miserable”, or maybe give you a blank stare and say “Ummm, I forgot”.