# = preinitialize.rb # # == Copyright (c) 2005 Thomas Sawyer # # Ruby License # # This module is free software. You may use, modify, and/or redistribute this # software under the same terms as Ruby. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. # # == Author(s) # # * Thomas Sawyer # Author:: Thomas Sawyer # Copyright:: Copyright (c) 2005 Thomas Sawyer # License:: Ruby License require 'facets/core/kernel/object_class' # = Preinitialize # # This is an object preinitialize system, which provides # an elegant way to initialize an object allowing the # class to provide additional default structure to an # object prior to calling initialize. # # In effect it does two things after allocating the object # but prior to initializing it. # # First, it calls the class method #default_instance_variables, # which returns a hash and by default returns the hash # stored in @default_instance_variables. It uses this to # pre-define instance variables. # # Then it goes to the top of the class hierarchy and works # it's way down calling #preinitialize if defined. # WARNING! It is rather useless to use super # inside the any preinitialize hook. # # module M # def preinitialize # @a = 23 # end # end # # class X # include M # def a ; @a ; end # end # # x = X.new # x.a #=> 23 # # If neded the original new method has been aliased, albeit # postinitialize_new is probably a bit of a misnomer. #-- # class Module # # def default_instance_variables(complete=false) # @default_instance_variables ||= {} # unless complete # return @default_instance_variables # else # parent = ancestors[1] # if parent # return @default_instance_variables.merge(parent.default_instance_variables) # else # return @default_instance_variables # end # end # end # # end #++ class Class alias_method :postinitialize_new, :new def new(*args, &blk) o = allocate #if respond_to?(:default_instance_variables) # default_instance_variables.each{|k,v| o.instance_variable_set( "@#{k.to_s.gsub(/\W$/,'')}",v )} #end a = ancestors until a.empty? m = a.pop #if m.private_instance_methods.include?('preinitialize') or m.public_instance_methods.include?('preinitialize') if m.method_defined?('preinitialize') or m.private_method_defined?('preinitialize') im = instance_method('preinitialize') im.arity == 0 ? im.bind(o).call : im.bind(o).call(*args, &blk) end end o.__send__(:initialize, *args, &blk) if o.object_class.private_method_defined?(:initialize) o end end # _____ _ # |_ _|__ ___| |_ # | |/ _ \/ __| __| # | | __/\__ \ |_ # |_|\___||___/\__| # =begin test require 'test/unit' class TC_Preinitalize < Test::Unit::TestCase module M def preinitialize @a = 10 end end class X include M def a ; @a ; end end class Y < X def initialize super end end def test_01 x = X.new assert_equal( 10, x.a ) end def test_01 y = Y.new assert_equal( 10, y.a ) end end =end