Robert Klemme
2/24/2007 1:08:00 PM
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<<self`
> block.
There is:
irb(main):017:0> class Bar
irb(main):018:1> def add(*syms)
irb(main):019:2> cl = class<<self;self;end
irb(main):020:2> cl.instance_eval { attr_accessor *syms }
irb(main):021:2> end
irb(main):022:1> end
=> nil
irb(main):023:0> f=Bar.new
=> #<Bar:0x3c1a40>
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
=> #<OpenStruct>
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