Kernel#__dir__
__dir__ What __dir__ returns
Kernel#__dir__ is a private instance method that returns the canonicalized absolute path of the directory containing the source file that called it. The return value equals File.dirname(File.realpath(__FILE__)) for any script loaded from disk. The method was added in Ruby 2.0 and lives on Kernel, which Object includes, so it is callable from any context without a receiver.
__dir__ sits next to the other “magic” identifiers from the same family: __FILE__, __LINE__, __method__, and __ENCODING__. It is a method, not a constant, even though the leading and trailing underscores make it look like one. The signature takes no arguments and returns a String, or nil when there is no source file. You call it as a bareword because it is private, and the parenthesized form is equivalent:
__dir__
# => "/home/ludwig/project/lib"
__dir__() == __dir__
# => true
If __FILE__ is nil, __dir__ returns nil. That happens at the top level of irb or pry, when running ruby -e 'p __dir__', and inside eval whose filename argument does not point at a real file.
Why it is useful
The most common use is loading files that sit next to the script you are running, without depending on the process’s current working directory:
config_path = File.join(__dir__, "config", "app.yml")
data = YAML.load_file(config_path)
This pattern is the right default for any library or CLI tool that ships with a config file, a SQLite database, or a template. Library authors can move the gem to any install path; users can launch the script from any directory. The lookup still works because __dir__ is resolved against the file’s location, not the shell’s.
Unlike Dir.pwd, the value does not change when something calls Dir.chdir. That makes __dir__ the right tool for “where is this file on disk” questions, and Dir.pwd the right tool for “where is the shell looking right now” questions:
__dir__
# => "/home/ludwig/project/lib"
Dir.chdir("/tmp")
Dir.pwd
# => "/tmp"
__dir__
# => "/home/ludwig/project/lib"
A second pattern is logging. Including the file directory next to the file name and method name gives a breadcrumb that is stable across Dir.chdir, which is useful for daemon logs that record the working directory at the start of each request:
def load_config
logger.info("[#{File.basename(__dir__)}] loading config in #{__method__}")
File.join(__dir__, "config", "app.yml")
end
Gems lean on __dir__ heavily for the same reason. A gem’s lib/ and data/ directories are part of the gem’s install location, which is owned by RubyGems and not by the user. Resolving data file paths through __dir__ keeps the lookup correct whether the gem lives at ~/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/my_gem-1.2.0/ or a system path, without making the caller set an environment variable.
How it compares to __FILE__
__dir__ is the dirname of __FILE__ after it passes through File.realpath. The canonicalization resolves any symlinks and . / .. segments along the way. The two expressions below always agree for any script loaded from a real file on disk. Run the comparison in a freshly written script and the result holds even when the file sits behind a symlink or under a relative path:
__dir__ == File.dirname(File.realpath(__FILE__))
# => true
The two values diverge in two common situations. First, when the script is invoked through a symlink, __FILE__ keeps the path Ruby was given, while __dir__ is resolved through File.realpath and points at the real target. Second, when the path contains . or .. segments, those are kept verbatim in __FILE__ but normalized in __dir__. The same rule applies if you want the file path itself to be canonical: use File.expand_path(File.realpath(__FILE__)) instead of reaching for __FILE__ directly:
# /home/ludwig/project/./lib/utils.rb, invoked via a symlink at /opt/utils.rb
__FILE__
# => "/opt/utils.rb"
__dir__
# => "/home/ludwig/project/lib"
File.expand_path(__FILE__)
# => "/opt/utils.rb"
File.expand_path(File.realpath(__FILE__))
# => "/home/ludwig/project/lib/utils.rb"
__dir__ resolves to a fresh value on every call, because it walks the filesystem through File.realpath each time. In a tight loop, cache the value once at load time and reuse the local. The same rule applies to __FILE__ if you wrap it with File.realpath, since the underlying call is the same.
Behavior in blocks, procs, and lambdas
The value is bound to the file in which the closure was defined, not where it is invoked. That is the same rule as __FILE__:
loader = -> { __dir__ }
loader.call
# => "/home/ludwig/project/lib"
This makes it safe to capture a loader at load time and call it later from anywhere, including from a worker thread or a long-running process. The closure carries the file location with it, so the path stays correct even if the caller has since Dir.chdir’d somewhere else.
Behavior in eval
__dir__ inside eval follows the rules of __FILE__ in eval. If you pass a binding, the eval’d code sees the directory of the file that owned the binding. If you pass a filename string that is not a real file, __FILE__ is the literal string you passed (or nil), and __dir__ is nil:
eval("p __dir__", nil, "(eval)")
# prints: nil
The same nil case covers heredocs and string-only inputs to eval. Any code path that touches __dir__ inside eval should branch on a nil check before building a path with File.join, because File.join(nil, ...) raises TypeError rather than failing silently. Guard the call site in a library entry point with a nil check, or rescue TypeError, so the same code still works when invoked from ruby -e or a REPL:
# /lib/my_gem/entry_point.rb
def load_optional_config
base = __dir__ || File.expand_path("../..", __FILE__)
File.join(base, "config", "default.yml")
end
Inspecting the method
__dir__ is a private method on Kernel, not a constant. Reflection gives you a quick look at its owner, visibility, and arity. The result holds regardless of how the file is loaded, because no stdlib class overrides the method:
Kernel.method(:__dir__).owner
# => Kernel
Kernel.private_method_defined?(:__dir__)
# => true
Kernel.instance_method(:__dir__).arity
# => 0
The private visibility is why the bareword form works in every scope, including inside class and module bodies, without a receiver. If you need to call it as an instance method on a specific object, use send(:__dir__) because send bypasses private visibility. The same call through public_send(:__dir__) raises NoMethodError.
Wrapping with Pathname
The return value is a String. If you want path-like methods such as join, dirname, or basename, wrap it with Pathname:
require "pathname"
base = Pathname.new(__dir__)
base.join("config", "app.yml")
# => #<Pathname:/home/ludwig/project/lib/config/app.yml>
base.parent
# => #<Pathname:/home/ludwig/project>
This pairs well with __dir__ because Pathname#join handles trailing slashes and platform separators for you. For long path chains, Pathname(__dir__).join("config", "app.yml") reads more cleanly than a nested File.join.
Gotchas
A few sharp edges worth knowing:
__dir__is a method, not a constant. You cannot assign to it.Kernel.method(:__dir__).ownerreturnsKernel.- It is not the current working directory. It is the source file’s directory, which usually equals CWD but diverges the moment you
Dir.chdiror invoke the script via a relative path from elsewhere. - It calls
File.realpath, so if the source file is moved or unlinked between calls, the next call raisesErrno::ENOENT. That matters in long-running daemons that ship code updates. - On Windows the path uses backslashes. Build paths with
File.join(__dir__, "x.yml")and not with hard-coded"/"literals. - The return value is a
String. Wrap withPathname.new(__dir__)if you want path-like methods. - In
-escripts, REPLs, andevalwith a non-file filename,__dir__returnsnil. Guard with a fallback, or rescueTypeError:
# Library entry point: fall back to CWD when there is no source file
config_path = File.join(__dir__ || Dir.pwd, "config", "app.yml")
See also
- Kernel#require: load files whose path is built from
__dir__. - Kernel#require_relative: pair with
__dir__for paths relative to the current source file. - Kernel#eval:
__dir__insideevalfollows the__FILE__rules of the binding or filename argument.