Managing Gems
Ruby gems can be installed on Heroku via Gem Bundler or the Heroku gem manifest. Both are supported, but it it is recommended you only use one in your app in order to avoid confusion. Bundler is the up-and-coming community standard, and can be used to manage gems locally.
Gem Bundler
Gem Bundler is the default gem manager for Rails 3, and can be used with any Ruby project as it has no dependency on framework. To use, install bundler:
$ sudo gem install bundler
Create a Gemfile in the root of your app, with contents like this:
gem 'sinatra', '0.9.4'
| Do not use disable_system_gems in your Gemfile |
Bundle your gems by running:
$ gem bundle
The gems will be placed into vendor/gems. Make sure to add the following to your .gitignore
vendor/gems
When you push to Heroku, the Gemfile will be detected and your gems bundled automatically:
-----> Heroku receiving push
-----> Gemfile detected, running gem bundle
Calculating dependencies...
Updating source: http://gems.rubyforge.org
Downloading rack-1.1.0.gem
Downloading sinatra-0.9.4.gem
Installing rack (1.1.0)
Installing sinatra (0.9.4)
Done.
Using Bundler from Sinatra or any Rack app
To use gems from your bundler environment under Sinatra or other non-Rails framework, add the following to the top of your app:
require File.dirname(__FILE__) + '/vendor/gems/environment'
Bundler.require_env
Using Bundler from Rails 2.x
If you’re using Rails below version 3.0, you’ll need to add the following line to the top of your Gemfile:
bundle_path 'vendor/bundler_gems'
Then add the following to your .gitignore
vendor/bundler_gems
Finally, create config/preinitializer.rb with the following contents:
require "#{File.dirname(__FILE__)}/../vendor/bundler_gems/environment"
class Rails::Boot
def run
load_initializer
extend_environment
Rails::Initializer.run(:set_load_path)
end
def extend_environment
Rails::Initializer.class_eval do
old_load = instance_method(:load_environment)
define_method(:load_environment) do
Bundler.require_env RAILS_ENV
old_load.bind(self).call
end
end
end
end
Binary gems
Gem bundler will build binary gems for you. Because your gems directory is ignored, developers who get a fresh checkout of the app will need to run “gem bundle” to build the gems for their local system.
Bundle everything
The recommended use of gem bundler is to bundle absolutely every gem your app depends upon. This includes your framework (Rails, Sinatra, etc). This doesn’t add anything to your repository size if you've gitignored the vendored gems directory.
Bundling Rails and other dependencies does increase your slug size by a few megabytes. Increased slug size is a worthwhile tradeoff for vastly simplified dependency management, as well as the flexibility of being able to use any version of Rails you wish.
Further reading
- Yehuda Katz: Using the New Gem Bundler Today
- Nick Quaranto: Gem Bundler is the Future
- Gem Bundler readme
Heroku gem manifest
Another method for specifying your gem dependencies is the Heroku gems
manifest, which is a .gems file in the root of your app. When a new version
of the app is pushed to Heroku, any changes to the .gems file are detected
and new gems are installed along with their dependencies.
System gems
All apps deployed to Heroku have access to a number of gems installed as part of the base platform. See installed-gems.heroku.com for a comprehensive listing.
Gems installed as part of your application override Heroku system gems.
Creating a gem manifest file
The gems manifest is a simple text file that includes information about each of
the gems required by an application. Each entry in the manifest includes a
mandatory gem name along with optional version and source options. The
following is an example .gems manifest file that includes two gems:
hpricot --version '>= 0.2' --source code.whytheluckystiff.net
dm-core --version 0.9.10
Each line of the .gems file includes the following:
The name of the gem, as would be specified on the
gem installcommand line.An optional gem version specifier. This can be a basic version number like
"0.1.2"or an advanced version specifier with comparison constraints like">= 0.1.2"(see Specifying Versions in the RubyGems manual for more information). When no explicit version is specified, the most recent version of the gem is installed.An optional gem repository source URL, like
gems.rubyforge.org,gems.github.com, orgems.mycustomrepo.com. Thegems.rubyforge.orgrepository is always included as the last source. Multiple--sourcearguments may be provided for cases where dependencies cross multiple repositories.
TIP: The --version and --source options have short forms just like the
gem command; use -v and -s, respectively.
Deploying gem changes
When a .gems manifest file is added or modified, it must be committed to the
app’s git repository and pushed to Heroku for changes to take effect. The actual
gem install process occurs during the git push operation.
The following example adds a .gems file to the app’s git repository, commits
it, and pushes to the remote heroku repository:
$ git add .gems
$ git commit -m 'added gems manifest file'
$ git push heroku
Counting objects: 4, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 356 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
-----> Heroku receiving push
-----> Installing gem hpricot >= 0.2 from http://code.whytheluckystiff.net
Building native extensions. This could take a while...
Successfully installed hpricot-0.6
1 gem installed
-----> Installing gem dm-core 0.9.10 from http://gems.rubyforge.org
Successfully installed addressable-2.0.2
Successfully installed extlib-0.9.10
Successfully installed data_objects-0.9.11
Successfully installed dm-core-0.9.10
4 gems installed
-----> Rails app detected
Compiled slug size is 4.3MB
-----> Launching.............. done
App deployed to Heroku
To git@heroku.com:vivid-moon-60.git
91425e3..fe10e87 master -> master
The push is aborted if the gem manifest is invalid or a gem fails to install, ensuring that an application is not deployed in an inconsistent state.