Logo Devoh

Implicit Gemsets with rbenv

The biggest change so far has been switching from compiling everything myself to using Homebrew. The other big change I decided to make, inspired by a recent talk at PDX.rb, was to switch from RVM to rbenv and ruby-build.

Getting a new machine is always an exciting time. It's a chance to start fresh, free of the cruft that inevitably accumulates over years of use. When that opportunity recently presented itself to me, I decided to take full advantage of it by not copying over anything until I need it, carefully evaluating each component along the way to make sure it's still the best choice.

The biggest change so far has been switching from compiling everything myself to using Homebrew. The other big change I decided to make, inspired by a recent talk at PDX.rb, was to switch from RVM to rbenv and ruby-build.

Concluding RVM

RVM has served me well over the years as I've used it to develop dozens of applications and gems. I don't really have any complaints about it from a usability perspective, but I have noted a few annoyances of late.

  • RVM is a fairly heavyweight solution to the problem of needing to run multiple versions of Ruby. It overrides the cd command, for instance, automatically evaluating the trusted .rvmrc shell scripts scattered throughout your system as you navigate the directory hierarchy.

  • While it's nice to be able to specify an application's required Ruby version in its repository, it isn't quite as nice to do the same with the gemset name, which seems to be the modus operandi. Everyone has their own gemset naming and segregation schemes, and having to resort to hacks like git update-index --assume-unchanged .rvmrc to override the settings in the repo's .rvmrc is a pain.

  • The ~/.rvm directory can grow considerably in size over time as new Ruby versions are installed and the same gems are installed over and over again across gemsets. At last check, mine had grown to over 9 GB in size.

All of these are non-issues in the grand scheme of things, and, if anything, enumerating them just goes to show how great RVM is at doing what it's supposed to do.

Introducing rbenv

After installing Homebrew, I used brew install rbenv ruby-build to install rbenv. I opted to attempt to forego gemsets completely, despite the fact that there's an rbenv-gemset plugin to provide that support for rbenv.

While it has evolved a bit throughout the experiment, my current strategy is to configure Bundler with the following settings:

bundle config --global bin bin
bundle config --global path .bundle

The first command configures Bundler to generate binstubs in the bin directory of the application. This part isn't really new to me. I had already been using this setting under RVM, and have gotten into the habit of always running commands as ./bin/rspec instead of bundle exec rspec.

The second tells Bundler to store the installed gems in the app's .bundle directory. This effectively causes each project with a Gemfile to be treated as an implicit gemset. To make sure this directory never finds its way into source control, I've added .bundle to my global ~/.gitignore (you may need to run git config --global core.excludesfile ~/.gitignore, too).

Issues

While this approach has worked remarkably well on the whole, there have been a few pain points during the transition.

  • I used to use RVM's rmv wrapper command to created isolated gemsets for global commands like heroku. With rbenv, I've had to resort to just using gem install and letting them live in the global GEM_HOME. In some ways, I actually prefer this to RVM's gem wrappers.

  • Because the gems are all tied to the bundle, the core Ruby commands like ruby and irb don't have access to them without using bundle exec. Through a lot of experimentation, I've found that using the rbenv-vars plugin and the following ~/.rbenv/vars file to be the best solution:

    GEM_PATH=.bundle
  • Switching Ruby versions in a project may require the binary gems to be recompiled. This probably isn't really an issue in most day-to-day development, but it's something to be aware of.

Benefits

There have also been a few beneficial side effects, too.

  • I appreciate not having to deal with .rvmrc files any more. It's nice not seeing the trust prompts when changing directories, or having to resort to tricks like the cd shuffle (cd .. ; cd -) and running rvm gemset list to make sure RVM is pointing to the right place.

  • The .rbenv-version file is an effective replacement for the .rvmrc file's Ruby version specification, without the cruft of including a gemset name, too.

  • I like that all the code for an app, including its dependencies, is contained within the application root. This makes installing, moving, and removing applications simpler, in the same way that .app bundles are convenient in OS X.

  • Having the gem source code contained within the project also means it can be more easily accessed. I can now use .bundle as the path, where I previously had to use rvm gemset dir.

Summary

In making this switch, I feel a bit like I've cheated on Wayne Seguin. He's one of the nicest guys you'll ever meet, and I truly respect the work that he does. I still think RVM is a valuable tool, and I hope it continues to be used by lots of developers.

My primary motivation for giving rbenv a try was to see if I could get away with less. I'm not sure the experiment could be deemed a success on that metric alone. Each project requires a number of compromises, some of which have been detailed here. On the whole, though, I'm enjoying this rbenv approach, and I don't feel a pressing need to switch back to RVM any time soon.

and tagged with ruby