require "user"
Where is the user.rb file located? Perhaps in the current directory? That’s a
trick question. There isn’t enough information to determine that from that
single line.
The answer is more complex, flexible, and involves some UNIX history.
$LOAD_PATH
$LOAD_PATH is a global variable in Ruby that points to an array of path
strings. The following is the (truncated) result of viewing $LOAD_PATH in pry:
[1] pry(main)> $LOAD_PATH
=>
["/Users/joelquenneville/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/did_you_mean-1.0.0/lib",
"/Users/joelquenneville/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/slop-3.6.0/lib",
"/Users/joelquenneville/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/method_source-0.8.2/lib",
"/Users/joelquenneville/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/pry-0.10.4/lib",
"/Users/joelquenneville/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/coderay-1.1.1/lib",
"/Users/joelquenneville/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/pry-byebug-3.4.0/lib",
"/Users/joelquenneville/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/byebug-9.0.6/lib",
...skipping...
"/Users/joelquenneville/.rbenv/versions/2.3.1/lib/ruby/vendor_ruby/2.3.0",
"/Users/joelquenneville/.rbenv/versions/2.3.1/lib/ruby/vendor_ruby/2.3.0/x86_64-darwin14",
"/Users/joelquenneville/.rbenv/versions/2.3.1/lib/ruby/vendor_ruby",
"/Users/joelquenneville/.rbenv/versions/2.3.1/lib/ruby/2.3.0",
"/Users/joelquenneville/.rbenv/versions/2.3.1/lib/ruby/2.3.0/x86_64-darwin14"]
The paths are mostly going to various gem’s lib/ directories or Ruby
installations. When executing require "user, Ruby looks for a file named
user.rb in all of these locations.
What if there’s a user.rb in several of the locations? Ruby will load the
first one it finds.
Gems will automatically add themselves to your load path.
Custom load path
That’s all great but what about adding your own paths? This can be accomplished
by mutating the $LOAD_PATH. Using Array#unshift will prepend your path:
$LOAD_PATH.unshift File.expand_path(".", "lib")
Now require "user" will first look for ./lib/user.rb.
Complex paths
The same logic applies to more complex requires:
require "/api/client"
would look for /api/client.rb in all of the paths, starting with
./lib/api/client.rb
UNIX
UNIX systems take a very similar approach with the environment variable $PATH.
A (truncated) path might look like:
$ echo $PATH
/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/texbin:/usr/local/sbin
$PATH is a list of paths separated by colons. When you type a command into the
shell, it looks at all these locations for an executable matching the name you
typed and will execute the first one it finds.
This is why some tool installation guides ask you to modify $PATH in your
shell config.
You can see which path your shell is using for a given command with which
$ which ls
/bin/ls
Sometimes you will have multiple versions of a command installed. For example
psql from Homebrew and psql from Postgres.app. The which command is
great for finding out which one is being used. Use this info to tweak your
$PATH to ensure the right one loads. You can also use the -a flag on
which to show all installed executables:
% which -a psql
/Applications/Postgres.app/Contents/Versions/latest/bin/psql
/usr/local/bin/psql
Wandering off the path
Ruby has another way of requiring code:
require_relative "./lib/user"
Now where is user.rb located? This doesn’t use $LOAD_PATH. Instead, it looks
for ./lib/user.rb relative to the location of the current file.
Both require and require_relative take a path to a Ruby file without the
.rb extension.
A few UNIX shell commands are hard-coded into the shell and don’t use the
$PATH lookup system. For example, where is the which executable?
$ which which
which: shell built-in command
Now you know a bit more about how Ruby locates files, and explored a bit of UNIX history.