Syed Humza Shah

Logo

Engineering leader. Likes coffee. Loves to travel. Runs on a combination of optimism and pragmatism.

Read more about him here.

Ruby's class variables not friends with inheritence

Nope, don’t do it!

Look at this and see how class variables are being treated:

# ruby code

class A
  def method_a
    @@var = 1
  end
end

class B < A
  def method_b
    @@var = 2
  end
end

a = A.new
a.method_a #=> 1
A.class_variable_get("@@var") #=> 1

b = B.new
b.method_b #=> 2
B.class_variable_get("@@var") #=> 2

And now, this little gem:

# ruby code

A.class_variable_get("@@var") #=> 2 o.O

Let’s go about it the other way around:

# ruby code

b = B.new
b.method_b #=> 2
B.class_variable_get("@@var") #=> 2

a = A.new
a.method_a #=> 1
A.class_variable_get("@@var") #=> 1

B.class_variable_get("@@var") #=> 1 o.O

Clearly, inheritence and class variables aren’t the best of friends in Ruby.

So how do you get around this?

It depends on what you’re trying to accomplish. If you want a “globally” accessible constant value, what you should be using is a constant.

# ruby code

class A
  VAR = 1
end

class B < A
  VAR = 2
end

A::VAR #=> 1
B::VAR #=> 2

A::VAR #=> 1

And what if your value isn’t constant? Class-level instance variables to the rescue:

# ruby code

class A
  class << self
    def method_a
      @var = 1
    end
  end
end

class B < A
  class << self
    def method_b
      @var = 2
    end
  end
end

A.method_a #=> 1
A.instance_variable_get("@var") #=> 1

B.method_b #=> 2
B.instance_variable_get("@var") #=> 2

A.instance_variable_get("@var") #=> 1

Plus a little something for convenience:

# ruby code

class A
  class << self
    attr_reader :var

    def method_a
      @var = 1
    end
  end
end

A.method_a #=> 1
A.var #=> 1

And now you know. :neckbeard: