Mike Slinn

Setting Up a Ruby Development Environment

Published 2022-02-12. Last modified 2023-12-07.
Time to read: 9 minutes.

This page is part of the ruby collection.

This article describes how to install Ruby for software development. Ruby is useful for making special-purpose programmable websites and system programming. A working Ruby development setup is necessary for using and developing Jekyll plugins, which are written in Ruby.

The Ruby language is interesting and fun to work with, but the immediate value it provides comes from the large number of high-quality, open-source libraries available for free, which are known as gems.

Standalone or Version Manager?

Because Ruby applications are normally constructed from a collection of Ruby gems, the system on which you installed Jekyll must somehow store those gems. There are several ways to install Ruby plugins, including standalone and virtualized installations using a version manager.

Why Virtualize Ruby?

It is better to use virtualized user- and project-specific Ruby instances instead of working with a system-wide installation of Ruby. This allows you to install and upgrade Ruby packages without using supervisor privileges.

The benefits of virtualized Ruby are similar those provided by Python venv and node.js nvm.

Using multiple virtualized instances allows you to work on many different independent Ruby projects at the same time without package version collisions.

Redundant Virtualization

Some environments should not switch between multiple Ruby versions. Container images meant for either Ruby development or production should just use a standalone Ruby installation.

For example, using virtualized Ruby in a docker image or a VM just adds complexity and overhead. You should choose between docker and virtualized Ruby; avoid using both for the same project.

Use Docker & VMs Appropriately

I believe docker is oversold. It adds unnecessary complexity to software projects.

Instead of virtualizing the entire software environment, as docker attempts to do, virtualize the Ruby programming environment as described in this article. Your system will run faster, plus setup and maintenance will be simpler.

I think docker has been pushed hard in the media because it is a gateway technology to PaSS. This is a trend that PaSS vendors like AWS and Azure want to encourage, but customers are pushing back.

Ruby Installation Options

Various popular choices for installing the Ruby language exist that follow best practices. The less popular choices have smaller user communities; follow the path less traveled at your peril.

The JetBrains 2022 Ruby Survey found that only 5% of Ruby developers did not use a Ruby version manager:

Let’s briefly consider three of the available options:

  1. Install the current version of Ruby standalone, without a Ruby version manager. This option might be most appropriate for a production Linux system that has the minimum amount of software installed. It is the simplest option for Ubuntu and WSL. This option is problematic for Macs, however, so read on for a better option. If you use this option, you will have to set the GEM_HOME environment variable and add $GEM_HOME/bin to the PATH. The details are explained below.
  2. As a regular user using the rbenv Ruby version manager. This is by far the most popular option for software developers, in part because it is lightweight and also because it works so well on Linux and Mac. This option does not require you to mess around with environment variables.

    If you use a Mac, this option provides the best experience.

    This article explains all the details.
  3. As a regular user using the rvm version manager. rvm has more features than rbenv and is easier to install, but it is older and has features that are no longer required with modern versions of Ruby. rvm also redefines some system commands, which can be awkward and confusing. Like the rbenv option, rvm does not require you to mess around with environment variables. This article will not discuss rvm any further.

Ruby Packages

The official Ruby installation instructions are inconsistent across different OSes. For example, following the Ubuntu installation instructions for Ruby also installs the Ruby system headers, while the macOS instructions do not mention the subject, even though they are required. Fear not, dear reader; I will tell you all in this article.

Regardless of how you install Ruby, gems should never be installed by using elevated user privileges (in other words, without using sudo).

Standalone Ruby

This is how to perform the first option described above: installing the current version of Ruby without a version manager. I have done my best to discourage you from following this path, and to motivate you to install rbenv instead.

WSL, Ubuntu and Friends

These instructions apply if your computer has an Ubuntu-derived Linux distro, such as Ubuntu Desktop, Edubuntu, Kubuntu, Lubuntu, Budgie, Cinnamon, Kylin, MATE, Ubuntu Studio, Unity, and Xubuntu. They also apply equally well if have a Windows computer with WSL installed, and one of the Ubuntu flavors just mentioned is installed in WSL.

The apt package manager documentation for the ruby-full package lists three other packages related to installing Ruby, all of which are dependencies of ruby-full. This means those packages automatically get installed when ruby-full is installed. Some of these dependencies are redundant; for example, ruby-dev is a direct dependency of ruby-full and a transitive dependency because it is also a dependency of ruby. That is good because ruby-dev needs to be installed.

ri
Ruby Interactive reference
ruby
Includes the interpreter for the Ruby language
ruby-dev
Contains header files for compiling extension modules for Ruby

Ubuntu 23.04 (Lunar Lobster) Update: The new standard Ruby debugger for Visual Studio Code (debug) requires that the libyaml-dev package also be installed, or psych, which is a transitive dependency of debug, will not install.

The Ruby community has standardized around using git as a source code management tool. For example, the rake command and bundler (discussed below) both have special considerations for git repositories. In the following incantation, I ensure that git is installed with Ruby for this reason.

Shell
$ yes | sudo apt install git ruby-full libyaml-dev

Mac

Installing Xcode and the command line tools needs to be done first because that installs gcc, which is required for building dependencies.

Shell
$ xcode-select --install

$ sudo xcodebuild -license

You will also need to install the XCode command-line tools and the header files available from XCode:

  1. Start XCode.
  2. Select Preferences / Downloads.
  3. Click on Command Line Tools and install them.
  4. Restart the computer.

Install Homebrew, rbenv and Ruby:

Shell
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

$ brew update

$ brew install rbenv rbenv-gem-rehash

GEM_HOME and PATH

This section continues the discussion of how to set up a standalone Ruby instance without a version manager.

The official installation instructions describe how to install Ruby, but do not mention setting GEM_HOME or the need to include $GEM_HOME/bin into the PATH.

If you opt for a Ruby version manager such as rbenv, described next, there is no need to do the following.

  1. Create a directory to hold the gems:
    Shell
    $ mkdir $HOME/.gems
  2. Add the following to ~/.bashrc, ~/.bash_profile or ~/.zshrc:
    ~/.bashrc, ~/.bash_profile or ~/.zshrc
    export GEM_HOME=$HOME/.gems
    export PATH=$GEM_HOME/bin:$PATH

Rbenv

Now we can discuss the second option: using the rbenv version manager to install and maintain Ruby instances of various versions. Rbenv works well, and I strongly urge you to consider using it for your daily Ruby programming needs.

A feature that delighted me the first time I encountered it: If the file .ruby-version exists in a top-level project directory, rbenv automagically ensures that version of Ruby is used for the project. For example, to run Jekyll with Ruby 3.1.0 for a given website, the file .ruby-version just needs to exist in the top-level git directory for the source code, as shown:

Shell
$ cat .ruby-version
3.1.0 

If the required version of Ruby is not available, an error is issued and execution stops: rbenv: version '3.1.0' is not installed (set by /var/myproj/.ruby-version)

Installing Rbenv

If you opted to install Ruby using rbenv on any platform, including Linux and Mac, follow the rbenv official installation instructions. Unfortunately, I found a few errors and continuity gaps in the instructions. The following is an annotated transcript of what I did to install on WSL/Ubuntu, supplemented with notes for Mac.

Ubuntu 23.04 (Lunar Lobster) Update: The new standard Ruby debugger (debug) requires that the libyaml-dev package also be installed, or psych, which is a transitive dependency of debug, will not install.
Shell
$ yes | sudo apt install git rbenv ruby-dev libyaml-dev
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
rbenv is already the newest version (1.1.2-1).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 

Ubuntu bash:

Shell
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc

Mac bash:

Shell
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile

Zsh, all platforms:

Shell
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.zshrc

You still need to install ruby-build before you can install Ruby itself, so please read on!

Using Rbenv

Rbenv builds Ruby using gcc instead of installing a prebuilt version. As you have just learned, Ruby will be installed into ~/.rbenv/versions.

Unlike installing standalone Ruby, when using rbenv to install Ruby, there is no need to set GEM_HOME. Instead, the location of the gems is inferred by the currently active Ruby instance.

The help information provided by man about rbenv is rather limited:

Shell
$ man rbenv
RBENV(1)                                                      RBENV(1)
NAME rbenv - Simple Ruby Version Management
USAGE Initialize rbenv for your acccount:
$ rbenv init $ echo 'eval "$(rbenv init -)"' >> ~/.bashrc # restart your shell after this
Install different Ruby interpreters (requires the ruby‐build package):
$ rbenv install 2.4.0
Switch between different Ruby interpreters:
$ rbenv global 2.4.0 $ ruby -v ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-linux] $ rbenv global system $ ruby -v ruby [whatever version of Ruby Debian provides by default]
The original rbenv README with a more comprehensive documentation, including all of the available commands, is available at /usr/share/doc/rbenv/README.md.gz
ABOUT This manual page was written for the Debian system by Antonio Terceiro <terceiro@debian.org> and may be used by others.
2020‐12‐16 RBENV(1)

The rbenv command-line help is more useful:

Shell
$ rbenv help
Usage: rbenv <command> [<args>]
Some useful rbenv commands are: commands List all available rbenv commands local Set or show the local application-specific Ruby version global Set or show the global Ruby version shell Set or show the shell-specific Ruby version install Install a Ruby version using ruby-build uninstall Uninstall a specific Ruby version rehash Rehash rbenv shims (run this after installing executables) version Show the current Ruby version and its origin versions List installed Ruby versions which Display the full path to an executable whence List all Ruby versions that contain the given executable
See `rbenv help <command>' for information on a specific command. For full documentation, see: https://github.com/rbenv/rbenv#readme

Unfortunately, when you attempt to get a listing of all the available rbenv commands, you get this pathetic excuse for a help message:

Shell
$ rbenv help commands
Usage: rbenv commands [--sh|--no-sh]
List all available rbenv commands

I happen to know that the first command that you should run is rbenv init. Lets see its help message:

Shell
$ rbenv help init
Usage: eval "$(rbenv init - [--no-rehash] [<shell>])"
Configure the shell environment for rbenv

Again, not the most informative help message. Now bravely go ahead and initialize rbenv, even though we are not quite sure what it does:

Shell
$ rbenv init
# Load rbenv automatically by appending
# the following to ~/.bashrc or ~/.bash_profile:

export PATH="$HOME/.rbenv/shims:$PATH"
source /usr/lib/rbenv/completions/rbenv.bash
eval "$(rbenv init -)" 

Contrary to what the instructions say, there is no need to put the following lines into ~/.bashrc or ~/.bash_profile. I will show you why in a moment.

Shell
export PATH="$HOME/.rbenv/shims:$PATH"
source /usr/lib/rbenv/completions/rbenv.bash

Also, contrary to what the instructions say, there is no need to open another shell to run the magic incantation. I call it magic because it is not obvious what the incantation does or why it is needed. Simply type:

Shell
$ eval "$(rbenv init -)"

It is easy enough to learn what the incantation does; just run it without evaluating the output:

Shell
$ rbenv init -
export PATH="/home/mslinn/.rbenv/shims:${PATH}"
export RBENV_SHELL=bash
source '/usr/lib/rbenv/libexec/../completions/rbenv.bash'
command rbenv rehash 2>/dev/null
rbenv() {
  local command
  command="${1:-}"
  if [ "$#" -gt 0 ]; then
    shift
  fi
case "$command" in rehash|shell) eval "$(rbenv "sh-$command" "$@")";; *) command rbenv "$command" "$@";; esac }

Now we know that the magic incantation generates a bash script that does the following:

  1. Adds ~/.rbenv/shims to the PATH
  2. Reads in bash tab completions for rbenv
  3. Defines a bash function called rbenv that shadows the actual rbenv command

Placing the magic incantation into ~/.bashrc or ~/.bash_profile causes the script to be regenerated each time a shell is opened. Go ahead and do that.

Now you know why I said that the earlier instruction to place the following two lines into ~/.bashrc or ~/.bash_profile is redundant because the magic incantation does that.

Redundant
export PATH="$HOME/.rbenv/shims:$PATH"
source /usr/lib/rbenv/completions/rbenv.bash

Call the Doctor

Run the Doctor script to verify all is well with rbenv:

Shell
$ curl -fsSL \
  https://github.com/rbenv/rbenv-installer/raw/main/bin/rbenv-doctor \
  | bash
Checking for `rbenv' in PATH: /usr/bin/rbenv
Checking for rbenv shims in PATH: OK
Checking `rbenv install' support: not found
Unless you plan to add Ruby versions manually, you should install ruby-build.
Please refer to https://github.com/rbenv/ruby-build#installation

Counting installed Ruby versions: none
  There aren’t any Ruby versions installed under ’/home/mslinn/.rbenv/versions’.
  You can install Ruby versions like so: rbenv install 2.7.1
Checking RubyGems settings: OK
Auditing installed plugins: OK 

Ruby-build

Ruby-build is a command-line utility that makes it easy to install virtually any version of Ruby from source. Ruby-build is available as a plugin for rbenv that provides the rbenv install subcommand. Ruby-build also installs a standalone command of the same name. I find that installing ruby-build as an rbenv plugin is the most flexible and portable option.

The following commands can be executed from any directory.

Shell
$ echo "$(rbenv root)"
/home/mslinn/.rbenv 

$ mkdir -p "$(rbenv root)"/plugins

$ ls "$(rbenv root)"
plugins  shims  versions 

$ ls "$(rbenv root)"/shims
bundle  bundler  erb  gem  irb  racc  racc2y  rake  rbs  rdbg  rdoc  ri  ruby  typeprof  y2racc 

$ git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build
Cloning into '/home/mslinn/.rbenv/plugins/ruby-build'...
remote: Enumerating objects: 12063, done.
remote: Counting objects: 100% (756/756), done.
remote: Compressing objects: 100% (304/304), done.
remote: Total 12063 (delta 481), reused 626 (delta 402), pack-reused 11307
Receiving objects: 100% (12063/12063), 2.54 MiB | 14.37 MiB/s, done.
Resolving deltas: 100% (7948/7948), done. 

To upgrade Ruby-build at a later time, run the following from any directory:

Shell
$ git -C "$(rbenv root)"/plugins/ruby-build pull

To list all available versions of Ruby:

Shell
$ rbenv install --list
3.0.6
3.1.4
3.2.2
jruby-9.4.5.0
mruby-3.2.0
picoruby-3.0.0
truffleruby-23.1.1
truffleruby+graalvm-23.1.1

Only latest stable releases for each Ruby implementation are shown.
Use 'rbenv install --list-all / -L' to show all local versions.
8 lines have been placed on the clipboard. 

This is the help message of the ruby-build command:

Shell
$ ruby-build
Usage: ruby-build [-kpv] <definition> <prefix>
  ruby-build --definitions
  ruby-build --version
-k/--keep Do not remove source tree after installation -p/--patch Apply a patch from stdin before building -v/--verbose Verbose mode: print compilation status to stdout -4/--ipv4 Resolve names to IPv4 addresses only -6/--ipv6 Resolve names to IPv6 addresses only --definitions List all local definitions -l/--list List latest stable releases for each Ruby --version Show version of ruby-build

According to the above documentation, the following incantation should show you all available versions of Ruby. However, I will now demonstrate that that the information it provides is usually out-of-date:

Shell
$ ruby-build -l
2.6.10
2.7.6
3.0.4
3.1.2
jruby-9.3.4.0
mruby-3.0.0
rbx-5.0
truffleruby-22.1.0
truffleruby+graalvm-22.1.0 

The output of rbenv install --list should theoretically match the output of ruby-build -l; however, it does not. The reason is because ruby-build was cloned from a git repository, so it is by definition completely up-to-date, while the rbenv Ubuntu package can lag considerably behind.

Use the rbenv install --list command instead of ruby-build -l

Installing Latest Stable Ruby

Now we can use rbenv to install ruby.

Use the following incantation to install the latest stable version of Ruby:

Shell
$ rbenv install $(rbenv install -l 2>/dev/null | sed -ne 3p)

Installing A Specific Ruby Version

The following installs Ruby version 3.1.0.

Shell
$ rbenv install 3.1.0
Downloading ruby-3.1.0.tar.gz...
-> https://cache.ruby-lang.org/pub/ruby/3.1/ruby-3.1.0.tar.gz
Installing ruby-3.1.0...
Installed ruby-3.1.0 to /home/mslinn/.rbenv/versions/3.1.0 

The build took 5 minutes, 11 seconds on a fast laptop with an M.2 SSD.

Activating A Ruby Version

Now that we have installed a Ruby interpreter, we need to activate it before it will be used.

Before doing anything, please notice that we are still using the system version of Ruby:

Shell
$ echo $GEM_HOME

$ gem env home  # System version of Ruby
/var/lib/gems/2.7.0 

The following command sets Ruby 3.1.0 as the default version of Ruby. This setting is persistent.

Shell
$ rbenv global 3.1.0

$ rbenv rehash

$ gem env home
/home/mslinn/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0 

Now two important gems can be installed into the currently active Ruby instance: bundler and rake.

Shell
$ gem install bundler rake
Fetching bundler-2.3.8.gem
Successfully installed bundler-2.3.8
Parsing documentation for bundler-2.3.8
Installing ri documentation for bundler-2.3.8
Done installing documentation for bundler after 0 seconds
Successfully installed rake-13.0.6
Parsing documentation for rake-13.0.6
Installing ri documentation for rake-13.0.6
Done installing documentation for rake after 0 seconds
2 gems installed 

Lets discover information about these gems:

Shell
$ gem info -b rake bundler

*** LOCAL GEMS ***
rake (13.1.0) Authors: Hiroshi SHIBATA, Eric Hodel, Jim Weirich Homepage: https://github.com/ruby/rake License: MIT Installed at: /home/mslinn/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0
Rake is a Make-like program implemented in Ruby
*** REMOTE GEMS ***
rake (13.1.0) Authors: Hiroshi SHIBATA, Eric Hodel, Jim Weirich Homepage: https://github.com/ruby/rake
Rake is a Make-like program implemented in Ruby
*** LOCAL GEMS ***
bundler (2.4.22, 2.4.20, 2.3.7) Authors: André Arko, Samuel Giddins, Colby Swandale, Hiroshi Shibata, David Rodríguez, Grey Baker, Stephanie Morillo, Chris Morris, James Wen, Tim Moore, André Medeiros, Jessica Lynn Suttles, Terence Lee, Carl Lerche, Yehuda Katz Homepage: https://bundler.io License: MIT Installed at (2.4.22): /home/mslinn/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0 (2.4.20): /home/mslinn/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0 (2.3.7, default): /home/mslinn/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0
The best way to manage your application's dependencies
*** REMOTE GEMS ***
bundler (2.4.22) Authors: André Arko, Samuel Giddins, Colby Swandale, Hiroshi Shibata, David Rodríguez, Grey Baker, Stephanie Morillo, Chris Morris, James Wen, Tim Moore, André Medeiros, Jessica Lynn Suttles, Terence Lee, Carl Lerche, Yehuda Katz Homepage: https://bundler.io
The best way to manage your application's dependencies

Ruby Development Components

Common components for Ruby development include the two gems we just installed:

  • Bundler provides the bundle command, which lets you define the libraries that a project needs. It automatically resolves version conflicts and downloads all necessary gems from the sources you provide.
  • Rake is a make-like program implemented in Ruby. Tasks and dependencies are specified in standard Ruby syntax.
  • A testing framework: rspec, minitest, bacon, test::unit, cucumber, capybara, etc.

If you are developing a web app, you will likely need to use one each of the following:

Speeding up Gem Installations

New gems can be extremely slow to install. You can accelerate the process by ignoring ri and rdoc information. To accomplish that, create the file ~/.gemrc as follows.

~/.gemrc
install: --no-ri --no-rdoc
update: --no-ri --no-rdoc

Of course, that means you will not have documentation locally available for the gems that you install or update after that.

About the Author

I, Mike Slinn, have been working with Ruby for a long time now. Back in 2005, I was the product marketing manager at CodeGear (the company was formerly known as Borland) for their 3rd Rail IDE. 3rd Rail supported Ruby and Ruby on Rails at launch.

In 2006, I co-chaired the Silicon Valley Ruby Conference on behalf of the SD Forum in Silicon Valley. As you can see, I have the t-shirt. I was the sole chairman of the 2007 Silicon Valley Ruby Conference.

Several court cases have come my way over the years in my capacity as a software expert witness. The court cases featured questions about IP misappropriation for Ruby on Rails programs. You can read about my experience as a software expert if that interests you.

I currently enjoy writing Jekyll plugins in Ruby for this website and others, as well as Ruby utilities.

* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.