There are many Ruby gems that provide commandline interfaces that are useful for development tasks. The Garage Git eXtensions gem is a good example of a CLI application that adds additional Git subcommands for common development workflows and there are numerous other gems that provide functionality ranging from interacting with third party services to simplifying tedious jobs.
The Easy Button
Bundler makes distribution of these utility gems to your entire team super easy by just adding a few lines to your Gemfile. All your developers will have access to the tools they need right out of the box, and you have a simple way to rollout new gem versions over time as well.
Since these gems are primarily developer utilities, you may be tempted to save these gems in
your :development
Bundler group…but STOP!
These are tools…not libraries!
group :development do
# FIXME!
# This actually loads thegarage/gitx.rb source
# into your application runtime
gem "thegarage-gitx"
end
By default, every gem declared in the :development
group is automatically loaded
during application initialization which means that all Ruby files within those gems
will be included into your application. Not only does this bloat your
application’s runtime environment, but it also has potential for nasty and unexpected
side-effects when the gem’s CLI source code, monkeypatches, and all dependency gems
are loaded into your running application. This is so bad that some CLI gems have
started to recommend against including the gem in your Bundler Gemfile.
Can we get the benefits of simplified cross team distribution of the toolset gems provided by Bundler and avoid polluting our Ruby runtime environment with the code from these gems?
Of course we can!
Bundler :require Flag
Bundler actually supports the ability to opt-out of the auto-loading behavior by passing
an optional require: false
flag for any gem in your Gemfile.
group :development do
gem "thegarage-gitx", require: false
end
This solves the immediate issue, but it is quite a manual process to add this option to every gem. As your project grows, it is also quite easy to overlook the optional flag and accidentally add a new gem that will get included into your runtime. Any other options?
The :toolbox Bundler Group
Custom Bundler groups are an extremely effective way to group related gems into unique buckets.
Let’s create a new custom group and shove all of these gems into an aptly named :toolbox
group.
group :toolbox do
gem "thegarage-gitx"
gem "bundler-reorganizer"
gem "foreman"
gem "travis"
end
Perfect! Bundler only loads a set of known groups
into the running application, so any gem listed in the :toolbox
Bundler group will
not be automatically loaded during Rails startup. We also have an
expressively named set of gems that is easily identifiable for future additions!
NOTE: You can actually give your :toolbox
group any name that you see fit as long as it is
not one of the default groups loaded by Bundler.
[optional] Exclude Gems From Production Deployments
Since these toolbox gems are primarily used for development purposes, you may
want to exclude these gems from being installed on production machines.
Bundler makes this process very straightforward
via the bundle install --without
command or the BUNDLE_WITHOUT
environment
variable. Production deployments typically exclude the development
and test
groups by default, so you just need to add the toolbox
group to the list as well.
$ export BUNDLE_WITHOUT="development:test:toolbox"
If you’re deploying to Heroku, checkout their documentation for configuring environmental variables.