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 (ruby-build
) and another (rbenv
) to just switch between Rubies available on the machine.
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.
####Ruby-Install:
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.
The 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 version 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.
####Chruby:
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 /opt/binaries/ruby/2.1.0
. That ruby
executable can be accessed if the folder is in the environment’s PATH
variable.
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 PATH
variable.
Overall, this combination of ruby-install
+ 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.
A full length guide to set this toolchain up can be found here for Ubuntu and here for Mac.
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 cd
. 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 whereas rbenv
handles switching the Ruby and it’s environment.
####ruby-build:
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 case of 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.
And path to this definition file can be passed to the ruby-build
tool to install that Ruby version.
####rbenv:
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 2.0
and 2.1
on your machine and installed the gem sass
when you selected 2.1
as the current version via Rbenv. After installation and running rbenv rehash
, Rbenv will install a sass
executable (different from the one that comes with the actual gem) into the ~/.rbenv/shims
directory. 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 parameters.
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.
RVM
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 chruby
.
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 chruby
and 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 bundle install --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 rake
or 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 rake 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 per-project cache.
List of zsh themes supplied by oh-my-zsh that have support for Rbenv: