Confusion with class_eval

class A
end
A.class_eval do
  @@attr = 100
  def self.get_attr
    @@attr
  end
  def self.set_attr(_x)
      p "hi"
    @@attr = _x
  end
end
p A.class_variable_get(:@@attr)
p Object.class_variable_get(:@@attr)

Output:

D:/Rubyscript/My ruby learning days/Scripts/TEST.RB:78: warning: class
variable access from toplevel
100
100

C:\Program Files\Notepad++>

My question is how @attr becomes Object class's class variable?

···

--
Posted via http://www.ruby-forum.com/.

Pritam Dey wrote in post #1104053:

My question is how @attr becomes Object class's class variable?

Not at my workstation to verify, but I'd suggest that this error:

D:/Rubyscript/My ruby learning days/Scripts/TEST.RB:78: warning: class
variable access from toplevel

is telling you that using a cvar inside #class_eval doesn't do what you
expect it should. It appears to be searching for the cvar outside
the scope of class_eval (i.e. on the toplevel object, Object)

If I was at my workstation I'd use this to test:

    class A
      def A.bar
        @@bar
      end
    end
    class B
      class << self
        def foo
          A.class_eval { @@bar = 1 }
        end
      end
      def B.bar
        @@bar
      end
    end
    p A.bar, B.bar
    B.foo
    p A.bar, B.bar

(Just made up that code, haven't verified it in the slightest.)
I'd assume that A.bar would always return nil, and B.bar would return
nil and then 1.

Perhaps you should use:

    A.class_eval do
      class_variable_set :@@attr, 100
      # etc.
    end

(Again, I just made up that code, no idea if it's correct.)

···

--
Posted via http://www.ruby-forum.com/\.

Hi,

Note that the effect you are seeing doesn't occur in this case:

class A
  @@x = 10
end

puts A.class_variable_get(:@@x)
puts Object.class_variable_get(:@@x)

--output:--
10
1.rb:6:in `class_variable_get': uninitialized class variable @@x in
Object (NameError)
  from 1.rb:6:in `<main>'

So the problem does have to do with class_eval. I don't have a
definitive explanation of what's going on, but "Metaprogramming Ruby"
teaches us that class_eval changes self to the receiver, and the
'current class' also changes to the receiver as well. Based on that
'rule', it would be natural to assume that class variables appearing for
the firs time inside the block would attach to the current class.
However, that is clearly not the case.

Because the problem manifests itself when you use a block (as part of
your class_eval), the block is the key to the problem. Blocks are
closures, which means they can see the variables defined in the
surrounding code. Blocks can also change the values in the surrounding
code. Here is an example of that:

x = 10
code = lambda {puts x; x = 'hello'}

code.call
puts x

--output:--
10
hello

In your case, the surrounding code is the 'top level' where an object
called 'main' exists (created by ruby at startup), and main is an object
of class Object. So your block can see those things in the surrounding
scope, or, in other words, your block travels around with a 'binding'
that includes all the variables in the surrounding code and their
values. I think ruby must look for something in the binding to attach
class variables to--rather than using the 'current class'.

Your output shows that the class variable attaches to the class Object,
but the details of how that works are not clear to me. However, the
fact that class variables leak out of a block seems like yet another
reason not to use class variables.

···

--
Posted via http://www.ruby-forum.com/.