uninitialized constant (NameError)とは
"uninitialized constant"はそのまま日本語にすると「定義されていない定数」という意味になる。
呼び出されたクラスやメソッドが定義されていないことでエラーが発生しているということなので、何かしらの理由でクラスやメソッドの呼び出しが出来ていない。
エラーの出力例としてはこんな感じ
/Users/user/project/sample.rb:7:in `<module:LS>': uninitialized constant LS::Command (NameError) class ShortFormat < Command ^^^^^^^ #--以下バックトレースが続くが省略--
この場合は「LS::Commandクラスが定義されてないので呼び出せない」ということになる。
解決方法
解決方法はエラーが発生しているクラスやメソッドが「なぜ呼び出せないのか」を特定して、呼び出せるようにする必要がある。
この「なぜ呼び出せないのか」が特定できず、ハマった。
「requireの相互参照」が原因だったのだけれど、"uninitialized constant (NameError)"とかでググってもエラーの原因としてrequireを挙げている情報を見つけられず*1、「requireの相互参照」という言葉も知らなかった(又は聞いたことはあったのかもしれないが思いつかなかった)ので途方にくれた。
requireの相互参照とは
2つのファイル間でお互いのファイル同士をrequireすること
たとえば以下のようにお互いがお互いをrequireしていること。*2
エラーの発生しない相互参照
foo.rb
require_relative 'bar' class Foo end
bar.rb
require_relative 'foo' class Bar end
irbでrequireしてみる。
$ irb >> require_relative 'bar' => true >> require_relative 'foo' => false
後に実行したrequire_relative 'foo'
はfalseになっている。
るりまによると、
ライブラリのロードに成功した時には true を返し、ロードした feature の名前を(拡張子も含めて) 変数 $" に追加します。ただし、feature の名前が既に $" に含まれていた場合はロードせずに false を返します。
ということなので、2回目の場合にはすでにロードされているのでfalse
が返っている。
Kernel.#require (Ruby 3.1 リファレンスマニュアル)
エラーの発生する相互参照の例
たとえば以下2つのファイルを作り、irbでrequireするとuninitialized constant (NameError)
が発生する。
coke.rb
require_relative 'fanta' module Soda class Coke end end
fanta.rb
require_relative 'coke' module Soda class Fanta < Coke end end
$ irb >> require_relative 'coke' /Users/user/project/fanta.rb:4:in `<module:Soda>': uninitialized constant Soda::Coke (NameError) class Fanta < Coke ^^^^ #--以下略--
エラーを回避するには
サンプルコードのような継承が必要な設計はたぶんおかしい。
設計を見直す。
「継承より委譲」
まとめ
このエラーに当たったのは、フィヨルドブートキャンプの課題のひとつ「lsコマンドを作る(オブジェクト指向版)」に取り組んでいるときでした。 モジュールや継承などについて理解が浅いことが重なり「どうにもならんーー」という感じになったのですが、メンターさんに質問したところ回答を得ることができました。