Question

The following code snippet:

class A
end
    @class = eval("A")
    @class.class_eval do
        @@attr = 100
        def self.get_attr
            @@attr
        end
        def self.set_attr(_x)
            @@attr = _x
        end
    end

    class B
    end

    @class = eval("B")
    @class.class_eval do
        @@attr = 100
        def self.get_attr
            @@attr
        end
        def self.set_attr(_x)
            @@attr = _x
        end
    end
    a = A.new
    b = B.new
    A.set_attr(103)
    B.set_attr(222)
    puts A.get_attr
    puts B.get_attr

Class variable is getting shared.

Result:

222 222

Was it helpful?

Solution

It's because you set not the variable you think you do.

class A; end

@class = eval("A")
@class.class_eval do
  class_variable_set :@@attr, 100

  def self.get_attr
    class_variable_get :@@attr
  end

  def self.set_attr(_x)
    class_variable_set :@@attr, _x
  end
end

class B
end

@class = eval("B")
@class.class_eval do
  class_variable_set :@@attr, 100

  def self.get_attr
    class_variable_get :@@attr
  end
  def self.set_attr(_x)
    class_variable_set :@@attr, _x
  end
end

A.set_attr(103)
B.set_attr(222)
puts A.get_attr
puts B.get_attr

# >> 103
# >> 222

When I run your code, it gives several warnings of "Access to class variable from toplevel". So, apparently, you're setting and reading class variables of main object, not of your classes.

OTHER TIPS

Check the below code,

class A
end

class B
end

ADD_ATTR = lambda do
    @@attr = 100
    def self.get_attr
      puts "self : #{self} getting attr : #{@@attr}"
        @@attr
    end
    def self.set_attr(_x)
      puts "self : #{self} setting attr : #{@@attr} to _x : #{_x}"
        @@attr = _x
    end
end

A.class_eval &ADD_ATTR
B.class_eval &ADD_ATTR

a = A.new
b = B.new

A.set_attr(103)
B.set_attr(222)
puts A.get_attr
puts B.get_attr


ADD_BTTR = '
    @@bttr = 100
    def self.get_bttr
      puts "self : #{self} getting bttr : #{@@bttr}"
        @@bttr
    end
    def self.set_bttr(_x)
      puts "self : #{self} setting bttr : #{@@bttr} to _x : #{_x}"
        @@bttr = _x
    end
'
A.class_eval ADD_BTTR
B.class_eval ADD_BTTR

A.set_bttr(103)
B.set_bttr(222)
puts A.get_bttr
puts B.get_bttr

It seems that ruby does the block evaluation in the context of object method before joining it to the class, hence the evaluation happens before the code gets attached to the class, it needs to attach the attribute to object method, to prove the point i used the code with the lambda operator.

In the mean while the same code works if we do the class eval with a string

This is the output I got:

self : A setting attr : 100 to _x : 103
self : B setting attr : 103 to _x : 222
self : A getting attr : 222
222
self : B getting attr : 222
222
self : A setting bttr : 100 to _x : 103
self : B setting bttr : 100 to _x : 222
self : A getting bttr : 103
103
self : B getting bttr : 222
222

see the below code:

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

class B
end

@class = eval("B")
@class.class_eval do
    @@attr = 100
    def self.get_attr
        @@attr
    end
    def self.set_attr(_x)
        p "hi"
        @@attr = _x
    end
end
a = A.new
b = B.new
A.set_attr(103)
B.set_attr(222)

p A.class_variable_defined?(:@@attr) 
p B.class_variable_defined?(:@@attr) 
p Object.class_variable_defined?(:@@attr)

p A.class_variable_get(:@@attr)
p B.class_variable_get(:@@attr)
p Object.class_variable_get(:@@attr)

p A.class_variables
p B.class_variables
Object.remove_class_variable(:@@attr)
p A.class_variables
p B.class_variables

Output:

true
true
true
222
222
222
[:@@attr]
[:@@attr]
[]
[]

Explanation: As per your code @@attr belongs to Object class class variables. And as per Ruby A and B both are subclass of Object. Thus the class variables @@attr has been shared.

Module#class_eval says:

(a) This can be used to add methods to a class.

(b) when a block is given, constant/class variable look-up is not affected.

This is the feature of ruby that if you use class variable inside class_eval it searches that class variable in the class Object.

To make it visible I write the below code, which in turn will prove that: constant/class variable look-up is not affected.

@@avar = 1
class A < BasicObject
@@avar = "hello"
end
A.class_eval { puts @@avar }

Look at the code output:

D:/Rubyscript/My ruby learning days/Scripts/TEST.RB:91: warning: class variable
access from toplevel
D:/Rubyscript/My ruby learning days/Scripts/TEST.RB:95: warning: class variable
access from toplevel
1
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top