Ruby(和Rails)嵌套模块语法

ioekq8ef  于 2023-02-03  发布在  Ruby
关注(0)|答案(5)|浏览(147)

我想知道以下两个模块之间的区别

# First Example
module Parent
  module Child
  end
end

以及

# Second Example
module Parent::Child
end

使用第二种方法,看起来好像父模块必须预先定义,否则我会得到一个“未初始化常量”错误
既然如此,定义这样的模块,然后添加语法和文件结构(如文件夹等)方面的嵌套子模块的首选方法是什么?参考Rails方法将非常感谢。
这两个示例在所有意图和目的上是否等同?

yduiuuwa

yduiuuwa1#

在第一个示例中,它定义了Parent模块,然后定义了Child模块。第二个示例,正如您自己所说的,必须事先定义Parent模块。以多一行代码为代价,您可以确保使用第一个示例嵌套的模块总是要定义的。
以Rails为例,让我们看一下 railties/lib/rails/engine.rb 文件,该文件重新打开Rails模块,然后在其中定义一个Engine类。

class Rails::Engine

但是,可能是由于上面提到的原因,也可能是为了清楚起见,首先定义了模块,然后定义了其中的类。

sulc1iza

sulc1iza2#

我更喜欢第二种方法(如果我确定已经定义了Parent),因为它看起来更干净,特别是当嵌套很深的时候。
然而,第一种方法有一些优点,一个尚未讨论的是嵌套模块可以访问封装模块中任何词法上可用的常量。

0ejtzxu1

0ejtzxu13#

一般来说,除非你能完全确定父模块已经存在,否则你不想使用模块Parent::Child语法定义一个模块。子模块只能使用::语法。在您的示例中,如果执行以下操作,则不会出现未初始化常量错误。

module Parent
end

module Parent::Child
end
pieyvz9o

pieyvz9o4#

看来,班尼斯特的回答也是这种行为的原因:

ruby-1.9.2-p290 :001 > module A; module A; A.ancestors; end; end
 => [A::A] 
ruby-1.9.2-p290 :002 > module A::A; A.ancestors; end
 => [A]

内模A是外模A内部的一个常数,因此使用第二种方法是不可见的。
编辑我以前的评论:
这在《Ruby编程语言》(第一版)的7.9“常量查找”中有解释,这里相关的部分是Module.nesting,它不包含第二个例子中的外部模块,因此A只能在全局范围内找到。

68bkxrlz

68bkxrlz5#

第二个版本(A::B)与第一个版本的区别有三个方面:
1.它假设外部模块A已经定义,如果没有,Ruby将抛出NameError: uninitialized constant A错误,因此只有在确定模块A存在时才使用此语法。
1.它可以让你减少缩进量。而不是3级缩进,只有一个是必要的。
1.如果没有作用域解析(::)运算符,就不能访问B模块中A模块中定义的常量。下一节将详细介绍它。

访问嵌套模块中的常量

常量访问的作用域根据嵌套模块的定义而不同,让我们考虑一下第一个版本。

module A
  Z = 10
  
  module B
    puts Z
  end
end

# output 
10

让我们试着在使用第二种语法定义的模块中访问常量,它抛出一个NameError

module A
  Z = 10
end

module A::B
  puts Z
end

# output
uninitialized constant A::B::Z (NameError)

在这里,您必须使用作用域解析操作符(::)访问它,如下所示:

module A
  Z = 10
end

module A::B
  puts A::Z
end

# output
10

模块嵌套

Module.nesting方法返回调用时嵌套的模块列表。下面的示例显示两个版本在模块嵌套方面的不同。

module A
  p Module.nesting  # [A]
  
  module B
    p Module.nesting  # [A::B, A]
  end

  module B::C
    p Module.nesting  # [A::B::C, A]
  end
end

module A::D
  p Module.nesting  # [A::D]
end

当模块D被定义时,嵌套不包括模块A。因此,模块D不能访问在模块A中定义的常量,除非显式地使用作用域解析(::)运算符。

相关问题