Extending object instances with <<

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.

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?
   -tim

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

···

On 24.02.2007 13:52, Tim Becker wrote:

Test=Struct.new(*syms)
or
Test=Struct.new(:foo,:bar,:baz)

This will guard you against spelling errors.

--Ken

···

On Sat, 24 Feb 2007 14:08:19 +0100, Robert Klemme wrote:

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
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.

--
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/

That's true. However, I interpreted the OP's posting that he needs to to the extension on a per instance basis. That does not work with the approach you presented.

Kind regards

  robert

···

On 26.02.2007 02:38, Ken Bloom wrote:

On Sat, 24 Feb 2007 14:08:19 +0100, Robert Klemme wrote:

Major difference is that you do not explicitly control accessor creation
but automatically get *all* - even spelling errors.

Test=Struct.new(*syms)
or
Test=Struct.new(:foo,:bar,:baz)

This will guard you against spelling errors.

I think you're correct. The original poster's problem was to add
accessors to instances, splats are handy in the general case but
instance_eval is what you need here.

···

On 2/26/07, Robert Klemme <shortcutter@googlemail.com> wrote:

On 26.02.2007 02:38, Ken Bloom wrote:
> On Sat, 24 Feb 2007 14:08:19 +0100, Robert Klemme wrote:
>> Major difference is that you do not explicitly control accessor creation
>> but automatically get *all* - even spelling errors.
>
> Test=Struct.new(*syms)
> or
> Test=Struct.new(:foo,:bar,:baz)
>
> This will guard you against spelling errors.

That's true. However, I interpreted the OP's posting that he needs to
to the extension on a per instance basis. That does not work with the
approach you presented.

Kind regards

        robert

--
Giles Bowkett
http://www.gilesgoatboy.org

http://gilesgoatboy.blogspot.com