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.