RVM - Gemsets - Bundler - Gemfiles - Dependencies - Packaging
AKA the production mindset.
$ sudo apt-get install ruby1.8
Install the required Ruby libs:
$ sudo apt-get install libhopefullymygemispackaged-ruby1.8
Well, we’re not there yet, and anyway we might have two Ruby projects in need of two different versions of the same library. So… what do we do?
AKA the… well… development mindset. And Sysadmin’s Nightmare.
I need libA with version 1.2.3 and libB with version 4.5.6 for projectC, running on rubyX.Y.Z, and I don’t want to mess around with my carefully crafted list of system gems.
Install Ruby Version Manager (RVM):
$ bash < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)
Install required ruby version from source (JRuby, Rubinius and other Ruby runtimes are also available)
$ rvm install X.Y.Z $ rvm use X.Y.Z
Create a project-specific
gemset, that will contain all the gems installed
for this project:
$ rvm gemset create projectC-gemset $ rvm gemset use projectC-gemset $ gem install libA --version 1.2.3 $ gem install libB --version 4.5.6
Under the hood,
rvm creates a specific
GEM_HOME for each
rvm gemset use project-gemset, it will change the
environment variable. In this way, the installed gems are isolated from the
gems that were installed system-wide.
RVM is for managing multiple versions of Ruby on the same machine, and switching between them. Gemsets is a powerful feature RVM provides that isolates gems for one application/library from the rest of your system.
Wait, I’m working with other developers, and even I don’t want to have to remember the specific lib versions I want. Here comes Bundler, which is available as a ruby gem:
Bundler makes it easy to make sure that your application has the dependencies it needs to start up and run without errors.
You describe your dependencies for the target project in a specific file named
Gemfile, that you place in your project’s root directory:
projectC $ cat Gemfile source "http://rubygems.org" gem "rails", "3.0.0.rc" gem "rack-cache" gem "nokogiri", "~> 1.4.2"
Then install the specified gems in your newly created ruby environment:
$ rvm use X.Y.Z && rvm gemset use projectC-gemset $ gem install bundler $ bundle install
Bundler will fetch the requested dependencies (and check that they can be
fulfilled), and then install the gems in the specified gemset (this is
transparent for Bundler since it’s based on
GEM_HOME environment variable).
This means any developer can then install all the required dependencies by
$ bundle install on their machine.
Also, some libraries come with executables (e.g.
sequel, etc.). If
you want to run these executables in the current dependency context, you must
bundle exec command:
$ bundle exec rails ...
Bundler is for managing Ruby dependencies. Those dependencies are described in a
You don’t need to use RVM with Bundler. They are separate tools. By default Bundler will install your gems in the default
GEM_HOMEpath. When using RVM and Bundler together, RVM “tells” (by setting a different
GEM_HOME) Bundler where the gems should go, and Bundler installs them into the RVM-folder.
Obviously there is the
Gemfile file, which lists the dependencies required
by your project. Commit this into your repository, be it a Ruby application
or a gem.
The first time bundle runs, it will create a
Gemfile.lock file that will
contain a dump of the library dependency tree for the gems it just
installed. If you commit this file into your project’s repository, then when
other developers will run
$ bundle install on their machines, they’ll get
the exact same versions for the gems. If one of the developers want to
update a dependency, he’ll have to modify the
Gemfile file and run
bundle update. Do not commit this file if you’re developing a Ruby gem,
otherwise you’ll be always testing against a static view of the dependency
tree your gem is using, which is probably not the one that your users are
To avoid entering
$ rvm use X.Y.Z && rvm gemset use projectC-gemset each
time you work on
projectC, you can create a
.rvmrc file that will be
picked up by RVM when you
cd into the project’s directory:
projectC $ cat .rvmrc rvm use ruby1.9.2@projectC-gemset --create
You may commit this file into the project’s repository, but only if it’s a Ruby application, not a Ruby gem.
There is also a
.bundle/config file, which contains the Bundler
configuration for the project. This is especially useful (and must be
shipped with the project) if you’re using Bundler in deployment mode (see
next section). Do not commit this into the repository.
Where we’ll talk about packaging.
Usually it makes a lot of sense to install the Ruby version with your
distribution package manager => Sysadmin is happy, and you get security
patches for free. Same for standard non-ruby dependencies (
Vendor all the Ruby libraries you’re using. Vendoring means: shipping all
the source code of your dependencies with your project. It’s usually located
vendor/ folder, and Bundler can create it for you by running it in
$ bundler install --deployment
You should even package Bundler in your
$ gem install bundler -i vendor/bundle/
Then you just need to change the
$LOAD_PATH in your application, so that
Ruby looks for libraries in your
vendor/ folder first. Note that the
installation of the gems in
vendor/ must be done on a machine that has the
same distribution and architecture of the target machine, unless all your
dependencies are pure Ruby libraries.
Go crazy on your development machine. You need a quick and easy way to test
your app on different versions of Ruby, switch between library versions,
reinstall dependencies from scratch, etc. RVM fits the bill perfectly. And
Bundler is the de-factor standard for managing dependencies in your
applications (for Ruby gems just use the
.gemspec file and point your
Gemfile to the
Once you push into production, I would go against using RVM and instead picking the adequate Ruby version from your distribution package manager (assuming that version exists). However, you should vendor any Ruby dependency with your project.