6

(Question already posted at Ruby Forum, but did not evoke any answer there).

This is my code:

class MC
  def initialize
    @x = 5
    @@y = 6
  end

  def f
    puts @x
    puts @@y
  end
end

m = MC.new
m.f

m.f produces the expected output without an error:

5
6

But this:

def m.g
  puts @x
  puts @@y
end

m.g

produces:

5
warning: class variable access from toplevel
NameError: uninitialized class variable @@y in Object

Why can I access @@y from f, but not from g?

Mentioning of toplevel and Object in the warning and the error message is puzzling to me.

@x is printed as 5, so its environment is MC. This excludes the possibility that @x and @@y in the definition of m.g refer to the toplevel environment (Object) instead of MC.

Why did I get the error message?

Stefan
  • 109,145
  • 14
  • 143
  • 218
user1934428
  • 19,864
  • 7
  • 42
  • 87

2 Answers2

5

All variants below work:

def m.g; puts self.class.send(:class_eval, '@@y') end

def m.g; puts self.class.class_variable_get(:@@y) end

class << m; def g; puts self.class.send(:class_eval, '@@y') end end

class << m; puts class_variable_get(:@@y) end

But these fail:

def m.g; puts @@y; end

class << m; puts class_eval('@@y') end

I would consider this being a ruby parser glitch.

Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
  • Note that the method `#class` "skips" the singleton class and returns `MC`. So the first 3 are not really surprising. You should also add the variants with `singleton_class` which show the same behavior. – undur_gongor Mar 08 '16 at 09:01
  • 1
    Seems like the rules for constant lookup also apply to class variables, i.e. they are lexically scoped. – Stefan Mar 08 '16 at 09:03
  • @undur_gongor `class << m` are the variants with `singleton_class`. – Aleksei Matiushkin Mar 08 '16 at 09:06
4

You do not create g in the class MC but in m's singleton class (a.k.a. eigenclass).

This is a class existing specifically for the object m to store the singleton methods that are defined just for m.

undur_gongor
  • 15,657
  • 5
  • 63
  • 75
  • `class << m ; puts class_variable_get(:@@y) ; end` works perfectly. As well as `def m.g; puts self.class.class_variable_get(:@@y); end`. Which is a bit weird. – Aleksei Matiushkin Mar 08 '16 at 07:50
  • Note that this is only a part of the answer. The singleton class is a subclass of `MC` (`m.singleton_class.superclass # => MC`) and it thus "sees" `@@y` (`m.singleton_class.class_variables # => [:@yy]`). I'm missing why the lookup fails. – undur_gongor Mar 08 '16 at 08:14
  • I guess it’s a parser glitch. – Aleksei Matiushkin Mar 08 '16 at 08:17