Rails Dispatch

Rails news delivered fresh

Presented by Engine Yard

Rails 3 revolutionizes gem dependency management by including a new library called Bundler. This new system supersedes the old config.gem system that was built in to Rails 2. It provides all of the functionality of that system and a lot more — now you can be confident that the gems your application depends on will be the same on every developer’s machine as well as every application server.

This week, we’ve got a screencast showing how you would work with gems in Rails 3, and a post describing the most common features of the Bundler in more detail. Both should be suitable for those who have never used Bundler before, but we assume at least a passing knowledge of Rubygems.

Specifying Dependencies

In Rails 3, you list all of the gems that your application requires, along with any version requirements you might have in a Gemfile. The rails command creates a default Gemfile for you when you generate a new empty Rails application. The default Gemfile includes just two gems: Rails itself, and the default database driver, the sqlite3-ruby gem.

If your application requires other gems, you can edit the Gemfile to list them:

gem "nokogiri"
gem "will_paginate", "3.0.pre"

As you can see, you can request specific versions of a given gem by adding the version number after the name.

Once you have set up your Gemfile, run the command bundle install to set up all of the gems in your bundle for use with your application. If any of the gems aren’t installed, Bundler will automatically download and install them.

When Rails loads, it will automatically require each of the gems listed in your Gemfile, using the same name as the gem. If the file Rails should require differs from the gem name (for example, sqlite3-ruby is required as “sqlite3”), you should specify the correct name in the Gemfile like this:

gem "sqlite3-ruby", :require => "sqlite3"

Environment Groups

While you’re developing and testing your Rails application, you may find that there are some gems that you only want to load in certain environments. If there are gems that you only want to load while you are developing your application (like ruby-debug or rack-bug, perhaps), you can specify that in your Gemfile:

group :development do
  gem "ruby-debug"
  gem "rack-bug"
end

You can specify gems that should only load in any of the Rails environments, which includes :test, :development, and :production in a default application. If you have a gem that should be in more than one group, you can either put it in each group block, or you can specify multiple groups on the gem line itself, like gem "ruby-debug", :group => [:test, :development].

You can avoid installing certain groups of gems if they will not compile. For example, some Rails applications use PostgreSQL in production, and sometimes getting the pg gem to compile can be hard. To exclude the production and test groups, you would run bundle install --without production test.

Snapshotting

You can take a snapshot of the exact versions of every gem used by your application with the bundle lock command. When you run bundle lock, it will create a file named Gemfile.lock in the root of your application. That file stores the snapshot, including the names and versions of every gem.

You should check this file in to your version control, so that everyone who checks the code out will have the exact same gems. When you run bundle install on an application that has a snapshot, it will install the exact same versions of every gem (including all dependencies and dependencies’ depencencies) that the application was using when you ran bundle lock. This is a good way to ensure that all development and production machines are running the exact same versions of every gem that your application uses.

Deploying to a server

When you are ready to deploy your Rails application to a server, commit both the Gemfile and Gemfile.lock files to your source control. Then, as part of your deploy process, run bundle install before you start your application servers. All of the gems your application depends on will be installed and configured, and your application will be running in production on the exact same gems and versions that you used in development. It’s that easy.

Advanced uses of Bundler

In order to develop and deploy your Rails application, you don’t need to know more than this article already covered. However, Bundler comes with several advanced features that can be very useful under specific circumstances. If you are interested in learning about some of the more advanced capabilities, they are described below.

Advanced: Git dependencies

While you should always use gems that have been released to Rubyforge if you can, sometimes there is a fix that you absolutely need that is only available in the gem’s git repository. In this case, you can use the :git option to specify a git repository as the source for a specific gem. Gems that come from git repositories participate in dependency resolution exactly like regular gems, and their dependencies will be installed if needed.

When you are using the :git option, you can also specify a :branch, :tag, or :ref option to get the gem from a specific git checkout, like so:

gem "devise", 
  :git => "git://github.com/plataformatec/devise.git", 
  :branch => "v1.0"

If you use the :tag or :ref option, your git gem will always use that commit, and will never be updated. However, if you use the :branch option (or don’t supply any of the three checkout options), the bundle install command will update the git gem to the latest revision of the given (or master) branch.

C extensions that are specified in the .gemspec file inside the repository will be compiled and installed. Executables in the .gemspec will be installed for use via bundle exec executable.

If the git repository that you supply has a .gemspec file in it, you don’t need to put a version number on the gem line in the Gemfile. However, if there is no .gemspec file, you will need to specify a version so that bundler can generate a .gemspec file that uses default gem conventions.

Advanced: Packaging

Occasionally, the machines you deploy your application to are not able to connect to internet servers, including the Rubygems server. (Blocking outgoing connections from application servers is a somewhat common approach to security.) In that case, you will need to package up the .gem files for all the gems your application needs to run with the bundle package command.

Once you have run the command, all the .gem files will be deposited into the vendor/cache/ directory. Add those files to git and commit them, and on your next deploy, bundle install will simply use those gems instead of looking for them on rubygems.org.

Advanced: Installing to vendor

If you develop on the same architecture that you deploy onto, it is even possible to fully install every gem into your application directory to be checked in to source control. The command to do this is bundle install vendor. After running it, you can check the entire vendor/ directory in to your source control, and bundle install will no longer install anything.

However, this is not a recommended option. It can save installation time, but lead to problems if another developer (or another server you eventually deploy onto) needs to compile native extensions for a different platform or architecture.

More Advanced Features

Bundler supports a number of other use-cases through features not described here. Its full feature-set is described on its official web site

Wrapping up

As you can see, gem dependency management with Rails 3 is simple, yet effective. The bundler adds many new capabilities and features, while at the same time solving the gem conflict issues that Rails 2’s config.gem could suffer from. If you have feedback, need help, or just want to chat about bundler, visit the IRC channel #bundler on Freenode.