Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.ruby > #2875 > unrolled thread
| Started by | "Aaron D. Gifford" <astounding@gmail.com> |
|---|---|
| First post | 2011-04-14 14:47 -0500 |
| Last post | 2011-04-14 19:01 -0500 |
| Articles | 17 — 5 participants |
Back to article view | Back to comp.lang.ruby
What do you do when you need to attach data to an object instance? "Aaron D. Gifford" <astounding@gmail.com> - 2011-04-14 14:47 -0500
Re: What do you do when you need to attach data to an object instance? Kevin Mahler <kevin.mahler@yahoo.com> - 2011-04-14 16:11 -0500
Re: What do you do when you need to attach data to an object instance? "Aaron D. Gifford" <astounding@gmail.com> - 2011-04-14 17:33 -0500
Re: What do you do when you need to attach data to an object instance? "Aaron D. Gifford" <astounding@gmail.com> - 2011-04-14 18:45 -0500
Re: What do you do when you need to attach data to an object instance? 7stud -- <bbxx789_05ss@yahoo.com> - 2011-04-14 19:50 -0500
Re: What do you do when you need to attach data to an object instance? Kevin Mahler <kevin.mahler@yahoo.com> - 2011-04-14 20:07 -0500
Re: What do you do when you need to attach data to an object instance? "Aaron D. Gifford" <astounding@gmail.com> - 2011-04-15 01:35 -0500
Re: What do you do when you need to attach data to an object instance? Robert Klemme <shortcutter@googlemail.com> - 2011-04-15 03:46 -0500
Re: What do you do when you need to attach data to an object instance? 7stud -- <bbxx789_05ss@yahoo.com> - 2011-04-15 13:40 -0500
Re: What do you do when you need to attach data to an object instance? Robert Klemme <shortcutter@googlemail.com> - 2011-04-15 21:00 +0200
Re: What do you do when you need to attach data to an object instance? 7stud -- <bbxx789_05ss@yahoo.com> - 2011-04-15 18:10 -0500
Re: What do you do when you need to attach data to an object instance? Kevin Mahler <kevin.mahler@yahoo.com> - 2011-04-16 10:32 -0500
Re: What do you do when you need to attach data to an object instance? 7stud -- <bbxx789_05ss@yahoo.com> - 2011-04-16 12:14 -0500
Re: What do you do when you need to attach data to an object instance? Kevin Mahler <kevin.mahler@yahoo.com> - 2011-04-16 12:53 -0500
Re: What do you do when you need to attach data to an object instance? Adam Prescott <adam@aprescott.com> - 2011-04-18 07:10 -0500
Re: What do you do when you need to attach data to an object instance? 7stud -- <bbxx789_05ss@yahoo.com> - 2011-04-14 18:15 -0500
Re: What do you do when you need to attach data to an object instance? 7stud -- <bbxx789_05ss@yahoo.com> - 2011-04-14 19:01 -0500
| From | "Aaron D. Gifford" <astounding@gmail.com> |
|---|---|
| Date | 2011-04-14 14:47 -0500 |
| Subject | What do you do when you need to attach data to an object instance? |
| Message-ID | <BANLkTinVjc-Lv4LYB6ODXCRDnrDOQj+2uA@mail.gmail.com> |
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.
[toc] | [next] | [standalone]
| From | Kevin Mahler <kevin.mahler@yahoo.com> |
|---|---|
| Date | 2011-04-14 16:11 -0500 |
| Message-ID | <6fce5b6386258c51794d0b55b63e1ed6@ruby-forum.com> |
| In reply to | #2875 |
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/.
[toc] | [prev] | [next] | [standalone]
| From | "Aaron D. Gifford" <astounding@gmail.com> |
|---|---|
| Date | 2011-04-14 17:33 -0500 |
| Message-ID | <BANLkTi=ih21apYDBBBgDpVkfJcRXR6CmJg@mail.gmail.com> |
| In reply to | #2879 |
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 Nice. Thank you! Aaron out.
[toc] | [prev] | [next] | [standalone]
| From | "Aaron D. Gifford" <astounding@gmail.com> |
|---|---|
| Date | 2011-04-14 18:45 -0500 |
| Message-ID | <BANLkTinGsu4_yM51dETQBgrN4c4enz1FaA@mail.gmail.com> |
| In reply to | #2886 |
> 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. 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.
[toc] | [prev] | [next] | [standalone]
| From | 7stud -- <bbxx789_05ss@yahoo.com> |
|---|---|
| Date | 2011-04-14 19:50 -0500 |
| Message-ID | <4538531e35c3965ef6d57ef9d76e5d88@ruby-forum.com> |
| In reply to | #2893 |
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/.
[toc] | [prev] | [next] | [standalone]
| From | Kevin Mahler <kevin.mahler@yahoo.com> |
|---|---|
| Date | 2011-04-14 20:07 -0500 |
| Message-ID | <ebf5e68c050f1d14ab1e4188d6d392c2@ruby-forum.com> |
| In reply to | #2893 |
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/.
[toc] | [prev] | [next] | [standalone]
| From | "Aaron D. Gifford" <astounding@gmail.com> |
|---|---|
| Date | 2011-04-15 01:35 -0500 |
| Message-ID | <BANLkTinhTOVYDq+86Ps0_YcbLWBTJE+1vA@mail.gmail.com> |
| In reply to | #2906 |
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.
[toc] | [prev] | [next] | [standalone]
| From | Robert Klemme <shortcutter@googlemail.com> |
|---|---|
| Date | 2011-04-15 03:46 -0500 |
| Message-ID | <BANLkTinXMvdb8b9kf5AadH3=R2vyW-qtXA@mail.gmail.com> |
| In reply to | #2919 |
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. 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 -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/
[toc] | [prev] | [next] | [standalone]
| From | 7stud -- <bbxx789_05ss@yahoo.com> |
|---|---|
| Date | 2011-04-15 13:40 -0500 |
| Message-ID | <001494f476e4e3708d5c9e2945f18cf2@ruby-forum.com> |
| In reply to | #2924 |
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/.
[toc] | [prev] | [next] | [standalone]
| From | Robert Klemme <shortcutter@googlemail.com> |
|---|---|
| Date | 2011-04-15 21:00 +0200 |
| Subject | Re: What do you do when you need to attach data to an object instance? |
| Message-ID | <90riufFnroU1@mid.individual.net> |
| In reply to | #2968 |
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? No. I wasn't even aware of the fact. :-) Cheers robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/
[toc] | [prev] | [next] | [standalone]
| From | 7stud -- <bbxx789_05ss@yahoo.com> |
|---|---|
| Date | 2011-04-15 18:10 -0500 |
| Message-ID | <d96516e82c6c2b2d395098f86cf62bc2@ruby-forum.com> |
| In reply to | #2906 |
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/.
[toc] | [prev] | [next] | [standalone]
| From | Kevin Mahler <kevin.mahler@yahoo.com> |
|---|---|
| Date | 2011-04-16 10:32 -0500 |
| Message-ID | <41c1bcee810455547ca7d7844c1113d2@ruby-forum.com> |
| In reply to | #2983 |
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/.
[toc] | [prev] | [next] | [standalone]
| From | 7stud -- <bbxx789_05ss@yahoo.com> |
|---|---|
| Date | 2011-04-16 12:14 -0500 |
| Message-ID | <7b5e8d93ddba3dc3daa51fd319e3d800@ruby-forum.com> |
| In reply to | #3010 |
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/.
[toc] | [prev] | [next] | [standalone]
| From | Kevin Mahler <kevin.mahler@yahoo.com> |
|---|---|
| Date | 2011-04-16 12:53 -0500 |
| Message-ID | <1d1ebc367f9cde56225d927c8cbf0b4b@ruby-forum.com> |
| In reply to | #3015 |
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/.
[toc] | [prev] | [next] | [standalone]
| From | Adam Prescott <adam@aprescott.com> |
|---|---|
| Date | 2011-04-18 07:10 -0500 |
| Message-ID | <BANLkTikxZAF_9+hVrh12+NFoQemKHrsd+w@mail.gmail.com> |
| In reply to | #3016 |
[Note: parts of this message were removed to make it a legal post.] 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. 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.
[toc] | [prev] | [next] | [standalone]
| From | 7stud -- <bbxx789_05ss@yahoo.com> |
|---|---|
| Date | 2011-04-14 18:15 -0500 |
| Message-ID | <24ba002d74b2765157d36054768e8e73@ruby-forum.com> |
| In reply to | #2875 |
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/.
[toc] | [prev] | [next] | [standalone]
| From | 7stud -- <bbxx789_05ss@yahoo.com> |
|---|---|
| Date | 2011-04-14 19:01 -0500 |
| Message-ID | <8de4b1a0d2a83352a656f12fff91d581@ruby-forum.com> |
| In reply to | #2875 |
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/.
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.ruby
csiph-web