~clay

merely my musings

The various flavors of Ruby class attributes

with one comment

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.

Written by clay

May 16th, 2009 at 9:57 pm

Posted in Engineering, Geek

Tagged with

One Response to 'The various flavors of Ruby class attributes'

Subscribe to comments with RSS or TrackBack to 'The various flavors of Ruby class attributes'.

  1. 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

Leave a Reply