Yehuda Katz is a member of the Ember.js, Ruby on Rails and jQuery Core Teams; he spends his daytime hours at the startup he founded, Tilde Inc.. Yehuda is co-author of best-selling jQuery in Action and Rails 3 in Action. He spends most of his time hacking on open source—his main projects, like Thor, Handlebars and Janus—or traveling the world doing evangelism work. He can be found on Twitter as @wycats and on Github.
The How and Why of Bundler Groups
May 9th, 2010
Since version 0.9, Bundler has had a feature called “groups”. The purpose of this feature is to allow you to specify groups of dependencies which may be used in certain situations, but not in others.
For instance, you may use ActiveMerchant only in production. In this case, you could say:
group :production do gem "activemerchant" end
Specifying groups allows you to do two things. First, you can install the gems in your Gemfile, minus specific groups. For instance, Rails puts
pg in a
database group so that if you’re just working on
ActionPack, you can
bundle install --without db and run the
ActionPack tests without having to worry about getting the gems installed.
Second, you can list specific groups to autorequire using
Bundler.require. By default,
Bundler.require requires all the gems in the
default group (which is all the gems that have no explicit group). You can also say
Bundler.require(:default, :another_group) to require specific groups.
Note the difference between these operations:
bundle install is opt-out, while
Bundler.require is opt-in. This is because the common usage of groups is to specify gems for different environments (such as development, test and production) and you shouldn’t need to specify that you want the “development” and “test” gems just to get up and running. On the other hand, you don’t want your test dependencies loaded in development or production.
It is also worth noting that all gems that you installed (i.e. not the ones that you excluded at install time with
--without) will be available to
require. This has no effect unless you actually require them. This means that in development mode, if you explicitly require
rspec, it will work.
Rails 3 defaults to mapping groups to environment names, and explicitly autorequiring the implicit
default group and the group named the same as the current environment. For example, in development mode, Rails will require the
default group and the
development group. The code that does this is in your application.rb:
Bundler.require(:default, Rails.env) if defined?(Bundler)
In order to ensure consistency across all environments, bundler resolves the dependencies of your application using the gems listed in all groups, even if you specify
--without. This means that while you can skip installing the gems listed in the production group by saying
--without production, bundler will still download and examine the gems in order to properly resolve all dependencies.
As a result, the dependencies you install in development mode and test with will be compatible with the gems in other environments. In essence, this policy ensures that if your tests pass and run in development, your app will not fail to run in production because the dependencies resolved differently.
Multiple Inconsistent Configurations
Sometimes, especially when developing gems for wider use, you want to test your code against multiple incompatible configurations. At first glance, you might think that you could use groups for this case, but as described above, groups are designed for cases where all of the gems are compatible, but you don’t always want to have to install them in all situations.
Instead, use multiple Gemfiles, one for each incompatible configuration. When installing, do
bundle install --gemfile Gemfile.rails2. This will tell Bundler to use
Gemfile.rails2 rather than the default
Gemfile. As in all cases in Bundler, you can also specify this option globally with an environment variable (