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?
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.)
--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.