Delayed Instantiation - delayed.rb

Discussion in 'Ruby' started by matt, Jun 16, 2004.

  1. matt

    matt Guest

    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:
    ####
    #### 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
    matt, Jun 16, 2004
    #1
    1. Advertising

  2. matt

    matt Guest

    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: ###
    ### 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
    matt, Jun 17, 2004
    #2
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Guru Prasad

    unloading assemblies - delayed ?

    Guru Prasad, Jul 16, 2003, in forum: ASP .Net
    Replies:
    2
    Views:
    409
    Guru Prasad
    Jul 21, 2003
  2. e

    delayed page requests

    e, Oct 30, 2003, in forum: ASP .Net
    Replies:
    0
    Views:
    299
  3. George

    delayed session creation

    George, Jun 17, 2005, in forum: ASP .Net
    Replies:
    5
    Views:
    514
    Juan T. Llibre
    Jun 18, 2005
  4. Replies:
    1
    Views:
    560
    Salt_Peter
    Dec 25, 2006
  5. matt
    Replies:
    8
    Views:
    214
    Mauricio Fernández
    Jun 19, 2004
Loading...

Share This Page