Advertisement
LiTTleDRAgo

[Ruby] Singleton.rb (untouched)

Oct 15th, 2013
131
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 8.35 KB | None | 0 0
  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 instanciation 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._instanciate?()  -  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 "instanciation 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.           def self.instance; @__instance__ end
  99.         else
  100.           @__instance__ = nil #  failed instance creation
  101.         end
  102.       end
  103.     elsif  _instanciate?()
  104.       Thread.critical = false    
  105.     else
  106.       @__instance__  = false
  107.       Thread.critical = false
  108.       begin
  109.         @__instance__ = new
  110.       ensure
  111.         if @__instance__
  112.           def self.instance; @__instance__ end
  113.         else
  114.           @__instance__ = nil
  115.         end
  116.       end
  117.     end
  118.     @__instance__
  119.   end
  120.  
  121.   module SingletonClassMethods  
  122.     # properly clone the Singleton pattern - did you know
  123.     # that duping doesn't copy class methods?  
  124.     def clone
  125.       Singleton.__init__(super)
  126.     end
  127.    
  128.     private
  129.    
  130.     #  ensure that the Singleton pattern is properly inherited  
  131.     def inherited(sub_klass)
  132.       super
  133.       Singleton.__init__(sub_klass)
  134.     end
  135.    
  136.     def _load(str)
  137.       instance
  138.     end
  139.    
  140.     # waiting-loop hook
  141.     def _instanciate?()
  142.       while false.equal?(@__instance__)
  143.         Thread.critical = false
  144.         sleep(0.08)   # timeout
  145.         Thread.critical = true
  146.       end
  147.       @__instance__
  148.     end
  149.   end
  150.  
  151.   def __init__(klass)
  152.     klass.instance_eval { @__instance__ = nil }
  153.     class << klass
  154.       define_method(:instance,FirstInstanceCall)
  155.     end
  156.     klass
  157.   end
  158.  
  159.   private
  160.   #  extending an object with Singleton is a bad idea
  161.   undef_method :extend_object
  162.  
  163.   def append_features(mod)
  164.     #  help out people counting on transitive mixins
  165.     unless mod.instance_of?(Class)
  166.       raise TypeError, "Inclusion of the OO-Singleton module in module #{mod}"
  167.     end
  168.     super
  169.   end
  170.  
  171.   def included(klass)
  172.     super
  173.     klass.private_class_method  :new, :allocate
  174.     klass.extend SingletonClassMethods
  175.     Singleton.__init__(klass)
  176.   end
  177. end
  178.  
  179.  
  180.  
  181. if __FILE__ == $0
  182.  
  183. def num_of_instances(klass)
  184.     "#{ObjectSpace.each_object(klass){}} #{klass} instance(s)"
  185. end
  186.  
  187. # The basic and most important example.
  188.  
  189. class SomeSingletonClass
  190.   include Singleton
  191. end
  192. puts "There are #{num_of_instances(SomeSingletonClass)}"
  193.  
  194. a = SomeSingletonClass.instance
  195. b = SomeSingletonClass.instance # a and b are same object
  196. puts "basic test is #{a == b}"
  197.  
  198. begin
  199.   SomeSingletonClass.new
  200. rescue  NoMethodError => mes
  201.   puts mes
  202. end
  203.  
  204.  
  205.  
  206. puts "\nThreaded example with exception and customized #_instanciate?() hook"; p
  207. Thread.abort_on_exception = false
  208.  
  209. class Ups < SomeSingletonClass
  210.   def initialize
  211.     self.class.__sleep
  212.     puts "initialize called by thread ##{Thread.current[:i]}"
  213.   end
  214. end
  215.  
  216. class << Ups
  217.   def _instanciate?
  218.     @enter.push Thread.current[:i]
  219.     while false.equal?(@__instance__)
  220.       Thread.critical = false
  221.       sleep 0.08
  222.       Thread.critical = true
  223.     end
  224.     @leave.push Thread.current[:i]
  225.     @__instance__
  226.   end
  227.  
  228.   def __sleep
  229.     sleep(rand(0.08))
  230.   end
  231.  
  232.   def new
  233.     begin
  234.       __sleep
  235.       raise  "boom - thread ##{Thread.current[:i]} failed to create instance"
  236.     ensure
  237.       # simple flip-flop
  238.       class << self
  239.         remove_method :new
  240.       end
  241.     end
  242.   end
  243.  
  244.   def instanciate_all
  245.     @enter = []
  246.     @leave = []
  247.     1.upto(9) {|i|  
  248.       Thread.new {
  249.         begin
  250.           Thread.current[:i] = i
  251.           __sleep
  252.           instance
  253.         rescue RuntimeError => mes
  254.           puts mes
  255.         end
  256.       }
  257.     }
  258.     puts "Before there were #{num_of_instances(self)}"
  259.     sleep 3
  260.     puts "Now there is #{num_of_instances(self)}"
  261.     puts "#{@enter.join '; '} was the order of threads entering the waiting loop"
  262.     puts "#{@leave.join '; '} was the order of threads leaving the waiting loop"
  263.   end
  264. end
  265.  
  266.  
  267. Ups.instanciate_all
  268. # results in message like
  269. # Before there were 0 Ups instance(s)
  270. # boom - thread #6 failed to create instance
  271. # initialize called by thread #3
  272. # Now there is 1 Ups instance(s)
  273. # 3; 2; 1; 8; 4; 7; 5 was the order of threads entering the waiting loop
  274. # 3; 2; 1; 7; 4; 8; 5 was the order of threads leaving the waiting loop
  275.  
  276.  
  277. puts "\nLets see if class level cloning really works"
  278. Yup = Ups.clone
  279. def Yup.new
  280.   begin
  281.     __sleep
  282.     raise  "boom - thread ##{Thread.current[:i]} failed to create instance"
  283.   ensure
  284.     # simple flip-flop
  285.     class << self
  286.       remove_method :new
  287.     end
  288.   end
  289. end
  290. Yup.instanciate_all
  291.  
  292.  
  293. puts "\n\n","Customized marshalling"
  294. class A
  295.   include Singleton
  296.   attr_accessor :persist, :die
  297.   def _dump(depth)
  298.     # this strips the @die information from the instance
  299.     Marshal.dump(@persist,depth)
  300.   end
  301. end
  302.  
  303. def A._load(str)
  304.   instance.persist = Marshal.load(str)
  305.   instance
  306. end
  307.  
  308. a = A.instance
  309. a.persist = ["persist"]
  310. a.die = "die"
  311. a.taint
  312.  
  313. stored_state = Marshal.dump(a)
  314. # change state
  315. a.persist = nil
  316. a.die = nil
  317. b = Marshal.load(stored_state)
  318. p a == b  #  => true
  319. p a.persist  #  => ["persist"]
  320. p a.die      #  => nil
  321.  
  322.  
  323. puts "\n\nSingleton with overridden default #inherited() hook"
  324. class Up
  325. end
  326. def Up.inherited(sub_klass)
  327.   puts "#{sub_klass} subclasses #{self}"
  328. end
  329.  
  330.  
  331. class Middle < Up
  332.   include Singleton
  333. end
  334.  
  335. class Down < Middle; end
  336.  
  337. puts  "and basic \"Down test\" is #{Down.instance == Down.instance}\n
  338. Various exceptions"  
  339.  
  340. begin
  341.   module AModule
  342.     include Singleton
  343.   end
  344. rescue TypeError => mes
  345.   puts mes  #=> Inclusion of the OO-Singleton module in module AModule
  346. end
  347.  
  348. begin
  349.   'aString'.extend Singleton
  350. rescue NoMethodError => mes
  351.   puts mes  #=> undefined method `extend_object' for Singleton:Module
  352. end
  353.  
  354. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement