I was a long time user of
RVM for installing and switching
Rubies. It made my life pretty easy even as I listened to others struggle with
it. I was a little uncomfortable putting other things like project-specific
environment variables in my .rvmrc
files. It seemed dirty, but it worked and I
rolled with it for many years.
Eventually, I tried something different. I’d been sold on the idea of using multiple, simpler tools together. Here’s the result.
Installing a Ruby: ruby-install
> brew install ruby-install
> ruby-install ruby-2.2
Got me the Ruby I wanted. “Too simple,” I thought. “Now I won’t be able to say I want to use that version.”
Use a Ruby: chruby
> brew install chruby
> chruby
ruby-2.2.4
And then chruby
spat out a list of installed Rubies.
OK.
> chruby ruby-2.2.4
> chruby
* ruby-2.2.4
> ruby --version
ruby 2.2.4p230 (2015-12-16 revision 53155) [x86_64-darwin15]
“Hunh,” thinks I. chruby
will by default find Rubies installed in the default
directory used by ruby-install
: /opt/rubies/
and ~/.rubies/
.
“OK. But now I have to run that for each project to get the Ruby the project needs.”
Project-Specific Environments: direnv
Enter direnv
.
> brew install direnv
Then create an .envrc
file in a project root directory with the appropriate
commands for the project. It can be made pretty convenient by adding the
following function to a .direnvrc
file in your HOME directory.
# add to ~/.direnvrc
use_ruby() {
# enable the chruby command in an environment
source /usr/local/opt/chruby/share/chruby/chruby.sh
# desired Ruby version as first parameter
local ver=$1
# if version not given as parameter and there is a .ruby-version file, get
# version from the file
if [[ -z $ver ]] && [[ -f .ruby-version ]]; then
ver=$(cat .ruby-version)
fi
# if the version still isn't set, error cause we don't know what to do
if [[ -z $ver ]]; then
echo Unknown ruby version
exit 1
fi
# switch to the desired ruby version
chruby $ver
# Sets the GEM_HOME environment variable to `$PWD/.direnv/ruby/RUBY_VERSION`.
# This forces the installation of any gems into the project’s sub-folder. If
# you’re using bundler it will create wrapper programs that can be invoked
# directly instead of using the `bundle exec` prefix.
layout_ruby
}
Thanks to Steve Tooke for most of the above.
So one of my projects is a Rails app. It’s .envrc
looks like:
# .envrc for a Rails project
export RUBY_GC_MALLOC_LIMIT=90000000
export RUBY_GC_HEAP_FREE_SLOTS=200000
use ruby 2.2.4
This sets some environment variables suitable for testing and switches my Ruby
environment around. I get Ruby version 2.2.4 and GEM_HOME, GEM_PATH, and
GEM_ROOT adjusted to bring in global gems from the 2.2.4 install and
project-specific gems from <project-root>/.direnv/ruby
thanks to direnv’s
layout ruby
feature.
I recommend adding .direnv
to your project’s .gitignore
.
Bonus Round - Chef DK!
I use Chef and the recommended way to install Chef on a development workstation
is to
install the Chef DK. Chef DK
includes its own embedded Ruby and gem environment and chruby
knows nothing
about it. We can use direnv
to twiddle the environment for Chef projects.
# add to ~/.direnvrc
use_chefdk() {
EXPANDED_HOME=`expand_path ~`
# Override the GEM environment
log_status "Overriding default Ruby environment to use ChefDK"
RUBY_ABI_VERSION=`ls /opt/chefdk/embedded/lib/ruby/gems/`
export GEM_ROOT="/opt/chefdk/embedded/lib/ruby/gems/$RUBY_ABI_VERSION"
export GEM_HOME="$EXPANDED_HOME/.chefdk/gem/ruby/$RUBY_ABI_VERSION"
export GEM_PATH="$EXPANDED_HOME/.chefdk/gem/ruby/$RUBY_ABI_VERSION:/opt/chefdk/embedded/lib/ruby/gems/$RUBY_ABI_VERSION"
# Ensure ChefDK and its embedded tools are first in the PATH
log_status "Ensuring ChefDK and it's embedded tools are first in the PATH"
PATH_add $EXPANDED_HOME/.chefdk/gem/ruby/$RUBY_ABI_VERSION/bin/
PATH_add /opt/chefdk/embedded/bin
PATH_add /opt/chefdk/bin
}
Thanks to Seth Chisamore for the Chef DK function.
Now my .envrc
files in Chef projects have a line like this:
# .envrc in a Chef project
use chefdk
It’s working for now and I can happily switch between Chef DK projects and projects needing their own Rubies (e.g. Rails sites).