Ruby on Rails developers are no strangers to Gemfile
and Gemfile.lock.
These two files are essential
for installing Ruby gems,
but they can be confusing if one is unfamiliar with how they work.
Here we’ll explore what a Gemfile
is,
what it contains,
and how to use it.
To start, let’s create a default Rails 7 application, then we’ll be able to go through each line of the Gemfile and understand what it all means.
Inside the newly created app directory,
we’ll find our Gemfile
and Gemfile.lock
.
Now let’s start with the most basic of questions.
What is a Gemfile?
In the simplest of terms,
A Gemfile is a file that lists all the gems an application requires.
When we run bundle install
,
Bundler finds and installs these gems.
For example, in the above Gemfile,
gem "rails", "~> 7.0.3", ">= 7.0.3.1"
indicates we’re using the Rails gem in our application.
Where do the gems in the Gemfile come from?
Let’s dive into the first two lines of our newly generated Gemfile and understand where these gems come from.
The source
keyword is called the Global Source
.
There should be only one global source per Gemfile.
In most cases, the source
will be “https://rubygems.org”
But if we have our own Gemfury
account,
then the source can be:
The git_source
method lets us use gems from Git repositories.
The built-in :github
Git source is one example,
but we can also create our own Git sources.
Let’s assume we need a popular authentication gem called devise
in our application.
And we have decided to install the gem from their Github repo.
We can do it in the following different ways.
- The first approach is the most common way to install a gem from github. It finds the latest version and installs it.
- The second way is installing a specific gem version from a particular branch.
- The third way is to install a specific gem version from a particular tag.
- The fourth way is to install a specific version of the gem from a particular commit.
PS: Other than :github
, Bundler also provides :gist
and :bitbucket
as their built-in git_sources
.
But we can always create our own git_sources
.
How does one specify a ruby version in a Gemfile?
The above line instructs Bundler to use Ruby version 3.1.0. Bundler will use the default ruby version if we do not specify one. If the running ruby version does not match the one specified in the Gemfile, Bundler will raise an exception.
If our local version of Ruby is 2.7.6
and the version specified in the Gemfile is 3.1.0
,
then an exception will be raised.
Further, using the :engine
and the :engine_versions
options,
we can specify the ruby engine
and the version we want to use.
What are the various configuration options for a gem?
There are plenty of small and large config options that let us drill down specific attributes of a gem.
Let’s look at the Gemfile again.
Now, let us assume we need to install the devise
gem.
One immediately noticeable thing
when looking at our Gemfile is that there are multiple ways to include a gem.
Most of the gems have different versions
and different platforms associated with them.
We may notice that there are multiple
different types of version specifiers used.
- = Equal to (default)
- != Not equal to
- > Greater than
- < Less than
- >= Greater than or equal to
- <= Less than or equal to
- ~> Pessimistically greater than or equal to
Equal To Symbol
The simplest way to specify a gem is to use the “equal to” operator.
The above line of code will only install the devise
gem with the version
4.0.0
. If we don’t specify the version number,
Bundler will simply install the most recently available version of the gem.
Not Equal To Symbol
Let’s assume we are okay with any devise versions except 3.1.1
,
as it is not supported by our application.
We can do that too!
The code above will install the latest version of the devise
gem
that is not equal to 3.1.1
.
Greater Than and Less Than Symbols
What if we want to install the latest version of the devise
gem that is greater than 3.1.0
and less than 4.1.0
?
Greater Than or Equal To and Less Than or Equal To Symbols
Now if we want to install the latest version of the devise
gem
that is greater than or equal to 3.1.0
and less than or equal to 4.1.0
?
Pessimistically Greater Than or Equal To Symbols
Let’s say we want our application to work with future versions of the devise
gem without breaking the codebase.
The only way to ensure this will be to install only the versions which will be backward compatible.
So this would mean if we have devise
3.1.0 installed in our system,
then 3.1.1 has fewer chances of breaking our application rather
than directly installing the latest version 4.1.0 which most probably will have breaking changes.
To ensure only a limited range of versions are available
for a gem, we can use the Pessimistic Version Operator.
To be more specific, we can add more digits after the ~ operator.
These version specifiers raise a very important question we all must have had at some point.
Should we add gems without specifying a version?
Adding a gem without a version specifier is considered to be a bad practice.
When we add a gem without a version specifier,
we are implicitly adding a version specifier of = 0.0.0
to the gem.
This might work for the current application but in the future, when we upgrade the application, we might get an incompatible version of the gem that might break our application. So we should always specify a version specifier to be safe!
What does require: false mean?
If we look at the Gemfile, we will see that we have added a gem called bootsnap
with the option require: false
.
This means that we don’t want to require the gem bootsnap
when we run bundle install
but we do want to install it.
Requiring simply means loading all the related gem files when our application boots up.
This is useful when we are installing a gem that is not directly required by the application.
We might have many gems that are responsible for checking code quality or measuring metrics.
Such gems might not be in use within the application but are useful for developers.
Using require: false
allows us to load it lazily, only when we need it.
For example, bootsnap is being used inside the config/boot.rb
file.
So inside the config/boot.rb
file, we can require bootsnap only when we need it.
We can find the above code in the config/boot.rb
file
where we’re requiring bootsnap to be loaded lazily.
How to install gems in specific environments?
From our Gemfile,
We see there are two groups (:development and :test) added to our Gemfile.
This helps us in installing gems only for specific environments.
Gems installed in the development group will only be available
in the development environment
and the same goes for the rest.
debug
gem is required only in development and test groups.
It is not required in the production group.
web-console
gem is required only in the development group.
It is not required in production and test groups.
Now, for example, let’s assume we want to run sqlite3 in our development and test environment, and PostgreSQL in our production environment. To do this, we’ll have to add sqlite3 in our development and test group and PostgreSQL in our production group.
Please remember that all gems will be installed in the source machine regardless of the environment we run the application in. This means that your production server will still contain gems from both the test and development environment.
Platforms
Platforms are similar to groups in that they are used to group gems together, but they are used to specify the platforms that a gem is available on.
The above gem is available for platforms that are mri, mingw, and x64_mingw.
The different platforms available are:
- ruby C Ruby (MRI), Rubinius or TruffleRuby, but NOT Windows
- mri Same as ruby, but only C Ruby (MRI)
- mingw Windows 32 bit ‘mingw32’ platform (aka RubyInstaller)
- x64_mingw Windows 64 bit ‘mingw32’ platform (aka RubyInstaller x64)
- rbx Rubinius
- jruby JRuby
- truffleruby TruffleRuby
- mswin Windows
What is a Gemfile.lock?
Finally the Gemfile.lock file contains all the information about
the gems that are currently installed.
This file is created after we run the bundle install
command.
A Gemfile.lock has a list of the exact versions of the gems required for the application.
Let’s take a look at our generated Gemfile.lock file.
In the above Gemfile.lock file, we see that the file is divided into four sections.
GEM
This is the section where we can see the actual gems that are installed. This section has two parts:
- remote This is the location of the source from where the gems are installed. In our case, the remote is “https://rubygems.org/”
- specs This is the list of gems that are installed. Here we see that the gems that are installed are more than the ones that are listed in the Gemfile. This is because we need to also install the dependent gems required by the gems that are listed in the Gemfile. For example, from the Gemfile.lock file above, we can see that the rails gem is dependent on several other gems. So we need to install those other gems as well.
PLATFORMS
This section has the list of platforms that the gems are available on.
If we run bundle platform
on our application, we will see the following output.
DEPENDENCIES
This section has a list of dependencies that are currently installed for the application. These are the gems that are listed in the Gemfile.
RUBY VERSION
This section has the Ruby version that is specified in the Gemfile.
BUNDLED WITH
This section has the version of Bundler that was last used to install the required gems.
For more information on Gemfile and Bundler, check out the documentation.
We hope you have a good understanding of Gemfile and Gemfile.lock after reading this article!