Multiple Ruby Versions
Learn how to install RVM and use it for Ruby programs.
We'll cover the following
How to keep multiple Ruby versions
It is possible for us to keep multiple versions of Ruby on the same computer. If we continue to experiment, we’ll find a way to keep multiple Ruby versions on our filesystem, and all of them will work seamlessly. As we can imagine, this problem already existed a long time ago, when developers realized they needed multiple Ruby versions and some way to switch between them. This is how the Ruby Version Manager (RVM) was born.
RVM is not unique. Similar concepts with slightly different variations exist for other languages, as well. For example, there is the Node Version Manager (NVM) for Node.js. Let’s look closer into RVM.
Installation
Instructions for RVM installation are available at the RVM website, and a summary explains what it is:
“RVM is a command-line tool that allows you to easily install, manage, and work with multiple Ruby environments from interpreters to sets of gems.”
We need to run these two commands to install RVM:
$ gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
$ \curl -sSL https://get.rvm.io | bash -s stable
The installation log says the following:
Installing RVM to /Users/ro/.rvm/
Adding rvm PATH line … /Users/ro/.bashrc /Users/ro/.zshrc.
Adding rvm loading line to ... /Users/ro/.bash_profile /Users/ro/.zlogin.
Installation of RVM in /Users/ro/.rvm/ is almost complete:
- To start using RVM you need to run
source /Users/ro/.rvm/scripts/rvm
in all your open shell windows, in rare cases, you need to reopen all shell windows.
It recommends running source /Users/ro/.rvm/scripts/rvm
(our path is probably different) if we want to use RVM immediately, without restarting a terminal. Then, we can run RVM to see its version:
$ rvm -v
rvm 1.29.4 (latest) by Michal Papis, Piotr Kuczynski, Wayne E. Seguin [https://rvm.io]
Or help:
$ rvm --help
Usage:
rvm [--debug][--trace][--nice] <command> <options>
for example:
rvm list # list installed interpreters
rvm list known # list available interpreters
rvm install <version> # install ruby interpreter
rvm use <version> # switch to specified ruby interpreter
rvm remove <version> # remove ruby interpreter
rvm get <version> # upgrade rvm: stable, master
...
The RVM installer modifies the $PATH
variable and installs itself into the ~/.rvm
directory (we can see what’s inside with ls -lah ~/.rvm
). RVM hijacks the $PATH
, prefixes it with its directories, and feeds us this or another Ruby language, depending on certain circumstances. What exactly are the circumstances that define which Ruby version is going to be used at any moment?
RVM utility
Here, the utility of RVM comes into play, and some people don’t like RVM because of this magic. RVM replaces the cd
(change directory) command of our shell. When we change a directory, RVM tries to detect which Ruby needs to be used. The detection algorithm is relatively simple and explained below, but RVM has two options when the directory changes:
- To silently (or almost silently) feed us the correct Ruby version, so we don’t notice anything
- To do anything
But how does RVM know which version we need? The logic is pretty simple. There is a convention in the Ruby-using community that the Ruby version for a project should be specified in the .ruby-version
dot-file, right in the root directory of a project. This file has a semantic version, like “2.5.1.” When we change directories, RVM reads up this file and switches the current version to the required version. If the Ruby version hasn’t been downloaded yet, RVM notifies us and shows the command we need to run to download this specific Ruby version.
Why don’t we experiment with RVM? Let’s create a test directory, write down the .ruby-version
file, change the directory to the parent, and then change it into this directory again. We need this for RVM to trigger things. This is because initially, there won’t be any .ruby-version
file. Before we do that, let’s look at the current Ruby version:
$ ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
$ which ruby
/usr/local/bin/ruby
Now we can test the RVM magic:
$ mkdir rvm-test # create rvm-test
$ cd rvm-test # switch to rvm-test
$ echo "2.3.1" > .ruby-version # write down 2.3.1 to the file
$ cd .. # go to the parent directory
$ cd rvm-test # and back
Required ruby-2.3.1 is not installed.
To install do: 'rvm install "ruby-2.3.1"'
It worked! RVM said “ruby-2.3.1” isn’t installed and suggested a command to install. Other commands are available with rvm --help
.
Let’s give it a try and run the command. RVM tries to find the precompiled binary for our operating system:
Searching for binary rubies might take some time.
No binary rubies are available for osx/10.13/x86_64/ruby-2.3.1.
If the file is not found, the source code downloads from the official Ruby website and compiles on our computer, the same way we did before manually! So, RVM is just a set of handy scripts, and it’s much easier to use than reinventing the wheel and doing it ourselves with ./configure
and make
.
When everything’s done, we can check the version to make sure it’s installed and works:
$ ruby -v
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin17]
$ which ruby
/Users/ro/.rvm/rubies/ruby-2.3.1/bin/ruby
These manipulations allow RVM to override the default Ruby with the Ruby specified in .ruby-version
:
Before:
- Version:
ruby 2.5.1p57
- Path:
/usr/local/bin/ruby
After:
- Version:
ruby 2.3.1p112
- Path:
/Users/ro/.rvm/rubies/ruby-2.3.1/bin/ruby
We should keep in mind that the old file /usr/local/bin/ruby
still exists; we just modified the environment variables. All of this was done automatically while changing the command directory (cd
).
As we might already notice, having .ruby-version
is crucial for RVM and similar tools to work without issues. This also lets us avoid questions like, “Which Ruby version should I use for the project?”
We’ve installed a specific Ruby version knowing how RVM internals work. However, can we install and use a Ruby language binary of a specific version without the trick of creating the file? Yes, we can. There are a few commands we can use for it:
rvm list known
: Shows the list of rubies available to install. We need MRI (Matz’s Ruby Interpreter) releases.rvm install ...
: Install a specific version of Ruby runtime.
$ rvm install 2.5.1
Searching for binary rubies might take some time.
No binary rubies are available for osx/10.12/x86_64/ruby-2.5.1.
Continuing with compilation. Please read 'rvm help mount' to get more information on binary rubies.
...
We’re installing Ruby 2.5.1 above. The debugging information says that there is no precompiled binary for our operating system, and we’re about to download and compile from the source code.
The RVM searched for Ruby based on the following features:
osx
: A type of the operating system, “osx” for macOS (the legacy name for macOS), Linux, Windows, etc.10.12
: A version of the operating systemx86_64
: CPU architectureruby
-2.5.1: Ruby version
We multiply the number of supported operating systems by the number of different versions for these operating systems, then by the number of CPU architectures, and then by the number of Ruby versions. As a result, we get the number of Ruby binaries that RVM needs to keep on its servers.
Why is there a need for all this? Downloading the Ruby binary takes seconds, while compilation takes much more time and is less eco-friendly. Imagine how many computers need to consume electricity for a significant amount of time to get the same binary! This could be optimized, and RVM keeps at least some of the Ruby binaries to help with that.
From a “consumer” perspective, we don’t need to know too much about these details. What we wanted to do was to understand how to install and use RVM without a dot-file. Now that we know how to install a particular version of Ruby (rvm install 2.5.1
), how do we use it?
Imagine we have multiple versions: 1, 2, 3. If there is no .ruby-version
in the current directory, we must somehow pick the version we want to use. For that purpose, we have the RVM use
sub-command, with a clear syntax:
$ rvm use 2.5.1
Using /Users/ro/.rvm/gems/ruby-2.5.1
$ ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin16]
$ rvm use 2.3.1
Using /Users/ro/.rvm/gems/ruby-2.3.1
$ ruby -v
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin16]
To see all the Ruby versions installed in our system, we use the list
sub-command:
$ rvm list
ruby-2.3.1 [ x86_64 ]
ruby-2.4.2 [ x86_64 ]
* ruby-2.5.0 [ x86_64 ]
=> ruby-2.5.1 [ x86_64 ]
## => - current
## =* - current && default
## * - default
RVM also has a default
version concept. In other words, it is the version that we currently prefer when no configuration is specified. We can enable the default version with the following commands:
$ rvm alias create default 2.5.1
Creating alias default for ruby-2.5.1.....
$ rvm use default
Using /Users/ro/.rvm/gems/ruby-2.5.1
We can now use rvm use default
to use 2.5.1. We can create as many aliases as we want. In practice, we probably won’t need to use this feature very often.
Our quick introduction to RVM is now over. We may come across the definition “gemset” in some documents or while talking to our team. It’s not used that often because the latest versions of Bundler solve the problem RVM gemsets used to solve in the past. However, we know that gemsets are sets of gems, and RVM allows us to keep a particular set of gems for the same Ruby version.
- NVM - Node.js version manager
- VirtualEnv - the similar version manager, but for Python
- Version managers for Golang, Elixir, Java, and so on.
Get hands-on with 1400+ tech skills courses.