What’s the relationship between require, $LOAD_PATH and $LOADED_FEATURES in Ruby?

It is common to see require 'something' in a Rails application. But what exactly does require do?

What is require used for in Ruby?

require is used to load external libraries or modules into your program. For example,

Before you require the JSON module

# in irb

JSON
#=> Traceback (most recent call last):
        4: from /Users/lynnbright/.rvm/rubies/ruby-2.7.8/bin/irb:23:in `<main>'
        3: from /Users/lynnbright/.rvm/rubies/ruby-2.7.8/bin/irb:23:in `load'
        2: from /Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/gems/2.7.0/gems/irb-1.2.6/exe/irb:11:in `<top (required)>'
        1: from (irb):3
NameError (uninitialized constant JSON)

After you require the JSON module

# in irb

require 'JSON'
#=> true

JSON
#=> JSON

data = '{"name": "John", "age": 30}'
#=> => "{\"name\": \"John\", \"age\": 30}"

JSON.parse(data)
#=> {"name"=>"John", "age"=>30}

What’s the relationship between require, $LOAD_PATH and $LOADED_FEATURES in Ruby?

Before the file is loaded, there are some steps that Ruby will do.

First, when we use require, it means that we ask Ruby to load the file. However, it is needless to load the same file again and again, right? This is how the $LOAD_PATH and $LOADED_FEATURES play important roles.

  • $LOAD_PATH: It is a global variable used to locate the file.

  • $LOADED_FEATURES: It is a global variable, too. It’s used to keep tracking of which files have been loaded successfully using require.

Search for the file

When require is called, Ruby looks for a specific file from $LOAD_PATH which contains an array of directories.

If the file is found, Ruby loads and executes it.

If the file is not found listed in the $LOAD_PATH, LoadError is raised.

# in irb

require 'abc'

#=> Traceback (most recent call last):
        6: from /Users/lynnbright/.rvm/rubies/ruby-2.7.8/bin/irb:23:in `<main>'
        5: from /Users/lynnbright/.rvm/rubies/ruby-2.7.8/bin/irb:23:in `load'
        4: from /Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/gems/2.7.0/gems/irb-1.2.6/exe/irb:11:in `<top (required)>'
        3: from (irb):9
        2: from /Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:83:in `require'
        1: from /Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:83:in `require'
LoadError (cannot load such file -- abc)

Check if the file already loaded:

If the file is found, before actually loading it, Ruby uses $LOADED_FEATURES, which contains an array of files, to check if the file is already included in the array.

If it is included in the array, that means the file has been loaded. If not, it will be added to $LOADED_FEATURES to prevent it from loaded again.

Let’s see a real example.

# in irb

$LOAD_PATH
#=> ["/Users/lynnbright/.rvm/gems/ruby-2.7.8/gems/timeout-0.4.1/lib", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/site_ruby/2.7.0", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/site_ruby/2.7.0/x86_64-darwin21", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/site_ruby", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/vendor_ruby/2.7.0", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/vendor_ruby/2.7.0/x86_64-darwin21", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/vendor_ruby", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/2.7.0", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/2.7.0/x86_64-darwin21"]

$LOADED_FEATURES
#=> ["enumerator.so", "thread.rb", "rational.so", "complex.so",....]

$LOADED_FEATURES.length
#=> 171

require 'json'
#=> true

# You can see that the "json" directory has been included into the $LOAD_PATH.
$LOAD_PATH
#=> ["/Users/lynnbright/.rvm/gems/ruby-2.7.8/gems/timeout-0.4.1/lib", "/Users/lynnbright/.rvm/gems/ruby-2.7.8/gems/json-2.7.1/lib", "/Users/lynnbright/.rvm/gems/ruby-2.7.8/extensions/x86_64-darwin-21/2.7.0/json-2.7.1", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/site_ruby/2.7.0", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/site_ruby/2.7.0/x86_64-darwin21", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/site_ruby", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/vendor_ruby/2.7.0", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/vendor_ruby/2.7.0/x86_64-darwin21", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/vendor_ruby", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/2.7.0", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/2.7.0/x86_64-darwin21"]

# The array elements increased from 171 to 180, indicating that new elements have been added.
$LOADED_FEATURES.length
#=> 180

$LOADED_FEATURES
#=> [...., "/Users/lynnbright/.rvm/gems/ruby-2.7.8/gems/json-2.7.1/lib/json/version.rb", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/2.7.0/ostruct/version.rb", "/Users/lynnbright/.rvm/rubies/ruby-2.7.8/lib/ruby/2.7.0/ostruct.rb", "/Users/lynnbright/.rvm/gems/ruby-2.7.8/gems/json-2.7.1/lib/json/generic_object.rb", "/Users/lynnbright/.rvm/gems/ruby-2.7.8/gems/json-2.7.1/lib/json/common.rb", "/Users/lynnbright/.rvm/gems/ruby-2.7.8/gems/json-2.7.1/lib/json/ext/parser.bundle", "/Users/lynnbright/.rvm/gems/ruby-2.7.8/gems/json-2.7.1/lib/json/ext/generator.bundle", "/Users/lynnbright/.rvm/gems/ruby-2.7.8/gems/json-2.7.1/lib/json/ext.rb", "/Users/lynnbright/.rvm/gems/ruby-2.7.8/gems/json-2.7.1/lib/json.rb"]

# Attempting to require 'json' again returns false because it is already included in $LOADED_FEATURES.
require 'json'
#=> false

In summary:

  • require is used to load the specific file.

  • $LOAD_PATH is used to locate where the file is.

  • $LOADED_FEATURES is used to keep tracking of which files have been loaded successfully using require.