home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / lib / ruby / 1.8 / singleton.rb < prev    next >
Encoding:
Ruby Source  |  2005-03-15  |  8.1 KB  |  361 lines

  1. # The Singleton module implements the Singleton pattern.
  2. #
  3. # Usage:
  4. #    class Klass
  5. #       include Singleton
  6. #       # ...
  7. #    end
  8. #
  9. # *  this ensures that only one instance of Klass lets call it
  10. #    ``the instance'' can be created.
  11. #
  12. #    a,b  = Klass.instance, Klass.instance
  13. #    a == b   # => true
  14. #    a.new    #  NoMethodError - new is private ...
  15. #
  16. # *  ``The instance'' is created at instantiation time, in other
  17. #    words the first call of Klass.instance(), thus
  18. #
  19. #      class OtherKlass
  20. #        include Singleton
  21. #        # ...
  22. #      end
  23. #      ObjectSpace.each_object(OtherKlass){} # => 0.
  24. #
  25. # *  This behavior is preserved under inheritance and cloning.
  26. #
  27. #
  28. #
  29. # This is achieved by marking
  30. # *  Klass.new and Klass.allocate - as private
  31. #
  32. # Providing (or modifying) the class methods
  33. # *  Klass.inherited(sub_klass) and Klass.clone()  - 
  34. #    to ensure that the Singleton pattern is properly
  35. #    inherited and cloned.
  36. #
  37. # *  Klass.instance()  -  returning ``the instance''. After a
  38. #    successful self modifying (normally the first) call the
  39. #    method body is a simple:
  40. #
  41. #       def Klass.instance()
  42. #         return @__instance__
  43. #       end
  44. #
  45. # *  Klass._load(str)  -  calling Klass.instance()
  46. #
  47. # *  Klass._instantiate?()  -  returning ``the instance'' or
  48. #    nil. This hook method puts a second (or nth) thread calling
  49. #    Klass.instance() on a waiting loop. The return value
  50. #    signifies the successful completion or premature termination
  51. #    of the first, or more generally, current "instantiation thread".
  52. #
  53. #
  54. # The instance method of Singleton are
  55. # * clone and dup - raising TypeErrors to prevent cloning or duping
  56. #
  57. # *  _dump(depth) - returning the empty string.  Marshalling strips
  58. #    by default all state information, e.g. instance variables and
  59. #    taint state, from ``the instance''.  Providing custom _load(str)
  60. #    and _dump(depth) hooks allows the (partially) resurrections of
  61. #    a previous state of ``the instance''.
  62.  
  63.  
  64.  
  65. module Singleton
  66.   #  disable build-in copying methods
  67.   def clone
  68.     raise TypeError, "can't clone instance of singleton #{self.class}"
  69.   end
  70.   def dup
  71.     raise TypeError, "can't dup instance of singleton #{self.class}"
  72.   end
  73.   
  74.   private 
  75.   #  default marshalling strategy
  76.   def _dump(depth=-1) 
  77.     ''
  78.   end
  79. end
  80.  
  81.  
  82. class << Singleton
  83.   #  Method body of first instance call.
  84.   FirstInstanceCall = proc do
  85.     #  @__instance__ takes on one of the following values
  86.     #  * nil     -  before and after a failed creation
  87.     #  * false  -  during creation
  88.     #  * sub_class instance  -  after a successful creation
  89.     #  the form makes up for the lack of returns in progs
  90.     Thread.critical = true
  91.     if  @__instance__.nil?
  92.       @__instance__  = false
  93.       Thread.critical = false
  94.       begin
  95.         @__instance__ = new
  96.       ensure
  97.         if @__instance__
  98.           class <<self
  99.             remove_method :instance
  100.             def instance; @__instance__ end
  101.           end
  102.         else
  103.           @__instance__ = nil #  failed instance creation
  104.         end
  105.       end
  106.     elsif  _instantiate?()
  107.       Thread.critical = false    
  108.     else
  109.       @__instance__  = false
  110.       Thread.critical = false
  111.       begin
  112.         @__instance__ = new
  113.       ensure
  114.         if @__instance__
  115.           class <<self
  116.             remove_method :instance
  117.             def instance; @__instance__ end
  118.           end
  119.         else
  120.           @__instance__ = nil
  121.         end
  122.       end
  123.     end
  124.     @__instance__
  125.   end
  126.   
  127.   module SingletonClassMethods  
  128.     # properly clone the Singleton pattern - did you know
  129.     # that duping doesn't copy class methods?  
  130.     def clone
  131.       Singleton.__init__(super)
  132.     end
  133.     
  134.     private
  135.     
  136.     #  ensure that the Singleton pattern is properly inherited   
  137.     def inherited(sub_klass)
  138.       super
  139.       Singleton.__init__(sub_klass)
  140.     end
  141.     
  142.     def _load(str) 
  143.       instance 
  144.     end
  145.     
  146.     # waiting-loop hook
  147.     def _instantiate?()
  148.       while false.equal?(@__instance__)
  149.         Thread.critical = false
  150.         sleep(0.08)   # timeout
  151.         Thread.critical = true
  152.       end
  153.       @__instance__
  154.     end
  155.   end
  156.   
  157.   def __init__(klass)
  158.     klass.instance_eval { @__instance__ = nil }
  159.     class << klass
  160.       define_method(:instance,FirstInstanceCall)
  161.     end
  162.     klass
  163.   end
  164.   
  165.   private
  166.   #  extending an object with Singleton is a bad idea
  167.   undef_method :extend_object
  168.   
  169.   def append_features(mod)
  170.     #  help out people counting on transitive mixins
  171.     unless mod.instance_of?(Class)
  172.       raise TypeError, "Inclusion of the OO-Singleton module in module #{mod}"
  173.     end
  174.     super
  175.   end
  176.   
  177.   def included(klass)
  178.     super
  179.     klass.private_class_method  :new, :allocate
  180.     klass.extend SingletonClassMethods
  181.     Singleton.__init__(klass)
  182.   end
  183. end
  184.  
  185.  
  186.  
  187. if __FILE__ == $0
  188.  
  189. def num_of_instances(klass)
  190.     "#{ObjectSpace.each_object(klass){}} #{klass} instance(s)"
  191. end 
  192.  
  193. # The basic and most important example.
  194.  
  195. class SomeSingletonClass
  196.   include Singleton
  197. end
  198. puts "There are #{num_of_instances(SomeSingletonClass)}" 
  199.  
  200. a = SomeSingletonClass.instance
  201. b = SomeSingletonClass.instance # a and b are same object
  202. puts "basic test is #{a == b}"
  203.  
  204. begin
  205.   SomeSingletonClass.new
  206. rescue  NoMethodError => mes
  207.   puts mes
  208. end
  209.  
  210.  
  211.  
  212. puts "\nThreaded example with exception and customized #_instantiate?() hook"; p
  213. Thread.abort_on_exception = false
  214.  
  215. class Ups < SomeSingletonClass
  216.   def initialize
  217.     self.class.__sleep
  218.     puts "initialize called by thread ##{Thread.current[:i]}"
  219.   end
  220. end
  221.   
  222. class << Ups
  223.   def _instantiate?
  224.     @enter.push Thread.current[:i]
  225.     while false.equal?(@__instance__)
  226.       Thread.critical = false
  227.       sleep 0.08 
  228.       Thread.critical = true
  229.     end
  230.     @leave.push Thread.current[:i]
  231.     @__instance__
  232.   end
  233.   
  234.   def __sleep
  235.     sleep(rand(0.08))
  236.   end
  237.   
  238.   def new
  239.     begin
  240.       __sleep
  241.       raise  "boom - thread ##{Thread.current[:i]} failed to create instance"
  242.     ensure
  243.       # simple flip-flop
  244.       class << self
  245.         remove_method :new
  246.       end
  247.     end
  248.   end
  249.   
  250.   def instantiate_all
  251.     @enter = []
  252.     @leave = []
  253.     1.upto(9) {|i|  
  254.       Thread.new { 
  255.         begin
  256.           Thread.current[:i] = i
  257.           __sleep
  258.           instance
  259.         rescue RuntimeError => mes
  260.           puts mes
  261.         end
  262.       }
  263.     }
  264.     puts "Before there were #{num_of_instances(self)}"
  265.     sleep 3
  266.     puts "Now there is #{num_of_instances(self)}"
  267.     puts "#{@enter.join '; '} was the order of threads entering the waiting loop"
  268.     puts "#{@leave.join '; '} was the order of threads leaving the waiting loop"
  269.   end
  270. end
  271.  
  272.  
  273. Ups.instantiate_all
  274. # results in message like
  275. # Before there were 0 Ups instance(s)
  276. # boom - thread #6 failed to create instance
  277. # initialize called by thread #3
  278. # Now there is 1 Ups instance(s)
  279. # 3; 2; 1; 8; 4; 7; 5 was the order of threads entering the waiting loop
  280. # 3; 2; 1; 7; 4; 8; 5 was the order of threads leaving the waiting loop
  281.  
  282.  
  283. puts "\nLets see if class level cloning really works"
  284. Yup = Ups.clone
  285. def Yup.new
  286.   begin
  287.     __sleep
  288.     raise  "boom - thread ##{Thread.current[:i]} failed to create instance"
  289.   ensure
  290.     # simple flip-flop
  291.     class << self
  292.       remove_method :new
  293.     end
  294.   end
  295. end
  296. Yup.instantiate_all
  297.  
  298.  
  299. puts "\n\n","Customized marshalling"
  300. class A
  301.   include Singleton
  302.   attr_accessor :persist, :die
  303.   def _dump(depth)
  304.     # this strips the @die information from the instance
  305.     Marshal.dump(@persist,depth)
  306.   end
  307. end
  308.  
  309. def A._load(str)
  310.   instance.persist = Marshal.load(str)
  311.   instance
  312. end
  313.  
  314. a = A.instance
  315. a.persist = ["persist"]
  316. a.die = "die"
  317. a.taint
  318.  
  319. stored_state = Marshal.dump(a)
  320. # change state
  321. a.persist = nil
  322. a.die = nil
  323. b = Marshal.load(stored_state)
  324. p a == b  #  => true
  325. p a.persist  #  => ["persist"]
  326. p a.die      #  => nil
  327.  
  328.  
  329. puts "\n\nSingleton with overridden default #inherited() hook"
  330. class Up
  331. end
  332. def Up.inherited(sub_klass)
  333.   puts "#{sub_klass} subclasses #{self}"
  334. end
  335.  
  336.  
  337. class Middle < Up
  338.   include Singleton
  339. end
  340.  
  341. class Down < Middle; end
  342.  
  343. puts  "and basic \"Down test\" is #{Down.instance == Down.instance}\n
  344. Various exceptions"  
  345.  
  346. begin
  347.   module AModule
  348.     include Singleton
  349.   end
  350. rescue TypeError => mes
  351.   puts mes  #=> Inclusion of the OO-Singleton module in module AModule
  352. end
  353.  
  354. begin
  355.   'aString'.extend Singleton
  356. rescue NoMethodError => mes
  357.   puts mes  #=> undefined method `extend_object' for Singleton:Module
  358. end
  359.  
  360. end
  361.