How require loads a gem03 Nov 2017
In modern versions of Ruby, you can use the good old
require method to load a gem.
For instance, if you’ve got the gem
activesupport installed, you can require
everything inside of activesupport (including the kitchen sink) with this line:
You might’ve just tried to open up
irb and run that line, and it might’ve
worked for you… assuming you have activesupport actually installed. It works
on my machine, at least.
But how does
require know where to find gems’ files in Ruby? Wouldn’t those
files need to be on the load path? Well, thanks to a cheeky hack in RubyGems
code, no, those files don’t need to be on the load path. Instead, these gems’
lib directories are added to the load path as they’re needed. I’ll show you
A default load path
When you initialize
irb it already has some directories added to its load
path, which you can see with this code:
My list looks like this:
[ "/Users/ryanbigg/.rubies/ruby-2.4.1/lib/ruby/gems/2.4.0/gems/did_you_mean-1.1.0/lib", "/Users/ryanbigg/.rubies/ruby-2.4.1/lib/ruby/site_ruby/2.4.0", "/Users/ryanbigg/.rubies/ruby-2.4.1/lib/ruby/site_ruby/2.4.0/x86_64-darwin16", "/Users/ryanbigg/.rubies/ruby-2.4.1/lib/ruby/site_ruby", "/Users/ryanbigg/.rubies/ruby-2.4.1/lib/ruby/vendor_ruby/2.4.0", "/Users/ryanbigg/.rubies/ruby-2.4.1/lib/ruby/vendor_ruby/2.4.0/x86_64-darwin16", "/Users/ryanbigg/.rubies/ruby-2.4.1/lib/ruby/vendor_ruby", "/Users/ryanbigg/.rubies/ruby-2.4.1/lib/ruby/2.4.0", "/Users/ryanbigg/.rubies/ruby-2.4.1/lib/ruby/2.4.0/x86_64-darwin16" ]
These paths make it possible for me to do things like
(haha just kidding I use
require 'csv'. At least one of
those directories contains files called
csv.rb which makes
But none of these directories include a file called
require 'active_support/all still work?!
The cheeky hack
The “cheeky hack” in the bundled RubyGems code is shown here in all its glory. The comment at the top of this file gives away what happens:
When you call
require 'x', this is what happens:
- If the file can be loaded from the existing Ruby loadpath, it is.
- Otherwise, installed gems are searched for a file that matches. If it's found in gem 'y', that gem is activated (added to the loadpath).
I won’t walk through the whole thing – consider it homework! – but the short
version is that RubyGems checks to see if there are any unresolved dependencies and if there’s not, then it will try a regular
require. This results in a
LoadError being raised, which is then rescued a little further down.
This error message is checked to see if it ends with the path that we passed
in, and if it does then it calls
Gem.try_activate(path). This method will
activate any gem that matches the specified path. Inside of the
gem, it has a file called
‘active_support/all’, and so the activesupport gem will be activated here.
Activating a gem adds that gem’s
lib directory to the load path, which will
then make requiring any of that gem’s files possible.
Once the gem is activated, this
require method tries to require the path
more. Due to the gem being activated, it is now possible to