In Progress
Unit 1, Lesson 1
In Progress

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