I have the following:
class Test @@a = 10 def show_a() puts "a: #{@@a}" end class << self @@b = '40' def show_b puts "b: #{@@b}" end end end
Why does following work:
Test.instance_eval{show_b} b: 40 => nil
But I can't access @@b
directly?
Test.instance_eval{ @@b } NameError: uninitialized class variable @@b in Object
Likewise, the following works
t = Test.new t.instance_eval{show_a} a: 10 => nil
but the following fails
t.instance_eval{ @@a } NameError: uninitialized class variable @@a in Object
I don't understand why I can't access the Class Variables directly from the instance_eval
blocks.
Ruby Class VariablesClass variables begin with @@ and must be initialized before they can be used in method definitions. Referencing an uninitialized class variable produces an error. Class variables are shared among descendants of the class or module in which the class variables are defined.
class_eval(array_second) adds the method second to any instance of Array by passing a String that will be evaluated in the context of the class Array . The call to String. class_eval with a block will evaluate the content of the block in the context of the class String .
In Ruby OOP, class << self is a syntax you would often encounter. It's usually used to define class methods.
A variable itself is not an object. "A variable in Ruby is just a label for a container. A variable could contain almost anything - a string, an array, a hash. A variable name may only contain lowercase letters, numbers, and underscores.
I just asked the same question to Matz during the RubyKaigi party. I was half-drunk, but he was perfectly sober, so you can take this as the definitive answer.
Anton is right - the reason why you cannot access class variables through instance_eval() is "just because". Even class_eval() shares the same issue (Matz himself wasn't totally sure about class_eval() until I told him I'd already tried it). More specifically: scope-wise, class variables are more like constants than instance variables, so switching self (as instance_eval() and class_eval() do) is not going to make any difference when it comes to accessing them.
In general, it might be a good idea to avoid class variables altogether.
EDIT: below code was tested with 1.8.7 and 1.9.1...it seems the situation is different again with 1.9.2 :/
The situation actually isn't that straight forward. There are differences in behaviour depending on whether you're using 1.8 or 1.9 and whether you're using class_eval
or instance_eval
.
The examples below detail the behaviour in most situations.
I also included the behaviour of constants, for good measure, as their behaviour is similar to, but not exactly the same as, class variables.
class_eval
in Ruby 1.8:
class Hello @@foo = :foo end Hello.class_eval { @@foo } #=> uninitialized class variable
class_eval
in Ruby 1.9:
Hello.class_eval { @@foo } #=> :foo
So class variables are looked up in 1.9 (but not in 1.8) when using class_eval
instance_eval
in Ruby 1.8 and 1.9
Hello.instance_eval { @@foo } #=> uninitialized class variable Hello.new.instance_eval { @@foo } #=> uninitialized class variable
It appears class variables are not looked up in 1.8 or 1.9 when using instance_eval
What is also interesting is the case for constants:
class_eval
in Ruby 1.8
class Hello Foo = :foo end Hello.class_eval { Foo } #=> uninitialized constant
class_eval
in Ruby 1.9
Hello.class_eval { Foo } #=> :foo
So, as with class variables, constants are looked up in 1.9 but not in 1.8 for class_eval
instance_eval
in Ruby 1.8
Hello.instance_eval { Foo } #=> uninitialized constant Hello.new.instance_eval { Foo } #=> uninitialized constant
instance_eval
in Ruby 1.9
Hello.instance_eval { Foo } #=> uninitialized constant Hello.new.instance_eval { Foo } #=> :foo
It appears that constant lookup is not quite analogous to class variable look up for Ruby 1.9. A Hello
instance does get access to the constant while the Hello
class does not.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With