• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    Ruby常量查找路径问题深入研究

    Ruby 的常量查找路径问题是一直困扰我的一个问题,在工作中遇到过好几次,一直没有彻底弄清楚到底为什么,最近在读一本书《Ruby 元编程》,对 Ruby 对象模型有了更深入的认识,另外读了一篇 blog《Everything you ever wanted to know about constant lookup in Ruby》, 让我总算把 Ruby 常量查找路径这个问题搞得比较清楚。

    第一个遇到的问题,我还曾经在 Ruby-China 上发过帖。

    复制代码 代码如下:

    module M1
      CT = "ok"
    end

    class C1
      CK = "ck"
      include M1

      def self.method1
        puts self
        puts "#{CK} in method1"
        puts "#{CT} in method1"
      end

      class self
        def method2
          puts self
          puts "#{CK} in method1"
          puts "#{CT} in method2"
        end
      end
    end

    C1.method1
    C1.method2

    输出结果是

    复制代码 代码如下:

    C1
    ck in method1
    ok in method1
    C1
    ck in method2
    NameError: uninitialized constant Class::CT
        from (irb):16:in `method2'

    这是我在重构薄荷网代码时候遇到的问题,method1 和 method2 都是常见的类方法的定义方面,我向来认为它们是等价可替换的写法,但是从实际执行的结果看,它们里面的常量查找路径不一样。

    如果我把 M1 的定义改成下面的样子:

    复制代码 代码如下:

    module M1
      def self.included(base)
        base.extend(self)
      end
      CT = "ok"
    end

    执行结果是:

    复制代码 代码如下:

    C1
    ck in method1
    ok in method1
    C1
    ck in method2
    ok in method2

    还有一个问题是也是经常遇到的,抽象成问题代码如下:

    复制代码 代码如下:

    module A
      module M
        def a_method
          #...
        end
      end
    end

    class A::B
      include M
    end

    会报异常:

    复制代码 代码如下:

    NameError: uninitialized constant A::B::M
      from (irb):10:in `class:B>'

    Ruby 常量查找时依据两条路径

    A. Module.nesting
    B. open class/module 的 ancestors

    A 比 B 优先,A 找不到了才到 B 中查找。

    A.Module.nesting 的概念比较容易理解,它是指代码位置的 module 嵌套情况,它是一个数组,从最内层的嵌套一直到最外层的嵌套,如果没有嵌套,数组为空。任何一处代码位置都有 Module.nesting 值,可以通过下面的代码打印出各个位置的 Module.nesting 值。

    复制代码 代码如下:

    p Module.nesting

    module A
      module B
        p Module.nesting
        module C
          p Module.nesting
        end
      end
    end

    module A::B
      p Module.nesting
    end

    输出是:

    复制代码 代码如下:

    []
    [A::B, A]
    [A::B::C, A::B, A]
    [A::B]

    大家有没有注意到,module A::B 这种快捷写法会导致 A 不在 Module.nesting 里,这就是上述第二个问题的根源,因为 M 是 A module 下的常量,module A::B 写法导致不会查找 A::M。

    说完 A Module.nesting,再说一下 B open class/module 的 ancestors,这个问题相对复杂很多。简单的说,在 Ruby 代码的任何位置,都有一个 self 存在,同样也有一个 open class/module 存在,在模块和类定义处,它通常就是对应的模块和类,在方法内部,它是方法对应的类。对于 ancestors,我们可以通过代码位置 open class/module 的 ancestors 方法取得。

    (备注:ancestors 在引入 singleton_class 概念之后变得有点复杂,如不清楚可参考《Ruby 元编程》)

    上述第一个问题: 在method1 中 A 是 [C1] open class/module 是 C1,所以 ancestors 是 [C1, M1, Object, Kernel, BasicObject] CK 在 A 可以找到,CT 在 B 可以找到。

    method2 中 A 是 [C1] open class/module 是 C1 的 singleton_class , 所以 ancestors 是 [Class, Module, Object, Kernel, BasicObject] CK 在 A 可以找到,CT 在 A 和 B 都找不到。

    对于

    复制代码 代码如下:

    module M1
      def self.included(base)
        base.extend(self)
      end
      CT = "ok"
    end

    可运行,是因为这时,在 method2 中,open class/module C1 的 singleton_class 扩展了 M1,所以 ancestors 变成了 [M1, Class, Module, Object, Kernel, BasicObject]。

    至此,这两个困扰我多时的问题终于彻底搞清楚了。这个过程给我的一个体会是:面对技术上的一些疑问,如果只是浅尝辄止,是永远不能够真正掌握它的,只有深入专研,透彻理解它的原理,才能够真正掌握它,获得真正的能力提升。

    您可能感兴趣的文章:
    • 在Ruby中查找和执行方法
    • Ruby实现的最优二叉查找树算法
    • Ruby实现二分搜索(二分查找)算法的简单示例
    上一篇:Ruby单元测试框架TestUnit的替代者MiniTest介绍
    下一篇:Ruby 多线程的潜力和弱点分析
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    Ruby常量查找路径问题深入研究 Ruby,常量,查找,路径,问题,