So, you’ve decided — or have been forced — to use a Ruby version manager on your machine but aren’t able to decide which to settle on. RVM seems to be the popular one but Rbenv is touted as minimal. Should you go with one of them or an entirely different, I-never-heard-about-it-earlier tool?
RVM and Rbenv set aside, there is a long list of Ruby version managers available on the interwebs these days. For any version manager tool, the two main components are:
- Installing Rubies.
- Switching installed Rubies reliably.
It’s how these libraries handle these two components is what creates the
difference between them. For instance, RVM
comes with both these components built into a single program whereas
Rbenv uses a different library to install the Rubies (
rbenv) to just switch between Rubies available on the
We will start with the tool chain with the least amount of WTFs and feature-set and end with the library that tries to do everything. Incidentally, the same order holds good even on the much more reliable metric of “least number of pages required for documenting the features”
Chruby + Ruby-install
The (current?) epitome of Ruby version manager minimalism. Both these tools are created by postmodern with a goal of following the principles of Unix in mind. They do the trick with the least number of surprises.
At it’s basic level, this applications fetches a tar-ed blob, extracts it and compiles the contents. You can think of this as a glorified download script. Not only is the program really small, it is also designed to be modular upfront.
ruby-install ships with a
versions.txt file that
has a list of ruby versions and the corresponding short-hand syntax.
However, it is not essential to update this list after every release
since the application tries to guess the URL
if you specify a ruby version in full. For example, let’s say the
2.1.0-preview1 is not in the
versions.txt file. One can
still install this ruby by running the command:
ruby-install -- 2.1.0-preview1
Clean and simple. Optionally, additional compiler optimization directives, patch URLs, md5 hashes for verification etc., can be passed to the program if required. I’ve written a writeup on how to install a patched version of Ruby.
Any Unix command that you type in at the command-line is looked up by
the shell in the folders specified by the
PATH variable for that shell
session. Let’s say you have installed a fresh Ruby at
ruby executable can be accessed if
the folder is in the environment’s
This simple principle is what
chruby uses to do the job.
Additionally, the Ruby environment requires that when
ruby is run, it
also needs the gems it has to load and a bunch of environment variables
specific to Rubygems. (These can be found by typing in
gem env in a
terminal window). Chruby also sets the environment settings for
Rubygems to work via the
Overall, this combination of
chruby is the simplest
way to have a multiple ruby setup on your machine. If you haven’t
noticed already, I am biased towards this tool chain and use it myself.
Rbenv + ruby-build
This was a project created, apparently (and misunderstood),
out of the author’s dislike with RVM
taking control of Unix’s native commands like
Rbenv is written in Ruby, unlike Chruby or RVM
which are shell scripts. The
ruby-build project, in a similar vein to
ruby-install, handles the downloading and installing of Ruby versions
rbenv handles switching the Ruby and it’s environment.
Ruby-build stores definitions of how to install a
particular ruby in files called…well, definitions. The program needs a
definition to be present for it to install a Ruby, unlike that in the
ruby-install. So, if the version of
ruby-build on your
machine is old, you might not be able to install the latest ruby unless
you update the
ruby-build tool itself. If you want to install a ruby
version with a specific patch, the definition for which is not present
in the director or the project’s
master branch, you need to write a
definition yourself. FWIW, writing a
definition is not that hard.
install_package "openssl-1.0.1e" <url-for-open-ssl> install_package "ruby-2.1.0" <url-for-ruby-2.1.0> <optional compiler flags or options>
And path to this definition file can be passed to the
to install that Ruby version.
rbenv is the ruby-version-switcher. The way it deals with switching a
version is nothing similar to
chruby however, Rbenv updates the
PATH variable and adds an entry pointing to
~/.rbenv/shims. This is
the folder where Rbenv places lightweight executables corresponding to
actual ruby-based executables that are installed on your machine. You
need to run [
rbenv rehash] once the gem installation is complete for
this to happen though.
For example, let’s say you installed two Ruby versions
on your machine and installed the gem
sass when you selected
the current version via Rbenv. After installation and running
rehash, Rbenv will install a
sass executable (different from the one
that comes with the actual gem) into the
When you invoke
sass afterwards, the one inside the shims folder is
invoked and it passes all the arguments to
rbenv, which then decides
which version of the actual
sass executable to run based on various
As you can observe, this way of doing this has a lot of moving parts
(definitions, shims, hooks) etc. However, that didn’t stop this project
from being widely adopted by the Ruby ecosystem for it’s ease of use and
debugging. This has resulted in support for
rbenv in other tools like
Jenkins CI, and much more importantly, support in
terminal prompt setting plugins.
Probably the most hated and the most
used one out of the bunch. RVM is a Ruby
version installer-and-switcher that became a huge hit with Ruby
programmers since it’s creation. It is still widely used and has a lot
of features. Since it packs both the downloading and
installing programs inside a single executable, it can be used to
download and compile rubies for other ruby switchers like
If all you need to do is to install a newer ruby version so that you can update your blog’s Jekyll installation, then you might be better off staying away from RVM; it’s like using a Jackhammer to punch a nail to the wall. That said, the help that you might get if you’re ever stuck when using this jackhammer might be easier due to it’s wider audience.
Detour: Gem maintenance
One thing that is missing in
rbenv as a native feature is
a way to manage gems. RVM’s gemsets
is (was?) a popular way to install gems to an isolated directory — which
can be changed per project — and not pollute the global directory with
unused gems. However, this is not really a handicap these days since
Bundler provides a somewhat similar functionality.
Bundler provides a way to download and install gems to the project
folder and not the global folder by running the command
--path=vendor which will create a folder called
vendor in the current
directory and install gems to it. This makes it trivial to maintain
project specific gems in an isolated environment.
To refer the vendored gems while running executable like
rails, the commands need to be prepended with
bundle exec. So, if
you wanted to run the tests for a library that uses the command
test to run the suite, you’d need to do
bundle exec rake test.
One more important feature of RVM’s
gemsets is that it maintains a global cache of gems. That is, if the
project needs a gem that was already installed on a different (but same
major version) Ruby version, it will not fetch the gem from Rubygems’
remote mirror but will use the one from the cache. So a gem that was
installed using Ruby
1.9.3-p448 will be cached and the cached gem will
be used to install it in all Ruby versions of
1.9.x. A somewhat
limited caching feature is present in Bundler where you can maintain a
List of zsh themes supplied by oh-my-zsh that have support for Rbenv:
> ag -l rbenv ~/.oh-my-zsh/themes alanpeabody.zsh-theme amuse.zsh-theme bira.zsh-theme crunch.zsh-theme dallas.zsh-theme eastwood.zsh-theme fino.zsh-theme gallois.zsh-theme gnzh.zsh-theme itchy.zsh-theme jaischeema.zsh-theme jonathan.zsh-theme josh.zsh-theme macovsky-ruby.zsh-theme macovsky.zsh-theme murilasso.zsh-theme nebirhos.zsh-theme superjarin.zsh-theme suvash.zsh-theme wuffers.zsh-theme