Assuming the file ‘dog.rb’ exists in the load path and defines a Dog class, what will be the output of this code?

Dog.new
p require 'dog.rb'

Will this raise a NameError? Will require return true or false? Well, that depends…

Loading source files

Ruby offers several methods for loading source files, including require, require_relative, load, and autoload. These methods are well-documented, and generally behave in predictable ways.

require loads a file with the given name, and returns true. If the file has already been loaded, the method will simply return false.

p require 'dog.rb'
#=> true
p require 'dog.rb'
#=> false

load works similarly, but it will reload the file and return true each time it is called. This is useful in development, where you might want to reload a file whenever it changes.

p load 'dog.rb'
#=> true
p load 'dog.rb'
#=> true

If a file has already been loaded with require, load will reload it.

p require 'dog.rb'
#=> true
p load 'dog.rb'
#=> true

This next example surprised me. If you load a file and then require it, require will reload the file and return true. You wouldn’t normally do this, but I did get into trouble once when I mixed require with Rails’ autoloading (more on autoloading below).

p load `dog.rb`
#=> true
p require `dog.rb`
#=> true

require only returns false when the file already appears in the $LOADED_FEATURES array. While require adds an entry to $LOADED_FEATURES, thereby preventing the second require, load does not.

def dog_required?
  $LOADED_FEATURES.include? File.join(Dir.pwd, 'dog.rb')
end

p dog_required?
#=> false

load 'dog.rb'
p dog_required?
#=> false

require 'dog.rb'
p dog_required?
#=> true

For those interested in the internals, Ruby defines load and require in load.c. These call the C functions rb_f_load and rb_f_require, which in turn call rb_load_internal and rb_require_internal. The function that pushes the entry into $LOADED_FEATURES is called rb_provide_features. Notice that rb_require_internal calls rb_provide_feature, but rb_load_internal does not.

I played around with a branch that adds a call to rb_provide_feature inside rb_load_internal, but the tests for Kernel#load make it clear that is not the expected behavior.

Rails autoloading

Let’s get back to our first example. If we run this file without any other dependencies, Dog.new will raise an error, since we refer to the Dog constant before loading the file that defines it.

Dog.new
#=> NameError: uninitialized constant Dog

But what if this is part of a Rails application? Rails autoloads constants based on a few naming conventions. If ‘dog.rb’ is defined in one of the directories in the config.autoload_paths array, Rails will load the file automatically when you refer to the Dog constant.

When you refer to an undefined constant, Ruby calls const_missing. Rails overrides const_missing and attempts to load the missing constant.

But will Rails load your file, or require it? Well that depends on the environment. Specifically, it depends on the value of config.cache_classes, which is normally set to false in development and true in production. This translates to loading files in development and requireing them in production.

So what will be the output of this code?

Dog.new
p require 'dog.rb'

Usually it will raise a NameError on line 1. But in Rails it will print false in production (the file will already have been required by Rails’ autoloading system), and true in development (the file will have been loaded, but that does not populate $LOADED_FEATURES)

But remember, this is Ruby; it is possible that ‘dog.rb’ will redefine require.

class Dog
end

def require(filename)
  filename
end

In this case the output of our example would be "dog.rb".

So our example will either raise a NameError, print true or false, or do something else.