I'm defining a Box class in ruby which has 3 instance variables: @length, @width, @height, and 2 class variables: @@boxcounter and @@totalvolume.
While the value of @@boxcounter can be updated inside the object constructor (initializer), updating the value of @@totalvolume becomes less trivial, since we have to recalculate the volume of the given object each time we change any of the instance variables (i.e. length, width, or height).
I've come up with the following code to handle this use case:
class Box
  @@boxcounter = 0
  @@totalvolume = 0.0
  def initialize(length, width, height)
    @length = length
    @width = width
    @height = height
    @@boxcounter += 1
    @@totalvolume += volume
  end
  def volume
    volume = @length * @width * @height
  end
  def report
    puts "# of boxes: #{@@boxcounter}"
    puts "Total volume: #{@@totalvolume}"
    puts "Average volume per box: #{@@totalvolume / @@boxcounter}"
  end
  def my_print
    p self
    puts "Length: #{@length}"
    puts "Width: #{@width}"
    puts "Height: #{@height}"
    puts
  end
  def length=(length)
    @@totalvolume -= volume
    @length = length
    @@totalvolume += volume
  end
  def width=(width)
    @@totalvolume -= volume
    @width = width
    @@totalvolume += volume
  end
  def height=(height)
    @@totalvolume -= volume
    @height = height
    @@totalvolume += volume
  end
end
Since the idea of first class objects is still in my head after studying Scheme, I wondered, can I create a generic setter and use that to reduce the code repetition in each of the setters listed above? I tried this, but using eval seems to be a bit of a hack:
  def update_class_state(update_instance_variable)
    @@totalvolume -= volume
    eval update_instance_variable
    @@totalvolume += volume
  end
  def length=(length)
    update_class_state("@length = #{length}")
  end
  def width=(width)
    update_class_state("@width = #{width}")
  end
  def height=(height)
    update_class_state("@height = #{height}")
  end
– My question: Is it a bad practice to write code like this? Is there a more elegant solution to this approach?
 
    