Mike Slinn
Mike Slinn

Setting Up a Ruby Development Environment

Published 2022-02-12. Last modified 2023-09-16.
Time to read: 7 minutes.

This page is part of the ruby collection, categorized under Jekyll, Ruby.

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

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 that you installed Jekyll onto must store those gems somehow. There are several ways to install Ruby plugins, including standalone and virtualized installation 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. Also, virtualized instances allows you to work on many different independent Ruby projects at the same time, without package version collisions.

Docker is over-sold. It adds unnecessary complexity to software projects. Instead of virtualizing the entire software environment, as docker attempts to do, virtualizing the Ruby programming environment as described in this blog post, Python venv, or node.js nvm are much easier and more productive approaches.

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.

3 Choices For Installing Ruby

Three popular choices for installing the Ruby language exist that follow best practices, depending on your needs. Other choices exist, but the user communities are much smaller; follow the path less traveled at your peril. Note that only 5% of Ruby developers do not use a Ruby version manager in the image below:

Let’s consider 3 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.
  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, 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. Jump to this section.
  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, this option does not require you to mess around with environment variables.

The official Ruby installation instructions are inconsistent across different OSes. For example, 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 post.

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

Standalone Ruby

This is how to perform the first option described above: installing the current version of Ruby without a version manager.

Debian, Ubuntu, Etc.

These instructions apply if your computer has a Debian-derived Linux distro, such as Ubuntu. The apt package manager docs for the ruby-full package describe 3 other packages related to installing Ruby, all of which are included in ruby-full:

ri
Ruby Interactive reference
ruby
Interpreter of object-oriented scripting language Ruby (default version)
ruby-full
Includes ruby-dev, which contains header files for compiling extension modules for Ruby (default version)
Shell
$ sudo apt install git ruby-full

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 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 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 are not using a Ruby version manager, you should:

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

If you opt for a Ruby version manager such as rbenv, described next, there is no need to set GEM_HOME or modify the PATH.

About rbenv

Now we can discuss the second option, using the rbenv version manager to install and maintain Ruby instances of various versions. Rbenv is pretty cool, and works very well.

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/sites/www.mslinn.com/.ruby-version)

However, this awkwardly-worded warning comes from the documentation:

Some environments—such as container images meant for either Ruby development or production—should not be switching between multiple Ruby versions at all, so if you are installing rbenv there, you are likely doing something wrong.

Installing Rbenv

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

Shell
$ sudo apt install git rbenv ruby-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

Here is the help information for rbenv:

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 ridiculous 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.

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=jekyll
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.

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. It is available as a plugin for rbenv that provides the rbenv install command, and also is provided as a standalone program. 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.2.0
mruby-3.2.0
picoruby-3.0.0
truffleruby-22.3.1
truffleruby+graalvm-22.3.1

Installing Ruby

I decided to install a recent version of Ruby, 3.1.0. As you now know, it will install into ~/.rbenv/versions. Rbenv builds Ruby using gcc, instead of installing a prebuilt version.

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.

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

Shell
$ echo $GEM_HOME

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

The following command set 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 gems can be installed into the currently active Ruby instance.

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.0.6)
    Authors: Hiroshi SHIBATA, Eric Hodel, Jim Weirich
    Homepage: https://github.com/ruby/rake
    License: MIT
    Installed at: /home/mslinn/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0

    Rake is a Make-like program implemented in Ruby

*** REMOTE GEMS ***

rake (13.0.6)
    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.13)
    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: /home/mslinn/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0

    The best way to manage your application's dependencies

*** REMOTE GEMS ***

bundler (2.4.20)
    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:

  • Bundler provides the bundle command, which lets you define the libraries that a project needs. It will automatically resolve version conflicts and download all appropriate gems from the sources you provide.
    Shell
    $ gem install bundler
  • rake is a Make-like program implemented in Ruby. Tasks and dependencies are specified in standard Ruby syntax.
    Shell
    $ gem install rake
  • A testing framework: rspec, minitest, minispec, bacon, test::unit, cucumber, capybara, etc. rspec seems to be winning the war

If you are developing a web app:

  • An app server: unicorn, puma, thin, mongrel
  • Web server: nginx, apache httpd
  • Ruby on Rails, Thin, etc.

Speeding up Gem Installations

New gems can be extremely slow to install. You can speed up the process by ignoring ri and rdoc information. To to that, create the file ~/.gemrc containing:

~/.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 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 SD Forum in Silicon Valley. As you can see, I have the t-shirt. I was 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, and Ruby utilities.