From: Albert Wagner Date: 2002-01-20T03:45:03+09:00 Subject: Re: dynamic method creation On Saturday 19 January 2002 06:33 am, you wrote: > 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 > Yes, That is how I originally implemented the problem. It's clean and simple. I like Structs conceptually. But there turned out to be some issues with Struct and Marshal that I didn't understand, and once I lose faith in a mechanism, then it's hard to rely on it again until a period of "forgetfulness" passes. Also, I discovered that I wanted more functionality than Struct offered. > > 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 > I haven't yet worked with method_missing, which is peculiar as I come from smalltalk. > > 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.) Yes, thank you. I did enjoy it and, as always, I learn from it. > > > Massimiliano -- Quantum Mechanics: The dreams stuff is made of