Automatically Initializing Instance Variables






Automatically Initializing Instance Variables

Problem

You're writing a class constructor that takes a lot of arguments, each of which is simply assigned to an instance variable.

	class RGBColor(red=0, green=0, blue=0)
	  @red = red
	  @green = green
	  @blue = blue
	end

You'd like to avoid all the typing necessary to do those variable assignments.

Solution

Here's a method that initializes the instance variables for you. It takes as an argument the list of variables passed into the initialize method, and the binding of the variables to values.

	class Object
	  private
	  def set_instance_variables(binding, *variables)
	    variables.each do |var|
	      instance_variable_set("@#{var}", eval(var, binding))
	    end
	  end
	end

Using this method, you can eliminate the tedious variable assignments:

	class RGBColor
	  def initialize(red=0, green=0, blue=0)
	    set_instance_variables(binding, *local_variables)
	  end
	end

	RGBColor.new(10, 200, 300)
	# => #<RGBColor:0xb7c22fc8 @red=10, @blue=300, @green=200>

Discussion

Our set_ instance_variables takes a list of argument names to turn into instance variables, and a Binding containing the values of those arguments as of the method call. For each argument name, an eval statement binds the corresponding instance variable to the corresponding value in the Binding. Since you control the names of your own variables, this eval is about as safe as it gets.

The names of a method's arguments aren't accessible from Ruby code, so how do we get that list? Through trickery. When a method is called, any arguments passed in are immediately bound to local variables. At the very beginning of the method, these are the only local variables defined. This means that calling Kernel#local_variables at the beginning of a method will get a list of all the argument names.

If your method accepts arguments that you don't want to set as instance variables, simply remove their names from the result of Kernel#local_variables before passing the list into set_instance_variables:

	class RGBColor
	  def initialize(red=0, green=0, blue=0, debug=false)
	    set_instance_variables(binding, *local_variables-['debug'])
	    puts "Color: #{red}/#{green}/#{blue}" if debug
	  end
	end

	RGBColor.new(10, 200, 255, true)
	# Color: 10/200/255
	# => #<RGBColor:0xb7d309fc @blue=255, @green=200, @red=10>



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows