Monday, January 4, 2010

I'm a Huge Fan of Bundler

Richard pointed me to Bundler the other day (see: Using the New Gem Bundler Today), and at first it just seemed like a neat way to avoid having to manage (freezing and unfreezing) gems manually.

Boy was I wrong. Basically it is a huge leap in solving some major issues with dependency management in Ruby (more specifically Rails, but it works off-rails also).

If you work with Ruby/Rails, you're familiar with there really being only a few standard locations for local gems, which are (RAILS_ROOT)/vendor/gems, (one of) the gems directories in Ruby (or JRuby), and ~/.gem (the latter being much less-used). If you aren't "riding Rails", then you might have to put in some additional code to load from vendor/gems/... unless whatever you are using loads that for you.

However, one issue with Rails 2.3 (and previous versions) is that there seems to be no standard place for gems that only need to be loaded when Rake is running functional/unit tests (i.e. there is no standard, built-in way to handle scoped dependencies). You either freeze the gems needed for testing (which puts them into (RAILS_ROOT)/vendor/gems) or you only put the stuff needed at runtime in there and keep your test dependencies in your main Ruby (or JRuby) gems. This works for some people, but can break down when you (for example) have a continuous integration environment that needs to have multiple versions of Ruby/JRuby running different versions of gems, as the complexity of how you have to have things setup to manage dependencies starts to really weigh the process down and make it error-prone. Note: If you have an environment where you need to switch Ruby/JRuby versions, check out RVM.

What Bundler does is allow you to have a Gemfile in what can be a very simple format that defines what gems are required for that project and optionally what versions are required, among a number of other options that are fairly intuitive, some being that you can define which gems should be loaded in what environment, like:

gem "name", "version", :only => :testing

That on its own is a huge step, especially when Bundler is included in Rails 3.

However, here is the thing that may not jump out at first, but that is the seed of something much bigger and Maven/Ivy-like (for those on the Java side), which is the ability to pull the dependency from Git automatically. By including a Gemfile that points to the Git projects it requires, it will release Rails developers from having to manually get gems from places like Github. This is very similar to being able to define repositories in a pom.xml in Maven2 in Java projects, and should reduce the amount of time spent reading the README and/or seeing messages when trying to test/run the app stating that gems are missing.

An important difference here though (other than one being for Java and the other for Ruby/Rails) is that Maven2 basically requires a webserver to serve artifacts (dependencies). This is commonly done by Maven2 repository servers like Archiva. In comparison, Bundler is letting you pull dependencies directly from source control (Git), which eliminates the need for setup and administration of a specialized webserver to server and manage dependencies. Maven could eventually do similar if it pulled whole projects down from source control and built them on demand, eliminating the need for a repo server, but I don't think that it would pay off because of the time to compile each project.

(Clarification: While Bundler's ability to get dependencies from git might become well-used, for open-source Ruby projects, it is suggested that you use GitHub to store source and Gemcutter to host gems, since the latter has taken over as the primary source of gems.)

No comments: