Delayed Instantiation - delayed.rb

M

matt

Here's some code I wrote to delay object instantiation until an
object's methods are needed. If there's anything fooey about it, or
if there's a better version out there, please let me know :)



--begin--
#########################################################################
#########################################################################
#### delayed.rb: Thunks for Object Insantiation
####
#### Author: Matthew Maycock
####
#### Email: (e-mail address removed)
####
#### Date: June 16th, 2004 AD!
####
####
####
#### Purpose:
####
#### Delay instantiation of objects until needed.
####
#### Overhead given by an extra indirection of dispatch
####
#### Use at your own risk!
####
####
####
#### License:
####
#### The least restrictive license applicable to this software
####
#### given that it was written in ruby and uses the ruby library.
####
#### I don't really know that much about such things, so if there
####
#### isn't anything in the way, consider this released under the
####
#### public domain, free for all!
####
#########################################################################
#########################################################################



# Delay creation of an object until some method tries to use it.
class DelayedInstance < Object
def initialize(klass, *args)
@object = Object.new
ivars = Object.instance_method:)instance_variables)
ieval = Object.instance_method:)instance_eval)
iclss = Object.instance_method:)class)
inspt = Object.instance_method:)inspect)

switched = @delayed_switched = false


switch = @delayed_switch = Proc.new {
raise "Already switched!" if switched
switched = @delayed_switched = true
@object = klass.send:)new, *args)
}

delayed_self = self
@object.instance_eval {
@delayed_parent = delayed_self
@delayed_switch = switch
@delayed_added = false
@delayed_eval = ieval
}

[:==, :===, :=~, :__id__, :__send__, :class, :clone, :display,
:dup, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :id,
:inspect, :instance_eval, :instance_of, :instance_variable_get,
:instance_variable_set, :instance_variables, :is_a?, :kind_of?,
:method, :methods, :new, :nil?, :eek:bject_id, :private_methods,
:protected_methods, :public_methods, :remove_instance_variable,
:respond_to?, :send, :singleton_method_added,
:singleton_method_removed, :singleton_method_undefined,
:singleton_methods, :taint,
:tainted?, :to_a, :to_s, :type, :untaint].each {|sym|
eval <<-END_EVAL
def @object.#{sym.to_s}(*args, &block)
return super(*args, &block) unless @delayed_added
parent = @delayed_parent
@delayed_switch[]
obj = @delayed_eval.bind(parent).call { @object }
obj.send(#{sym.inspect}, *args, &block)
end
END_EVAL
}

Object.instance_method:)instance_eval).bind(@object).call
{@delayed_added = true}
end

def method_missing(sym, *args, &block)
@delayed_switch[] unless @delayed_switched
@object.send(sym, *args, &block)
end
end

[:==, :===, :=~, :__id__, :__send__, :class, :clone, :display, :dup,
:eql?, :equal?, :extend, :freeze, :frozen?, :hash, :id,
:inspect, :instance_eval, :instance_of, :instance_variable_get,
:instance_variable_set, :instance_variables, :is_a?, :kind_of?,
:method, :methods, :new, :nil?, :eek:bject_id, :private_methods,
:protected_methods, :public_methods, :remove_instance_variable,
:respond_to?, :send, :singleton_method_added,
:singleton_method_removed, :singleton_method_undefined,
:singleton_methods, :taint,
:tainted?, :to_a, :to_s, :type, :untaint].each {|sym|
DelayedInstance.module_eval <<-END_EVAL
def #{sym.to_s}(*args, &block)
@object.send(#{sym.inspect}, *args, &block)
end
END_EVAL
}



if __FILE__ == $0 then

class Test
def initialize(index)
$stdout.puts "Something that takes a thousand hours..."
@index = index
end

def display
$stdout.puts @index
end
end

data = (1..10).map {|i| DelayedInstance.new(Test, i)}
data.each {|d| d.display}

$stdout.puts
foo = DelayedInstance.new(Test, 1000)
foo.instance_eval {
$stdout.puts "Instance Eval!"
@santa = 100
}

$stdout.puts foo.instance_variables.inspect
$stdout.puts foo.instance_eval {@santa}
end

=begin
### ruby -v: ruby 1.8.1 (2003-12-25) [i386-mswin32]
# OUTPUT
Something that takes a thousand hours...
1
Something that takes a thousand hours...
2
Something that takes a thousand hours...
3
Something that takes a thousand hours...
4
Something that takes a thousand hours...
5
Something that takes a thousand hours...
6
Something that takes a thousand hours...
7
Something that takes a thousand hours...
8
Something that takes a thousand hours...
9
Something that takes a thousand hours...
10

Something that takes a thousand hours...
Instance Eval!
["@index", "@santa"]
100
=end
 
M

matt

Sorry for the double post earlier (google issues) and the poorly
formatted code. The code is available at
http://www.cs.drexel.edu/~ummaycoc/delayed.rb

I have run into a problem as I developed this a little further:

on line 97, which is the commented on in the following context

def self.singleton_method_added(sym)
return if @skip

mget = Object.instance_method:)method).bind(self)
m = mget.call(sym).unbind
@object.instance_eval {
@_____meth_hash_____[sym] = m
}

code = <<-END_EVAL
def @object.#{sym}(*a, &b)
m = @_____meth_hash_____[#{sym.inspect}]
------------> #m.bind(self).call(*a, &b) unless m.nil?
end

class << self
undef_method(#{sym.inspect})
end
END_EVAL
$stdout.puts code # :-(
eval code # :-(
end

I need to make a singleton method out of an object. I can't just
redirect to the parent when a singleton is made, as the parent doesn't
have the same state as the child - nor should it. My current issue is
just finding a way to change what a singleton method is bound to
binding wise for looking up self->instance_variables... (not doing so
lets them be set/used from the DelayedInstance object instead of the
@object object).

I'd most like a way to do this without just fixing the given line
(line 97 in the entire file - which is given below and at the
aforementioned website) - I dislike the idea of having to have the
@_____meth_hash_____ object, but I think that if anything will work,
it's prolly gonna be some hack similar to that. Any help is
appreciated...

-begin

###################################################################
###################################################################
### ###
### delayed.rb: Thunks for Object Insantiation ###
### Author: Matthew Maycock ###
### Email: (e-mail address removed) ###
### Date: June 16th, 2004 AD! ###
### ###
### Purpose: ###
### Delay instantiation of objects until needed! ###
### Overhead given by an extra indirection of dispatch! ###
### Use at your own risk! ###
### ###
### License: ###
### The least restrictive license that's applicable to this ###
### software given that it was written in ruby and uses the ###
### ruby library. I don't really know that much about such ###
### things, so if there isn't anything in the way, consider ###
### this released under the public domain, free for all! ###
### ###
### Problems: ###
### eval redefining __id__ and __send__ produces warnings ###
### for now, they are not redefined. I don't know how that ###
### will play out for everything else, however. ###
### ###
### problem with singleton methods and how to pass them on ###
### ###
###################################################################
###################################################################

require 'thread'

# Delay creation of an object until some method tries to use it.
class DelayedInstance < Object
@@inst_rejects = [:__id__, :__send__].freeze
@@clss_rejects = [:__id__, :__send__,
:singleton_method_added].freeze

@@symbols = ([
:==, :===, :=~, :__id__, :__send__, :class, :clone, :display,
:dup, :eql?, :equal?, :extend, :freeze, :frozen?, :hash,:id,
:inspect, :instance_eval, :instance_of, :instance_variable_get,
:instance_variable_set, :instance_variables, :is_a?, :kind_of?,
:method, :methods, :new, :nil?, :eek:bject_id, :private_methods,
:protected_methods, :public_methods, :remove_instance_variable,
:respond_to?, :send, :singleton_method_added,
:singleton_method_removed, :singleton_method_undefined,
:singleton_methods, :taint, :tainted?, :to_a, :to_s, :type,
:untaint
]).freeze


def DelayedInstance.symbols(sym = :class)
@@symbols - (sym == :class ? @@clss_rejects : @@inst_rejects)
end

def DelayedInstance.notify(&notify)
@@notify = notify
end
@@notify = nil

def initialize(klass, *kargs, &kblock)
@object = Object.new
@skip = false
ieval = Object.instance_method:)instance_eval)
self_id = self.id
switched = false
mutex = Mutex.new

switch = Proc.new {
mutex.synchronize {
next if switched
@@notify[self_id] unless @@notify.nil?
switched = true
@object = klass.send:)new, *kargs, &kblock)

@skip = true
@object.instance_eval {
@_____meth_hash_____ = Hash.new
}
def self.method_missing(s, *a, &b)
@object.send(s, *a, &b)
end

def self.singleton_method_added(sym)
return if @skip

mget = Object.instance_method:)method).bind(self)
m = mget.call(sym).unbind
@object.instance_eval {
@_____meth_hash_____[sym] = m
}

code = <<-END_EVAL
def @object.#{sym}(*a, &b)
m = @_____meth_hash_____[#{sym.inspect}]
#m.bind(self).call(*a, &b) unless m.nil?
end

class << self
undef_method(#{sym.inspect})
end
END_EVAL
$stdout.puts code # :-(
eval code # :-(
end
@skip = false
}
}

delayed_self = self
@object.instance_eval {
@delayed_switch = switch
@delayed_added = false
@delayed_eval = ieval.bind(delayed_self)
}

DelayedInstance.symbols:)instance).each {|sym|
eval <<-END_EVAL
def @object.#{sym.to_s}(*args, &block)
return super(*args, &block) unless @delayed_added
@delayed_switch[]
@delayed_eval.call {
@object
}.send(#{sym.inspect}, *args, &block)
end
END_EVAL
}

Object.instance_method:)instance_eval).bind(@object).call {
@delayed_added = true
}
end

def method_missing(sym, *args, &block)
@object.id
method_missing(sym, *args, &block)
end

def singleton_method_added(sym)
return if @skip
@object.id
m = Object.instance_method:)method).bind(self).call:)singleton_method_added)
res = m.call(sym)
end
end

DelayedInstance.symbols:)class).each {|sym|
DelayedInstance.module_eval <<-END_EVAL
def #{sym.to_s}(*args, &block)
@object.send(#{sym.inspect}, *args, &block)
end
END_EVAL
}



if __FILE__ == $0 then

DelayedInstance.notify {|obj_id|
$stdout.puts "Switching with object #{obj_id}"
}

class Test
def initialize(index)
$stdout.puts "Something that takes a thousand hours..."
@index = index
end

def display_data
$stdout.puts @index
end
end

$stdout.puts "Building array..."
data = (1..4).map {|i| DelayedInstance.new(Test, i)}

i = data[0]

$stdout.puts "Displaying Items..."
data.each {|d|
d.display_data
$stdout.puts
}

$stdout.puts
foo = DelayedInstance.new(Test, 1000)
foo.instance_eval {
$stdout.puts "Instance Eval!"
@santa = 100
}
$stdout.puts

ivars = Object.instance_method:)instance_variables)
ieval = Object.instance_method:)instance_eval)

$stdout.puts
$stdout.puts "Variables!"
$stdout.puts foo.instance_variables.inspect
$stdout.puts foo.instance_eval {@santa}
$stdout.puts
$stdout.puts ivars.bind(foo).call.inspect
$stdout.puts ieval.bind(foo).call {@santa}

$stdout.puts
$stdout.puts "Define Singleton!"
def foo.meow
self.class.to_s
@gagaga = 4
end

$stdout.puts
$stdout.puts "Singleton, then Variables!"
$stdout.puts foo.meow # UnboundMethod#inspect for now.

$stdout.puts foo.instance_variables.inspect
$stdout.puts foo.instance_eval {@gagaga}
$stdout.puts
$stdout.puts ivars.bind(foo).call.inspect
$stdout.puts ieval.bind(foo).call {@gagaga}

end


=begin
###################################################################
### OUTPUT
Building array...
Displaying Items...
Switching with object 22220152
Something that takes a thousand hours...
1

Switching with object 22193320
Something that takes a thousand hours...
2

Switching with object 22656588
Something that takes a thousand hours...
3

Switching with object 22629756
Something that takes a thousand hours...
4


Switching with object 22601604
Something that takes a thousand hours...
Instance Eval!


Variables!
["@_____meth_hash_____", "@index", "@santa"]
100

["@skip", "@object"]
nil

Define Singleton!
def @object.meow(*a, &b)
m = @_____meth_hash_____[:meow]
#m.bind(self).call(*a, &b) unless m.nil?
end

class << self
undef_method:)meow)
end

Singleton, then Variables!
#<UnboundMethod: #<Class:#<DelayedInstance:0x2b1bf98>>#meow>
["@_____meth_hash_____", "@index", "@santa"]
nil

["@skip", "@object"]
nil
=end
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,021
Latest member
AkilahJaim

Latest Threads

Top