From: "Chr. Rippel" Date: 2002-01-29T08:59:07+09:00 Subject: (the diff) Re: TypeError verus NoMethodError This is a multi-part message in MIME format. ------=_NextPart_000_0180_01C1A85D.44EF4180 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit "Yukihiro Matsumoto" wrote in, .... > |Ah, next time, send me a "patch", preferably in unified diff (diff -u). > |It's TypeError because they are *special (immediate) values*. I don't > |think Fixnums and Symbols are singletons. But, hmm, maybe it's good > |to remove dup and close from those happened to be singletons. I don't think of Fixnum and Symbols as singleton either however the fact is that disabling a method from a superclass is uniformly done undef_method raising a NoMethodError exception (which previously was a TypeError after all). /OT It would be kind of neat to introduce singleton types for Fixnums and Symbols without compromising ``equal?" equality (see for example Bignum) . > And also could you tell me how your fix different from the original. > It's kinda hard for me to guess your intention from the source. Sorry, here is the diff against the latest cvs-version (1.12) . I added some comments in the code, cleaned up the documentation and added examples clarifying (well there is always hope;-) the code changes. ---- If you were talking about the ``fix of the revision'' I stupidly removed the class methods #dup, #clone (which needs to be modified) instead of the instance #dup, and #clone. ---- The main problem with the current cvs-version is that the #inherited hook is modified without taking into account that the ``original_inherited'' hook might have been overridden already - a silly example would be $num_klass = BasicNumber def Object.inherited(sub_klass) $num_klass+=1 end After the modification klass.inherited(sub_klass) first calls ``klass.original_inherited(sub_klass)'' and then turns sub_klass into a singleton class - i.e. the self modifying singleton method sub_klass#instance is declared. To refactor the declaration of sub_klass#instance the idiom define_method (prog_which_is_the_body_of_#instance) is used (I find this very useful). Also the hook #_wait() was renamed to #_instanciate?() since I gave it a return value nil or ``the instance'' indicating the (un)successful creation during the waiting period. I believe that these modifications are useful and reasonable, however the exceptions preventing the inclusion of Singleton in another Module etc. are possibly over the top. Anyway I sleep over it and everything still looks okay tonight and I am praying hard I'll never see a single singleton in my live;-) /Christoph ------=_NextPart_000_0180_01C1A85D.44EF4180 Content-Type: application/octet-stream; name="diff.dat" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="diff.dat" --- singleton.rb.old Mon Jan 28 23:14:07 2002=0A= +++ singleton.rb Sun Jan 27 22:10:18 2002=0A= @@ -1,130 +1,203 @@=0A= -# The Singleton module implements the Singleton pattern - i.e.=0A= +# The Singleton module implements the Singleton pattern.=0A= #=0A= -# class Klass=0A= -# include Singleton=0A= -# # ...=0A= -# end=0A= +# Usage:=0A= +# class Klass=0A= +# include Singleton=0A= +# # ...=0A= +# end=0A= #=0A= -# * ensures that only one instance of Klass called ``the instance''=0A= -# can be created.=0A= +# * this ensures that only one instance of Klass lets call it=0A= +# ``the instance'' can be created.=0A= #=0A= # a,b =3D Klass.instance, Klass.instance=0A= # a =3D=3D b # =3D> true=0A= # a.new # NoMethodError - new is private ...=0A= #=0A= -# * ``The instance'' is created at instanciation time - i.e. the first = call=0A= -# of Klass.instance().=0A= +# * ``The instance'' is created at instanciation time, in other words=0A= +# the first call of Klass.instance(), thus=0A= #=0A= # class OtherKlass=0A= # include Singleton=0A= # # ...=0A= # end=0A= -# p "#{ObjectSpace.each_object(OtherKlass) {}}" # =3D> 0=0A= +# ObjectSpace.each_object(OtherKlass){} # =3D> 0.=0A= #=0A= -# * This behavior is preserved under inheritance.=0A= +# * This behavior is preserved under inheritance and cloning.=0A= #=0A= #=0A= -# This achieved by marking=0A= -# * Klass.new and Klass.allocate - as private and modifying =0A= -# * Klass.inherited(sub_klass) - to ensure=0A= -# that the Singleton pattern is properly inherited.=0A= -#=0A= -# In addition Klass is provided with the class methods=0A= -# * Klass.instance() - returning ``the instance''=0A= -# * Klass._load(str) - returning ``the instance''=0A= -# * Klass._wait() - a hook method putting a second (or n-th)=0A= -# thread calling Klass.instance on a waiting loop if the first call=0A= -# to Klass.instance is still in progress.=0A= +# This is achieved by marking=0A= +# * Klass.new and Klass.allocate - as private=0A= +# * removing #clone and #dup and modifying =0A= +# * Klass.inherited(sub_klass) and Klass.clone() - =0A= +# to ensure that the Singleton pattern is properly=0A= +# inherited and cloned.=0A= +#=0A= +# In addition Klass is providing the additional class methods=0A= +# * Klass.instance() - returning ``the instance''. After a successful=0A= +# self modifying instanciating first call the method body is a simple=0A= +# def Klass.instance()=0A= +# return @__instance__=0A= +# end=0A= +# * Klass._load(str) - calls instance()=0A= +# * Klass._instanciate?() - returning ``the instance'' or nil=0A= +# This hook method puts a second (or nth) thread calling=0A= +# Klass.instance() on a waiting loop. The return value signifies=0A= +# the successful completion or premature termination of the=0A= +# first, or more generally, current instanciating thread.=0A= #=0A= # The sole instance method of Singleton is=0A= -# * _dump(depth) - returning the empty string=0A= -# The default Marshalling strategy is to strip all state information = - i.e.=0A= -# instance variables from ``the instance''. Providing custom=0A= -# _dump(depth) and _load(str) method allows the (partial) = resurrection=0A= -# of a previous state of ``the instance'' - see third example.=0A= -#=0A= +# * _dump(depth) - returning the empty string. Marshalling strips=0A= +# by default all state information, e.g. instance variables and taint=0A= +# state, from ``the instance''. Providing custom _load(str) and=0A= +# _dump(depth) hooks allows the (partially) resurrections of a=0A= +# previous state of ``the instance''.=0A= =0A= module Singleton=0A= - def Singleton.included (klass)=0A= - # should this be checked?=0A= - # raise TypeError.new "..." if klass.type =3D=3D Module=0A= - klass.module_eval {=0A= - undef_method :clone=0A= - undef_method :dup=0A= - }=0A= - class << klass=0A= - def inherited(sub_klass)=0A= - # @__instance__ takes on one of the following values=0A= - # * nil - before (and after a failed) creation=0A= - # * false - during creation=0A= - # * sub_class instance - after a successful creation=0A= - sub_klass.instance_eval { @__instance__ =3D nil }=0A= - def sub_klass.instance=0A= - unless @__instance__.nil?=0A= - # is the extra flexiblity having the hook method=0A= - # _wait() around ever useful?=0A= - _wait() =0A= - # check for instance creation=0A= - return @__instance__ if @__instance__=0A= - end=0A= - Thread.critical =3D true=0A= - unless @__instance__=0A= - @__instance__ =3D false=0A= - Thread.critical =3D false=0A= - begin=0A= - @__instance__ =3D new=0A= - ensure=0A= - if @__instance__=0A= - define_method(:instance) {@__instance__ }=0A= - else=0A= - # failed instance creation=0A= - @__instance__ =3D nil=0A= - end=0A= - end=0A= - else=0A= - Thread.critical =3D false=0A= - end=0A= - return @__instance__=0A= - end=0A= - end=0A= - def _load(str)=0A= - instance=0A= - end=0A= - def _wait=0A= - sleep(0.05) while false.equal?(@__instance__)=0A= - end=0A= - private :new, :allocate=0A= - # hook methods are also marked private=0A= - private :_load,:_wait=0A= - end=0A= - klass.inherited klass=0A= - end=0A= - private=0A= - def _dump(depth)=0A= - return ""=0A= - end=0A= + private =0A= + # default marshalling strategy=0A= + def _dump(depth=3D-1) '' end=0A= + =0A= + class << self=0A= + # extending an object with Singleton is a bad idea=0A= + undef_method :extend_object=0A= + private=0A= + def append_features(mod)=0A= + # This catches ill advisted inclusions of Singleton in=0A= + # singletons types (sounds like an oxymoron) and =0A= + # helps out people counting on transitive mixins=0A= + unless mod.instance_of? (Class)=0A= + raise TypeError.new "Inclusion of the OO-Singleton = module in module #{mod}"=0A= + end =0A= + unless (class << mod; self end) <=3D (class << Object; self = end)=0A= + raise TypeError.new "Inclusion of the OO-Singleton = module in singleton type"=0A= + end=0A= + super=0A= + end=0A= + def included (klass)=0A= + # remove build in copying methods=0A= + klass.class_eval do =0A= + undef_method(:clone) rescue nil=0A= + undef_method(:dup) rescue nil=0A= + end=0A= + =0A= + # initialize the ``klass instance variable'' @__instance__ = to nil=0A= + klass.instance_eval do @__instance__ =3D nil end=0A= + class << klass=0A= + # a main point of the whole exercise - make=0A= + # new and allocate private=0A= + private :new, :allocate=0A= + =0A= + # declare the self modifying klass#instance method=0A= + define_method (:instance, Singleton::FirstInstanceCall) =0A= + =0A= + # simple waiting loop hook - should do in most cases=0A= + # note the pre/post-conditions of a thread-critical = state=0A= + private =0A= + def _instanciate?()=0A= + while false.equal?(@__instance__)=0A= + Thread.critical =3D false=0A= + sleep(0.08) =0A= + Thread.critical =3D true=0A= + end=0A= + @__instance__=0A= + end=0A= + =0A= + # default Marshalling strategy=0A= + def _load(str) instance end =0A= + =0A= + # ensure that the Singleton pattern is properly = inherited =0A= + def inherited(sub_klass)=0A= + super=0A= + sub_klass.instance_eval do @__instance__ =3D nil end=0A= + class << sub_klass=0A= + define_method (:instance, = Singleton::FirstInstanceCall) =0A= + end=0A= + end =0A= + =0A= + public=0A= + # properly clone the Singleton pattern. Question - Did=0A= + # you know that duping doesn't copy class methods?=0A= + def clone=0A= + res =3D super=0A= + res.instance_eval do @__instance__ =3D nil end=0A= + class << res=0A= + define_method (:instance, = Singleton::FirstInstanceCall)=0A= + end=0A= + res=0A= + end=0A= + end # of << klass=0A= + end # of included=0A= + end # of << Singleton=0A= + =0A= + FirstInstanceCall =3D proc do=0A= + # @__instance__ takes on one of the following values=0A= + # * nil - before and after a failed creation=0A= + # * false - during creation=0A= + # * sub_class instance - after a successful creation=0A= + # the form makes up for the lack of returns in progs=0A= + Thread.critical =3D true=0A= + if @__instance__.nil?=0A= + @__instance__ =3D false=0A= + Thread.critical =3D false=0A= + begin=0A= + @__instance__ =3D new=0A= + ensure=0A= + if @__instance__=0A= + def self.instance() @__instance__ end=0A= + else=0A= + @__instance__ =3D nil # failed instance creation=0A= + end=0A= + end=0A= + elsif _instanciate?()=0A= + Thread.critical =3D false =0A= + else=0A= + @__instance__ =3D false=0A= + Thread.critical =3D false=0A= + begin=0A= + @__instance__ =3D new=0A= + ensure=0A= + if @__instance__=0A= + def self.instance() @__instance__ end=0A= + else=0A= + @__instance__ =3D nil=0A= + end=0A= + end=0A= + end=0A= + @__instance__ =0A= + end=0A= end=0A= =0A= +=0A= +=0A= +=0A= if __FILE__ =3D=3D $0=0A= =0A= -#basic example=0A= +def num_of_instances(klass)=0A= + "#{ObjectSpace.each_object(klass){}} #{klass} instance(s)"=0A= +end =0A= +=0A= +# The basic and most important example. The latter examples demonstrate=0A= +# advanced features that have no relevance for the general usage=0A= +=0A= class SomeSingletonClass=0A= include Singleton=0A= end=0A= +puts "There are #{num_of_instances(SomeSingletonClass)}" =0A= +=0A= a =3D SomeSingletonClass.instance=0A= b =3D SomeSingletonClass.instance # a and b are same object=0A= -p a =3D=3D b # =3D> true=0A= +puts "basic test is #{a =3D=3D b}"=0A= +=0A= begin=0A= SomeSingletonClass.new=0A= rescue NoMethodError =3D> mes=0A= puts mes=0A= end=0A= =0A= -# threaded example with exception and customized hook #_wait method=0A= +=0A= +=0A= +puts "\nThreaded example with exception and customized #_instanciate?() = hook"; p=0A= Thread.abort_on_exception =3D false=0A= -def num_of_instances(mod)=0A= - "#{ObjectSpace.each_object(mod){}} #{mod} instance"=0A= -end =0A= =0A= class Ups < SomeSingletonClass=0A= def initialize=0A= @@ -132,18 +205,23 @@=0A= puts "initialize called by thread ##{Thread.current[:i]}"=0A= end=0A= class << self=0A= - def _wait=0A= + def _instanciate?=0A= @enter.push Thread.current[:i]=0A= - sleep 0.02 while false.equal?(@__instance__)=0A= + while false.equal?(@__instance__)=0A= + Thread.critical =3D false=0A= + sleep 0.04 =0A= + Thread.critical =3D true=0A= + end=0A= @leave.push Thread.current[:i]=0A= + @__instance__=0A= end=0A= def __sleep=0A= - sleep (rand(0.1))=0A= + sleep (rand(0.08))=0A= end =0A= def allocate=0A= __sleep=0A= def self.allocate; __sleep; super() end=0A= - raise "allocation in thread ##{Thread.current[:i]} aborted"=0A= + raise "boom - allocation in thread ##{Thread.current[:i]} = aborted"=0A= end=0A= def instanciate_all=0A= @enter =3D []=0A= @@ -159,9 +237,9 @@=0A= end=0A= end=0A= end=0A= - puts "Before there were #{num_of_instances(Ups)}s"=0A= - sleep 3=0A= - puts "Now there is #{num_of_instances(Ups)}"=0A= + puts "Before there were #{num_of_instances(self)}"=0A= + sleep 5=0A= + puts "Now there is #{num_of_instances(self)}"=0A= puts "#{@enter.join "; "} was the order of threads entering = the waiting loop"=0A= puts "#{@leave.join "; "} was the order of threads leaving = the waiting loop"=0A= end=0A= @@ -177,8 +255,17 @@=0A= # 2; 3; 6; 1; 7; 5; 9; 4 was the order of threads entering the waiting = loop=0A= # 3; 2; 1; 7; 6; 5; 4; 9 was the order of threads leaving the waiting = loop=0A= =0A= +puts "\nLets see if class level cloning really works"=0A= +Yup =3D Ups.clone=0A= +def Yup.allocate=0A= + __sleep=0A= + def self.allocate; __sleep; super() end=0A= + raise "boom - allocation in thread ##{Thread.current[:i]} aborted"=0A= +end=0A= +Yup.instanciate_all=0A= =0A= -# Customized marshalling=0A= +=0A= +puts "\n","Customized marshalling"=0A= class A=0A= include Singleton=0A= attr_accessor :persist, :die=0A= @@ -195,6 +282,7 @@=0A= a =3D A.instance=0A= a.persist =3D ["persist"]=0A= a.die =3D "die"=0A= +a.taint=0A= =0A= stored_state =3D Marshal.dump(a)=0A= # change state=0A= @@ -203,6 +291,47 @@=0A= b =3D Marshal.load(stored_state)=0A= p a =3D=3D b # =3D> true=0A= p a.persist # =3D> ["persist"]=0A= -p a.die # =3D> nil=0A= +p a.die # =3D> nil=0A= +=0A= +puts "\n\nSingleton with overridden default #inherited() hook"=0A= +class Up=0A= + def Up.inherited(sub_klass)=0A= + puts "#{sub_klass} subclasses #{self}"=0A= + end=0A= +end=0A= +=0A= +=0A= +class Middle < Up=0A= + undef_method :dup=0A= + include Singleton=0A= +end=0A= +class Down < Middle; end=0A= +=0A= +puts "basic test is #{Down.instance =3D=3D Down.instance}" =0A= +=0A= +=0A= +puts "\n","Various exceptions"=0A= +=0A= +begin=0A= + module AModule=0A= + include Singleton=0A= + end=0A= +rescue TypeError =3D> mes=0A= + puts mes #=3D> Inclusion of the OO-Singleton module in module = AModule=0A= +end=0A= +=0A= +begin=0A= + class << 'aString'=0A= + include Singleton=0A= + end=0A= +rescue TypeError =3D> mes=0A= + puts mes # =3D> Inclusion of the OO-Singleton module in singleton = type=0A= +end=0A= +=0A= +begin=0A= + 'aString'.extend Singleton=0A= +rescue NoMethodError =3D> mes=0A= + puts mes #=3D> undefined method `extend_object' for = Singleton:Module=0A= +end=0A= =0A= end=0A= ------=_NextPart_000_0180_01C1A85D.44EF4180--