Not really a blog, just some stuff that a future me might need to remember one day.
If you work on multiple projects and those projects use different
versions of Ruby you need some means of switching between different
rubies. rbenv
is a Ruby version manager and solves this problem, without
attempting to do too much else.
To understand rbenv
we first need to a quick recap of RubyGems and
Bundler.
RubyGems is the package management framework for Ruby. In modern
versions of Ruby it’s a built-in, though it used to be a separate
add-on. Ruby packages are known as gems and can be installed using the
gem
command, e.g.
$ gem install rails
You can list installed gems with:
$ gem list
You can also use the gem
command to create and build new Ruby gems but
we won’t go into that here.
Each gem declares a list of dependencies, for example the rails
gem
depends on several other gems like activesupport
, activerecord
,
actionpack
and several others. The gem
command itself will ensure
that whenever you install a particular gem it’s dependencies are
installed but it doesn’t help when it comes to managing the dependencies
for a complete project.
For example a typical Rails project is likely to have dependencies on several dozen if not a hundred or more gems. Each gem will declare a version or more commonly a version constraint for each of it’s dependencies so the process of working out which set of gem versions is required to run a particular application is non-trivial so taking care of this manually is going to be tough. And even if you do manage to get a compatible set of gems installed on your dev machine how are going to ensure that the same gems are installed on your team-mates computers let alone the production server?
This is where Bundler comes in, it manages gem dependencies for us. For
each project we can create a Gemfile
to declare what our project
needs, including rules about the versions we need. Bundler then helps:
Bundler has been around for a while now but seasoned Ruby developers will be able to tell you about pre-bundler days, I’m not sure how they managed without it.
Having created a Gemfile or checked out a project with a Gemfile and Gemfile.lock you can install the full set of gems required for that project with:
$ bundle install
The first time you run this Bundler will create a Gemfile.lock
for
you, this lists all the gems and the versions that you have
installed. You check this into version control so that your colleagues
end up installing exactly the same versions of each gem. That’s the
basic idea, though there are a few other Bundler commands to learn.
rbenv
works by creating a directory of shims for each Ruby executable
(like ruby
, irb
and gem
), and ensures that those appear in your
PATH
variable before the actual Ruby installation directories. The
shims themselves work out which Ruby version to use based on an optional
environment variable RBENV_VERSION
or more likely a .ruby-version
file in the root of your project directory or failing that a global
version.
rbenv
needs to be hooked into your shell in order to set up it’s
shims, e.g. I’ve got the following line in my .zlogin
file:
eval "$(rbenv init -)"
The shims that rbenv
needs have to be updated from time to time, such
as when you install a new gem that contains a new executable (e.g. you
install rspec
). Rebuilding the shims is known as rehashing and is
done automatically as part of the rbenv
initialisation when you open a
new shell, though it can also be done manually with the rbenv rehash
.
Ruby versions are installed under the ~/.rbenv/versions
directory,
though rbenv
defers to ruby-build
to do the job of installing them.
You can install a particular Ruby version as follows:
$ rbenv install 2.3.0
Or if you aren’t sure which one you need you can list the available rubies with:
$ rbenv install -l
rbenv
is designed to do just enough to manage different Ruby versions
without overlapping with anything that Bundler does. So unlike RVM
rbenv
doesn’t create a separate gemset for each project, it installs
gems in the correct Ruby version and lets bundler take care of selecting
the correct version of each gem at runtime. This means it’s critical to
run ruby
and other executables with bundle exec
. Alternatively you
can have Bundler generate binstubs for each executable in your
project’s bin
directory with:
$ bundle install --binstubs
Binstubs are just wrappers around the real executable and in this case
they will ensure that Bundler is loaded before invoking the wrapped
executable, just like bundle exec
does.
rbenv
doesn’t play nicely with RVM so if you are an RVM user as I was
you’ll need to completely remove it from your computer:
$ rvm implode
You’ll also need to remove all references to RVM in your .bashrc
or
.zshrc
etc.
On OSX you can install rbenv
with Homebrew, after opening a fresh
shell:
$ brew update
$ brew install rbenv
Now hook rbenv init
into your shell initialisation, e.g.
$ echo 'eval "$(rbenv init -)"' >> ~/.zlogin
You’ll also need to install ruby-build
in order to install new Ruby
versions:
$ brew install ruby-build
Then you can install whatever Ruby versions you need, e.g.:
$ rbenv install 2.3.0