When working in Ruby — or any language for that matter — using a version manager for jumping between installed versions is wise. We’ll focus on two in particular: Frum and chruby.
Versions
Before discussing Ruby version managers, it’s important distinguish between defining global and local versions. The assumption is that you are on macOS and using both Bash and Homebrew. If not, the syntax shouldn’t be difficult to translate for your own shell.
First and foremost, you’ll want to populate $HOME/.ruby-version with the latest version of Ruby. Run the following to set this up by replacing 3.0.0 with whatever is the most current version of Ruby:
printf "%s\n" "3.0.0" > "$HOME/.ruby-version"
Having a global version set to the latest version of Ruby via $HOME/.ruby-version allows you to work with modern Ruby at a global level while being able to use specific versions for each project using their own .ruby-version file which takes precedence over the global version. Now you have maximum flexibility to automatically switch versions when moving between different projects.
OK, now we are ready to talk about Ruby version managers.
Frum
Setup
To install and configure frum, you’ll only need to run the following Bash code from your terminal:
brew install frum
printf "%s\n" 'FRUM_DIR="$HOME/.cache/frum"' >> "$HOME/.bashrc"
printf "%s\n" 'eval "$(frum init)"' >> "$HOME/.bashrc"
exec $SHELL
Usage
Usage is straightforward:
frum install --list # List available Ruby versions for install.
frum versions # List currently installed Ruby versions.
frum install 3.0.2 # Install a specific Ruby version.
frum local 3.0.0 # Switch, locally, to specific version within current directory.
frum global 3.1.0 # Switch, globally, to specific version.
frum uninstall 2.7.0 # Uninstall a specific version.
Aliases
In case you are like me and don’t like to type a lot, the following aliases are a great way to speed up your workflow:
alias rb="frum"
alias rbd="frum global"
alias rbl="frum versions"
alias rbs="frum local"
alias rbu="frum uninstall"
Functions
In addition to aliases, I use a single function for installing Ruby. When coupled with Homebrew, this helps to ensure Ruby is built with a modern version of OpenSSL along with additional options such as building a shared library for integration with programs like Vim and disabling silent rules which might cause Ruby to not build correctly.
# Label: Ruby Install
# Description: Install a specific version with safe defaults.
# Parameters: $1 (required) - Version.
rbi() {
local version="$1"
frum install "$version" \
--with-openssl-dir="$(brew --prefix openssl)" \
--enable-shared \
--disable-silent-rules
}
💡 Should you forget the options used to build Ruby, you can ask Ruby directly via this one liner:
ruby -e 'RbConfig::CONFIG["configure_args"].split.each(&method(:puts))' \
| tr -d "'"
# Output:
--prefix=$HOME/.cache/frum/versions/3.4.1
--with-openssl-dir=/opt/homebrew/opt/openssl@3
--enable-shared
--disable-silent-rules
Past Versions
The situation gets tricky when you need to install older versions of Ruby. One resource is the Old Ruby Build project maintained by Hiroshi Shibata. This project uses rbenv to build older versions of Ruby that are no longer officially supported.
Otherwise, if you don’t want to use the Old Ruby Build project, keep an eye out for warnings and/or errors in the output. Look for -W flags and added them to your CFLAGS export. Usually, that’ll do it but sometimes you have to use Ruby build flags as well. The following are a few examples that might be of interest.
3.0.6
CFLAGS="-DUSE_FFI_CLOSURE_ALLOC \
-Wno-error=implicit-function-declaration \
-Wno-deprecated-non-prototype"
PKG_CONFIG_PATH="$(brew --prefix openssl@1.1)/lib/pkgconfig"
frum install 3.0.6 \
--with-openssl-dir=$(brew --prefix openssl@1.1) \
--with-readline-dir="$(brew --prefix readline)"
--enable-shared \
--disable-silent-rules
2.6.6
CFLAGS="-DUSE_FFI_CLOSURE_ALLOC \
-Wno-error=implicit-function-declaration"
PKG_CONFIG_PATH="$(brew --prefix openssl@1.1)/lib/pkgconfig"
frum install 2.6.6 \
--with-openssl-dir=$(brew --prefix openssl@1.1) \
--enable-shared \
--disable-silent-rules
Last Resort
As a last resort, you can fallback to Ruby Install (mentioned below) as a workaround. For example, say you want to use Ruby 3.0.6 with Frum, then you could do this:
ruby-install ruby-3.0.6 -- \
--with-openssl-dir=/opt/homebrew/opt/openssl@1.1 \
--enable-shared \
--disable-silent-rules
cp -R ~/.rubies/ruby-3.0.6 ~/.cache/frum/versions/3.0.6
A few key points to the above:
-
Ruby Install allows you to compile and install Ruby.
-
You’ll need an older version of OpenSSL when using older Ruby versions.
-
You must copy, not move, the installed Ruby version due to hard file links in order to work with Frum.
Summary
To sum up everything discussed thus far, all commands can be executed with only a few keystrokes now:
rbl # List currently installed Ruby versions.
rbi 3.0.2 # Install a specific Ruby version.
rbs 3.0.0 # Switch to a specific version within current directory.
rbd 3.0.0 # Make Version 3.0.0 the default version globally.
rbu 2.7.0 # Uninstall a specific version.
chruby
chruby is written in Bash and a tool I’ve used for years. The documentation for chruby is straightforward but I’ll walk you through how I’ve used it in case my experience is helpful.
Setup
To setup and use chruby, you’ll need Ruby Install
for installing various Ruby versions. Within your terminal, run the following Bash code:
brew install chruby
brew install ruby-install
ruby-install "ruby-3.0.0"
Ruby Install, by the way, is a specialized tool for installing different versions of Ruby. While
Frum has this functionality baked in, what’s nice about Ruby Install — or Frum — is you don’t have
to wait for any updates to frum, chruby or ruby-install in order to download and install a
Ruby version. As long as the Ruby core team has published a versioned download, you’re good to go!
Configuration
Now that both chruby and Ruby Install are on your machine, you only need to teach your shell how
to auto-switch Ruby versions when you change into a directory that has a .ruby-version file. To do
that, you’ll want to add this to your .bashrc file:
if [[ -r "$HOMEBREW_PREFIX/opt/chruby/share/chruby/chruby.sh" ]]; then
source "$HOMEBREW_PREFIX/opt/chruby/share/chruby/chruby.sh"
source "$HOMEBREW_PREFIX/opt/chruby/share/chruby/auto.sh"
fi
💡 For further details on auto-switching, see the associated chruby documentation.
Usage
Using chruby is straightforward. First, for a list of all currently installed Ruby versions, type:
chruby
Then depending on your system, you should see a list like the following:
ruby-2.7.2 * ruby-3.0.0
Your current version of Ruby will have an asterisk. To switch to a different version, run the following:
# Long
chruby ruby-2.7.2
# ...or...
# Short
chruby 2.7.2
And there you have it, that simple.
Aliases
The following aliases are a great way to speed up your workflow:
alias rb="chruby"
alias rbi="ruby-install"
Now I can list Ruby versions, change to a new Ruby version, or install a new version of Ruby as follows:
# List Ruby versions.
rb
# Switch to a specific Ruby version.
rb 3.0.0
# Install a specific Ruby version.
rbi ruby-3.0.0
Alternatives
Should frum or chruby not be your cup of tea, here are a few other version managers to be aware of:
-
rv: Written in Rust with a focus on speed and the ability to manage your Ruby versions, associated gems, and related resources. Essentially another multi-use tool with multiple responsibilities. One unique feature is the ability to run any version of a Ruby gem, script, application in an isolated environment.
-
mise: This program aims to be a version manager for any language, is written in Rust, and is compatible with XDG. This is also a relatively new entrant in the ecosystem so your mileage might vary since it’s a multi-use tool including all of the complications that might come with that.
-
asdf: Similar in nature to mise in that it works for any language but is written in JavaScript. For Ruby, that means installing the Ruby plugin via:
asdf plugin-add ruby. Unfortunately,asdfsuffers the same problems as RVM in that it tries to do too much. I’d rather my tools be specialized to do one thing really well. -
rbenv: Aims to be a lighter weight version of RVM and succeeds, for the most part. It does require the use of shims to jump between versions, which requires extra maintenance.
-
RVM: One of the first — and worst — version managers to hit the scene. RVM is way more complicated than it needs to be in terms of install, setup, and gem management. I would avoid using, if possible, to save yourself from debugging headaches.
-
All Ruby: This one is different than all of the above — but deserves special mention — because you can use this project to build/test against historic Ruby versions. There is also Docker support as well.
Conclusion
You’ve learned about a couple version managers and how important they are in allowing you to quickly jump between different Ruby versions with minimal effort. Use what best suites your workflow. A good version manager makes a world of difference especially when working on different projects.
