Class Variables – A Ruby Gotcha

Class variables in Ruby have a bad name. It’s true, they are considered harmful, borderline evil. So why the bad rap? Well, most groans are about how they behave when inheritance enters the fray. Correction: It’s all about how they behave compared to what we expect.

To paint a picture of our little problem child lets look at what we expect with PHP.

<?php

class DemoClass {
    public $myvar;

  public function __construct() {
    $this->myvar = "hello world : ";
  }

  public function getMyVar() {
    echo $this->myvar;
  }

}
class Demo2Class extends DemoClass {
    public function __construct() {
        $this->myvar = "goodbye world : ";
    }
}

$demo1 = new DemoClass();
$demo2 = new Demo2Class();

$demo1->getMyVar();
$demo2->getMyVar();
$demo1->getMyVar();

// Produces
// hello world : goodbye world : hello world : "

That makes sense to me. We define a class with a concrete constructor and abstract getter. When inherited we can override the constructor and set the “class variable” to something else. The base class is unaffected, the world keeps spinning.

In Ruby if we try and do something similar using class variables we end up with one or two problems.

class DemoClass
  @@my_var = nil
 
  def initialize
    @@my_var = "hello world"
  end

  def my_var
    puts @@my_var
  end
end

class Demo2Class < DemoClass
  def initialize
    @@my_var = "goodby world"
  end
end

demo1 = DemoClass.new
demo1.my_var
demo2 = Demo2Class.new

demo2.my_var
demo1.my_var

# Produces

#hello world

#goodbye world

#goodbye world

When you approach class variables in Ruby in this manner they just seem wrong. The change to my_var in Demo2Class has bubbled up to our base class. To mitigate the problem, say we subclass DemoClass again the behaviour of the class variable my_var is nothing short of unpredictable. They are global variables in disguise.

So Why Use Class Variables?

On the face of it the rumours about class variables are true. But now we know what not to do, is there a scenario where class level variables are actually useful? Well, I confess. I use them in almost every web application.

Using the methods described in our examples, depending on initialization data implemented using class level variables as a class ‘state’ is a pretty bad idea. However, I do use class variables for initialization data.

Rails applications generally have at least 4 environments, production, staging, development and test. It’s very likely that configuration changes per environment, be it email addresses to mail exceptions to or a URL of a web service sandbox. The code itself looks like:

class AppConfig
  @@app_config = YAML::load(ERB.new((IO.read("#{RAILS_ROOT}/config/app_config.yml"))).result)[RAILS_ENV]

  def self.method_missing(sym, *args, &block)
    @@app_config[sym.to_s]
  end
end

It should be pretty self explanatory. A YAML file is parsed into the class variableapp_config. Then usingmethod_missingat class level I can retrieve values easily withAppConfig.exception_email_addresses`.

Class Level Configuration

So now hopefully we can agree that class variables are not completely evil. Sometimes we do want to share traits at a class level down class hierarchy. A common technique to achieve this without incurring the problems described previously is to use class instance variables.

Although a bit of a mouthful, class instance variables are pretty straightforward, (coming from PHP they did not make sense at first) we just have to adjust our thinking slightly. I always find the best way to discover first is irb.

ruby-1.9.2-p290 :001 > class Demo
ruby-1.9.2-p290 :002?>   @my_instance_variable = "whaaa?"
ruby-1.9.2-p290 :003?>   end
 => "whaaa?"
ruby-1.9.2-p290 :004 > Demo.class
 => Class
ruby-1.9.2-p290 :005 > Demo.superclass
 => Object
ruby-1.9.2-p290 :006 > Demo.instance_variables
 => [:my_instance_variable]

There it is, unlike more classical languages Ruby classes (like everything else) are just objects with instance variables. So how does this knowledge help us? Returning to the previous example and employing a little accessors trick.

class DemoClass
  class << self
    attr_accessor :my_var
  end

  @my_var = nil

  def initialize
    self.class.my_var = "hello world"
  end

  def my_var
    puts self.class.my_var
  end
end

class Demo2Class < DemoClass
  def initialize
    self.class.my_var = "goodby world"
  end
end

demo1 = DemoClass.new
demo1.my_var
demo2 = Demo2Class.new

demo2.my_var
demo1.my_var

# Produces

# hello world

# goodbye world

# hello world

The example is looking pretty horrible now, before any explanation lets clean it up so it offends our eyes no longer.

class DemoClass
  class << self
    attr_accessor :my_var
  end

  @my_var = "hello world"
end

class Demo2Class < DemoClass
  @my_var = "goodby world"
end

puts DemoClass.my_var
puts Demo2Class.my_var
puts DemoClass.my_var

If you are unsure about eigenclasses ( a.k.a singleton classes, but I'm originally a PHP guy and a singleton class meant something totally different at first) in this demonstration we are simply using it to create a attr_accessor as we normally would, only doing it this way creates it for our class instance variable.

Wrapping up

Remember when I said class variables are not always bad and showed an example when they are perfectly acceptable (well in my book anyway). I did come across a problem with that AppConfig snippet.

I had to duplicate an application for two different clients. Obviously the configuration data was different, but the codebase was to be the same. I wanted to maintain the codebase in a single SCM repository (bug fixes or updates would apply to both versions) and I didn't want to hit git merge mashes, in fact I didn't want the SCM to take on a new role in my workflow, it's there to do SCM only, not manage configuration across clients.

Obviously, we would look to persist the configuration in a database, which is simple to achieve in Rails using an ActiveRecord model like so:


# app/models/application_config.rb

class ApplicationConfig < ActiveRecord::Base
  serialize :config_value
  after_save :reset_config

  def reset_config
    AppConfig.reset_configs
  end
end

# lib/app_config.rb

class AppConfig
  class << self
    attr_accessor :configs
  end

  @configs = {}

  def self.method_missing(sym, *args, &block)
    @configs[sym.to_s] ||= ""
  end

  def self.reset_configs
    self.configs = {}
    configs = ApplicationConfig.all

  configs.each do |setting|
    self.configs = {setting.config_key => setting.config_value}.merge self.configs
  end
  end
end

Using the code above I could drop in a persisted configuration for the application without refactoring any code where the configuration data was being accessed (this was a big win as it was a pretty large app). I could also use a simple scaffold to update and edit configuration on the fly. The use of class instance variables allowed me to cache the config data without hitting the database for every method_missing message the class received.

Hopefully, that has dispelled the class variables are useless/evil myth. Like most things, a lack of understanding and naive implementations gives class variales a bad name. It certainly took me a while (and plenty of naive implementations) to get a grasp of what's going on with them. As for the eigenclass stuff, check out Nathan Kleyns article on metaprogramming for more detail.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Guillotine

    The PHP example is not using a class variable.

    However, in PHP there is an easy way to control with the way you’re referencing the variables. The static keyword (static::$myvar) acts like the PHP example in this article and self (self::$myvar) like the first Ruby example.

  • http://brianewing.me Brian

    You show how it should work with PHP, but you use the equivalent of *instance variables* in PHP, not class variables. For comparison you should use static variables in PHP, eg:

    class DemoClass {
    static $myvar;

    public function __construct() {
    self::$myvar = “hello world! “;
    }
    /* etc */
    }

  • http://yoan.dosimple.ch/ Yoan

    The first exemple in PHP seems to be buggy, compare the PHP and Ruby documentation: http://ch2.php.net/manual/en/language.oop5.static.php, https://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Classes#Class_Variables

    This is your fixed PHP example that behave exactly like PHP.

    getMyVar();
    $demo2 = new Demo2Class();
    $demo2->getMyVar();
    $demo1->getMyVar();

    How much you know about OOP?

    • http://yoan.dosimple.ch/ Yoan

      the comment system ate the PHP I wrote: https://gist.github.com/1378671

      • hurp

        $this::$myVar != self::$myVar. How much do you know about OOP?

  • http://bangline.co.uk Dave Kennedy

    Hi all, Thanks for the constructive comments. To reply to the general feeling of the comment stream,

    The aim of the article was not to replicate Ruby class variables in PHP, nor show how static variables in PHP, like Ruby class variables “break inheritance” so to speak.

    The (first) Ruby example shown is a very common mistake when starting out in Ruby.

    I believe the misconception drills down to me NOT using static variables in the PHP example, but this was intentional… I take a common enough looking piece of PHP code, build the naive representation in Ruby to illustrate why it is a mistake to use class variables in this manner.

    Ill try and made my intentions clearer next time.

  • b0bv

    Poor PHP. You’d been compared to Ruby by mistake.