In Progress
Unit 1, Lesson 21
In Progress

Require

Video transcript & code

In the last episode we talked about loading code from files. We learned about the Kernel#load method, and how it locates files to be loaded using the ruby $LOAD_PATH. Today we're going to step up a level, and talk about the requiring code.

In theory, we could construct an entire program using nothing by load to load code. But this plan has some problems. Sooner or later we're going to have two different files which both depend on some third file. So they will both load the third file.

# File 1:

load "greet.rb"

# File 2:

load "greet.rb"

This is problematic for several reasons. First off, it's inefficient. The more files we load at program start, the longer it will take to start up. If some files are loaded more than once, that's just waste.

Second, it will lead to a barrage of warnings, as methods and constants are redefined.

$ ruby -w files.rb
greet.rb:2: warning: already initialized constant MAGIC_NUMBER
greet.rb:2: warning: previous definition of MAGIC_NUMBER was here
greet.rb:4: warning: method redefined; discarding old greet
greet.rb:4: warning: previous definition of greet was here

In rare cases, loading a file twice may cause an error. For instance, this code optionally allocates a new array, appends two values to it, and then freezes the array. If it were to be executed twice, it would raise an error when it tried to append to the now-frozen array.

MAGIC_NUMBERS ||= []
MAGIC_NUMBERS << 42
MAGIC_NUMBERS << 23
MAGIC_NUMBERS.freeze

There are even more obscure potential bugs that may stem from repeatedly loading the same file. In some cases, the program can wind up in an inconsistent state because different variables refer to different revisions of the same constant.

Clearly, it's not a good idea to load files full of Ruby code more than once if we can avoid it. And this is the principle way require differs from load: Where load re-evaluates a file every time it is invoked, require keeps a global list of all the files it has already processed. When we try to require the same file a second time, it silently does nothing rather than re-reading and re-evaluating the code in that file.

We can illustrate this by creating a little file that just reports when it is being loaded. When we load this file twice, we see two lines of output. When we require it twice, we see only one.

puts "Loading #{__FILE__}"
load "./noisy.rb"
load "./noisy.rb"
# >> Loading /home/avdi/Dropbox/rubytapas/236-require/noisy.rb
# >> Loading /home/avdi/Dropbox/rubytapas/236-require/noisy.rb
require "./noisy.rb"
require "./noisy.rb"
# >> Loading /home/avdi/Dropbox/rubytapas/236-require/noisy.rb

It's worth noting that this very feature of require makes it less suitable than load in certain circumstances. For example, if we are using a REPL like IRB or Pry and reloading code as we edit it, require would be the opposite of what we need.

irb(main):003:0> load "greeter.rb"
=> true
irb(main):004:0> load "greeter.rb"
=> true
irb(main):005:0>

Earlier we said that require keeps a global list of the files it has already loaded. We can inspect that list by looking at the contents of the $LOADED_FEATURES variable. This global contains an array listing every file that has been required.

$LOADED_FEATURES
# => ["enumerator.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/enc/encdb.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/enc/trans/transdb.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/rbconfig.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/compatibility.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/defaults.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/deprecate.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/errors.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/version.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/requirement.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/platform.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/basic_specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/stub_specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/util/stringio.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/exceptions.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_gem.rb",
#     "thread.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/thread.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/monitor.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/prettyprint.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/pp.rb"]

If we require a new library and then inspect the list again, we can see that it contains a new entry at the bottom.

$LOADED_FEATURES
# => ["enumerator.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/enc/encdb.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/enc/trans/transdb.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/rbconfig.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/compatibility.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/defaults.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/deprecate.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/errors.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/version.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/requirement.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/platform.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/basic_specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/stub_specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/util/stringio.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/exceptions.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_gem.rb",
#     "thread.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/thread.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/monitor.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/prettyprint.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/pp.rb"]

require "set.rb"

$LOADED_FEATURES
# => ["enumerator.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/enc/encdb.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/enc/trans/transdb.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/rbconfig.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/compatibility.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/defaults.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/deprecate.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/errors.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/version.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/requirement.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/platform.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/basic_specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/stub_specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/util/stringio.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/exceptions.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_gem.rb",
#     "thread.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/thread.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/monitor.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/prettyprint.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/pp.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/set.rb"]

Note that we required the file without any directory information. But the entry in the $LOADED_FEATURES list is a fully-qualified filename. As we saw in the preceding episode, Ruby maintains a $LOAD_PATH of directories to search in for a file to load.

$LOAD_PATH
# => ["/home/avdi/.rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/x86_64-linux",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/site_ruby",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/vendor_ruby/2.1.0",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/vendor_ruby/2.1.0/x86_64-linux",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/vendor_ruby",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux"]

Like the load method, require searches this list when trying to find a file to load. When it does find the file, it inserts the absolute filename into the $LOADED_FEATURES list.

Why the absolute filename? Remember, Ruby maintains this list in order to prevent duplicate loading. Consider the following scenario: we add the current directory to the load path. Then we require the same file twice. The first time, we require it using a relative path. The second time, we require it with no path information. Because the current working directory is in the load path, it is found both times.

What this illustrates is that the same file can be loaded with different strings. If Ruby only kept track of the name that was used to require a file, it might be fooled into thinking that this second require was loading a new and different file. By expanding the filename to its full, absolute, canonical path each time, Ruby is able to ensure that a given file on disk is only loaded once, no matter how it is specified.

$LOAD_PATH.unshift(".")

require "./noisy.rb"
require "noisy.rb"

Something you might have wondered about is why the list of loaded files is called $LOADED_FEATURES instead of $LOADED_FILES. To explain this, we need to talk about the Ruby concept of features.

Up until now, we've been loading specific files in the filesystem. If we wanted to load "noisy.rb", we told Ruby to load exactly that. No matter where in the load path it is ultimately found, the file that winds up getting loaded is going to be a file named noisy.rb.

But Ruby source files aren't the only thing that can be loaded into a running Ruby VM. Some libraries aren't implemented as Ruby files at all; instead they come in the form of compiled binary shared object files, otherwise known as dynamically-linked libraries. And sometimes a library may start out as a pure Ruby file, and then later be converted to a binary library written in C as an optimization.

Ruby files have the file extension ".rb". Dynamic library files have various extensions on different operating systems, such as ".so" and ".dll". Clients of a library shouldn't have to know whether that library is implemented in a Ruby file or a dynamic library. And they certainly shouldn't have to change their code when running on a different OS, or when the library rolls out a new version that has switched to a binary implementation.

To hide these details of implementation, Ruby's require normally works at a slightly higher level of abstraction than that of files. When we require some library in a Ruby program, we don't require a specific filename. We require a feature by name. Ruby takes this feature name, and tries to find a corresponding file somewhere in the $LOAD_PATH. It will match any file that has a file extension which is supported on the current platform.

For example, if we want to work with decimal numbers, we require the bigdecimal feature. We don't specify a file extension. When we then examine the loaded features, we can see that the feature "bigdecimal" was resolved to a compiled dynamic library called, at least on my system, bigdecimal.so.

require "bigdecimal"

$LOADED_FEATURES
# => ["enumerator.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/enc/encdb.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/enc/trans/transdb.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/rbconfig.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/compatibility.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/defaults.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/deprecate.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/errors.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/version.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/requirement.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/platform.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/basic_specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/stub_specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/util/stringio.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/exceptions.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_gem.rb",
#     "thread.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/thread.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/monitor.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/dependency.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/path_support.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/bigdecimal.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/prettyprint.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/pp.rb"]

Now that we understand the concept of features, we might like to be able to query Ruby about whether a given feature is currently loaded. Unfortunately, while Ruby has internal C functions to do exactly that, none of them are exposed to Ruby code. The only way we can find out if a feature we want to require is already loaded is after the fact, using the return value of require. If the required feature is found and loaded for the first time, require returns true. But if it has already been loaded, require returns false.

require "bigdecimal"            # => true
require "bigdecimal"            # => false

If we wanted, we could approximate Ruby's feature-checking code. We'll start by requiring a library. Then we'll assign a variable for the feature we want to check for. Next we duplicate Ruby's load path. Now we need to write some code to mimic a special case Ruby has for relative paths: if the path looks like a relative one, we add the current directory to the list of directories to search.

Next we get a set of file glob patterns by mapping over the load path. For each base directory in the path, we expand out an absolute filename based on the feature name with .* appended, which is a file glob pattern that should match any file extension.

At this point, we have a set of glob patterns that we can use to search for a file to match the feature. We use the Dir.glob method to find out if any files on the system match any of those glob patterns. Then we extract out the first of those matches, if any. Remember, the first match in the load path is the file Ruby will load, so none of the later matches matter.

Finally, we check to see if the found absolute filename is present in the $LOADED_FEATURES list.

require "bigdecimal"

feature = "bigdecimal"

load_path = $LOAD_PATH.dup
if feature[0] == "."
  load_path.unshift(".")
end
globs = load_path.map{|base_dir|
  File.expand_path("#{feature}.*", base_dir)
}
globs
  # => ["/home/avdi/.rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/bigdecimal.*",
  #     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/x86_64-linux/bigdecimal.*",
  #     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/site_ruby/bigdecimal.*",
  #     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/vendor_ruby/2.1.0/bigdecimal.*",
  #     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/vendor_ruby/2.1.0/x86_64-linux/bigdecimal.*",
  #     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/vendor_ruby/bigdecimal.*",
  #     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/bigdecimal.*",
  #     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/bigdecimal.*"]
files = Dir.glob(globs)
# => ["/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/bigdecimal.so"]

library = files.first

$LOADED_FEATURES
# => ["enumerator.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/enc/encdb.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/enc/trans/transdb.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/rbconfig.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/compatibility.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/defaults.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/deprecate.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/errors.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/version.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/requirement.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/platform.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/basic_specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/stub_specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/util/stringio.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/exceptions.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_gem.rb",
#     "thread.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/thread.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/monitor.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/dependency.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/path_support.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/bigdecimal.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/prettyprint.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/pp.rb"]

$LOADED_FEATURES.include?(library)
# => true

The result, in this case is true. If we alter the code to not require the bigdecimal library first, the result is false.

feature = "bigdecimal"

load_path = $LOAD_PATH.dup
if feature[0] == "."
  load_path.unshift(".")
end
globs = load_path.map{|base_dir|
  File.expand_path("#{feature}.*", base_dir)
}
globs
  # => ["/home/avdi/.rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/bigdecimal.*",
  #     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/site_ruby/2.1.0/x86_64-linux/bigdecimal.*",
  #     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/site_ruby/bigdecimal.*",
  #     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/vendor_ruby/2.1.0/bigdecimal.*",
  #     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/vendor_ruby/2.1.0/x86_64-linux/bigdecimal.*",
  #     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/vendor_ruby/bigdecimal.*",
  #     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/bigdecimal.*",
  #     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/bigdecimal.*"]
files = Dir.glob(globs)
# => ["/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/bigdecimal.so"]

library = files.first

$LOADED_FEATURES
# => ["enumerator.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/enc/encdb.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/enc/trans/transdb.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/rbconfig.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/compatibility.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/defaults.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/deprecate.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/errors.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/version.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/requirement.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/platform.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/basic_specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/stub_specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/util/stringio.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/specification.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/exceptions.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_gem.rb",
#     "thread.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/x86_64-linux/thread.so",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/monitor.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/rubygems.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/prettyprint.rb",
#     "/home/avdi/.rubies/ruby-2.1.2/lib/ruby/2.1.0/pp.rb"]

$LOADED_FEATURES.include?(library)
# => false

This code could be trivially extracted out to a method, an exercise I'll leave to you.

And that about wraps it up for now. Hopefully, this leaves you with a better understanding of exactly what require does. This isn't the full story, mind you; we haven't talked about Rubygems at all, and that adds a whole new layer to require. But we can discuss that in a future episode. Happy hacking!

Responses