TL;DR
RUBYGEMS_GEMDEPS is a ‘new’ environment variable for RubyGems(>=2.2.0). With this line of code in your .zshrc
or .bashrc
:
export RUBYGEMS_GEMDEPS=-
You don’t need to type bundle exec
anymore.
However, RubyGems
does not fully support Gemfile
syntax yet and still has some fairly serious bugs waiting to be fixed with RUBYGEMS_GEMDEPS
, so don’t use this feature in your daily development environment at the moment.
Now, if you are still interested, read on for more info :)
The Issue with binstub
Back in the day when Rails 3 had just been released, many Rails developers were thrilled by the introduction of bundler, a gem that helped our beloved Rails applications resolve gem dependencies in the project, letting it install and use the correct version of each gem. With bundler
, Rails applications always use the expected gems (and versions thereof), which makes us a bunch of happy developers.
But those happy days didn’t last very long. Soon, we found that when we run commands like rails
or rake
, they use binstubs generated by RubyGems, and don’t use the information from the Gemfile
or Gemfile.lock
files.
For example, let’s say there are three versions of rack
in my development environment, like this:
gem list rack
#=> rack (1.6.4, 1.5.5, 1.5.4)
And I have a Gemfile
like this in my project:
ruby "2.2.2"
source "https://rubygems.org"
gem 'rack', '~>1.5.4'
and the Gemfile.lock
looks like this:
GEM
remote: https://rubygems.org/
specs:
rack (1.5.4)
PLATFORMS
ruby
DEPENDENCIES
rack (~> 1.5.4)
BUNDLED WITH
1.10.6
Ideally, when I run rackup --version
, the result would be:
#=> Rack 1.2 (Release: 1.5.4)
But instead it returns:
#=> Rack 1.3 (Release: 1.6.4)
That is not what I want; I would expect it use rack
1.5.4, not the other version. Luckily, the smart developers who built bundler
had already foreseen this issue and provided the command bundle exec
for us to use; meaning that we can run binstubs
generated by bundler
with this command: bundle exec rackup --version
, and bundler
would pick correct version of rack
for us:
#=> Rack 1.2 (Release: 1.5.4)
Some of us live with it, but some don’t, because we still need to type bundle exec
every time we want to run a command. The latter group found some ways to get around this issue, like using rubygems-bundler, and adding a project-specific binstub folder to the top of $PATH. But we all wish that one day, RubyGems can support the Gemfile
and Gemfile.lock
files, so that we can just type rackup
without any setup required.
Obviously, the RubyGems team is aware of this issue, and they have finally introduced the RUBYGEMS_GEMDEPS
environment variable in rubygems
2.2.0.
RUBYGEMS_GEMDEPS
According to RubyGems:
> RubyGems can install a consistent set of gems across multiple environments using gem install -g when a gem dependencies file (gem.deps.rb, Gemfile or Isolate) is present. If no explicit file is given RubyGems attempts to find one in the current directory.
>
> When the RUBYGEMS_GEMDEPS environment variable is set to a gem dependencies file the gems from that file will be activated at startup time. Set it to a specific filename or to “-“ to have RubyGems automatically discover the gem dependencies file by walking up from the current directory.
In short, once you put this line in your ~/.bashrc
or ~/.zshrc
file:
export RUBYGEMS_GEMDEPS=-
Then RubyGems will look for a Gemfile
in the current directory, and use the correct version of gems for you.
To prove this, I ran the same command that I ran before, and got following output:
rackup --version
#=> Rack 1.2 (Release: 1.5.4)
RubyGems is smart enough to pick the version in Gemfile.lock
, not only the Gemfile
. So now we can get rid of rubygems-bundler
, and we no longer need project binstub
folders in the $PATH
. Great! ;) Moreover, note that the latest version of rubygems
(2.4.8) still works with Ruby 1.8.7, so you can even use RUBYGEMS_GEMDEPS
in ruby 1.8.7!
But…
Wait, if this function is so great, how come it still isn’t being widely used by the Ruby community? After all, rubygems
2.2.0 came out at the end of 2013. Unfortunately, that is because RUBYGEMS_GEMDEPS
still has some fairly serious bugs waiting to be fixed. One issue I found is that if there are git repos in your Gemfile
, many gem
related commands will become really slow. For example, with the following Gemfile
:
```
ruby “2.2.2” source “https://rubygems.org”
gem ‘multi_json’, git: “https://github.com/intridea/multi_json.git” gem ‘tilt’, git: “https://github.com/rtomayko/tilt.git” gem ‘rack’, git: “https://github.com/rack/rack.git” gem ‘vcr’, git: “https://github.com/vcr/vcr.git” gem ‘shoulda-matchers’, git: “https://github.com/thoughtbot/shoulda-matchers.git” ```
You can see that most of the commands take more than 15s to run:
time bundle #after all gems installed
#=> bundle 1.02s user 0.46s system 9% cpu 15.658 total
time gem --version
#=> gem --version 0.68s user 0.Matenia Rossidess system 7% cpu 14.536 total
time gem list tile
#=> gem list tilt 0.85s user 0.44s system 4% cpu 29.202 total
You can find more information about this here.
Conclusion
I don’t suggest that you use this feature in your daily development environment at the moment. However, I do believe that sometime in the near future, RUBYGEMS_GEMDEPS
will be ready, and will be the best approach for handling the binstub
issue. So I encourage you to give it a try, and submit any issues you find here!