As beginning Ruby programmers, we often use gems subconsciously. We can throw binding.prys left and right to debug our code, but it is possible to get deeper into the landscape of Ruby without truly understanding what the Pry gem really is or where it comes from. Since starting working with Ruby at the Flatiron School, I've noticed our labs and projects have included Gemfiles with longer and longer lists of gems, and we have begun using increasingly powerful gems such as ActiveRecord, Sinatra and Rails. I realized that gems add a practically infinite amount of free leverage to our Ruby code, but many aspects still seemed mysterious. Before continuing into the journey of Ruby-On-Rails, I wanted to make sure I had a solid grasp on what gems are so that I know how to maximize their potential and also manage bugs ... hence, this blog post!
While we won't delve much into the functionality of individual gems, this post will introduce you to what Ruby gems are, some quick tricks, and how to utilize them to their full potential.
RubyGems is a package manager for Ruby that provides a standard format for distributing Ruby programs and libraries (aka gems). RubyGems is the tool we use to create, share and install gems.
Gems include the following components: Code, Documentation, and gemspec. All gems follow the below structure of code organization:
The RubyGems.org website is the best place to find gems in most situations. However, you can also search RubyGems' repository through the terminal using
gems search -r.
--local option limits the search of gems to only those installed on your machine. For example,
gem search -r rails --local returns the following list of gems installed on my computer:
To install a gem use
gem install [gem name]. You can browse through all your installed gems using
There is also documentation available inside your terminal through
gem help commands is useful because it displays a list of all available gem commands.
Currently a month into life at Flatiron, I have probably coded the word
bundle over a hundred times, but rarely stopped to deeply understand what it does and why it is so useful. According to its page in RubyGems, Bundler is a gem that "manages an application's dependencies throughout its entire life, across many machines, systematically and repeatably." It provides a consistent environment for Ruby projects by tracking and installing the exact versions of gems that are needed to run an app. Without Bundler, it would be a nightmare to keep track of all the required dependencies necessary for a project, but Bundler ensures you have the correct gems for development, staging, and production. To install Bundler, simply run
$ gem install bundler from your terminal.
A Gemfile is a format for describing gem dependencies for Ruby programs. Gemfiles are placed in the root directory containing the associated code. At the top of the Gemfile, you must add a line for the RubyGems source containing all of the gems necessary for the project:
The real purpose of the Gemfile is listing the individual gem requirements, each on a separate line:
Each gem can have one or many version specifications:
gem "pry", ">= 0.10.3"
gem "nokogiri", ">= 1.10.1", "< 1.10.3"
Each gem may also specify a group to which it belongs. If no group is specified, it will be placed in the
gem "pry", :group => :test
gem "wirble", :groups => [:development, :test]
The real power of Bundler is the simplicity it provides the user to install all the required gems to run an application. All it takes is a quick
bundle install and all gems specified in your Gemfile will be installed.
You may also specify a group of gems to skip during installation using
--without. For example:
$bundle install --without production should be used if using a database in development mode that is different from one being used in production.
The Gemfile.lock is another file that I'd noticed hanging out in the root directory but overlooked, assuming I'd never need to open it. I was wrong!
Gemfile.lock is a snapshot of the versions of all gems in the Gemfile at a single point of time.
In our Gemfile, we write dependencies as below:
gem 'sqlite3', '~>1.3.6'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
~> symbol has a special meaning in the Gemfile. What
gem 'sqlite3', '~>1.3.6' means is "we want any version of sqlite3 as long as it's greater than or equal to 1.3.6 and less than 1.4." Similarly,
gem 'sass-rails', '~> 5.0' means "we want any version of sass-rails as long as it's greater than or equal to 5.0 and less than 5.1." If we built our app a year ago and today sqlite3 is on version 1.4.1, running
bundle install today could pose issues (assuming no Gemfile.lock). It's possible the current version of sqlite3 would break our app or a feature that was available in the previous version is no longer available, etc. This is where the Gemfile.lock comes in!
In Gemfile.lock, only the exact versions specified get installed when running
bundle install. So if you distribute your app with a Gemfile.lock, every machine will use the exact same version of every gem, providing the stable deployment stack.
Easily! A Gemfile.lock is automatically created when running
bundle install for the first time. After that, every time you run
bundle install, Bundler will first look up the Gemfile.lock and install the versions specified there.
If you would like to update the gems in your Gemfile.lock to the newest version, you can update the dependencies to the exact versions you want in the Gemfile, and the next time you run
bundle install the updated versions will reflect in the Gemfile.lock.
It is a best practice to check your Gemfile.lock into git, so that every machine that downloads your app also has your Gemfile.lock, is using the same versions of every gem and doesn't need to resolve all dependencies individually.
More recently, I have run into some issues with our labs where the Bundler version specified in the lab is preventing the correct installation of other necessary gems.
In one recent lab, the Gemfile.lock was bundled with an old version of Bundler (1.16.1), whereas the current version is 2.0.1.
Below is a snippet from the Gemfile.lock in the root directory:
As you can see, the Gemfile.lock includes the old (1.16.1) version of the Bundler gem.
When attempting to run
bundle install on this lab, I received the error message below:
We are prompted to install nokogiri-1.8.5 since capybara was resolved to v 3.10.0 and it depends on old nokogiri, however there are several dependencies that prevent the nokogiri-1.8.5 installation from succeeding, and it turned out that installing those dependencies also did not resolve the problem. The issue here was that the Bundler (1.16.1) was specifying a version of nokogiri I could no longer download. As a result, I tried running a
bundle _1.17.3_ install using a slightly later version of Bundler I had on my computer (1.17.3). Running a normal
bundle install would just use the version of Bundler specified in the Gemfile.lock (1.16.1 in this case) and keep requesting the version of nokogiri that doesn't work. But specifying the later version through
bundle _1.17.3_ install requests for many dependencies to be installed using slightly later versions, and hopefully will move the nokogiri version past 1.8.5. Alas, I now got a different error that my
bundle _1.17.3_ install command requested nokigiri >= 1.10.0 but the current bundle has nokogiri locked at 1.8.5 - making progress! :
After taking the error message's advice and running a quick
bundle update nokogiri using Bundler (1.17.3), I was able to get nokogiri updated to 1.10.3. Then I just ran a simple
bundle install, and all dependencies installed correctly including the correct version of capybara - the lab was good to go!
I hope this post provides a helpful reference for you as you continue your journey into Ruby-On-Rails. And ideally the bug example I walked through demonstrated the utility of being familiar with gems. There are many sources of truth online where you can find help on gems, but ideally this acts as a single repository for that knowledge, and can help you optimize your interactions with gems and stomp some bugs! Feel free to post questions / comments below.
Thanks for reading!