Ruby is a very complex programming language. It is incredibly beautiful and readable, but it has many themes and features that can remain a “dark forest” even for an experienced Ruby developer. One of these topics is the search for constants.
Despite the title, there will be no anger in the post.
The purpose of this post is not a detailed explanation of the search algorithm. I would say that the goal is to attract the attention of developers to the topic. In part, this is just a cry from the heart.
I will consider one small example. To begin with, we define several constants:
module M A = 'm' end module Namespace A = 'ns' class C include M end end
We have one mixin M
, a Namespace
module and its own class C
In the modules, definitely by the constant A
, which we will be looking for.
What do you think that the following code will display? I will post the answers below so that they do not catch the eye.
puts Namespace::C::A module Namespace class C puts A end end
Now let's define a couple of methods:
module M def m A end end module Namespace class C def f A end end end class Namespace::C def g A end end x = Namespace::C.new puts xf puts xg puts xm
Do you think there is a difference between them?
Here is the full code of our example with the answers in the comments:
module M A = 'm' end module Namespace A = 'ns' class C include M end end puts Namespace::C::A # m module Namespace class C puts A # ns end end module M def m A end end module Namespace class C def f A end end end class Namespace::C def g A end end x = Namespace::C.new puts xf # ns puts xg # m puts xm # m
Those. the output of the program will be:
m ns ns m m
In short, the search for constants occurs in several stages:
Namespace::C::A
constant, and in the second output, it first enters the Namespace
module, then enters the C
class, and only then does the puts
. You can learn more about this by reading about nesting, in particular, the Module.nesting
method.Object
classconst_missing
method is const_missing
by analogy to method_missing
. I suppose this method is utilized in Ruby on Rails for autoloading and reloading code.In this way:
# . # # M puts Namespace::C::A # m module Namespace class C # Namespace -> Namespace::C # Namespace puts A # ns end end module M def m # M. A # m end end module Namespace class C def f # Namespace -> Namespace::C A # ns end end end class Namespace::C def g # Namespace::C ( Namespace ) # # A # m end end
It can be said that Ruby makes us, when writing constants in code, calculate their value relative to the written code, and not relative to the execution context (sounds very strange, I'm sorry).
Ruby style guide defines one good rule :
it is necessary to define and rediscover nested classes / modules explicitly. Those. never need to write class A::B
This simple rule is enough to avoid surprises and in most cases not to think about finding constants at all.
What you can read:
The user DsideSPb gave a helpful comment about the additional feature of searching for constants. True, it was removed in the latest (2.5.0) release.
Personally, I do not know all the details, but in some circumstances, if you specify the wrong path to a constant, the interpreter can replace it with a top-level one. However, this does not work in all cases:
# 1.rb class A; end class B; end A::B # B, # 2.rb class A; end module M; end A::M # ==> M M::A # ==> NameError
Source: https://habr.com/ru/post/347272/