10

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 ?

David Unric
  • 7,421
  • 1
  • 37
  • 65
  • 1
    I got `[]` from the line `C.singleton_class.class_variables`. How you got the result? – Arup Rakshit Apr 19 '13 at 08:13
  • What Ruby implementation do you use ? In my question I mentioned it was tested with Ruby 1.9.3 and 2.0.0 . Sawa erased this note for no known reason. *Shame on you, sawa* ;) – David Unric Apr 19 '13 at 08:18
  • I am using `C:\>ruby -v ruby 1.9.3p374 (2013-01-15) [i386-mingw32]` – Arup Rakshit Apr 19 '13 at 08:20
  • 1
    @RubyLovely That's strange. I use Ruby 2.0.0p0 from system installation and Ruby 1.9.3p392 from rbenv (user's installation). Linux x86_64. – David Unric Apr 19 '13 at 08:27

4 Answers4

2

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.

Mike Woodhouse
  • 51,832
  • 12
  • 88
  • 127
  • I do understand the differences between `include` and `extend`. To make it brief, the question is about why `class_variable_get` does not find the variable `@@xyz` even when `class_variables` does return its symbol ! – David Unric Apr 19 '13 at 08:16
  • @DavidUnric `C.singleton_class.class_variables` should give `[]`. – Arup Rakshit Apr 19 '13 at 08:17
  • @RubyLovely It probably should, because by the documentation to `extend`, it should add instance methods only. However the current Ruby YARV-based implementations 1.9.x and 2.0.0 do return `[:@@xyz]` of singleton class of with module extended class. – David Unric Apr 19 '13 at 08:24
1

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.

Arie Xiao
  • 13,909
  • 3
  • 31
  • 30
0

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.

Community
  • 1
  • 1
Boris Stitnicky
  • 12,444
  • 5
  • 57
  • 74
  • I do get it, but why then `class_variables` called on singleton class does return "mixed-in" class variable `@@xyz` ? Is this a bug candidate to report ? – David Unric Apr 19 '13 at 08:42
  • Actually, I must admit that I provided a general answer. Your code does not even work the same way on my Ruby 1.9.3. For example, the `C.singleton_class.class_variables` line returns empty array `[]` for me... – Boris Stitnicky Apr 19 '13 at 08:46
0

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.

Boris Stitnicky
  • 12,444
  • 5
  • 57
  • 74
  • 1
    So it looks like a regression and issue to report to a bug tracker. – David Unric Apr 19 '13 at 08:52
  • I'm going to test on previous Ruby versions and report back the results. – David Unric Apr 19 '13 at 08:54
  • The difference is due to changes in latest 2.0.0 version. `class_variables` returns also class variables *inherited* from included module. Still curious how can `@@xyz` be inherited and inaccessible at the same time. – David Unric Apr 19 '13 at 11:24