Building a better Gemfile
If you’ve been hacking on a Rails project for a while, chances are
your Gemfile has spiraled out of control. For instance, the main
OrgSync Gemfile contains 127 gems. That’s a ton of dependencies,
and they slow us down. Running bundle exec rake environment
takes
at least 15 seconds. That might not sound like much, but it adds
up. Think of it as a 15 second tax every time you do anything.
Plus, we don’t specify versions for many of the gems. This makes
running bundle update
downright dangerous, as you could upgrade
to a new major version of a gem. Gems in the asset group, like
CoffeeScript and Sass, were the worst offenders. There’s a comment
right above them that says:
Make sure you perform a full asset precompile on deploy whenever you update any of the gems in the following group.
But none of them have any versions specified. You could argue that
that’s what Gemfile.lock
is for. I don’t buy that. There’s a world
of difference between gem 'sass'
and gem 'sass', '~> 3.2.10'
.
What can be done?
With one simple helper function, you can fix both problems. Drop this into your Gemfile before any gems:
def gem(name, version, options = {})
if options.has_key?(:require)
if options[:require].equal?(true)
options.delete(:require)
end
else
options.merge!(require: false)
end
super(name, version, options)
end
Happier Developers with Versions
By overwriting the gem
function and requiring a version
parameter,
you can be sure you’ll never end up with a gem with an unspecified
version. If you try to, bundle
will blow up at you:
.../Gemfile:3:in `gem': wrong number of arguments
This has the benefit of making bundle update
much less risky to
run. Also, it’s a lot easier to tell at a glance which versions of
gems your project needs.
Faster Startup with require: false
Instead of eagerly requiring all of your gems, the modified gem
function doesn’t require them unless you set require: true
. As a
result, most of your gems won’t be available any more. You’ll have
to explicitly require them at the top of the file.
As a Pythonista, this doesn’t bug me one bit. After all, explicit is better than implicit. If you still need convincing, consider this: It got the OrgSync project’s startup time down to 12 seconds (a 20% improvement). And that’s not even as fast as it could be — 43 gems are still eagerly required.