How does require rubygems help find rubygem files?
Asked Answered
L

1

9

While trying to solve Gem found in irb, not in Ruby , I tried seeing what effect require 'rubygems' had on my own installation:

$ irb
irb(main):001:0> RUBY_VERSION
=> "1.8.7"
irb(main):002:0> $:
["/usr/local/lib/site_ruby/1.8", "/usr/local/lib/site_ruby/1.8/x86_64-linux", "/usr/local/lib/site_ruby", "/usr/lib/ruby/vendor_ruby/1.8", "/usr/lib/ruby/vendor_ruby/1.8/x86_64-linux", "/usr/lib/ruby/vendor_ruby", "/usr/lib/ruby/1.8", "/usr/lib/ruby/1.8/x86_64-linux", "."]
=> nil
irb(main):003:0> require "rubygems" # Hasn't been required already
=> true
irb(main):004:0> require "rubygems" # Will return false, because it's already been required
=> false
irb(main):005:0> $: # Same as before
=> ["/usr/local/lib/site_ruby/1.8", "/usr/local/lib/site_ruby/1.8/x86_64-linux", "/usr/local/lib/site_ruby", "/usr/lib/ruby/vendor_ruby/1.8", "/usr/lib/ruby/vendor_ruby/1.8/x86_64-linux", "/usr/lib/ruby/vendor_ruby", "/usr/lib/ruby/1.8", "/usr/lib/ruby/1.8/x86_64-linux", "."]

If requring rubygems doesn't modify $:, then how does it help find files? Does it monkeypatch require? If so, what variable does it use to find rubygem-installed libraries?

Loutitia answered 30/5, 2011 at 23:52 Comment(0)
S
13

Here's the current version of the relevant source: https://github.com/rubygems/rubygems/blob/02ead548e38ff90923444fa7c0ff9f6a5dbd87b0/lib/rubygems/custom_require.rb. (Edit: here's an earlier version (1.5.2) that more clearly expresses what happens.)

The docs say:

When RubyGems is required, Kernel#require is replaced with our own which is capable of loading gems on demand.

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).

The normal require functionality of returning false if that file has already been loaded is preserved.

It does this by opening up module Kernel and aliasing the original require with alias gem_original_require require, then redefining require to first call the original version, and look at the gems if that doesn't work.

So the load path is only changed when you require a gem:

ruby-1.8.7-p330 :002 > $:.length
=> 9 
ruby-1.8.7-p330 :003 > require 'rubygems'
=> true 
ruby-1.8.7-p330 :004 > $:.length
=> 9 
ruby-1.8.7-p330 :005 > require 'haml'
=> true 
ruby-1.8.7-p330 :006 > $:.length
=> 10 
Skeptical answered 31/5, 2011 at 2:21 Comment(2)
Do you know what variable it uses?Loutitia
@Andrew Gem.path could be what you're looking for.Skeptical

© 2022 - 2024 — McMap. All rights reserved.