The require and load path in Ruby

The require is a Kernel method in Ruby. In this article, we will see how Ruby knows where to find the files. Can you require a ruby file in the same directory? Let's create two files first a.rb:

puts 'a' 

and b.rb:

require 'a' 

Will this work? Let's run the b.rb:

$ ruby b.rb
 `require': cannot load such file -- a (LoadError)

We get an error. We can fix this by changing b.rb to:

require './a'

Now it works:

$ ruby b.rb
a

Speficying the directory where a.rb file can be found in the require method is problematic. Because if we go back up one directory and run b.rb, it fails with the same error message.

cd ..
$ ruby geme/b.rb
`require': cannot load such file -- a (LoadError)

This fails because the dot refers to the Ruby process's current working directory not the directory of the source file. So in my case, Ruby looked for a file a.rb in the folder /Users/bparanj/projects, that is the directory where the Ruby process ran. But the file was inside /Users/bparanj/projects/geme. We can overcome this by using require_relative Kernel method. The require_relative method specifies files relative to the source file without relying on the directory where the Ruby process was run.

Load Path

Look at the Ruby help screen.

 ruby --help
Usage: ruby [switches] [--] [programfile] [arguments]
  -0[octal]       specify record separator (\0, if no argument)
  -a              autosplit mode with -n or -p (splits $_ into $F)
  -c              check syntax only
  -Cdirectory     cd to directory before executing your script
  -d, --debug     set debugging flags (set $DEBUG to true)
  -e 'command'    one line of script. Several -e's allowed. Omit [programfile]
  -Eex[:in], --encoding=ex[:in]
                  specify the default external and internal character encodings
  -Fpattern       split() pattern for autosplit (-a)
  -i[extension]   edit ARGV files in place (make backup if extension supplied)
  -Idirectory     specify $LOAD_PATH directory (may be used more than once)
  -l              enable line ending processing
  -n              assume 'while gets(); ... end' loop around your script
  -p              assume loop like -n but print line also like sed
  -rlibrary       require the library before executing your script
  -s              enable some switch parsing for switches after script name
  -S              look for the script using PATH environment variable
  -T[level=1]     turn on tainting checks
  -v, --verbose   print version number, then turn on verbose mode
  -w              turn warnings on for your script
  -W[level=2]     set warning level; 0=silence, 1=medium, 2=verbose
  -x[directory]   strip off text before #!ruby line and perhaps cd to directory
  --copyright     print the copyright
  --enable=feature[,...], --disable=feature[,...]
                  enable or disable features
  --external-encoding=encoding, --internal-encoding=encoding
                  specify the default external or internal character encoding
  --version       print the version
  --help          show this message, -h for short message
Features:
  gems            rubygems (default: enabled)
  rubyopt         RUBYOPT environment variable (default: enabled)

The -I switch specifies the load path directory. So if we change our require method argument back to just a in b.rb:

require 'a'

and use this flag to pass the current directory it should work.

$ ruby -I . b.rb
a

It works! The dot after -I switch specifies the directory where the require can find files. In this case, the current directory. What is load path in Ruby? In Ruby identifiers starting with a dollar sign is a global variable, if we run interactive Ruby we can see what load path contains by default.

$ irb
> $LOAD_PATH
 => ["/Users/bparanj/.rvm/rubies/ruby-2.2.3/lib/ruby/site_ruby/2.2.0", "/Users/bparanj/.rvm/rubies/ruby-2.2.3/lib/ruby/site_ruby/2.2.0/x86_64-darwin14", "/Users/bparanj/.rvm/rubies/ruby-2.2.3/lib/ruby/site_ruby", "/Users/bparanj/.rvm/rubies/ruby-2.2.3/lib/ruby/vendor_ruby/2.2.0", "/Users/bparanj/.rvm/rubies/ruby-2.2.3/lib/ruby/vendor_ruby/2.2.0/x86_64-darwin14", "/Users/bparanj/.rvm/rubies/ruby-2.2.3/lib/ruby/vendor_ruby", "/Users/bparanj/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0", "/Users/bparanj/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/x86_64-darwin14"]

Sidenote

Looking at this output I got curious about site_ruby and vendor_ruby directories. I found a good explanation by Ammar on the Ruby mailing list.

The theory is, things you or your admin installs go into site_ruby, as they are specific to the site (machine, server, etc.) Things installed by the distribution (for example MRI, jruby, etc.) go into vendor_ruby, they are specific to that distribution vendor. There are also architecture specific directories under each, for example site_ruby/1.9.1/i686-linux or vendor_ruby/1.9.1/i386-darwin, that contain modules specific to that machine's architecture. If you look at the contents of the default load paths (p $:) you'll notice that the site directories come before the vendor directories, allowing you to override certain vendor modules with ones you install, in site. BTW, this is not specific to ruby. perl, python, php, and others have a similar arrangement. -- Ammar (Ruby mailing list)

Coming back to our topic at hand, the load path is an array of absolute paths. So in b.rb let's add to load path the directory containing a.rb. In this case we can add the current directory before the require statement.

$LOAD_PATH.unshift('.')
require 'a'

Ruby will now know where to find the a.rb file without needing the load path modifier flag.

$ ruby b.rb
a

It now works. The unshift will put the argument in the beginning of the array. Here is a quick example:

$ irb
> [1,2].unshift(0)
 => [0, 1, 2]

But this still has the same problem when you run the program from some other directory.

$ cd ..
projects bparanj$ ruby geme/b.rb
 `require': cannot load such file -- a (LoadError)

We can fix it like this:

folder = File.expand_path('.',__dir__)
$:.unshift(folder) unless $:.include?(folder)

require 'a'

The line:

File.expand_path('.',__dir__)

gives the expanded path where the b.rb resides. In my case, it is /Users/bparanj/projects/geme. Now I can run this program from any folder, for instance I can go back up one directory:

$ cd ..
projects bparanj$ ruby geme/b.rb
a

It works. Even from my home directory:

$ ruby projects/geme/b.rb
a

It still works! So, Ruby will concatenate the folder and the a.rb to find the location of a.rb file, read this file into memory and execute the code for top to bottom. In this article, you learned how the require and load path work in Ruby and how you can run a ruby program using require from any directory.


Related Articles


Create your own user feedback survey