The various flavors of Ruby class attributes
Here’s a curious thing about Ruby: it’s got three flavors of class attributes. You can adorn your classes with class variables, class instance variables, and class constants. Not knowing the differences between them, and thinking that one of them might be useful for a project I was working on, I set out to figure out how they all worked, especially with respect to inheritance.
As a trivial example of the design scenario I was working with, consider the case of an object-oriented vegetable garden. Vegetables come in all shapes, sizes, and colors, but we might want to say that all vegetables should be green unless we’ve said otherwise. We might start modeling our vegetable garden with a Vegetable class, and we could set a color attribute on it with a default value of "green". Lettuce, which happens to be green, could inherit that attribute from Vegetable. Eggplant, however, should redefine color to be "purple".
While certainly a contrived and flawed example, it demonstrates the behavior I was looking for. Let’s see how Ruby’s various flavors of class attributes can help us solve this design problem — or not.
First up, class variables:
class Vegetable
@@color = 'green'
def color
@@color
end
end
class Eggplant < Vegetable
@@color = 'purple'
end
Vegetable.new.color # => "purple"
Eggplant.new.color # => "purple"
I wasn’t expecting that! Apparently class variables are shared among subclasses, so you can’t redefine their value in subclasses without changing the value in the base class.
Next up, class instance variables:
class Vegetable
@color = 'green'
class << self
attr_reader :color
end
def color
self.class.color
end
end
class Lettuce < Vegetable
# no need to set @color here, since lettuce is green ... right?
end
Vegetable.new.color # => "green"
Lettuce.new.color # => nil
No love here, either: class instance variables are not accessible from subclasses at all. Probably for the better, since the code needed to access class instance variables from instances is even uglier than that needed to access class variables from instances.
Class constants are right out:
class Vegetable
Color = 'green'
def color
Color
end
end
class Eggplant < Vegetable
Color = 'purple'
end
Vegetable.new.color # => "green"
Eggplant.new.color # => "green"
Class constants are statically bound, so the polymorphic call to Vegetable#color from an Eggplant instance references the Color constant defined in Vegetable, not the one defined in Eggplant.
Giving up on the class attributes approach, I resorted to defining the attributes at the instance level. I considered explicitly setting a @color instance variable in the class initialize method, but then the attribute wouldn’t be constant. Instead, the simplest implementation that does what I want seems to be to use methods that return constant values:
class Vegetable
def color
'green'
end
end
class Lettuce < Vegetable
end
class Eggplant < Vegetable
def color
'purple'
end
end
Vegetable.new.color # => "green"
Lettuce.new.color # => "green"
Eggplant.new.color # => "purple"
So as it turns out, each of Ruby’s class attribute mechanisms behaves differently in subclasses. I’m sure class variables, class instance variables, and class constants have their utility, but they aren’t useful for defining constant attributes shared by all instances of a class, but which can be redefined in subclasses.



You should have a look at class_inheritable_accessor defined in the active_support gem. That should do what you want
Nick E.
29 Jun 09 at 1:06 pm