views:

165

answers:

3

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.

+2  A: 

Well, probably the best answer is "just because": the instance_eval in a nutshell creates some kind of singleton proc that is invoked with the binding of a given object. I agree that is sounds a bit strange, but it is what it is.

If you execute instance_eval with a string, you will even get a warning that your method tries to access class variable:

irb(main):038:0> Test.new.instance_eval "@@a"
(eval):1: warning: class variable access from toplevel singleton method
NameError: (eval):1:in `irb_binding': uninitialized class variable ...
Anton
So in essence there is no way of accessing the variables directly?
haroldcampbell
Kind of, just through methods of the object.
Anton
+1  A: 

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.

Paolo Perrotta
bummer. Ok. Thanks to both of you @Anton and @Paolo for clearing this up for me.
haroldcampbell
This is not strictly true in ruby 1.9: `class Hello; @@foo = 5; end; Hello.class_eval { @@foo } #=> 5
banister
@Paolo, more info given in my answer below
banister
+2  A: 

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.

banister
Thanks for the info banister. All my tests were done 1.8. I'll have to give 1.9 a try.
haroldcampbell