Introduction

First Steps

Since I wanted to figure out what happens when I type bundle install, I figured a good place to start is the file where the bundle command lives. I did this by running which bundle in my terminal. I found the following:

$ which bundle
/Users/richiethomas/.rbenv/shims/bundle
$ 

By default, the which command returns just the first executable filepath it finds which matches the parameter we pass it (in this case, that parameter is bundle). If we pass the -a flag, it will return every executable filepath:

$ which -a bundle
/Users/richiethomas/.rbenv/shims/bundle
/usr/bin/bundle
$ 

Here we see that there are two executable filepaths in total, and that the one located at /Users/richiethomas/.rbenv/shims/ is found before the one located at /usr/bin/bundle. The filepath that is found first is the one that my computer will use to run this command, so this is the filepath I need to inspect if I want to know what my machine is doing.

Note- if you're wondering why ~/.rbenv/ begins with a dot, I go into more detail about that here.

Inspecting the filepath I found

For my code editor, I sometimes use VS Code and sometimes use vim as my editor, and I have a terminal command named code which will open up a filepath I pass in VS Code. So I run the following in my terminal:

$ code /Users/richiethomas/.rbenv/shims/bundle

When I do, I see the following in VS Code:

#!/usr/bin/env bash
set -e
[ -n "$RBENV_DEBUG" ] && set -x

program="${0##*/}"
if [ "$program" = "ruby" ]; then
  for arg; do
    case "$arg" in
    -e* | -- ) break ;;
    */* )
      if [ -f "$arg" ]; then
        export RBENV_DIR="${arg%/*}"
        break
      fi
      ;;
    esac
  done
fi

export RBENV_ROOT="/Users/richiethomas/.rbenv"
exec "/Users/richiethomas/.rbenv/bin/rbenv" exec "$program" "$@"

Yikes, that's a spicy meatball!

It'll take me quite a few posts to explain this code in its entirety. For now, the important take-away is that this code comes from RBENV, the version manager I use for Ruby. It does not come from the bundle command. There are a few clues which let me know this is true:

  • The file's path is /Users/richiethomas/.rbenv/shims/bundle, which includes .rbenv.
  • There are a few RBENV-specific references in the code, including environment variables named RBENV_DIR and RBENV_ROOT.
  • If we were to inspect other files in the ~/.rbenv/shims/ folder, we'd see they all look exactly the same! The following files all contain exactly the same code as the above:
    • ~/.rbenv/shims/rails
    • ~/.rbenv/shims/ruby
    • ~/.rbenv/shims/gem

If you're unfamiliar with the concept of version managers, I wrote a blog post about it here. If you know what version managers are but are unfamiliar with why someone might choose RBENV over other options, I wrote a blog post about that here.

Moving On

In the following chapters, I'll break down the code line-by-line. By the end, we'll see why all these files can have the same exact code, yet execute different programs. We'll also see that RBENV (and some other Ruby version managers) work by intercepting your call to the ruby command (or any other Ruby-specific command), doing some work to figure out which Ruby version you want to use, and then making sure that this Ruby version is used by the command you've typed.

In order to keep the focus of this guide on the RBENV codebase, I didn't want to dive too deeply into the concept of a "shim". If you're unfamiliar with the concept of a shim, I wrote a blog post about it, which you can read here.

The first line of code in the above shim is:

#!/usr/bin/env bash

In the next section, we'll talk about what this code does.