From: Massimiliano Mirra Date: 2002-01-19T21:33:19+09:00 Subject: Re: dynamic method creation On Sat, Jan 19, 2002 at 04:32:43AM +0900, Albert L. Wagner wrote: > I have a need to dynamically create methods with method names > that are not known until run time. Below is my current best > shot. Is there a better way? If methods allowed expressions as > names, and not just as qualifiers, I wouldn't need the eval. > > class Record > def initialize(name) > @name = name > end > end > > fieldNames = ['abc', 'dof', 'ghi'] > fieldTypes = ['String', 'Fixnum', 'Float'] > rec = Record.new('aRecord') > class << rec > end > fieldNames.size.times do | i | > f = fieldNames[i] > defStr = "def rec.#{f}=(obj)\n" > case fieldTypes[i] > when 'String' then defStr << "@#{f} = obj\n" > when 'Fixnum' then defStr << "@#{f} = obj\n" > when 'Float' then defStr << "@#{f} = obj\n" > end > defStr << "end\n" > puts defStr > eval defStr > end > p rec.abc=(nil) > p rec.dof=(nil) > p rec.ghi=(nil) > p rec.methods To do what your example does, I would just write: Record = Struct.new(nil, :abc, :dof, :ghi) rec = Record.new('one', 2, 3.0) p rec.abc p rec.dof p rec.ghi To do what your question asks, in the past I've written: class Triangle def initialize @variables = { :area => 0, :base => 0, :height => 0 } end def method_missing(id, *args, &block) id.to_s =~ /(\w+)(=)*/ id, assign = $1.intern, $2 if @variables.include?(id) if assign return @variables[id] = args.shift else return @variables[id] end else super end end end t = Triangle.new t.area = 10 t.base = 5 p t.area p t.base The @variables hash can, of course, be made accessible from outside the class to do what you want to do. However, this doesn't get more useful than a Struct. Unless... class SolvableTriangle < Triangle def initialize super @formulas = { :area => proc { base * height / 2.0 }, :base => proc { area * 2 / base.to_f }, :height => proc { area * 2 / height.to_f} } end def calc(id) @variables[id] = @formulas[id].call end end st = SolvableTriangle.new st.base = 5 st.height = 4 st.calc :area p st.area Lastly, I do most of the above (defining variables *and* their relations) through including a simple module that accepts one or more equations, extracts variables, and through YACAS (a symbolic algebra program) solves the equation(s) for each of them. class Triangle include Equation def initialize defeq 'area==base*height/2' compile end end class Equilateral < Triangle def initialize defeq 'height==side*0.866' super end end t = Equilateral.new t.side = 5 t.calc :height, [:side] t.base = 4 t.area = 10 t.calc :height, [:base, :area] As above, nothing prevents from defining the equations from outside. (Yep, this is more than what you asked, but I hope you enjoyed the overview.) Massimiliano