I am working on a small rails app and have a problem with ruby’s OOP model. I have the following simplified class structure.
class Foo
protected
@bar = []
def self.add_bar(val)
@bar += val
end
def self.get_bar
@bar
end
end
class Baz < Foo
add_bar ["a", "b", "c"]
end
My problem is now, that when I call add_bar in the class definition of Baz, @bar is apparently not initialized and I get an error that the + Operator is not available for nil. Calling add_bar on Foo directly does not yield this problem. Why is that and how can I initialize @bar correctly?
To make clear what I want, I will point out the behavior I would expect from these classes.
Foo.add_bar ["a", "b"]
Baz.add_bar ["1", "2"]
Foo.get_bar # => ["a", "b"]
Baz.get_bar # => ["a", "b", "1", "2"]
How could I achieve this?
Short answer: instance variables don’t get inherited by subclasses
Longer answer: the problem is that you wrote
@bar = []in the body of the class (outside any method). When you set an instance variable, it is stored on whatever is currentlyself. When you’re in a class body,selfis the class object Foo. So, in your example,@foogets defined on the class object Foo.Later, when you try to look up an instance variable, Ruby looks in whatever is currently
self. When you call add_bar from Baz,selfis Baz. Alsoselfis STILL Baz in the body of add_bar (even though that method is in Foo). So, Ruby looks for@barin Baz and can’t find it (because you defined it in Foo).Here’s an example that might make this clearer
This is admittedly a bit confusing, and a common stumbling point for people new to Ruby, so don’t feel bad. This concept finally clicked for me when I read the ‘Metaprogramming’ chapter in Programming Ruby (aka “The Pickaxe”).
How I’d solve your problem: Look at Rails’
class_attributemethod. It allows for the sort of thing you’re trying to do (defining an attribute on a parent class that can get inherited (and overidden) in its subclasses).