Scope of class variable-Collection of common programming errors
My notes below are taken from Metaprogramming Ruby (by Paolo Perrotta), which I happened to be reading right now just as I came across your question. I hope that these excerpts (page numbers will be in parentheses) and my explanation are helpful to you.
Keep in mind that class variables are different from class instance variables.
A Class instance variable belongs to an object of class
Class
, and is accessible only by the class itself – not by an instance or by a subclass. (106)
The class variable, on the other hand, belongs to class hierarchies. That means that it belongs to any class as well as all descendants of that class.
Here is an example from the author:
@@v = 1
class MyClass
@@v = 2
end
@@v # => 2
You get this result because class variables don’t really belong to classes – they belong to class hierarchies. Since @@v is defined in the context of
main
, it belongs tomain's
classObject
… and to all the descendants ofObject
.MyClass
inherits fromObject
, so it ends up sharing the same class variable. (107)
But also, since your specific question has to do not only with classes but also with modules:
When you include a module in a class, Ruby will create an anonymous class that wraps the module and inserts the anonymous class in the chain, just above the including class itself. (26)
So, as you look at B.ancestors
, you will see:
=> [B, A, Object, Kernel, BasicObject]
Similarly, for C.ancestors
, you will see:
=> [C, A, Object, Kernel, BasicObject]
If we keep in mind that class variables belong to class hierarchies, then the class variable @@foo
, as soon as it is defined in Module A
(and so, the anonymous class just above B
that is created as soon as B
includes A
), will belong to B
(and also to C
, since it includes A
).
To put it simply:
- When
@@foo
was only defined inB
and inC
(but not inA
), thenB
had a class variable@@foo
that was different than the class variable@@foo
inC
. This is because the class variables are only accessible to that class and to all descendants. ButB
andC
are related through their ancestorA
, and not through their descendants. - As soon as
@@foo
was defined inA
, that class variable became inherited by all descendants ofA
– that is,B
andC
. From here on out, the reference to@@foo
in classB
is really referencing the class variable that belongs toA
. The original@@foo
which was defined inB
has been overwritten replaced (taken over by its ancestor). The same has happened to the@@foo
inC
.B
andC
can both write to and read from the same class variable@@foo
, since it belongs to their common ancestor,A
.
At this point, anyone of A
, B
, or C
can all modify @@foo
. For example:
class B
p @@foo # => 3
@@foo = 1
end
module A
p @@foo # => 1
end