Ruby: Constant lookup

Ruby Constant lookup

Module.nesting

module A
  module B; end
  module C
    module D
      B == A::B
    end
  end
end

第一次会寻找A::C::D::B(不存在),然后寻找A::C::B(不存在),最终找到A::B(存在)。

module A
  module C
    module D
      Module.nesting == [A::C::D, A::C, A]
    end
  end
end
module A
  module B; end
end

module A::C
  B
end
# NameError: uninitialized constant A::C::B

这样ruby无法寻找到A::C::B,因为不在当前的Module.nesting。如果想要寻找到B, Module.nesting必须包含A

Ancestors

class A
  module B; end
end

class C < A
  B == A::B
end

如果在Module.nesting中未查找到常量的定义,ruby会沿着继承链来寻找。

class A
  def get_c
    C
  end
end

class B < A
  module C
  end
end

B.new.get_c
# => NameError: uninitialized constant A::C
class C < A
  class D
    puts Module.nesting
  end
end

class_eval

class A
  module B; end
end

class C
  module B; end
  A.class_eval{ B } == C::B
end
class A
  module B; end
end

class C
  module B; end
  A.class_eval("B") == A::B
end

Other Pitfalls

class A
  module B; end
end
class << A
  B
end
# NameError: uninitialized constant Class::B

class A
  module B; end
end
class << A; ancestors; end
[Class, Module, Object, Kernel, BasicObject]
# ancestors exclude A
class A
  def get_c; C; end
end

class B < A
  module C; end
end

B.new.get_c
# NameError: uninitialized constant A::C (because A ancestors not include C)
class A
  class B; end
end

class C < A
  class D
    Module.nesting # => [C::D, C]
    B
  end
end
# NameError: uninitialized constant C::D::B

Module.nesting 中包含 C,那为什么还找不到 C 超类 A 中的 B。因为 D 并没有继承 A,ruby 先在当前上下文 (surrounding lexical scope) 中找,然后再找继承 (ancestors)。

Reference:

Autoloading and Reloading Constants

Everything you ever wanted to know about constant lookup in Ruby

Ruby constant lookup: The good, the bad and the ugly

Rails autoloading — how it works, and when it doesn’t

Rails 中的类加载机制