Rubyの定数の探索順序

Ruby技術者認定試験の勉強のため、この機会にちゃんと理解しておく。

バージョン

$ ruby --version
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [arm64-darwin21]

継承

子クラスに定数がない場合、親クラスの定数を参照できる。

class A
  FOO = "foo"
  BAR = "bar"
end

class B < A
  BAR = "bar-B"
end

puts A::FOO #=> foo
puts A::BAR #=> bar
puts B::FOO #=> foo
puts B::BAR #=> bar-B

include, prepend

モジュールも継承と同様に参照できる。

継承関係で探索するため、 includeprepend が混ざった場合は prepend で追加したモジュールの定数が優先される。

module M1
  FOO = "foo"
  BAR = "bar"
end

module M2
  FOO = "foo-M2"
end

class A
  include M1

  BAR = "bar-A"
end

class B
  prepend M1

  BAR = "bar-B"
end

class C
  include M1
  include M2
end

class D
  prepend M1
  include M2
end

puts M1::FOO #=> foo
puts M1::BAR #=> bar
puts A::FOO  #=> foo
puts A::BAR  #=> bar-A
puts B::FOO  #=> foo
puts B::BAR  #=> bar-B
puts C::FOO  #=> foo-M2
puts D::FOO  #=> foo

スコープ

継承関係よりもネスト関係の方が優先されるが、トップレベルの定数はネスト外部とは対象外になる。詳細は 変数と定数 (Ruby 3.1 リファレンスマニュアル) を参照。

CONST = "top-level"

module M
  CONST = "M"

  class A
    CONST = "A"
  end

  class B < A
    puts CONST #=> M
  end
end

class M::A
  puts CONST #=> "A"
end

class M::A::C
  puts CONST #=> "top-level"
end