How to avoid duplicate object creations?

I have a program that will create a lot of objects, mainly of type
SomeDocumentation::Host and SomeDocumentation::Role.

Since the Role objects typically create a lot of Host objects, and the
other way around, I tried to be clever, and look up objects that
already exists with the same value of “name”, since the initialize
reads a bit from disk.

I’ve tried tried to solve it with the following code snippet:

def initialize(name)

ObjectSpace.each_object(self.class) do |anObject|
  return anObject if anObject.name == name
end

<do the real initialization, since we do not have an object with
the same .name attribute/method/whatever yet>

end

…but when it is included in the Host initialize method, I end up
with a lot of empty Host objects for the second (third, fourth, and so
on) Role object when doing this from the main script:

roles.uniq.each do |role|
aRole = SomeDocumentation::Role.new(role)
puts aRole.documentation
end

Does anyone see what I’m doing wrong?

···


Sandbeck Mathisen - http://fnord.no
Trust the Computer, the Computer is your Friend

— Stig Sandbeck Mathisen ssm@fnord.no wrote:

I have a program that will create a lot of objects, mainly of type
SomeDocumentation::Host and SomeDocumentation::Role.

Since the Role objects typically create a lot of Host objects, and the
other way around, I tried to be clever, and look up objects that
already exists with the same value of “name”, since the initialize
reads a bit from disk.

I’ve tried tried to solve it with the following code snippet:

def initialize(name)

ObjectSpace.each_object(self.class) do |anObject|
  return anObject if anObject.name == name
end

<do the real initialization, since we do not have an object with
the same .name attribute/method/whatever yet>

end

You might be able to use the “freeze” method from the Object class.

– Thomas Adam

···

=====
“The Linux Weekend Mechanic” – http://linuxgazette.net
“TAG Editor” – http://linuxgazette.net

“ We’ll just save up your sins, Thomas, and punish
you for all of them at once when you get better. The
experience will probably kill you. :)”

– Benjamin A. Okopnik (Linux Gazette Technical Editor)


Yahoo! Messenger - Communicate instantly…“Ping”
your friends today! Download Messenger Now
http://uk.messenger.yahoo.com/download/index.html

“Stig Sandbeck Mathisen” ssm@fnord.no schrieb im Newsbeitrag
news:90lekrj4kn4.fsf@talisker.nsc.no…

I have a program that will create a lot of objects, mainly of type
SomeDocumentation::Host and SomeDocumentation::Role.

Since the Role objects typically create a lot of Host objects, and the
other way around, I tried to be clever, and look up objects that
already exists with the same value of “name”, since the initialize
reads a bit from disk.

I’ve tried tried to solve it with the following code snippet:

def initialize(name)

ObjectSpace.each_object(self.class) do |anObject|
  return anObject if anObject.name == name
end

<do the real initialization, since we do not have an object with
the same .name attribute/method/whatever yet>

end

…but when it is included in the Host initialize method, I end up
with a lot of empty Host objects for the second (third, fourth, and so
on) Role object when doing this from the main script:

roles.uniq.each do |role|
aRole = SomeDocumentation::Role.new(role)
puts aRole.documentation
end

Does anyone see what I’m doing wrong?


Sandbeck Mathisen - http://fnord.no
Trust the Computer, the Computer is your Friend

module SomeDocumentation
class Named
attr :name

def initialize(name)
  @name = name
end

end

class Host < Named
INSTANCES = Hash.new {|h,name| h[name] = self.new(name) }
end

class Role < Named
INSTANCES = Hash.new {|h,name| h[name] = self.new(name) }
end
end

SomeDocumentation::Host::INSTANCES[“foo”]
SomeDocumentation::Role::INSTANCES[“foo”]

Regards

robert

“Robert Klemme” bob.news@gmx.net writes:

module SomeDocumentation
class Named
attr :name

def initialize(name)
  @name = name
end

end

class Host < Named
INSTANCES = Hash.new {|h,name| h[name] = self.new(name) }
end

class Role < Named
INSTANCES = Hash.new {|h,name| h[name] = self.new(name) }
end
end

SomeDocumentation::Host::INSTANCES[“foo”]
SomeDocumentation::Role::INSTANCES[“foo”]

Brilliant, thank you. I think I need to study it for a while, to see
exactly what it does in combination with my code, though. :slight_smile:

In addition to this, I also got a message outside the newsgroup
telling me to use a @@instances hash in the classes, which seems to be
about the same solution.

···


Sandbeck Mathisen - http://fnord.no
Trust the Computer, the Computer is your Friend

“Stig Sandbeck Mathisen” ssm@fnord.no schrieb im Newsbeitrag
news:90lekrj4kn4.fsf@talisker.nsc.no…

I have a program that will create a lot of objects, mainly of type
SomeDocumentation::Host and SomeDocumentation::Role.

Since the Role objects typically create a lot of Host objects, and the
other way around, I tried to be clever, and look up objects that already
exists with the same value of “name”, since the initialize reads a bit
from disk.

check out

http://www.codeforpeople.com/lib/ruby/multiton/

a ‘multiton’ is a ‘singleton’ based on the args passed to the ctor.

it may help you.

-a

···

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
URL :: Solar-Terrestrial Physics Data | NCEI
TRY :: for l in ruby perl;do $l -e “print "\x3a\x2d\x29\x0a"”;done
===============================================================================

Wrote Stig Sandbeck Mathisen ssm@fnord.no, on Wed, Mar 24, 2004 at 01:59:30AM +0900:

“Robert Klemme” bob.news@gmx.net writes:
Brilliant, thank you. I think I need to study it for a while, to see
exactly what it does in combination with my code, though. :slight_smile:

Notice that these methods don’t modify YouClass.new() to return other
than a new object, they basically implement an alternate method to
get objects, one that may or may not create a new object.

This works great, unless you don’t control over the call to new.

Is there a variant of this trick that would work when unmarshalling
or un-yamling a lot of objects, where you would want there not to be
new instances of ocbject created which are equivalent to existing ones?

Just curious, maybe I’ll need to, some day.

Sam

···


Sam Roberts sroberts@certicom.com

“Stig Sandbeck Mathisen” ssm@fnord.no schrieb im Newsbeitrag
news:90lad274i50.fsf@talisker.nsc.no…

“Robert Klemme” bob.news@gmx.net writes:

module SomeDocumentation
class Named
attr :name

def initialize(name)
  @name = name
end

end

class Host < Named
INSTANCES = Hash.new {|h,name| h[name] = self.new(name) }
end

class Role < Named
INSTANCES = Hash.new {|h,name| h[name] = self.new(name) }
end
end

SomeDocumentation::Host::INSTANCES[“foo”]
SomeDocumentation::Role::INSTANCES[“foo”]

Brilliant, thank you. I think I need to study it for a while, to see
exactly what it does in combination with my code, though. :slight_smile:

In addition to this, I also got a message outside the newsgroup
telling me to use a @@instances hash in the classes, which seems to be
about the same solution.

I would not choose class variables in this case. First, you want scoping
by class and not through a hierarchy. If Host and Role somehow derive
from each other then you have to choose a more comples hash key (i.e. pair
of class and name) to ensure that you don’t mix up stuff.

The other is a bit more personal: I generally try to prevent the usage of
class variables since their usage can lead to unexpected results as well
as subtle errors. One thing is that order matters in which a class var is
referenced. Try this:

#!/usr/bin/ruby

def t(cl)
p [cl, cl.new.test]
end

------------------

class Base1
(@@foo ||= ) << “base1”

def test; @@foo; end
end

class Derived1 < Base1
(@@foo ||= ) << “derived1”
end

------------------

class Base2
def test; @@foo; end
end

class Derived2 < Base2
(@@foo ||= ) << “derived2”
end

class Base2
(@@foo ||= ) << “base2”
end

------------------

class Base3
end

class Derived3 < Base3
(@@foo ||= ) << “derived3”
def test; @@foo; end
end

class Base3
(@@foo ||= ) << “base3”
def test; @@foo; end
end

------------------

t Base1
t Derived1
t Base2
t Derived2
t Base3
t Derived3

Regards

robert

“Sam Roberts” sroberts@certicom.com schrieb im Newsbeitrag
news:20040323190124.GB10547@certicom.com

Wrote Stig Sandbeck Mathisen ssm@fnord.no, on Wed, Mar 24, 2004 at
01:59:30AM +0900:

“Robert Klemme” bob.news@gmx.net writes:
Brilliant, thank you. I think I need to study it for a while, to see
exactly what it does in combination with my code, though. :slight_smile:

Notice that these methods don’t modify YouClass.new() to return other
than a new object, they basically implement an alternate method to
get objects, one that may or may not create a new object.

This works great, unless you don’t control over the call to new.

Is there a variant of this trick that would work when unmarshalling
or un-yamling a lot of objects, where you would want there not to be
new instances of ocbject created which are equivalent to existing ones?

AFAIK there is no hook in Marshal and Yaml that would allow to provide your
own object creation. Someone correct me if I’m wrong here.

However, the good news is that both don’t instantiate an instance multiple
times that is referenced from several places, i.e.,

str = “foo”
Marshal.load( Marshal.dump([foo,foo]))

creates only two new instances (an array and one copy of the string).

Maybe you’ll have to do some post processing on what you get back in order
to replace instances. However, that will be tricky if there are multiple
references to a single instance. Not worth the effort I’d say.

Regards

robert

Robert Klemme wrote:

AFAIK there is no hook in Marshal and Yaml that would allow to provide your
own object creation. Someone correct me if I’m wrong here.

For Marshalled objects, you can define a YourClass._load and
YourClass#_dump (note the first is a class method). The former can look
at the marshalled data as a string and decide what object to create. The
latter is what generates that string. See the Pickaxe for details.

In Yaml, it’s possible too, if you don’t mind defining a custom
ruby-type, in which case you are allowed to construct an object from the
yaml data. I’m not sure if there is a hook to do this without defining a
custom type. For examples, see yaml/rubytypes.rb in your ruby lib. The
value returned from the block you give to “add_ruby_type” is the value
that used for the object.