Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing Ruby Class Variables with class_eval and instance_eval

Tags:

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.

like image 984
haroldcampbell Avatar asked Aug 08 '10 15:08

haroldcampbell


People also ask

How do I use a class variable in Ruby?

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.

What is Class_eval Ruby?

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 .

What is class << self in Ruby?

In Ruby OOP, class << self is a syntax you would often encounter. It's usually used to define class methods.

Are variables objects in Ruby?

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.


2 Answers

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.

like image 144
Paolo Perrotta Avatar answered Nov 27 '22 07:11

Paolo Perrotta


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 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:

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.

like image 22
horseyguy Avatar answered Nov 27 '22 06:11

horseyguy