Switching to chruby

20 Nov 2013

As all Ruby developers will know, dependency management and isolation in projects is paramount. Example, project ‘A’ is based on a Ruby 1.9.2 and has a version of a gem that only works with 1.9.2 whereas project ‘B’ is a bleeding edge Ruby 2.0.1 application that only works with a few custom gems that you have created.

Until recently I was using rbenv which worked great however it was incredibly slow at times - especially after I had accumulated 12 or so different versions that I used at one point or another. After drilling into the rbenv source code, it became obvious that the shim inside of my .zshrc file that rbenv required (eval "$(rbenv init -)") was actually doing a rehash of all the Rubies everytime a new terminal instance was started which would explain the ~1 sec additional load time when starting up. It can be avoided by passing the --no-rehash parameter however I also didn’t want to start remembering to rehash everytime I made an update.a

After this digging I decided it was time to re-evaluate rbenv and check to see if there were any better solutions that had popped up which I had missed. Not long into the search I stumbled upon a little project called chruby.

chruby has a lot of cool features and all in ~90 lines of code!

  • Updates $PATH.
  • Correctly sets $GEM_HOME and $GEM_PATH.
  • Users: gems are installed into ~/.gem/$ruby/$version.
  • Root: gems are installed directly into /path/to/$ruby/$gemdir.
  • Additionally sets $RUBY_ROOT, $RUBY_ENGINE, $RUBY_VERSION and $GEM_ROOT.
  • Calls hash -r to clear the command-lookup hash-table.
  • Fuzzy matching of Rubies by name.
  • Defaults to the system Ruby.
  • Optionally supports auto-switching and the .ruby-version file.
  • Supports bash and zsh.
  • Does not hook cd.
  • Does not install executable shims.
  • Does not require Rubies be installed into your home directory.
  • Does not automatically switch Rubies by default.
  • Does not require write-access to the Ruby directory in order to install gems.

Ok, cool. So now what? I started by removing all traces of rbenv (including the Ruby binaries as I wanted to start completely fresh). To install chruby, I used homebrew so it was as easy as brew install chruby however it is available from the source as well as a setup script if you are looking to get up and running quickly.

After that completes, you then need a way of installing additional Rubies. My pick was to use ruby-install due to it being simple and the easiest to get going with - there are other options such as ruby-build that will also work. Now that you have chruby installed and at least one of the many Rubies available, it is time to tell your shell that it needs to start using it. Add the following line to your .bashrc or .zshrc.

source /usr/local/share/chruby/chruby.sh

Additionally, chruby also supports auto switching based on a .ruby-version file that is located in the current or parent directory. Even though it doesn’t ship with it out of the box, it is also easy to enable. Just add the following line to your .bashrc or .zshrc below the previous declared chruby line.

source /usr/local/share/chruby/auto.sh

Excellent! Now to set a system wide default so we can get off 1.8.7. There are a couple of ways mentioned in the documentation on how to achieve this but I opted to use the .ruby-version file in my home directory as it was simple and I can add it to my dotfiles under version control ;) .

echo "ruby-1.9" > ~/.ruby-version

Restart your shell and bam, chruby. You can see what Rubies are available by running chruby.

$ chruby

And manually switching can be done using chruby <version>.

$ chruby

$ chruby ruby-1.9

$ chruby
  * ruby-1.9.3-p392

If you are migrating from another tool such as rbenv and don’t want to remove all your existing Rubies, you can just point $RUBIES to wherever you had them previously installed by defining it in your .bashrc or .zshrc. E.g.