Hubro
Hubro

Reputation: 59378

How do I override a variable in a Ruby subclass without affecting the superclass?

Let's say I have a class with a few "static" variables. I want to a subclass of that class to be able to override those variables without affecting the original class. This isn't possible using class variables, since those appears to be shared between subclasses and superclasses:

class Foo
  @@test = "a"

  def speak; puts @@test; end
end

class Bar < Foo
  @@test = "b"
end

Bar.new.speak
# b

Foo.new.speak
# b

It isn't possible using constants either:

class Foo
  TEST = "a"

  def speak; puts TEST; end
end

class Bar < Foo
  TEST = "b"
end

Bar.new.speak
# a

Foo.new.speak
# a

Methods defined in the superclass ignores constants in the subclass.

The obvious workaround is to define methods for the variables that needs to be "overridable":

class Foo
  def test; "a"; end
end

But that feels like a hack. I feel like this should be possible using class variables and that I'm probably just doing it wrong. For example, when I subclass Object (which is what happens by default):

class Foo < Object
  @@bar = 123
end

Object.class_variable_get(:@@bar)
# NameError: uninitialized class variable @@bar in Object

Why isn't @@bar set on Object like it was in my Bar < Foo example above?


To summarize: how do I override a variable in a subclass without affecting the superclass?

Upvotes: 8

Views: 8717

Answers (3)

Sahil Dhankhar
Sahil Dhankhar

Reputation: 3656

Class constants does what you want, you just need to use them differently:

class Foo
  TEST = "a"

  def speak
    puts self.class::TEST
  end
end

class Bar < Foo
  TEST = "b"
end

Bar.new.speak # => a
Foo.new.speak # => b

Upvotes: 8

Denis de Bernardy
Denis de Bernardy

Reputation: 78561

Add a variable to the class itself (as in the class instance, rather than a class variable):

class Foo
  @var = 'A'

  class << self
    attr_reader :var
  end

  def var
    self.class.var
  end
end

class Bar < Foo
  @var = 'B'
end

Foo.var # A
Foo.new.var # A
Bar.var # B
Bar.new.var # B

Upvotes: 4

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230521

The correct way (IMHO) is to use methods, because this way you're using inheritance and virtual dispatching, just as you want to.

Class variables are shared down the hierarchy, not up. That's why @@bar is not available in Object.

Upvotes: 3

Related Questions