I'm wondering what the difference is between the following two modules
# First Example
module Parent
module Child
end
end
and
# Second Example
module Parent::Child
end
Using the 2nd method, it appears as though the Parent module must be previously defined, otherwise I get an 'uninitialized constant' error
Given this, what is the preferred way of defining modules such as this and then adding nested children with regards to syntax and file structure (ie. folders etc). Reference to a Rails way would be greatly appreciated.
Are these two examples for all intents and purposes equivalent?
In the first example, it defines the Parent
module and then the Child
module. The second example, as you say yourself, must have the Parent
module defined before hand. At the expense of one more line of code, you ensure that the module that you're nesting under by using your first example is always going to be defined.
For a Rails example let's look into the railties/lib/rails/engine.rb file which re-opens the Rails
module and then defines an Engine
class inside of it. This could have been done with simply:
class Rails::Engine
But instead perhaps for the reasons stated above and perhaps also for clarity's sake, the module was defined first, then the class inside.
I prefer the second method (if im sure that Parent is already defined) because it looks cleaner, esp. when the nesting is very deep.
However the 1st method has some advantages, one not yet discussed is that a nested module gets access to any lexically available constants in the enclosing module.
Generally speaking, you don't want to define a module using the module Parent::Child syntax unless you can be absolutely certain that Parent is already there. A sub-Module can only be defined using the :: syntax if the parent module is defined. In your example, if you do the following, you will not get an uninitialized constant error.
module Parent
end
module Parent::Child
end
It seems that Banister's answer is also the reason for this behaviour:
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]
The inner module A is a constant inside the outer module A, and thus it's not visible using the second method.
Editing my previous comment:
This is explained in 7.9 "Constant Lookup" of "The Ruby Programming Language" (First Edition). The relevant part here is Module.nesting, which doesn't contain the outer module in the second example, thus A can only be found in the global scope.
There are three ways in which the second version (A::B)
differs from the first:
A
is already defined. If it's not, Ruby will throw a NameError: uninitialized constant A
error. So use this syntax only if you're sure that module A exists.A
inside module B
without the scope resolution ( ::
) operator. This is a significant difference; the next section covers it in detail.The scope of constant access is different depending on how the nested module is defined. Let's consider the first version.
module A
Z = 10
module B
puts Z
end
end
# output
10
Let's try accessing the constant in a module defined using the second syntax. It throws a NameError
.
module A
Z = 10
end
module A::B
puts Z
end
# output
uninitialized constant A::B::Z (NameError)
Here, you've to access it using the scope resolution operator ( ::
) as follows:
module A
Z = 10
end
module A::B
puts A::Z
end
# output
10
The Module.nesting
method returns the list of modules nested at the time of call. The following example shows how the two versions differ in their 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
When module D
is defined, the nesting does not include module A
. Hence, module D
cannot access the constants defined in module A
, without explicitly using the scope resolution ( ::
) operator.
Source: Nested Modules in Ruby
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.