From: mhm26@... (matt) Date: 2004-06-17T10:48:24+09:00 Subject: Re: Delayed Instantiation - delayed.rb 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: _mr_bill_98@yahoo.com ### ### 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?, :object_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(¬ify) @@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! #>#meow> ["@_____meth_hash_____", "@index", "@santa"] nil ["@skip", "@object"] nil =end