"attr" like functions

I came across an interesting problem today. Say you wanted to have an object where all the instance variables have an associated description. You also wanted to have a 'pretty_print' method, that would print out the description of the variable, along with the value. Here's how the ideal code would look:

class Coordinate < DescribedObject
   attr_with_desc :x, "X Coordinate"
   attr_with_desc :y, "Y Coordinate"
   attr_with_desc :z, "Z Coordinate"
end

coord = Coordinate.new
coord.x = 1
coord.y = 2
coord.z = 3

coord.pretty_print
X Coordinate: 1
Y Coordinate: 2
Z Coordinate: 3

I managed to get that kind of behaviour with this code:

class Module
   def attr_with_desc(sym, desc="Instance var")
     attr_accessor sym
     eval_str = "@@desc[\"@#{sym.id2name}\"] = \"#{desc.to_s}\""
     puts(eval_str)
     module_eval(eval_str)
   end
end

class DescribedObject
   @@desc = Hash.new

   def pretty_print
     instance_variables.each {
       >var>
       if @@desc.has_key?(var)
         puts "#{@@desc[var]}: #{eval(var)}"
       else
         puts "Instance var #{var}: #{eval(var)}"
       end
     }
   end
end

class Coordinate < DescribedObject
   attr_with_desc :x, "X Coord"
   attr_with_desc :y, "Y Coord"
   attr_with_desc :z, "Z Coord"
end

But there are still a couple of things that I don't think are ideal about this, and wonder if it is possible to fix:

1) Is there a way to add the 'attr_with_desc' method without having to modify the Module class? I'd like to keep it pristine, if possible.

2) Both Module and DescribedObject have to know that the variable is named '@@desc', so if they get out of sync, then that could be bad. Is there a way of grouping all the code into one class/module?

Ben

not sure why you're putting a class method in Module, putting it in
the class itself would suffice?

--<snip>--

class Describable
  def Describable.attr_with_desc(id, description)
    attr_accessor id
    @@desc ||= {}
    @@desc["@#{id.id2name}"] = description.to_s
  end
  def pretty_print
    instance_variables.each { |var|
      if @@desc.has_key?(var)
        puts "#{@@desc[var]}: #{eval(var)}"
      else
        puts "Instance var #{var}: #{eval(var)}"
      end
    }
  end
end

class Test<Describable
  attr_with_desc :name, "Name of the test"
  attr_with_desc :version, "Version of the test"
end

t = Test.new
t.name = "test"
t.version = 1.0
t.pretty_print

--<snip>--

regards,
leon

···

On Sat, 26 Jun 2004 09:22:57 +0900, Ben Giddings <bg-rubytalk@infofiend.com> wrote:

1) Is there a way to add the 'attr_with_desc' method without having to
modify the Module class? I'd like to keep it pristine, if possible.

Perhaps making Describable a module, rather than class, would be more
appropriate here. If nothing else, the 'attr_with_desc' method should
be available in objects that subclass a different type than
Describable.

Lennon