constant lookup in Ruby
Ruby 中常量查找路径是新手接触 Ruby 时候常会遇到的一个问题,比如下面两种写法:
# first
module A
class B; end
end
# second
module A; end
class A::B; end
他们之间会有什么区别呢?
Module.nesting
这是 Ruby 中很重要的一个概念,我们直接用代码说话吧
module A
module B; end
module C
module D
p Module.nesting # => [A::C::D, A::C, A]
end
end
end
通过 Module.nesting
我们可以看到常量的查找链
。因此继续上面的代码来说,我们可以在 D
这个 module 中调用查找到 B module
。具体的一个过程是,首先会去尝试寻找 A::C::D::B module
,不存在;再去寻找 A::C::B module
,还是不存在;直到最后找到 A::B module
,bingo! 这就是 Ruby 中常量寻找的一个大致的流程。
那么我们前面的那个问题的答案呢?接着上代码:
C = "At the top level"
module A
C = "In A"
end
module A
module B
p Module.nesting # => [A::B, A]
p C # => "In A"
end
end
module A::B
p Module.nesting # => [A::B]
p C # => "At the top level"
end
看到了吧,区别就在于 Module.nesting
这里,第二种写法直接进入的就是顶级作用域,当我们没有定义最外层的常量 C
的时候,就会出现错误。
module A
C = "In A"
end
module A::B
p Module.nesting # => [A::B]
p C # => NameError: uninitialized constant A::B::C
end
因此我们在写代码的时候要注意到这些问题,尤其是我们有时候会见到有的代码中这样写 ::A
。这就是为了主动声明常量的查找路径,以达到我们想要的结果。
突然想起了我前两天写代码中的一个小疑惑,继续上代码:
client/base_controller.rb
ruby
module Client
class BaseController < ApplicationController
# ...
end
end
.
├── application_controller.rb
├── client
│ └── base_controller.rb
代码中,client/base_controller.rb
是继承自 application_controller.rb
的,可是按照代码中的写法,ApplicationController
应该是在 Client
这个 namespace 下面的,为什么还是可以找到上一层目录中的 `application_controller.rb` 呢?按照 Ruby 的常量查找路径来说,在 BaseController 中的查找路径应该是 [Client::BaseController, Client],是 Rails 又为我们做了一些事情吗?
是的! Rails 有自己的一套 autoload system
,简而言之就是将 Ruby 内置的 autoload system
扩大增强了,所以能加载到这个文件,具体的内容在下一周再写更多。