What do you do when you need to attach data to an object instance?

What do you do when you see a need to be able to attach some data to
an object instance for later use somewhere else in a body of code?
Lately I've resorted to this:

## Generic utility to allow one to attach data with a getter/setter to
## any instance of any object so long as there isn't a method name
## collision:
def attach_data(obj, name, data)
  getter = name.to_sym
  setter = (name.to_s + '=').to_sym
  raise "method name collision for #{obj.class} instance" if
obj.respond_to?(getter) || obj.respond_to?(setter)
  ## The 'value' local variable will remain in existence in the lambda
closures below:
  value = data
  meta = class << obj ; self ; end
  meta.send(:define_method, getter, lambda { value }) ##
Getter closure
  meta.send(:define_method, setter, lambda {|val| value = val }) ##
Setter closure
  value
end

For example, in an application using SSH keys, I didn't want to create
a new subclass, nor use an array or hash container instance just to
carry an OpenSSL::PKey::RSA object instance around the code. But I
needed to associate a user ID (user@host) to a key so it could be
accessed somewhere else. I figured it was easiest to just attach it
to the OpenSSL::PKey::RSA instance (see code above) directly. That
made the code cleaner, portions that only required the RSA key
directly, yet still gave the benefit of the key instance containing
the additional meta data I required.

What do you do when you need stuff like that? Monkey patch? Use a
container and pass it around instead? Or?

Is there a module version of OpenStruct that one can just include in
whatever class one wants to attach additional arbitrary data to? So I
could have done this instead:

require 'ostructmod'
class OpenSSL::PKey
  include OpenStructModule
end

???

Aaron out.

Aaron D. Gifford wrote in post #992841:

What do you do when you see a need to be able to attach some data to
an object instance for later use somewhere else in a body of code?

...

require 'ostructmod'
class OpenSSL::PKey
  include OpenStructModule
end

You probably wouldn't want OpenStructModule even if it did exist because
you wouldn't want NoMethodError to be suppressed. Perhaps you're looking
for Object#extend.

  module UserId
    attr_accessor :user_id
  end
  thing = Object.new # whatever the thing is
  thing.extend(UserId).user_id = 123456
  p thing.user_id #=>123456

···

--
Posted via http://www.ruby-forum.com/\.

Aaron D. Gifford wrote in post #992841:

What do you do when you see a need to be able to attach some data to
an object instance for later use somewhere else in a body of code?
Lately I've resorted to this:

## Generic utility to allow one to attach data with a getter/setter to
## any instance of any object so long as there isn't a method name
## collision:
def attach_data(obj, name, data)
  getter = name.to_sym
  setter = (name.to_s + '=').to_sym
  raise "method name collision for #{obj.class} instance" if
obj.respond_to?(getter) || obj.respond_to?(setter)
  ## The 'value' local variable will remain in existence in the lambda
closures below:
  value = data
  meta = class << obj ; self ; end
  meta.send(:define_method, getter, lambda { value }) ##
Getter closure
  meta.send(:define_method, setter, lambda {|val| value = val }) ##
Setter closure
  value
end

For example, in an application using SSH keys, I didn't want to create
a new subclass, nor use an array or hash container instance just to
carry an OpenSSL::PKey::RSA object instance around the code. But I
needed to associate a user ID (user@host) to a key so it could be
accessed somewhere else. I figured it was easiest to just attach it
to the OpenSSL::PKey::RSA instance (see code above) directly. That
made the code cleaner, portions that only required the RSA key
directly, yet still gave the benefit of the key instance containing
the additional meta data I required.

What do you do when you need stuff like that? Monkey patch? Use a
container and pass it around instead? Or?

How about a decorator pattern?

Is there a module version of OpenStruct that one can just include in
whatever class one wants to attach additional arbitrary data to? So I
could have done this instead:

require 'ostructmod'
class OpenSSL::PKey
  include OpenStructModule
end

???

Aaron out.

class SSL
  def talk
    puts 'hi'
  end
end

class MyWrapper
  def initialize(ssl_obj, key)
    @ssl_obj = ssl_obj
    @key = key
  end

  attr_accessor :ssl_obj, :key

  def method_missing(name, *args)
    @ssl_obj.send(name, *args)
  end
end

···

--
Posted via http://www.ruby-forum.com/\.

Aaron D. Gifford wrote in post #992841:

What do you do when you see a need to be able to attach some data to
an object instance for later use somewhere else in a body of code?
Lately I've resorted to this:

## Generic utility to allow one to attach data with a getter/setter to
## any instance of any object so long as there isn't a method name
## collision:
def attach_data(obj, name, data)
  getter = name.to_sym
  setter = (name.to_s + '=').to_sym
  raise "method name collision for #{obj.class} instance" if
obj.respond_to?(getter) || obj.respond_to?(setter)
  ## The 'value' local variable will remain in existence in the lambda
closures below:
  value = data
  meta = class << obj ; self ; end
  meta.send(:define_method, getter, lambda { value })
  meta.send(:define_method, setter, lambda {|val| value = val })

1) Note that you don't need to use send() there -- but maybe that's your
preferred closure? Once you have the singleton class, you can define
methods on it like this:

def attach_data(obj, name, data)
  getter = name.to_sym
  setter = (name.to_s + '=').to_sym

  raise "method name collision for #{obj.class} instance" if
    obj.respond_to?(getter) || obj.respond_to?(setter)

  #value = data

  singleton = class <<obj
    self
  end

  singleton.class_eval do
    define_method(getter) do
      data
    end

    define_method(setter) do |x|
      data = x
    end
  end

  #meta.send(:define_method, getter, lambda { value })
  #meta.send(:define_method, setter, lambda {|val| value = val })

  #value
  obj
end

2) I don't understand why you are creating a new local variable called
value and closing over that? data is also a local variable and you can
create a closure over that. Additional calls to attach_data() will
create new local variables--including data, so if you call attach_data()
twice data will not be shared.

···

--
Posted via http://www.ruby-forum.com/\.

Nice. Thank you!

Aaron out.

···

On Thu, Apr 14, 2011 at 3:11 PM, Kevin Mahler <kevin.mahler@yahoo.com> wrote:

You probably wouldn't want OpenStructModule even if it did exist because
you wouldn't want NoMethodError to be suppressed. Perhaps you're looking
for Object#extend.

module UserId
attr_accessor :user_id
end
thing = Object.new # whatever the thing is
thing.extend(UserId).user_id = 123456
p thing.user_id #=>123456

So using Kevin's suggestion:

def extend_accessor(obj, name)
  mod = Module.new
  mod.send(:public).send(:attr_accessor, name.to_sym)
  obj.extend(mod)
  obj
end

That's a cleaner way to attach data:

irb(main):001:0> def extend_accessor(obj, name)
irb(main):002:1> mod = Module.new
irb(main):003:1> mod.send(:public).send(:attr_accessor, name.to_sym)
irb(main):004:1> obj.extend(mod)
irb(main):005:1> obj
irb(main):006:1> end
=> nil
irb(main):007:0> a = "this is a string"
=> "this is a string"
irb(main):008:0> extend_accessor(a, :bar)
=> "this is a string"
irb(main):009:0> a.bar
=> nil
irb(main):010:0> a.bar = "hi there"
=> "hi there"

Now another question. I noticed that if I don't include the
send(:public) bit up there, that the send(:attr_accessor) message
delivered to the anonymous module ends up creating private accessor
methods, not public.

I find that puzzling:

irb(main):001:0> def extend_accessor(obj, name)
irb(main):002:1> mod = Module.new
irb(main):003:1> mod.send(:attr_accessor, name.to_sym)
irb(main):004:1> obj.extend(mod)
irb(main):005:1> obj
irb(main):006:1> end
=> nil
irb(main):007:0> a = "this is a string"
=> "this is a string"
irb(main):008:0> extend_accessor(a, :bar)
=> "this is a string"
irb(main):009:0> a.bar = "hi there"
NoMethodError: private method `bar=' called for "this is a string":String
  from (irb):9
  from /opt/local/bin/irb:12:in `<main>'
irb(main):010:0>

Any ideas why sending :attr_accessor to a module would create
accessors as private?

I would think that attr_accessor implies public, but I guess if by
default an anonymous module is in "private" mode, that might explain
it.

However, if that's the case, why does this work to create public methods:
def extend_method(obj, name, &block)
  mod = Module.new
  mod.send(:define_method, name, &block)
  obj.extend(mod)
  obj
end

Sending define_method (at least for me) to an anonymous Module seems
to default to "public" mode. This seems a tad inconsistent.

All this was on Ruby 1.9.2 on a FreeBSD box.

Aaron out.

···

On Thu, Apr 14, 2011 at 3:11 PM, Kevin Mahler <kevin.mahler@yahoo.com> wrote:

You probably wouldn't want OpenStructModule even if it did exist because
you wouldn't want NoMethodError to be suppressed. Perhaps you're looking
for Object#extend.

Aaron D. Gifford wrote in post #992887:

Now another question. I noticed that if I don't include the
send(:public) bit up there, that the send(:attr_accessor) message
delivered to the anonymous module ends up creating private accessor
methods, not public.

I find that puzzling:

Me too:

module Mod
  attr_accessor :data

  def do_stuff
  end
end

p Mod.public_instance_methods.grep(/^d/)

--output:--
[:data, :data=, :do_stuff]

Test = Module.new
Test.send(:attr_accessor, :data)
Test.send(:define_method, :do_stuff, Proc.new {puts 'hi'})
p Test.public_instance_methods.grep(/^d/)
p Test.private_instance_methods.grep(/^d/)

--output:--

···

--
Posted via http://www.ruby-forum.com/\.

Aaron D. Gifford wrote in post #992887:

def extend_accessor(obj, name)
   mod = Module.new
   mod.send(:public).send(:attr_accessor, name.to_sym)
   obj.extend(mod)
   obj
end

Or more compactly,

  def extend_accessor(obj, name)
    obj.extend Module.new { attr_accessor name }
    obj
  end

But that's an odd maneuver anyway. My original example extended an
object with a specified, named mixin. For ad hoc methods the singleton
class is more natural.

  def extend_accessor(obj, name)
    obj.singleton_class.module_eval { attr_accessor name }
    obj
  end

You probably found a bug with attr_accessor; it should produce public
methods in any context, I think. Nobody has encountered the bug
because Module.new { } and module_eval { } are commonly used to do
those things, and those behave correctly.

···

--
Posted via http://www.ruby-forum.com/\.

Thanks 7stud for the food for thought. And thanks again, Kevin, for
pointing me to some much easier ways to do what I want.

Aaron out.

Kevin Mahler wrote in post #992907:

  def extend_accessor(obj, name)
    obj.singleton_class.module_eval { attr_accessor name }
    obj
  end

Hey, now. obj.singleton_class is a class so how about using the synonym
class_eval():

      obj.singleton_class.class_eval { attr_accessor name }

···

--
Posted via http://www.ruby-forum.com/\.

I'd still like to muse a bit about the wisdom of doing this. The main
problem that can arise from the approach to modify an existing class
or instance belonging to another component is a possible name clash.
Even if you know you are safe with the current version, it may happen
that an updated version later will introduce the exact attribute that
you are adding on the fly now which will likely have bad effects.

I do not know the specifics of your use case but of course using
delegation does not have this name clash issue. Your solution might
be as easy as

SSLContext = Struct.new :key, :user_id

You could also add more functionality to this class but as said, that
totally depends on your use case. I tend to favor composition over
inheritance (or modification) nowadays since it is often more modular.
Granted, in some places you need more boilerplate code (e.g. reading
an attribute before invoking the method that you really want) but you
do not end stuck with tightly coupled (e.g. via inheritance) classes
that you cannot easily untangle.

Kind regards

robert

···

On Fri, Apr 15, 2011 at 8:35 AM, Aaron D. Gifford <astounding@gmail.com> wrote:

Thanks 7stud for the food for thought. And thanks again, Kevin, for
pointing me to some much easier ways to do what I want.

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

7stud -- wrote in post #993115:

Kevin Mahler wrote in post #992907:

  def extend_accessor(obj, name)
    obj.singleton_class.module_eval { attr_accessor name }
    obj
  end

Hey, now. obj.singleton_class is a class, so how about using the
synonym class_eval():

      obj.singleton_class.class_eval { attr_accessor name }

A class is a module, but a module is not a class.

  Class.new.is_a? Module #=>true
  Module.new.is_a? Class #=>false

Because module_eval and class_eval are aliases, class_eval can be
called on a module, a situation which is at worst wrong and at best
confusing. module_eval can never be wrong or confusing. Not a hard
choice.

···

--
Posted via http://www.ruby-forum.com/\.

Robert K. wrote in post #992956:

Robert K.,

Any ideas why attr_accessor() creates public methods when placed
directly in a module, but if you send() :attr_accessor to a module, it
creats private methods?

···

--
Posted via http://www.ruby-forum.com/\.

Kevin Mahler wrote in post #993207:

7stud -- wrote in post #993115:

Kevin Mahler wrote in post #992907:

  def extend_accessor(obj, name)
    obj.singleton_class.module_eval { attr_accessor name }
    obj
  end

Hey, now. obj.singleton_class is a class, so how about using the
synonym class_eval():

      obj.singleton_class.class_eval { attr_accessor name }

A class is a module, but a module is not a class.

Yes, of course.

  Class.new.is_a? Module #=>true
  Module.new.is_a? Class #=>false

Because module_eval and class_eval are aliases, class_eval can be
called on a module, a situation which is at worst wrong

It certainly isn't "wrong"--ruby allows it.

and at best
confusing. module_eval can never be wrong or confusing. Not a hard
choice.

I thought using module_eval was confusing in your code, and that was why
I suggested class_eval().

···

--
Posted via http://www.ruby-forum.com/\.

No. I wasn't even aware of the fact. :slight_smile:

Cheers

  robert

···

On 15.04.2011 20:40, 7stud -- wrote:

Robert K. wrote in post #992956:

Robert K.,

Any ideas why attr_accessor() creates public methods when placed
directly in a module, but if you send() :attr_accessor to a module, it
creats private methods?

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

7stud

Because module_eval and class_eval are aliases, class_eval can be
called on a module, a situation which is at worst wrong

It certainly isn't "wrong"--ruby allows it.

Wrong is not synonymous with illegal. One could easily argue that the
alias is a mistake. class_eval, as the name suggests, should have been
a more restricted version of module_eval. Allowing class_eval to have
a module receiver is wrong because a module is not a class. I
presented this as the "at worst" case--one end of the spectrum.

and at best
confusing. module_eval can never be wrong or confusing. Not a hard
choice.

I thought using module_eval was confusing in your code, and that was why
I suggested class_eval().

A class is a module.

···

--
Posted via http://www.ruby-forum.com/\.

I think the point is that classes and modules, while related in the way
you've described, are still different. You don't instantiate or subclass
modules, and you don't include classes. I too found module_eval less
intuitive than class_eval, knowing that the receiver is a class, even
knowing that a class "is a" module.

···

On Sat, Apr 16, 2011 at 6:53 PM, Kevin Mahler <kevin.mahler@yahoo.com>wrote:

>> and at best
>> confusing. module_eval can never be wrong or confusing. Not a hard
>> choice.
>
>I thought using module_eval was confusing in your code, and that was why
>I suggested class_eval().

A class is a module.