This is a card in Dave's Virtual Box of Cards.

Ruby imports (require, load, etc.)

Created: 2024-04-27

Back to ruby

How do we import Ruby from external files into the current script?

The simplest is load(), which loads and executes a Ruby source file by name or path (relative to the current working directory). The file does not need to have an .rb extension.

load '/path/to/foo2.rb' # absolute path
load './foo2.rb'        # relative path
load 'foo2.rb'          # searches $LOAD_PATH, then current dir
load './foo1'           # no .rb will work
load 'foo1'             # implicit current dir will work

Classes, definitions, and global from the loaded file will be available, but NOT local variables.

load './foo2.rb'   # "foo1 executing..."
puts Foo2.new.hi   # "Hi!"
puts fn_foo2       # "Hello!"
puts $global_foo2  # "Hello world!"
puts foo2          # ERROR

You can load more than once:

load './foo1'   # "foo1 executing..."
load './foo1'   # "foo1 executing..."

require() is very similar except source files have more rigid path and filename requirements, and the file will only execute once, even if you require it multiple times.

require 'foo2.rb'    # ERROR! (needs explicit path)
require './foo2.rb'  # "foo2.rb executing..." --> true
require './foo2.rb'  # --> false
require './foo1'     # ERROR! (needs .rb extension)

The require_relative() method is identical to require() except that the file is searched for relative to the file doing the importing, not the current working directory.

All of these are in the "Kernel" module (and thus always available in the current scope when you start up Ruby). Here’s the official documentation for Ruby 3.0.6 where you can get the full details such as return values and the wrap parameter for load:

My test files

To perform tests for the above examples, I wrote a little script to generate nearly identical source files with a puts statement, a class, a gloabl method, a local variable, and a global variable. Here’s the script:

if not Dir.exist? 'bar'
  Dir.mkdir 'bar'
end

{
  # For each name/path...
  foo1: 'foo1',
  foo2: 'foo2.rb',
  bar1: 'bar/bar1',
  bar2: 'bar/bar2.rb',
}.each do |k, path|

  # Write a script with class and freestanding method
  File.open(path, 'w') do |f|
    f.puts <<~CODE
      puts "#{path} executing..."

      class #{k.to_s.capitalize}
        def hi
          puts "Hi"
        end
      end

      def fn_#{k.to_s}
        puts "Hello"
      end

      $global_#{k.to_s} = "hello world"
    CODE
  end
end

The output looks like this:

$ tree
.
├── bar
│   ├── bar1
│   └── bar2.rb
├── foo1
└── foo2.rb

$ cat foo1
puts "foo1 executing..."

class Foo1
  def hi
    puts "Hi"
  end
end

def fn_foo1
  puts "Hello"
end

$global_foo1 = "hello world"

(I ended up not using the bar/ directory example files as much as I thought I would.)

Then I simply tried different things interactively with irb:

$ irb
irb(main):001:0> load 'foo1'
foo1 executing...
=> true
irb(main):002:0>