Why is rbenv called the lighter alternative to RVM, and how do you isolate gems per project when rbenv has no gemsets?
answer
- no cd override, only PATH
- version resolved per command
- installing delegated to ruby-build
- one gem dir per installed Ruby
- lockfile isolation instead of gemsets
basics
~20 srbenv only prepends a shims directory to PATH and resolves the version on each command, without wrapping cd or loading heavy shell functions. It has no gemsets: each installed Ruby has its own gem directory, and Bundler's lockfile isolates each project.
solid answer
~30 srbenv hooks in by putting `~/.rbenv/shims` first on `PATH`; each shim resolves the version at call time from `RBENV_VERSION`, `.ruby-version` or `~/.rbenv/version`. It does not override `cd`, installs Rubies only through the ruby-build plugin, and is plain bash extended by plugins and `rbenv.d` hooks. RVM, by contrast, is sourced as shell functions, reacts to directory changes and manages gemsets itself. Under rbenv each installed Ruby is a normal installation with its own gem directory under `~/.rbenv/versions/<v>`, so gems are separated per Ruby version, and per-project isolation comes from Bundler's `Gemfile.lock` rather than from the version manager.
go deeper
Recall that rbenv has no gemsets, keeps gems per Ruby version, and leaves per-project gem pinning to Bundler.
Contrast the mechanisms: PATH shims resolved per call against shell functions reacting to cd, and ruby-build as the separate installer.
Argue why shims suit editors, cron and CI better than shell-function activation, and extend rbenv through plugins or rbenv.d hooks instead of editing shims.
Frame the choice as separating concerns: a minimal interpreter selector plus a lockfile-based dependency tool, versus one tool that owns both.
## Two designs for the same job Both rbenv and RVM let one machine hold several Rubies and switch between them per project. They differ in **how much of your shell they take over**. | Aspect | rbenv | RVM | |---|---|---| | How it hooks in | Prepends `~/.rbenv/shims` to `PATH`; a small `rbenv` shell function for two commands | Loads as a large set of shell functions sourced into every shell | | When the version is chosen | At each command, when a shim runs `rbenv exec` | When you change directory, through its `cd` handling | | Shell builtins | Does not override `cd` | Wraps `cd` (a `cd` function, or a zsh `chpwd` hook) | | Installing Rubies | Delegated to the ruby-build plugin | Built into RVM itself | | Gem isolation | One gem directory per installed Ruby; no gemsets | Gemsets on top of each Ruby | rbenv's README sums up the trade: "The simplicity of rbenv has its benefits, but also some downsides." Gemsets and RVM's `cd` handling belong to the RVM topic; the point here is what rbenv chose **not** to do. ## What the lighter design buys - **Predictability.** The only state is files (`.ruby-version`, `~/.rbenv/version`) plus one environment variable. `rbenv version` always names which one was used. - **Works outside interactive shells.** A shim is an ordinary executable, so editors, cron and CI can use it without loading shell functions, or can call `rbenv exec` directly. - **Nothing happens on `cd`.** Changing directory runs no code; the next Ruby command simply finds a different `.ruby-version`. - **Readable failures.** A missing version produces an error that names the file or variable that asked for it, so the fix is obvious. - **Easy to disable.** Removing the `rbenv init` line from the shell profile takes the shims off `PATH`, and the system Ruby answers again. - **Plain bash, extensible by hooks.** Each subcommand is a script in `libexec/`; plugins add commands or hook scripts. The cost: a small per-call overhead for every shim, and no built-in way to install Rubies or group gems. ## Where gems go under rbenv Each installed Ruby is a normal Ruby installation with its **own gem directory**, for example `~/.rbenv/versions/4.0.7/lib/ruby/gems/...`. `gem env home` shows it. Consequences: 1. Switching from 3.4.7 to 4.0.7 switches gem directories too; gems installed for one version are not visible to the other. 2. Gems land in your home directory, so `sudo gem install` is never needed. The README warns against it and blames permission errors on the system Ruby still being the global default. 3. Every project on the same Ruby version shares that version's gem directory. 4. A newly installed Ruby starts with only its default and bundled gems, so a project's gems are installed afresh for it. ## Per-project isolation without gemsets Point 3 is the one gemsets used to address. rbenv leaves it to **Bundler**: each project's lockfile records exact gem versions, and running commands through Bundler activates only those, even when many versions of a gem sit side by side in the shared gem directory. How that works is the Bundler topic's material; for rbenv it is enough to say that version management and dependency isolation are two separate tools. ## Extending rbenv: plugins and hooks rbenv's own behaviour is deliberately small, and extension happens through two mechanisms: - **Plugins** live in `~/.rbenv/plugins/<name>`. Their `bin/` directories join rbenv's command lookup, which is how ruby-build adds `rbenv install` and `rbenv uninstall`. - **Hooks** are bash scripts in directories named after a command (`exec`, `rehash`, `which`, `version-name`, `version-origin`, plus `install`, which ruby-build adds) inside `rbenv.d` folders listed on `RBENV_HOOK_PATH`. rbenv sources them at fixed points. rbenv's own RubyGems rehash integration is such a hook: `rbenv.d/exec/gem-rehash.bash` adds a RubyGems plugin to `RUBYLIB` whenever a command runs through `rbenv exec`. `rbenv hooks <command>` lists the hook scripts that will run for a command. ## When to pick which - Choose rbenv when you want the version manager to stay out of the shell and let Bundler own dependencies. - The absence of gemsets is not a gap to patch in most teams: lockfiles already give per-project isolation, reproducibly, on every machine and in CI. - rbenv's README describes it as a version manager for Unix-like systems.
- Under rbenv, why do gems installed for Ruby 3.4.7 disappear after switching the project to 4.0.7?Each version under `~/.rbenv/versions` is a separate Ruby installation with its own gem directory, so `gem list` under 4.0.7 shows only gems installed for 4.0.7. Nothing is lost; reinstall the project's gems for the new version, typically with `bundle install`, and the lockfile brings back the same gem versions.
- How does rbenv let a plugin run code at a fixed point, such as after every rehash?Through hooks: bash scripts in an `rbenv.d/<command>/` directory on `RBENV_HOOK_PATH`, which rbenv sources at fixed points of `exec`, `rehash`, `which`, `version-name` and others. `rbenv hooks rehash` lists what will run. rbenv uses this itself: an `exec` hook puts a RubyGems plugin on `RUBYLIB` so `gem install` triggers a rehash.
saying these in an interview costs you the question
- rbenv overrides cd to switch Ruby when you enter a project
- rbenv local creates a gemset that isolates the project's gems
- All rbenv Ruby versions share one global gem directory
- rbenv needs sudo to install gems because Rubies live in system paths
- No gemsets means rbenv projects cannot pin exact gem versions