From: Robert Klemme Date: 2007-02-24T22:10:07+09:00 Subject: Re: Extending object instances with << On 24.02.2007 13:52, Tim Becker wrote: > Hi, > > I'm trying to write a class with a method to extend instances of > itself to contain additional accessors. I thought using `class << > self` would be the most elegant way to go about it, but I'm running > into some problems. To illustrate: > > class Test > # takes an array of symbols to add to the instance. > def add syms > syms.each { |sym| > @@__tmp = sym > $__tmp = sym > class << self > #attr_accessor sym # this would be my preferance, but sym > isn't in scope here > #attr_accessor $__tmp # this works, but uses globals > attr_accessor @@__tmp # this is nearly as bad as using globals > end # << > } # each > end # add > end # Test > > t = Test.new > t.add [:thingie, :thingie2] > > t.thingie="whatever" > t.thingie2="bla" > puts t.thingie > puts t.thingie2 > > I don't like the idea of using globals to transport the symbol > information and the class members approach is nearly as bad > (synchronization issues mainly, apart from elegance). But I can't > think of another way to transport dynamic data into the `class< block. There is: irb(main):017:0> class Bar irb(main):018:1> def add(*syms) irb(main):019:2> cl = class< cl.instance_eval { attr_accessor *syms } irb(main):021:2> end irb(main):022:1> end => nil irb(main):023:0> f=Bar.new => # irb(main):024:0> f.add :bar => nil irb(main):025:0> f.bar=10 => 10 irb(main):026:0> f.bar => 10 > Alternatives would be to handle this using `method_missing` though > that wouldn't just affect a single instance or using `eval` which > would involve executing strings I'm banging together. > > Another thing I tried was: > > .. > self.class.attr_accessor sym > .. > > but that doesn't work because `attr_accessor` is private (contrary to > what it says in the documentation...) > > Any ideas? Am I missing something? See above. Apart from that you could simply use OpenStruct or inherit OpenStruct which does all this for you already automagically: irb(main):013:0> require 'ostruct' => true irb(main):014:0> f=OpenStruct.new => # irb(main):015:0> f.bar=10 => 10 irb(main):016:0> f.bar => 10 Major difference is that you do not explicitly control accessor creation but automatically get *all* - even spelling errors. Kind regards robert