Understanding require and Friends in Ruby

Author: Eric Mathison

Date: September 14, 2013

Last edited: April 5, 2020

Ruby's require method is a tool for referencing and executing code that is not actually contained in the current file. My initial attempts at using require left me a bit confused. Not until later did I realize that Ruby's require method sits atop something more organized and elegant than I had realized. It turns out that require's main use case is generally not one off, relative references to other random Ruby files as I had expected. Instead, require is generally used to look in certain pre-determined directory locations for Ruby libraries. I had previously sort of assumed that a require in Ruby would be something like the relative references to an image in CSS or a reference to a JavaScript file in HTML. Not so. In fact, Ruby's require seems to have drawn several concepts from the UNIX environment.

So How Does Ruby's require Work Anyway?

The best way to think of require is in relation to the UNIX $PATH variable. Just by way of a refresher, the $PATH variable in UNIX contains a list of directories where executables can be found. So when you type the name of a program on any UNIX terminal, your computer is looking through the executable files in the directories specified in your $PATH variable. require does something very similar. When, for example, you write require 'set' at the top of your Ruby file, you are telling Ruby to look through a bunch of directories for a library called set.rb (Ruby's set library).

So where does Ruby look for set.rb? Well, once again, Ruby has something very similar to UNIX's $PATH variable. It is the global variable $LOAD_PATH also sometimes known by it's ugly and undescriptive alias $: (which I don't suggest using by the way--short though it may be). It is an array of directory names where Ruby looks when it comes across a require.

Here's where the analogy falls short however. The UNIX $PATH generally contains a list of bin directories while the Ruby $LOAD_PATH contains an array of lib directories. Well, what on earth are lib and bin directories and what difference does it make? The lib and bin directories are another Ruby concept which originally comes from UNIX. A bin directory is a place for standalone executables. The name bin is a reference to binary files (as opposed to plain text files) since before the days of ubiquitous scripting languages, executables needed to be compiled from source into a binary file. Ruby, of course, is an interpreted language and doesn't need to be compiled but the concept of standalone executables still applies. A lib directory on the other hand is where library code goes. This is the code you reference in other code. Referencing library code is the whole reason we need requires in the first place.

When you require a library you are telling that code to be executed any time the file containing the require is executed. The required file is executed only once. Even if the require is repeated in the same file (why anyone would want to do this I don't know) it will be executed only once*. This is because Ruby checks whether the $LOADED_FEATURES variable a.k.a $" (not to be confused with $LOAD_PATH) already contains the name of the required file before executing

The really nice thing about the way require works in combination with the load path is that as long as a library is in the load path, there is no need to specify the full path or even to know where exactly it is located. You don't even need to put the '.rb' on the end since that is implicit. All you need to put is the extensionless name of the file. Personally, I think this makes for some very clean looking code.

It is important to also realize that in your application, there is generally no need to add directories to the load path manually as this is the environment's job. I mentioned above that the $LOAD_PATH contains an array of lib directory paths. RubyGems will, for example, automatically add the lib directory of gems installed on your computer to the load path. Well, that's actually only mostly true. If you actually run a Ruby script that just outputs $LOAD_PATH without doing anything else, you won't see every single installed gem's load path listed. This is because RubyGems lazily loads Gem's lib directories. So if you want to see your gem's lib directory listed in the load path you have to actually require it first. But things work essentially the same as if every single gem's lib directory was on the load path.

Slightly unrelated to the topic of this article but perhaps also interesting to note is that RubyGems will also add a gem's bin directory to the UNIX $PATH variable so that a gem's executables can be run from the terminal.

Understanding load

The load method is very similar to require. The main differences is that it will run the referenced code as many times as load calls it. As I mentioned above, require only runs the first time.

load must be given the '.rb' file extension because it won't be inferred like it was with require.

All in all, I would say that load has a fairly small amount of usefulness. Probably it's main use case is re-running a file from a Ruby REPL (like pry or irb) after making some edits. It's also very helpful when you need to load ruby files that do not have a .rb extension since require will automatically look for files ending in .rb. This situation can occur when trying to load ruby script files or executables which tend not to have an extension.

Understanding require_relative

require_relative is essentially what I had originally thought require would be used for. If you want to reference a file relative to another file, not treat it like a library, and bypass the load path, this is the tool for you.

In order to understand the need for require_relative, we must understand a bit more about require. So far, the only use I've mentioned for require is executing code on the load path (code that would be considered a library). That's for good reason. That's what it is good for and you probably shouldn't be using require for anything else.

In the past (Ruby 1.8 and earlier), if you wanted to do a one off require without managing the load path, the best way would have been to generate the full path to that file and pass it as an argument to require. It would look something like this:

require File.expand_path('my_file', File.dirname(__FILE__))

File.expand_path gives the full path to the file specified in the first argument. Specifying the second argument essentially says, look for my_file.rb relative to such and such directory. File.dirname(__FILE__) returns the directory path for the current file (that this line was written in). So put together, require is getting passed the full path to my_file.rb which is the file sitting in the same directory in which this line was written. Another way of writing the same thing looks like this:

require File.expand_path('../my_file', __FILE__)

The difference here is that the "directory" we are passing in the second parameter isn't actually a directory. It is just the current file. Fortunately, File.expand_path doesn't care since theoretically, we could make a directory called 'current_file.rb' strange as that would be. Assuming I'm running this line from my home directory, without the '../' before the file name in the first parameter, the string we are passing to require would look something like this: '/home/eric/current_file.rb/myfile.rb'. From File.expand_path's perspective, adding the '../' brings us up a directory but from our perspective, it simply causes current_file.rb to be removed from the path. So, with '../' back in place, the path would expand to '/home/eric/my_file.rb'.

Let me back up and explain why passing require the full path to a file would even be necessary. When I write something like require '../my_file.rb', that path is relative to the current working directory of the process executing this line NOT necessarily the file it was written in. That's important and it means that depending on what directory you are in when you run ruby current_file.rb, your code may or may not be able to find your relative reference to my_file.rb. Yikes!

Enter require_relative stage left. require_relative does essentially what the above two examples using require do except with the added benefit of a clean syntax. So instead of needing that massive monstrosity, all that require_relative needs to do is require_relative 'myfile' and my file will be reference relative the file containing that line (current_file.rb in our example). These techniques are not 100% equivalent though. Ruby's implementation of require_relative actually references the call stack (not __FILE__) in order to determine which file the references are relative to. So in a config.ru file which is used to start a Rack application, it is not currently possible to use require_relative as the contents of this file are run through the eval method instead of being run directly. That means that Ruby can't determine which file to be relative to.

Also note that even if you are running current_file.rb from it's containing directory (so that the current working directory of the process is the same as the location of current_file.rb), it is not possible to do something like require 'myfile' although require './myfile' would work. This is because since Ruby 1.9.2, the current working directory is no longer part of the load path. In the UNIX world, this is considered a good security practice since it avoids the issue of accidentally running an unexpected script by simply executing a program from a directory that just happens to have a file with the same name. Since require_relative is requiring relative to the current file instead of the current process, it doesn't have this security issue. That's one more reason to use require_relative for relative requires.

One last note about require_relative: Have you ever noticed that using require_relative doesn't work on a Ruby REPL like pry? Think about it. If require_relative is requiring relative to a file, which file would that be in a REPL? Well, there isn't really a file that fits that bill. Your alternatives in this situation are to use load or require. If you use require though, make sure to remember the leading './' or '../' and don't do this in production code!. And if you ever want to determine what the current working directory of your Ruby process is, you can always output Dir.pwd.

Conclusion

So, we have seen that require, require_relative and load all fulfil different use cases when used as intended. require is generally for referencing libraries, require_relative is for making one off local references within an application (typically deployed applications, not within libraries) and about all load is good for is re-loading a script in a REPL. Well, that's how I see it anyway :).