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) # => #
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 # => #