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:
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:
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.
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:
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"]
Looking at this output I got curious about
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
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 tooverridecertain 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'
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.