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.