How Bundler Groups relate to the Rails Environment
Recently I’ve seen more and more Gemfiles that organize gems into groups and it got me wondering how bundler knows which groups to load. For the most part two things happen
- At install time - Bundler includes a capistrano task that installs all gems except those only in the development or test groups on your server
- At execution time - Rails tells bundler to load the default gems and those specific to your environment (development, staging or production)
How Bundler installs gems into your bundle
To tell bundler to use bundler on the server all you need to do is add the one line below to your Capfile
This creates a capistrano task bundle:install
that ultimately runs something like the command below on your server
Okay so it ran a bundle install
but what really happened? Let’s take that command one piece at a time.
--gemfile /srv/my_app/releases/20110715204318/Gemfile
tells it to use our Gemfile, that makes sense.-
--path /srv/my_app/shared/bundle
tells it where to put the bundle. Let’s see what that means.It looks like it created all the
rubygems
directories for to isolate the gems for this project (very similarly to rvm gemsets)
-
--quiet
hmm what else can I say -
--without development test
Aha so here’s where it tells bundler to skip thedevelopment
andtest
groups. so allall gems outside a group or in a group other thandevelopment
ortest
are installed.
How does Bundler remember these settings when it loads Rails and tries to load the bundle?
It saves them away in a .bundle
directory cat .bundle/config
shows us
Now we understand how Bundler and Capistrano work together during a deployment to setup the bundle and install gems on the server. Let’s take a look at what happens when our app starts up.
How Rails and Bundler load your gems according to the Rails Environment
In your config/application.rb
, right near the top, you have a line like this.
Rails tells bundler to require all the gems in the :default
group and also the current Rails.env
group.
It uses the .bundle/config
file to know where the gems are installed and find them.
So that’s how the gems appropriate for your environment get automatically loaded when Rails starts.
What if you create a gem group that doesn’t correspond to any Rails env?
This is the problem that started me down this investigation. I came across a Gemfile
with a group called cruise
like this
It was working meaning our cruise server ran metric_fu but why?
-
We weren’t using capistrano to run bundle install and instead just checked whether we were on our cruise server and ran the command
bundle install
in our Rakefile. Aside: We are looking into Jenkins as a continuous integration server that supports bundler This explains why themetric_fu
was installed into our bundle (there was no--without
so all gems are installed) -
When our Rails app starts it would not load metric_fu becuase
Rails.env
will never becruise
when theapplication.rb
lineBundler.require(:default, Rails.env)
runs. We had worked around that by doing the require ourselves.
While this does work in that our cruise build works it has the downside of installing metric_fu
(and all the gems it depends on) on our production server!
That’s because the bundler/capistrano
task installs all gems not marked development
or test
and since metric_fu
is marked cruise
it gets installed.
Now Rails will not load it so its not that bad but its still not good.
We can take a quick look on our server to verify
Fortunately this is really simple to fix, we just need to change our Gemfile
and move metric_fu
into the test
group
My advice it do not create any gem groups that do not correspond to your Rails environments as that seems to be what the bundler-capistrano and bundler-rails integrations expect.