Reputation: 7719
I can't understand why accessing module's class variable fails in the following example:
module M
@@xyz = 123
end
M.class_variables # [:@@xyz]
M.class_variable_get :@@xyz # 123 , so far so good
class C
extend M
end
C.singleton_class.class_variables # [:@@xyz]
C.singleton_class.class_variable_get :@@xyz # NameError:
# uninitialized class variable @@xyz in Class
Can anybody explain why the class variable @@xyz
is suddenly inacessible/undefined in C
's singleton class?
Update: I re-tested the above code with different Ruby YARV versions and find it as a regression in the latest.
Update 2:
There was a change in definition of Module#class_variables
method in latest Ruby generation.
Ruby up to 1.9.3 the definition is
class_variables → array
Returns an array of the names of class variables in mod.
Ruby 2.0.0 latest stable version
class_variables(inherit=true) → array
Returns an array of the names of class variables in mod. This includes the names of class variables in any included modules, unless the inherit parameter is set to false.
So in latest Ruby incarnation, class_variables
returns by default also class variables of included modules. Just curious what's this feature for or if it still does concern modules "included" with include
and not extend
.
Can anybody explain ?
Upvotes: 10
Views: 5051
Reputation: 12578
Regarding the particular code example, it works differently on my Ruby 1.9.3p194:
module M
@@xyz = 123
end
M.class_variables # [:@@xyz]
M.class_variable_get :@@xyz # 123 , so far so good
class C
extend M
end
C.singleton_class.class_variables # [] - HERE BE THE DIFFERENCE
C.singleton_class.class_variable_get :@@xyz # NameError:
Of course, that's wha I'd expect. C.singleton_class is a direct subclass of class Class, and nowhere have I seen you setting up any class variables on Class or its descendants... But I believe you that the code works the way you write on your machine.
Upvotes: 0
Reputation: 14082
This is not answer, just some comments to the question.
If we include module M
in class C
, C
gets class variables define in M
:
module M
@@xyz = 123
end
class C
include M
end
C.class_variables #=> [:@@xyz]
C.class_variable_get(:@@xyz) # 123
To call extend M
in class definition is equivalant as to call include M
in eigen class (or singleton class) of that class.
module M
@@xyz = 123
end
eigenclass = class C
class << self
include M
self
end
end
eigenclass.class_variables #=>[:@@xyz]
eigenclass.class_variable_get(:@@xyz) #=>NameError: uninitialized class variable @@xyz in Class
It seems the difference lies on that Ruby treat normal classes and eigen classes differently.
Upvotes: 1
Reputation: 12578
In short, the difference you observe is because modules do not work the same way as classes. I've had this question of mine to my higher-ups not long ago: Inheriting class methods from mixins
And I concluded that while Module resembles Class in some aspects, in other aspects, Ruby treats it simply as ordinary object. Especially, that, which is known as 'class stuff' with classes (class methods, class variables...) is just known as 'singleton stuff' in other objects (singleton methods etc.) And modules' singleton classes are in many ways treated just like modules were ordinary objects.
Upvotes: 0
Reputation: 52316
Not sure if either of these are an answer, but I did find these
C::M.class_variables #=> ["@@xyz"]
# (but gives "warning: toplevel constant M referenced by C::M")
and
class D
include M
end
D.class_variables #=> ["@@xyz"]
(This from Ruby 1.8.7, don't have a later version to hand right now).
include
causes the module's instance methods to become instance methods of the class. According to Pickaxe, "It's almost as if the module becomes a superclass of the class that uses it".
Meanwhile, extend
is intend to add a module's methods to an object; when called in a class definition it's equivalent to self.extend
. It seems that they're not equivalent.
HTH.
Upvotes: 2