Ruby Gems, Gemfile & Bundler (The Ultimate Guide)

What is a Ruby gem?

A gem is a package that you can download & install. When you require an installed gem you’re adding extra functionality to your Ruby program.

Gems allow you to:

  • Add a login feature to your Rails app
  • Easily work with external services (like APIs)
  • Build a web application

That’s just some examples. Every gem has it’s own goal.

Why do we use gems?

  • It’s how we share libraries & tools in Ruby
  • The file structure & format of a gem makes it easy to understand how they work
  • A specification (“.spec”) file that comes with every gem describes dependencies (other required gems) so the code has everything it needs to work

Thanks to RubyGems, we have a rich ecosystem of helpful libraries just one gem install away!

A list of gems required for a given (non-gem) project can be listed on a special file called “Gemfile” so they can be automatically installed by Bundler. Both covered later in this guide.

What are some examples of gems?

  • Rails, and all of its components (ActiveRecord, ActiveSupport, etc.) are distributed as Ruby gems
  • Pry, the powerful alternative to irb
  • Nokogiri, a popular XML & HTML parser

Most gems are pure Ruby code.

A few gems include a Ruby C extension for improved performance.

This extension is built automatically for you when you install the gem. In some cases, you may need to manually install additional software that is not managed by RubyGems.

Now:

Let’s learn more about gems by building your own & looking at the files that make a gem.

How to Build a RubyGem

You can prepare the files for a new gem by running bundle gem <name>.

For example:

bundle gem awesome_gem

A Gem is composed of the following structure:

├── awesome_gem.gemspec
├── bin
│   ├── console
│   └── setup
├── Gemfile
├── lib
│   ├── awesome_gem
│   │   └── version.rb
│   └── awesome_gem.rb
├── Rakefile
├── README.md
└── test
    ├── awesome_gem_test.rb
    └── test_helper.rb

This .gemspec file is where you’ll find all the information about the gem.

It includes:

  • Gem name
  • Gem summary (short description)
  • Author name
  • Dependency list
  • A list of files to include in the gem
  • Optional: Author’s email address, project URL (homepage), executables, c extensions, long description.

The gem version itself is defined as a constant in lib/<gem_name>/version.rb.

Here’s an example gemspec:

Gem::Specification.new do |spec|
  spec.name          = "awesome_gem"
  spec.version       = AwesomeGem::VERSION
  spec.authors       = ["Jesus Castello"]
  spec.summary       = "Example gem for article about Ruby gems"

  spec.files         = Dir['**/**'].grep_v(/.gem$/)

  spec.require_paths = ["lib"]

  spec.add_development_dependency "bundler", "~> 1.16"
  spec.add_development_dependency "rake", "~> 10.0"
  spec.add_development_dependency "minitest", "~> 5.0"
end

The require_paths array is where Ruby will look for your gem files when you require them. This allows you to put your code under lib/<gem_name>/ & then require it with require "<gem_name>/<file_name>".

For example:

A file named lib/awesome_gem/parser.rb would be required as require "awesome_gem/parser" from anywhere inside the gem.

You will have most of your requires in lib/<gem_name>.rb (the only file at the root of /lib).

That’s the file that gets loaded when you require the gem!

Next:

The add_development_dependency lines define gems that you’re going to use during development.

These are gems like minitest, RSpec, or pry.

While add_dependency defines gems that you use as part of your code.

Once you’ve changed the summary & description from the default values… you’ll able to load an irb session with your gem using the bin/console program that bundle gem creates for you.

Example:

$ bin/console

irb(main):001:0>
irb(main):002:0>
irb(main):003:0> AwesomeGem
=> AwesomeGem
irb(main):004:0>
irb(main):005:0> AwesomeGem::VERSION
=> "0.1.0"

Then you can package the gem using gem build <name>.gemspec & publish it to rubygems.org using gem push.

What Is Bundler?

While learning about Ruby gems you may also read about Bundler.

But what is Bundler exactly?

Bundler is a tool for dependency management.

Didn’t RubyGems already handle this?

Well, it does… but only for the gems themselves.

Your regular Ruby application isn’t built as a gem, so it doesn’t get this feature.

That’s why Bundler exists!

Understanding Gemfiles

Have you seen those Gemfile files?

That’s where you write what gems you want to use for your Ruby application.

These gems will be loaded for you without having to require them.

A Gemfile looks like this:

ruby '2.5.0'

gem 'rails', '~> 5.2.1'
gem 'sqlite3'
gem 'puma', '~> 3.11'
gem 'bootsnap', '>= 1.1.0', require: false

Bundler (and RubyGems since version 2.0) can read this file & install the requested versions of these gems.

This is what you should see when running the bundle install command:

Using turbolinks-source 5.1.0
Using turbolinks 5.1.1
Using uglifier 4.1.18
Using web-console 3.6.2

Bundle complete! 18 Gemfile dependencies, 78 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

Now:

What are these symbols (like ~>) used when declaring the versions for every gem in your Gemfile?

These allow you to request a range of versions.

You can say things like, “I want the version to be equal or greater than 1.2, but less than 2.0”.

Which would look like this:

gem 'puma', '~> 1.2'

The ~> is a shortcut for this range:

gem 'puma', '>= 1.2', '< 2.0'

In the case of ~> 5.2.1, it means exactly this:

'>= 5.2.1', '< 5.3'

The more specific the version number is the more restricted the version range will be.

Gemfile Options & Gemfile.lock

You have a few options when requiring a gem inside a Gemfile.

For example:

You may want to pull a gem from a different source, like GitHub.

This is helpful when you need to use the latest version of the project, even if it hasn't been released on rubygems.org yet.

Here's an example:

gem "rails", git: "git@github.com:rails/rails.git"

You can pass a branch option to use the code from a branch that isn't master.

Like this:

gem "awesome_print", git: "git@github.com:awesome-print/awesome_print.git", branch: "v2"

Another option you may find is require: false.

What does that do?

It tells Bundler to not auto-require the gem for you. This means that you'll have to require it in your code when you need it.

This is used for gems that are limited in scope.

For example:

Gems that you use in a specific rake task, but you don't use in your Rails controllers & models. The benefit is that you save memory in your app code because you only load that gem when you need it.

You can also group gems by environment.

That means you can have gems that are only installed & loaded in development (like capybara & pry).

Finally, bundler creates a Gemfile.lock.

What's the difference?

A Gemfile.lock is auto-generated & it says exactly what versions of every gem were installed.

Bundler will install these versions so when you deploy this application to production, or share your project with other developers, everyone will be working with an identical set of gems.

Useful Gem & Bundler Commands

Command Description
gem list List all your installed gems. Accepts an argument for filtering gems by name (example: gem list active)
gem which <name> Gives you the path where a gem is installed.
gem search <name> Search gems from configured sources (default: rubygems.org). Takes a regular expression (example: gem search "\Aawesome_").
gem env Displays information about your gem environment (version, paths, configuration).
gem install <name> -v <version> Allows you to install a specific gem version (example: gem install sinatra -v 2.0.0).
bundle viz Generates a visualization of the dependency graph for your current project.
bundle show Shows info about a particular gem installed via bundler. Must be inside a folder with a Gemfile.
bundle outdated Displays a list of outdated gems in the current project. Can use the --groups option to group them.
bundle console Runs an irb session with the gems from the current project's Gemfile.

Summary

You've learned about RubyGems, the package system for Ruby. You've also learned how a gem is structured, what is a Gemfile, Bundler & the difference between the Gemfile & the Gemfile.lock.

I hope you found this useful!

Please share this article, subscribe to the newsletter if you haven't yet (9000+ Ruby developers!) & check out my Ruby book.

Thanks for reading & have a great day 🙂