litany against fear
Gem Bundler is the Future
If you haven’t checked out Yehuda Katz’s
and Carl Lerche’s gem bundler yet, now
is the time. This project replaces
the horribly broken dependency resolution in Rails and what we all know
and use as config.gem
’s. Ever seen can’t activate $gemname
($gemversion = runtime)
? Or maybe my favorite, when it can’t even
figure out what gem can’t be activated. If so, read on, bundler’s about
to make your life a lot easier.
Yehuda has a great roundup of what can be done (and has been done with the bundler) on his blog. This is going to be a part of Rails 3, so you’re going to have to run into this sooner or later. I was getting frustrated with managing gem dependencies in Gemcutter, so now it’s bundled up and ready as a decent example of the bundler in action.
Enter the bundler
I’m using the edge gem built from their repo, and I suggest you do as well. Hopefully soon they’ll push it to that new gem hosting site I’ve heard so much about.
git clone git://github.com/wycats/bundler
cd bundler
sudo rake install
The Gemfile
Now we’re ready to make a Gemfile
. This is basically a specification
of what gems should be included in your app. Here’s Gemcutter’s:
Some notes here:
- By default the gem host is
gems.rubyforge.org
, so I’ve cleared the sources to use gemcutter.org instead. - You can easily specify gems for a given environment and multiple
gems with
only
and pass it a symbol for the environment name. Thank goodness. - The default bundle path is
vendor/gems
. This won’t work for Rails since it assumes way too much about this directory, so we switch it tovendor/bundler_gems
. Note: Yehuda has told me that this will work as normal once Rails 3 is closer/done.
Bundle up!
Now we’re ready to run gem bundle
. This pulls down the gems from the
given sources along with any dependencies. This basically creates a
virtual RubyGems environment right inside of your vendor
directory.
(I’m cutting some out since the log is so long)
$ gem bundle
Calculating dependencies...
Updating source: http://gems.github.com
Updating source: http://gemcutter.org
Downloading actionmailer-2.3.4.gem
Downloading actionpack-2.3.4.gem
...
Downloading xml-simple-1.0.12.gem
Installing rr (0.10.4)
Installing fakeweb (1.2.6)
...
Installing nokogiri (1.3.3)
Building native extensions. This could take a while...
Done.
So now in your vendor/bundler_gems
directory, we’ve got the .gem
files pulled down in cache
, unpacked in the gems
directory, and the
gem specifications are unloaded into specifications
. Bundler also
makes its own environment.rb
for loading the dependencies.
vendor
|-- bundler_gems
| |-- cache
| | |-- actionmailer-2.3.4.gem
| | |-- actionpack-2.3.4.gem
| | |-- ...
| | `-- xml-simple-1.0.12.gem
| |-- doc
| |-- environment.rb
| |-- gems
| | |-- actionmailer-2.3.4
| | |-- actionpack-2.3.4
| | |-- ...
| | `-- xml-simple-1.0.12
| `-- specifications
| |-- actionmailer-2.3.4.gemspec
| |-- actionpack-2.3.4.gemspec
| |-- ...
| `-- xml-simple-1.0.12.gemspec
`-- plugins
Bundler will also dump gem executables in your Rails.root/bin
directory. This means you can then use bin/rake
, for example. Running
rake
as normal should still work though. As for your version control,
it’s recommended to check in the .gem
’s only, then run gem bundle
to
unpack/install them. This goes both for new developers and getting code
deployed.
Loading the Environment
Now the issue is to load up the bundled environment instead of the
system installed one. Start by creating a config/preinitializer.rb
,
which is loaded first before config/environment.rb
:
Then, in each config/environments/*.rb
file:
This basically does a require
for every gem listed in your Gemfile and
their associated dependencies. That should be it! Your app should
(hopefully) boot and now you should run your tests to ensure your
application is still working right.
If you want to see this all in action clone Gemcutter and follow the contribution guidelines for getting up and running.
Issues
So, I had a few roadblocks with the bundler, and I don’t think it would be fair to not mention them.
- The documentation sucks. I’m hoping this will improve before Rails 3 is ready (whenever that is). Maybe a Rails guide would be appropriate, and I’ll definitely help start it.
- Gemcutter’s on Heroku, so it’s necessary to check in a lot of the
vendored code (in fact, all of the development/production
dependencies). New contributors just have to run
gem bundle -u
to get the test dependencies. - In my staging environment I had to use
Bundler.require_env "production"
. Pretty self-explanatory but I missed it at first. - Shoulda macros just stopped working, since it assumes the location
of gems in
vendor/gems
orvendor/plugins
. I had to include this intest/test_helper.rb
to make it happy:
Wrapup
This guide went over how to use Bundler today, with a Rails 2.3.4 app.
According to Yehuda, this eventually will be packaged in Rails 3, so the
commands will be baked into Rails…so something like script/bundle
. The
nice thing is that you can use the bundler with any Ruby project, so
this is good to know in general.
The bundler is really the future of gem dependency management. If you’re
sick of fighting with config.gem
’s or are starting a new app it would
be well worth your time to start looking at it. If you’re having trouble
with the bundler (or success stories!) feel free to comment here or hop
in #carlhuda
on irc.freenode.net
. Check it out on
GitHub if you haven’t yet.