“Defining paths relative to a file name has always hurt my soul, with __dir__ we can restore order in the Universe.” - by @fxn.

This post about the method in Ruby Programming Language Kernel#__dir__ and restoring order in the Universe.

Let’s take a look at the method Kernel#__dir__ .

__dir__ => string Returns the canonicalized absolute path of the directory of the file from which this method is called. It means symlinks in the path is resolved. If __FILE__ is nil, it returns nil. The return value equals to File.dirname(File.realpath(__FILE__)).

This method was introduced in Ruby since version 2.0.0 (NEWS for Ruby 2.0.0).

More info about development of Kernel#__dir__ here: Feature #1961.

Since Ruby 2.0.0 it is time to define relative paths with __dir__ and refactor all definitions of relative paths with __FILE__ .

Why is __dir__ better than __FILE__ for a definition of path?

Imagine there is folder project/ that contains files: Rakefile , application.rb . Inside the file Rakefile need to get the path to the file application.rb . We can do it by using __FILE__ :

File . expand_path ( '../application.rb' , __FILE__ )

There is the definition of path to the file application.rb by using __dir__ :

File . expand_path ( 'application.rb' , __dir__ )

By using __dir__ we get rid of redundant ../ . Looks better, isn’t it? The result of these definitions is the same, but if we need number 5 , we don’t type 3+2 or 1+4 .

__FILE__ is definitely better than File.expand_path('Rakefile', __dir__) when inside the file Rakefile need to get the path to the file Rakefile (the current file).

It is important not to do and get rid of needless things in a code!

Examples of refactoring with __dir__ :

-APP_PATH = File.expand_path('../../config/application', __FILE__) +APP_PATH = File.expand_path('../config/application', __dir__)

-$:.unshift File.expand_path("..", __FILE__) +$:.unshift __dir__

namespace :isolated do task adapter => "test:env:#{adapter}" do - dir = File.dirname(__FILE__) - Dir.glob("#{dir}/test/cases/**/*_test.rb").all? do |file| - sh(Gem.ruby, "-w", "-I#{dir}/lib", "-I#{dir}/test", file) + Dir.glob("#{__dir__}/test/cases/**/*_test.rb").all? do |file| + sh(Gem.ruby, "-w", "-I#{__dir__}/lib", "-I#{__dir__}/test", file) end || raise("Failures") end end

-Dir[File.dirname(__FILE__) + "/stubs/*.rb"].each { |file| require file } +Dir[File.expand_path("stubs/*.rb", __dir__)].each { |file| require file }

-$:.unshift(File.dirname(__FILE__) + "/lib") -$:.unshift(File.dirname(__FILE__) + "/fixtures/helpers") -$:.unshift(File.dirname(__FILE__) + "/fixtures/alternate_helpers") +$:.unshift File.expand_path("lib", __dir__) +$:.unshift File.expand_path("fixtures/helpers", __dir__) +$:.unshift File.expand_path("fixtures/alternate_helpers", __dir__)

def self.base_root - File.dirname(__FILE__) + __dir__ end

-source_root File.expand_path("../templates", __FILE__) +source_root File.expand_path("templates", __dir__)

When I was investigating Rails’s sources, I found one wonderful commit related to the using of __dir__ for definition of relative path.

rails $ git show 5b8738c2df003a96f0e490c43559747618d10f5f

commit 5b8738c2df003a96f0e490c43559747618d10f5f Author: Xavier Noria <fxn@hashref.com> Date: Sat Mar 5 08:09:20 2016 +0100 define APP_PATH with __dir__ Defining paths relative to a file name has always hurt my soul, with __dir__ we can restore order in the Universe. diff --git a/railties/lib/rails/generators/rails/app/templates/bin/rails b/railties/lib/rails/generators/rails/app/templates/bin/rails index 80ec808..513a2e0 100644 --- a/railties/lib/rails/generators/rails/app/templates/bin/rails +++ b/railties/lib/rails/generators/rails/app/templates/bin/rails @@ -1,3 +1,3 @@ -APP_PATH = File.expand_path('../../config/application', __FILE__) +APP_PATH = File.expand_path('../config/application', __dir__) require_relative '../config/boot' require 'rails/commands'

I got inspiration from this commit and decided to “restore order” in Rails, Jekyll, and own projects.

I made 2 patches and they are already merged:

Also, I got applause from @fxn for my Pull Request in Rails: