How do RVM and rbenv actually work?

25,329

Solution 1

Short explanation: rbenv works by hooking into your environment's PATH. The concept is simple, but the devil is in the details; full scoop below.

First, rbenv creates shims for all the commands (ruby, irb, rake, gem and so on) across all your installed versions of Ruby. This process is called rehashing. Every time you install a new version of Ruby or install a gem that provides a command, run rbenv rehash to make sure any new commands are shimmed.

These shims live in a single directory (~/.rbenv/shims by default). To use rbenv, you need only add the shims directory to the front of your PATH:

export PATH="$HOME/.rbenv/shims:$PATH"

Then any time you run ruby from the command line, or run a script whose shebang reads #!/usr/bin/env ruby, your operating system will find ~/.rbenv/shims/ruby first and run it instead of any other ruby executable you may have installed.

Each shim is a tiny Bash script that in turn runs rbenv exec. So with rbenv in your path, irb is equivalent to rbenv exec irb, and ruby -e "puts 42" is equivalent to rbenv exec ruby -e "puts 42".

The rbenv exec command figures out what version of Ruby you want to use, then runs the corresponding command for that version. Here's how:

  1. If the RBENV_VERSION environment variable is set, its value determines the version of Ruby to use.
  2. If the current working directory has an .rbenv-version file, its contents are used to set the RBENV_VERSION environment variable.
  3. If there is no .rbenv-version file in the current directory, rbenv searches each parent directory for an .rbenv-version file until it hits the root of your filesystem. If one is found, its contents are used to set the RBENV_VERSION environment variable.
  4. If RBENV_VERSION is still not set, rbenv tries to set it using the contents of the ~/.rbenv/version file.
  5. If no version is specified anywhere, rbenv assumes you want to use the "system" Ruby—i.e. whatever version would be run if rbenv weren't in your path.

(You can set a project-specific Ruby version with the rbenv local command, which creates a .rbenv-version file in the current directory. Similarly, the rbenv global command modifies the ~/.rbenv/version file.)

Armed with an RBENV_VERSION environment variable, rbenv adds ~/.rbenv/versions/$RBENV_VERSION/bin to the front of your PATH, then execs the command and arguments passed to rbenv exec. Voila!

For a thorough look at exactly what happens under the hood, try setting RBENV_DEBUG=1 and running a Ruby command. Every Bash command that rbenv runs will be written to your terminal.


Now, rbenv is just concerned with switching versions, but a thriving ecosystem of plugins will help you do everything from installing Ruby to setting up your environment, managing "gemsets" and even automating bundle exec.

I am not quite sure what IRC support has to do with switching Ruby versions, and rbenv is designed to be simple and understandable enough not to require support. But should you ever need help, the issue tracker and Twitter are just a couple of clicks away.

Disclosure: I am the author of rbenv, ruby-build, and rbenv-vars.

Solution 2

I wrote an in-depth article: http://niczsoft.com/2011/11/what-you-should-know-about-rbenv-and-rvm/

The basic difference is where the shell environment is changed:

  • RVM: it's changed when you change Ruby.
  • rbenv: it's changed when you run a Ruby/gem executable.

Also, the thing about RVM is, it covers a lot more then just managing Rubies, it has a lot more than any other tool (there are others apart from RVM and rbenv: https://twitter.com/#!/mpapis/status/171714447910502401)

Do not forget about instant support you get on IRC in the "#rvm" channel on the Freenode servers.

Solution 3

So to summarise the excellent answers above, the main practical difference between RVM and rbenv is when the version of Ruby is selected.

rbenv:

rbenv adds a shim to the start of your path, a command with the same name as Ruby. When you type ruby at a command line the shim is run instead (because it is also called "ruby" and comes first in the path). The shim looks for an environment variable or .rbenv_version file to tell it which version of Ruby to delegate to.

RVM:

RVM allows you to set a version of Ruby directly by calling rvm use. In addition, it also overrides the cd system command. When you cd into a folder that contains a .rvmrc file, the code inside the .rvmrc file is executed. This can be used to set a Ruby version, or anything else you fancy.

Other differences:

There are of course other differences. RVM has gemsets out of the box, while rbenv requires just a little more hacking (but not much). Both are functional solutions to the problem.

Solution 4

rvm system
env > before
rvm jruby # or whatever
env > after
diff after before

Gives you approximately:

< GEM_HOME=$HOME/.gem/ruby/1.9.1
---
> GEM_HOME=$HOME/.rvm/gems/jruby-1.6.6
< GEM_PATH=$HOME/.gem/ruby/1.9.1
---
> GEM_PATH=$HOME/.rvm/gems/jruby-1.6.6:$HOME/.rvm/gems/jruby-1.6.6@global
*bunch of rvm_*
> MY_RUBY_HOME=$HOME/.rvm/rubies/jruby-1.6.6
> RUBY_VERSION=jruby-1.6.6
> IRBRC=$HOME/.rvm/rubies/jruby-1.6.6/.irbrc

And it prepends:

$HOME/.rvm/gems/jruby-1.6.6/bin:$HOME/.rvm/gems/jruby-1.6.6@global/bin

to $PATH

Solution 5

The main difference seems to be when and how ruby is switched. Ruby is switched:

  • for RVM manually (rvm use) or automatically during change of directories
  • for rbenv automatically each time a ruby command is executed

RVM relies on the modified cd command and manual selection of Ruby by rvm use. rbenv uses wrappers or "shims" for all basic ruby commands as the default mechanism to select ruby. RVM creates wrappers for basic command line tools like gem, rake, ruby, too. They are used for example in CronJobs ( see http://rvm.io/integration/cron/ ), but they are not the default mechanism to switch the Ruby version.

Thus both methods select "automatically" the right Ruby version by overwriting commands and using wrappers. rvm overrides shell commands like cd. rbenv overrides all basic ruby commands such as ruby, irb, rake and gem.

Share:
25,329
superluminary
Author by

superluminary

JavaScript Developer and Trainer working out of Brighton, UK: http://nicholasjohnson.com

Updated on March 04, 2020

Comments

  • superluminary
    superluminary about 4 years

    I am interested in how RVM and rbenv actually work.

    Obviously they swap between different versions of Ruby and gemsets, but how is this achieved? I had assumed they were simply updating symlinks, but having delved into the code (and I must admit my knowledge of Bash is superficial) they appear to be doing more than this.

  • superluminary
    superluminary about 12 years
    Thank you for taking the time to give such an excellent answer.
  • superluminary
    superluminary about 12 years
    Thanks, it's really great that people from both communities are getting involved.
  • racl101
    racl101 about 12 years
    Wow, thanks fo such a comprehensible and understandable explanation. A natural born teacher.
  • Nakilon
    Nakilon about 10 years
    Hey, Sam, since this answer is two years old, would you like to make any updates? Surely something has been changed in rbenv since that time.
  • Jeffrey 'jf' Lim
    Jeffrey 'jf' Lim about 9 years
    Nope. Best hacker's description I've ever seen. I think the only update that needs to change in there is to the link to rbenv-gemset (the link will still get you there. It's just another extra step from a redirect).