Search code examples
rubyclassinstance-variablesclass-variables

Class Level Instance Variables in Ruby


Please help me to understand Class Level Instance Variables.

@@ is a class variable and is an equivalent to instance variable (@) in a class instance.

But what is an instance variable (@) when used on class level? If it puts a definition to class instance then why not to define it in an initializer?

class MyClass
  cattr_reader :class_variable

  def self.new_instance(cv, cliv, iv)
    @@class_variable = cv
    @class_level_instance_variable = cliv
    self.new(iv)
  end

  def initialize(iv)
    @instance_variable = iv
  end

  def use
    puts "class_var=#{self.class.class_variable.inspect}\ninst_var=#{@instance_variable.inspect}\ncliv=#{@class_level_instance_variable.inspect}"
  end
end

c = []
c << MyClass.new_instance(1,2,3)
c[0].use
c << MyClass.new_instance(4,5,6)
c[1].use
c << MyClass.new_instance(7,8,9)
c[2].use

c[0].use
c[1].use
c[2].use

Solution

  • In your answer you don't output the class level instance variable. Besides the usual syntax (@foo), an instance variable can be accessed via a method (instance_variable_get(:@foo)). You can use this method to read instance variables of other objects, not only self.

    Here's a modified version of your code

    require 'active_support/core_ext'
    
    class MyClass
      cattr_reader :class_variable
    
      def self.new_instance(cv, cliv, iv)
        @@class_variable = cv
        @class_level_instance_variable = cliv
        self.new(iv)
      end
    
      def initialize(iv)
        @instance_variable = iv
      end
    
      def use
        puts "class_var=#{self.class.class_variable.inspect}"
        puts "class inst var: #{self.class.instance_variable_get(:@class_level_instance_variable)}"
        puts "inst_var=#{@instance_variable.inspect}"
      end
    end
    
    c = []
    c << MyClass.new_instance(1,2,3)
    c << MyClass.new_instance(4,5,6)
    c << MyClass.new_instance(7,8,9)
    
    c[0].use
    c[1].use
    c[2].use
    # >> class_var=7
    # >> class inst var: 8
    # >> inst_var=3
    # >> class_var=7
    # >> class inst var: 8
    # >> inst_var=6
    # >> class_var=7
    # >> class inst var: 8
    # >> inst_var=9
    

    See, class inst var is always 8 (just as class var is always 7). This is because you output values after all your modifications are made. And since class level variables are shared, the last modification wins.

    c << MyClass.new_instance(7,8,9)
    

    If you were to output from initializer (as was in your first version of code), you'd see different results.

    # >> class_var=1
    # >> class inst var: 2
    # >> inst_var=3
    # >> class_var=4
    # >> class inst var: 5
    # >> inst_var=6
    # >> class_var=7
    # >> class inst var: 8
    # >> inst_var=9