Home > Software design >  Accessing subclass variables in a superclass in Ruby
Accessing subclass variables in a superclass in Ruby

Time:11-29

So, I've been studying Ruby for over two weeks now and I'm having some issues understanding the whole OOP thing.

In this lesson, in the exercises in the end, we are asked to create a superclass of vehicle and add non-specific behavior to it, and then create a MyCar and a MyTruck subclasses that inherit from it and also have a constant that separates them. So I did like so:

class Vehicle

  attr_accessor :color, :curr_speed
  attr_reader :year, :model

  def initialize(y, c, m)
    @year = y
    @color = c
    @model = m
  end

  #SOME OTHER METHODS

  def to_s
    "My #{VEHICLE_TYPE} is a #{self.model} #{self.year} of color #{self.color}."
  end      
end

class MyCar < Vehicle
  VEHICLE_TYPE = 'car'
end

class MyTruck < Vehicle
  VEHICLE_TYPE = 'truck'
end

I also redefined the to_s method so that it would return a string that would say what kind of vehicle it is along with the other info. Now, the exercise does not ask us to do that -- in fact, they define a to_s method for MyCar and another to MyTruck that starts with "My car..." or "My truck..." but I feel like this goes against the DRY principle.

Thing is, it seems that Ruby does not accept a subclass variable passed in a superclass method. If I use #{VEHICLE_TYPE} it throws a uninitialized constant Vehicle::VEHICLE_TYPE (NameError), and if I use #{self.VEHICLE_TYPE} it throws a undefined method 'VEHICLE_TYPE' for Vehicle:Class (NoMethodError).

Am I correct in my assumption that Ruby does not accept a subclass variable in a superclass? And how could I go about to fix this issue in a similar fashion? Because I thought about simply adding another parameter to initizalize for type, store in a superclass instance variable and be done with it, but I don't think it would serve the purpose of the exercise.

And bear in mind that I'm a newbie!

Thanks in advance!

CodePudding user response:

That's right. The superclass can't use the shortcut notation of VEHICLE_TYPE to access the constant you've defined.

When you want to access a constant outside of the class it is defined in, you can access it using a qualified name like:

Car::VEHICLE_TYPE

You were getting pretty close on your self.VEHICLE_TYPE attempt. But to provide a generic replacement for something like Car::, you'd need to use self.class::.

So your to_s method would look like:

def to_s
  "My #{self.class::VEHICLE_TYPE} is a #{self.model} #{self.year} of color #{self.color}."
end

This will work as long as each instantiable class defines that constant.

  • Related