How To Update Gems In Your Gemfile

I've been helping a reader of mine get their Jekyll site working, and one thing I recommended was to update Jekyll to the latest version. I pointed to two ways to find the latest version of a gem:

Gem                    Current  Latest  Requested  Groups
i18n                   0.9.5    1.8.8
jekyll                 3.8.7    4.2.0   ~> 3.8.5   default
jekyll-sass-converter  1.5.2    2.1.0
kramdown               1.17.0   2.3.0
liquid                 4.0.3    5.0.0
mercenary              0.3.6    0.4.0

In their Gemfile, Jekyll was listed like this:

gem "jekyll", "~> 3.8.5"

You can also see this same version in the Requested column above.

So then they ran bundle update, which listed all the gems, and said this about Jekyll:

Using jekyll 3.8.7

They were confused as to why 3.8.7 was being installed, and they were also wondering if they should stick with 3.8.5 since that was the version being requested. Let's learn how Gemfile versioning works to clear things up.

Gemfile versioning

What does the ~> symbol before 3.8.5 mean? That's known as the "pessimistic operator" in Ruby, and it means that the requested version of Jekyll is at least 3.8.5, but less than 3.9.0. When there are 3 digits in the version number, using ~> means give me any version that starts with the first 2 digits (3.8 in this case), but is at least the one specified (3.8.5). This tells Bundler that when I run bundle update, I only want versions that start with 3.8, even if there are newer ones that start with 3.9 or 4.

If we look at all the Jekyll versions in Rubygems, we can see that the last version that starts with 3.8 is 3.8.7, which is why my reader can never get a version higher than that as long as they continue to specify ~> 3.8.5. To get version 4.2.0, they could use one of these versioning methods:

The pessimistic operator is the most common. I recommend using at least 2 digits to get small updates at a time, and so you can take advantage of new features and fixes before you upgrade to the next major version. The standard versioning system that many projects use is Semantic Versioning, so for the most part, you can expect that as long as the first digit doesn't change, your code should still work. If something breaks, and you don't have time to figure out how to make it work with the latest version, go back to the previous version and specify 3 digits.

Version conflicts

It's important to note that just because you specify a newer version number doesn't necessarily mean it can be installed. Most gems in your Gemfile will also depend on other gems. If you go back to the Jekyll page in Rubygems, you will see all the gems it depends on under "Runtime Dependencies". This means that when you install Jekyll, you are also installing a bunch of other gems, and the gems that they depend on, and so on. Bundler has a handy command to see all the gems your project installs:

bundle list

You can also look in your Gemfile.lock to see the exact versions of gems that depend on each other. Sometimes, a gem in your project will prevent you from updating another gem. For example, the latest version of the github-pages gem uses = 3.9.0 for Jekyll, which means exactly 3.9.0. You can't use any other version of Jekyll with the github-pages gem. So, if you tried to update Jekyll with both gems specified in your Gemfile like this:

gem "jekyll", "~> 4.2.0"
gem "github-pages", "211"

you would get an error like this after running bundle update:

Bundler could not find compatible versions for gem "jekyll":
  In Gemfile:
    jekyll (~> 4.2.0)

    github-pages (= 211) was resolved to 211, which depends on
      jekyll (= 3.9.0)

If you run into a similar situation, you could try removing the gem, looking for an alternative gem, or asking the maintainers if they would be willing to make their gem compatible with newer versions of their dependency. Most gems live on GitHub, where you can open an issue in the repo. Bundler has another nifty command for finding where a project lives on the web and where to report bugs or other issues:

bundle info [name of gem]

For example, bundle info jekyll shows this:

Summary: A simple, blog aware, static site generator.
Homepage: https://jekyllrb.com
Source Code: https://github.com/jekyll/jekyll
Changelog: https://github.com/jekyll/jekyll/releases
Bug Tracker: https://github.com/jekyll/jekyll/issues

Exact versioning

While you'll probably use the pessimistic operator most of the time, there are situations where you'll want to use an exact version. For example, let's say you have a gem like this:

gem "kaminari-actionview", "~> 1.1"

You currently use version 1.1.1, and it works with your Rails app. You notice that there is a new version 1.2.0, so you update just this gem:

bundle update kaminari-actionview

Updating one gem at a time and making sure your app still works after each update makes it easier to troubleshoot issues. If you update all your gems at once, and your app stops working, it could be hard to figure out which gem caused the problem.

But then your Rails app stops working. You undo the changes that this made to your Gemfile.lock, which allows you to go back to version 1.1.1. You then open a bug in the kaminari repo, but you have to wait for them to fix it. In the meantime, if you don't change your Gemfile, every time you try to update this gem, as long as the bug hasn't been fixed yet, you'll run into the same issue and will have to undo the changes.

While you wait for the fix, you can pin the gem to exactly version 1.1.1, so Bundler won't try to update it:

gem "kaminari-actionview", "= 1.1.1"

Always specify a version

Another scenario that could lead to errors or confusion is when you specify gems without a version number in the Gemfile, like this:

gem "jekyll"

This will attempt to install the latest compatible version, which could be an older version, and it won't warn you that it installed an older version. If you were expecting the latest version, you might wonder why things aren't working. Another reader of mine ran into this exact situation, and in an upcoming post, I will show you how I helped him debug it.

If you are running into any coding issues, don't be shy to contact me by email or Twitter, and I will do my best to solve them and explain them here on my site so everyone can benefit.