David Unric
David Unric

Reputation: 7719

Extend module & class variable access?

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.

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

Answers (4)

Boris Stitnicky
Boris Stitnicky

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

Arie Xiao
Arie Xiao

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

Boris Stitnicky
Boris Stitnicky

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

Mike Woodhouse
Mike Woodhouse

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

Related Questions