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 usingrequire.
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:
requireis used to load the specific file.$LOAD_PATHis used to locate where the file is.$LOADED_FEATURESis used to keep tracking of which files have been loaded successfully usingrequire.



