Ruby constant search path problem in-depth research, ruby constant
The constant path search problem in Ruby has been a problem that has plagued me all the time. I have encountered several times in my work and have never thoroughly figured out why. I recently read a book "Ruby metaprogramming", I have a deeper understanding of the Ruby object model and read a blog titled Everything you ever wanted to know about constant lookup in Ruby. finally, let me figure out the path of Ruby constants.
The first problem I encountered was that I posted a post on Ruby-China.
Copy codeThe Code is as follows:
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
The output result is
Copy codeThe Code is as follows:
C1
Ck in method1
OK in method1
C1
Ck in method2
NameError: uninitialized constant Class: CT
From (irb): 16: in 'method2'
This is a problem I encountered when restructuring the mint net code. Both method1 and method2 are common class methods and definitions. I have always considered them equivalent and replaceable methods, but from the actual execution results, the constant search paths in them are different.
If I change the M1 definition to the following:
Copy codeThe Code is as follows:
Module M1
Def self. included (base)
Base. extend (self)
End
CT = "OK"
End
The execution result is:
Copy codeThe Code is as follows:
C1
Ck in method1
OK in method1
C1
Ck in method2
OK in method2
Another problem is also frequently encountered. Abstract The problem Code as follows:
Copy codeThe Code is as follows:
Module
Module M
Def a_method
#...
End
End
End
Class A: B
Include M
End
An exception is reported:
Copy codeThe Code is as follows:
NameError: uninitialized constant A: B: M
From (irb): 10: in '<class: B>'
Ruby constants are searched based on two paths.
A. Module. nesting
B. ancestors of open class/module
A has A higher priority than B. A cannot be found before it can be found in B.
A. module. the concept of nesting is easy to understand. It refers to the module nesting of code positions. It is an array, from the inner layer to the outermost layer. If no nesting exists, the array is empty. The Module. nesting value exists in any code position. You can print the Module. nesting value in each position using the following code.
Copy codeThe Code is as follows:
P Module. nesting
Module
Module B
P Module. nesting
Module C
P Module. nesting
End
End
End
Module A: B
P Module. nesting
End
The output is:
Copy codeThe Code is as follows:
[]
[A: B, A]
[A: B: C, A: B, A]
[A: B]
Have you noticed that A is not in the module due to the shortcut writing method Module A: B. in nesting, this is the root cause of the second problem, because M is A constant in module A, and module A: B does not search for A: M.
After talking about A Module. nesting, let's talk about the ancestors of B open class/module. This problem is much more complicated. Simply put, a self exists in any location of Ruby code, and an open class/module exists in the module and class definition, it is usually the corresponding module and Class. Inside the method, it is the class corresponding to the method. For ancestors, we can use the ancestors method of open class/module in the code position.
(Note: ancestors becomes a bit complicated after introducing the singleton_class concept. For details, refer to Ruby metaprogramming.)
The first problem above: In method1, A is [C1] open class/module is C1, so ancestors is [C1, M1, Object, Kernel, BasicObject] CK which can be found in, CT can be found in B.
In method2, A is [C1] open class/module is the singleton_class of C1, so ancestors is [Class, Module, Object, Kernel, BasicObject] CK which can be found in, CT cannot be found in both A and B.
For
Copy codeThe Code is as follows:
Module M1
Def self. included (base)
Base. extend (self)
End
CT = "OK"
End
It can be run because, in method2, singleton_class of open class/module C1 extends M1, so ancestors becomes [M1, Class, Module, Object, Kernel, BasicObject].
So far, these two problems that have plagued me for a long time have finally been thoroughly clarified. This process gives me an experience: in the face of some technical questions, if you just give it a taste, you will never be able to really master it. Only by in-depth research and thorough understanding of its principles, in order to truly master it and achieve real capability improvement.