The fact that chruby assumes RUBY_VERSION is the correct way to segregate gems
on all Ruby implementations causes a great deal of grief for users. Two of the
primary problems involve fixed shebangs in gem binary wrappers and conflicting
native libraries in C-ext gems.
1. Fixed gem binary wrapper shebangs:
Sometimes (but not always), rubygems will install a gem binary wrapper with a
fixed sheband. For example, the 'bundle' binary wrapper will have a shebang
like:
/Users/johnny/.rubies/ruby-2.2.0/bin/ruby
The 'bundle' file itself is in the gem bin directory:
/Users/johnny/.gem/ruby/2.2.0/bin/bundle
Everything works fine until the user installs another Ruby version. Then the
user may switch to that version, run `bundle` and the wrong Ruby version will
get invoked due to the fixed shebang.
For MRI, this is less of an issue, because every release of MRI changes the
value of RUBY_VERSION. However, this is not the case for Rubinius nor any
other Ruby implementation. Forcing the gem env to use RUBY_VERSION mixes the
gem binary wrappers for multiple versions and leads to extremely confusing
errors for users. For example, when specifying the ':engine_version' in a
Gemfile, the version of Ruby running bundle differs from the version selected
with chruby:
$ chruby 2.5
$ ruby -v
rubinius 2.5.2 (2.1.0 7a5b05b1 2015-01-30 3.5.1 JI) [x86_64-darwin14.3.0]
$ bundle
Your rbx version is 2.4.1, but your Gemfile specified rbx 2.5.2
This results because the shebang in the gem wrapper is an absolute path to the
2.4.1 version of Rubinius. Since this directory for the gem binary wrappers is
shared across all Rubinius versions, this problem is inevitable.
2. Conflicting native libraries in C-ext gems
Very similar to the shebang issue above, this issue involves installing a
C-ext gem under a particular version of a Ruby implementation and then loading
that gem under another version. For example, Psych uses a C-ext. Installing
Psych under one version of Rubinius will build the C-ext against the C-API for
that version. When the user installs another version of Rubinius, and then
runs `bundle` when Psych is listed as a gem, Bundler will find the installed
version and not install a new version. But if the C-API has changed in the new
version of Rubinius, the C-ext built against the older version may not load in
the newer version. The reverse is also possible: the gem installed under a
newer version may not work with an older version.
Rubinius added the RUBY_ENGINE constant many years ago to address the
challenge of determining which implementation was running the Ruby code. A
similar need exists for distinguishing the Ruby engine version, as illustrated
above. Rubinius has added the RUBY_ENGINE_VERSION constant and it will be
available in 2.5.3+ releases. Just as with RUBY_ENGINE, which was added in MRI
1.9 but never available in MRI <= 1.8.7, we need conditional code to account
for RUBY_ENGINE_VERSION not being set. This provides backward compatibility
and results in zero behavior change for any existing engines and versions.