Managing Class Data
Problem
Instead of storing a bit of data along with every instance of a class, you want to store a bit of data along with the class itself.
Solution
Instance variables are prefixed by a single at sign; class variables are prefixed by two at signs. This class contains both an instance variable and a class variable:
class Warning @@translations = { :en => 'Wet Floor', :es => 'Piso Mojado' } def initialize(language=:en) @language = language end def warn @@translations[@language] end end Warning.new.warn # => "Wet Floor" Warning.new(:es).warn # => "Piso Mojado"
Discussion
Class variables store information that's applicable to the class itself, or applicable to every instance of the class. They're often used to control, prevent, or react to the instantiation of the class. A class variable in Ruby acts like a static variable in Java.
Here's an example that uses a class constant and a class variable to control when and how a class can be instantiated:
class Fate NAMES = ['Klotho', 'Atropos', 'Lachesis'].freeze @@number_instantiated = 0 def initialize if @@number_instantiated >= NAMES.size raise ArgumentError, 'Sorry, there are only three Fates.' end @name = NAMES[@@number_instantiated] @@number_instantiated += 1 puts "I give you… #{@name}!" end end Fate.new # I give you… Klotho! # => # Fate.new # I give you… Atropos! # => # Fate.new # I give you… Lachesis! # => # Fate.new # ArgumentError: Sorry, there are only three Fates.
It's not considered good form to write setter or getter methods for class variables. You won't usually need to expose any class-wide information apart from helpful constants, and those you can expose with class constants such as NAMES above.
If you do want to write setter or getter methods for class variables, you can use the following class-level equivalents of Module#attr_reader and Module#attr_writer. They use metaprogramming to define new accessor methods: [1]
[1] In Ruby 1.9, Object#send can't be used to call private methods. You'll need to replace the calls to send with calls to Object#funcall.
class Module def class_attr_reader(*symbols) symbols.each do |symbol| self.class.send(:define_method, symbol) do class_variable_get("@@#{symbol}") end end end def class_attr_writer(*symbols) symbols.each do |symbol| self.class.send(:define_method, "#{symbol}=") do |value| class_variable_set("@@#{symbol}", value) end end end def class_attr_accessor(*symbols) class_attr_reader(*symbols) class_attr_writer(*symbols) end end
Here is Module#class_attr_reader being used to give the Fate class an accessor for its class variable:
Fate.number_instantiated # NoMethodError: undefined method 'number_instantiated' for Fate:Class class Fate class_attr_reader :number_instantiated end Fate.number_instantiated # => 3
You can have both a class variable foo and an instance variable foo, but this will only end up confusing you. For instance, the accessor method foo must retrieve one or the other. If you call attr_accessor :foo and then class_attr_accessor :foo, the class version will silently overwrite the instance version.
As with instance variables, you can bypass encapsulation and use class variables directly with class_variable_get and class_variable_set. Also as with instance variables, you should only do this from inside the class, usually within a define_method call.
See Also
- If you want to create a singleton, don't mess around with class variables; instead, use the singleton library from Ruby's standard library
- Recipe 8.18, "Implementing Class and Singleton Methods"
- Recipe 10.10, "Avoiding Boilerplate Code with Metaprogramming"