I use chruby and it's auto-switching behavior to switch between Ruby versions. I also use Zsh instead of bash.
Not long ago, I noticed some errors that didn't make sense to me, like chruby: unknown Ruby: ruby-1.9.3
. But if I checked the .ruby-version
file in that directory, it would specify something else.
Eventually I figured out what I was doing. I'd open a terminal window in a folder with an outdated .ruby-version
and see no error. Then I'd cd
to a folder with a good .ruby-version
and see the error.
This is because chruby's way of auto-switching is "right before you run a command, check for .ruby-version
and switch to what it specifies." (A terrible hack is needed for this in bash.) In my case, it was running before cd
, which meant it was running in the wrong directory.
To me, it seemed better to use this strategy: "When the shell starts, and after any change of directory, check for .ruby-version
and switch to what it specifies".
I tried to fix this, but the bottom line is that it's seemingly impossible in bash, and postmodern wants chruby to work the same way for both shells. Which is admirable.
However, I only use Zsh, and Zsh provides a better way: hook functions. Specifically, chpwd_functions+=("chruby_auto")
means "run the function called chruby_auto
whenever you change directories."
So in my zsh configuration, I now source a file that contains this:
# Enable chruby
source /usr/local/opt/chruby/share/chruby/chruby.sh
# Enable auto-switching of Rubies specified by .ruby-version files
# in current or parent directory
# Taken from https://raw.githubusercontent.com/postmodern/chruby/c260570c49fedb77b30b9948b798ac0e5046b63d/share/chruby/auto.sh
unset RUBY_AUTO_VERSION
function chruby_auto() {
local dir="$PWD/" version
until [[ -z "$dir" ]]; do
dir="${dir%/*}"
if { read -r version <"$dir/.ruby-version"; } 2>/dev/null || [[ -n "$version" ]]; then
if [[ "$version" == "$RUBY_AUTO_VERSION" ]]; then return
else
RUBY_AUTO_VERSION="$version"
chruby "$version"
return $?
fi
fi
done
if [[ -n "$RUBY_AUTO_VERSION" ]]; then
chruby_reset
unset RUBY_AUTO_VERSION
fi
}
# Auto-detect whenever we change directories.
# It was too hard to contribute
# this back because bash just can't do it.
chpwd_functions+=("chruby_auto")
# Run once as the shell starts up
chruby_auto
It's a small difference, but it makes me happy; I never get an error about an unknown Ruby unless it applies to my current directory.
Update and a Warning
I found one way that this can cause trouble. I moved into a project directory, saw that its .ruby-version
called for a Ruby I didn't have, and did ruby-install
to get it. Then I ran gem install bundler
, followed by bundle
. I assumed that I was running ~/.gem/ruby/2.2.2/bin/bundle
, the right one for the new version of Ruby. But I was actually running /usr/bin/bundle
, which compiled some C gems like json
against the wrong version of Ruby and caused me mysterious segfaults until I figured it out.
This wouldn't have happened with the typical chruby configuration, because chruby calls hash -r
, which means "update your list of what command lives where", and the typical config does that before every command. (This effectively renders the hash of command locations useless, since it's cleared before every lookup. On the other hand, it prevents the problem I created for myself, and computers are fast, so...)
In my case, cd ..; cd -
or just hash -r
does the trick... but only once I knew to do it. Which I learned through much pain. :)
To prevent future mishaps, I edited /usr/bin/bundle
to yell at me and exit, because I never want it anyway.
So the moral is: never try anything new, and always follow the crowd. No, wait, the moral is: take risks, get hurt, and learn stuff from it.
Anyway, you've been warned.