Created
September 7, 2016 05:53
-
-
Save estum/c83a1eeb08a788f20108db1f3fe10d18 to your computer and use it in GitHub Desktop.
Abstract Singleton Module Class
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class AbstractSingleton < Module | |
def self.base_class | |
self < AbstractSingleton ? superclass : self | |
end | |
module SingletonInstanceMethods | |
::Singleton.instance_methods.each do |name| | |
define_method(name, ::Singleton.instance_method(name)) | |
end | |
end | |
module SingletonClassMethods # :nodoc: | |
def instance | |
raise NotImplemented | |
end | |
def clone # :nodoc: | |
@singleton__module__.__init__(super) | |
end | |
# By default calls instance(). Override to retain singleton state. | |
def _load(str) | |
instance | |
end | |
private | |
def inherited(sub_klass) | |
super | |
@singleton__module__.__init__(sub_klass) | |
end | |
end | |
def __init__(klass) # :nodoc: | |
klass.instance_variable_set(:@singleton__module__, self) | |
__reset__(klass) | |
klass | |
end | |
private | |
def extend_object(klass) | |
klass.extend(singleton_class_methods) | |
end | |
def append_features(klass) | |
klass.prepend(singleton_instance_methods) | |
end | |
def prepend_features(klass) | |
unless klass.instance_of?(Class) | |
raise TypeError, "Prepending of the OO-AbstractSingleton module in module #{mod}" | |
end | |
super | |
end | |
def prepended(klass) | |
super | |
klass.private_class_method(:new, :allocate) | |
klass.extend(self) | |
klass.include(self) | |
__init__(klass) | |
end | |
def singleton_instance_methods(inherit: true, &block) | |
_def_or_get_relative_module(:SingletonInstanceMethods, inherit, &block) | |
end | |
def singleton_class_methods(inherit: true, &block) | |
_def_or_get_relative_module(:SingletonClassMethods, inherit, &block) | |
end | |
def _def_or_get_relative_module(name, inherit = true, &block) | |
mod = const_defined?(name, false) ? const_get(name, false) : self.class.base_class.const_get(name, false) | |
return mod unless block_given? | |
if self.class < mod.parent | |
mod = const_set(name, Module.new).tap do |submod| | |
mod.send(:prepend_features, submod) if inherit | |
end | |
end | |
mod.module_eval(&block) if block_given? | |
end | |
end | |
Kernel.module_eval do | |
def abstract_singleton(name, supermod = ::AbstractSingleton, &block) | |
parent = respond_to?(:const_set) ? self : Object | |
parent.const_set name, supermod.new(&block) | |
end | |
end | |
# Implement the default +Singleton+ behavior | |
abstract_singleton :PlainSingleton do | |
singleton_class_methods do | |
def instance(klass) | |
return @singleton__instance__ if @singleton__instance__ | |
@singleton__mutex__.synchronize do | |
return @singleton__instance__ if @singleton__instance__ | |
@singleton__instance__ = new() | |
end | |
@singleton__instance__ | |
end | |
end | |
def self.__reset__(klass) | |
klass.instance_eval do | |
@singleton__instance__ = nil | |
@singleton__mutex__ = Mutex.new | |
end | |
nil | |
end | |
end | |
abstract_singleton :PerThreadSingleton do | |
singleton_class_methods do | |
attr_reader :singleton__key__ | |
def instance | |
Thread.current[singleton__key__] ||= new() | |
end | |
end | |
def self.__init__(klass) | |
klass.instance_variable_set(:@singleton__key__, :"singleton_#{klass.object_id}") | |
super(klass) | |
end | |
def self.__reset__(klass) | |
Thread.current[klass.singleton__key__] = nil | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment