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"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net