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