Current Dir
Video transcript & code
Recently, in episode #240, I showed how to determine what directory the current Ruby source file is in. The method I demonstrated made use of the __FILE__
special variable and the File.expand_path
method.
puts "__FILE__: #{__FILE__}"
puts "expand_path: #{File.expand_path("..", __FILE__)}"
Responding to that episode, alert viewer Marcello Rocha asked why I didn't use the __dir__
method instead. This is a method defined on Kernel
that simply returns the dirname of the current file.
puts "__FILE__: #{__FILE__}"
puts "expand_path: #{File.expand_path("..", __FILE__)}"
puts "__dir__: #{__dir__}"
Let's write this to a file named whereami.rb and see what it outputs when we run it.
$ ruby whereami.rb __FILE__: whereami.rb expand_path: /home/avdi/Dropbox/rubytapas/245-current-dir __dir__: /home/avdi/Dropbox/rubytapas/245-current-dir
We can see that the __FILE__
variable contains the relative pathname of the source file, and that both of our methods for getting the file's full directory succeed.
Let's try moving the file to a subdirectory and then running it.
$ mkdir foo $ mv whereami.rb foo $ ruby foo/whereami.rb __FILE__: foo/whereami.rb expand_path: /home/avdi/Dropbox/rubytapas/245-current-dir/foo __dir__: /home/avdi/Dropbox/rubytapas/245-current-dir/foo
From these results we can see that both techniques correctly locate the source file's location without regard for the current working directory.
There is a small difference between these approaches, however. To see it, let's read in the file and evaluate from inside another Ruby program. When we eval the code, it has no way of knowing what file it's actually in. We can see this reflected in the value of __FILE__
, which has a special placeholder value indicating that the code is being dynamically evaluated.
In this context, the expand_path
version winds up showing the current working directory, rather than the location of the source file. On the other hand, the output of the __dir__
approach is blank. This is because __dir__
has a nil
value when Ruby doesn't know what file the current code is from.
eval File.read("foo/whereami.rb")
# >> __FILE__: (eval)
# >> expand_path: /home/avdi/Dropbox/rubytapas/245-current-dir
# >> __dir__:
On balance I'd say that the blank result from __dir__
is more honest, and the expand_path
version is misleading.
So __dir__
is easier and better. Why didn't I show it before now? Quite honestly, because it was introduced in Ruby 2.0 and I never got into the habit of using it. As a result, I simply forgot about it. Fortunately I have observant viewers, and as a result I'm going to be using __dir__
a lot more in my own code.
There's just one last question to tackle today. Why is __dir__
lowercase, while __FILE__
is uppercase? Well, as I understand it, it's because __FILE__
is a language keyword masquerading as a constant. As a pseudo-constant, it makes sense for it to be capitalized.
__dir__
, on the other hand, is implemented as a method on Kernel
, just like its cousins __callee__
and __method__
. We'll talk more about these methods another day. All these methods share in common the fact that they reveal metadata about the code in which they are found, just like __FILE__
does. But as methods, not constants, convention dictates that they be in lowercase. I don't find this to be the most satisfying of explanations, but there it is.
And that's it for today. Happy hacking!
Responses