Ruby require と require_relative の違い

Gem やファイルを読み込むために使う requirerequire_relative。違いは以下の点だけかと思ったら、そこまで単純でもないらしい。

この前自分で作ったこんなディレクトリがあったので、requireをどう書いたかを見てみる。

.
├── blog.rb
└── blog_test.rb

require

blog_test.rb で、Minitest のライブラリと、blog.rb(自分で作ったファイル)を読み込んでいる部分。ファイル名の拡張子は省略できる。

require "minitest/autorun"
require "./blog"

require "./blog"./ を削除して require "blog" にしてみたところ、LoadError になった。

$ ruby blog_test.rb 
Traceback (most recent call last):
        2: from blog_minitest.rb:2:in `<main>'
        1: from /Users/masuyama/.rbenv/versions/2.7.0/lib/ruby/site_ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require'
/Users/masuyama/.rbenv/versions/2.7.0/lib/ruby/site_ruby/2.7.0/rubygems/core_ext/kernel_require.rb:92:in `require': cannot load such file -- blog (LoadError)

これは想定通りだけど、require "minitest/autorun" って全然絶対パスじゃないじゃないか…。

リファレンスを見てみる。

require(feature) -> bool
Ruby ライブラリ feature をロードします。拡張子補完を行い、同じファイルの複数回ロードはしません。
feature が絶対パスのときは feature からロードします。 feature が相対パスのときは組み込み変数 $: に示されるパスを順番に探し、最初に見付かったファイルをロードします。このとき、$: の要素文字列の先頭文字が ~ (チルダ) だと、環境変数 HOME の値に展開されます。また ~USER はそのユーザのホームディレクトリに展開されます。
Kernel.#require (Ruby 2.7.0 リファレンスマニュアル)

なるほど。相対パスで読み込めるパスは、以下で確認できる。これ以外の場所にあるファイルは、絶対パスで指定する必要があるということだ。

$ ruby -e 'puts $:'
/usr/local/Cellar/rbenv/1.1.2/rbenv.d/exec/gem-rehash
/Users/masuyama/.rbenv/versions/2.7.0/lib/ruby/site_ruby/2.7.0
/Users/masuyama/.rbenv/versions/2.7.0/lib/ruby/site_ruby/2.7.0/x86_64-darwin19
/Users/masuyama/.rbenv/versions/2.7.0/lib/ruby/site_ruby
/Users/masuyama/.rbenv/versions/2.7.0/lib/ruby/vendor_ruby/2.7.0
/Users/masuyama/.rbenv/versions/2.7.0/lib/ruby/vendor_ruby/2.7.0/x86_64-darwin19
/Users/masuyama/.rbenv/versions/2.7.0/lib/ruby/vendor_ruby
/Users/masuyama/.rbenv/versions/2.7.0/lib/ruby/2.7.0
/Users/masuyama/.rbenv/versions/2.7.0/lib/ruby/2.7.0/x86_64-darwin19

参考:requireとrequire_relativeの違いとは? - (゚∀゚)o彡 sasata299's blog

require_relative

現在のファイルからの相対パスで require します。
Kernel.#require_relative (Ruby 2.7.0 リファレンスマニュアル)

こちらのリファレンスは簡潔。

さっきの例を書き換えてみた。

require "minitest/autorun"
require_relative "blog"

エラーは起きず、ちゃんと読み込めた。

ちなみに、正しくない書き方だと思うが、require_relative "./blog" としても動く。

どっちを使うべきなのか

require_relativeは、requireより後からできたものだ。Ruby コミッターの znz さんのブログによると、以前はrequireでカレントディレクトリのファイルも読み込むことができたが、脆弱性の元になるということでカレントディレクトリのファイルを直接読み込めなくして、require_relativeができたそうだ。

明記はされていませんが require_relative が推奨されるようになったようです。
require './file'とrequire_relative 'file'の違い - @znz blog

require "./blog" のような書き方は、脆弱性の原因になりうるとのこと。

ということで、Gem などを読み込むときはrequireで、自分で作ったファイルなどを読み込むときはrequire_relativeを使うのがよさそう。知らなかった。