lib/micon/micon.rb in ruby-ext-0.4.2 vs lib/micon/micon.rb in ruby-ext-0.4.3
- old
+ new
@@ -2,49 +2,55 @@
#
# Micons :"custom_name" are managed by 'scope_begin' / 'scope_end' methods
#
# :"custom_name" can't be nested (it will destroy old and start new one) and always should be explicitly started!.
-class Micon
- REGISTRY_SYNC = Monitor.new
- APPLILCATION_SYNC = Monitor.new
+module Micon
+ SYNC, MSYNC = Monitor.new, Monitor.new
+
+ # quick access to Metadata inner variable.
+ # I intentially broke the Metadata incapsulation to provide better performance, don't refactor it.
+ @_r = {}
+
+ @application, @metadata = {}, Metadata.new(@_r, MSYNC)
- @application, @registry = {}, {}
-
class << self
- #
+ attr_accessor :metadata
+
+ #
# Scope Management
- #
+ #
def activate scope, container, &block
-
raise_without_self "Only custom scopes can be activated!" if scope == :application or scope == :instance
container.must_be.a Hash
scope_with_prefix = add_prefix(scope)
raise_without_self "Scope '#{remove_prefix(scope)}' already active!" if !block and Thread.current[scope_with_prefix]
if block
begin
outer_container_or_nil = Thread.current[scope_with_prefix]
Thread.current[scope_with_prefix] = container
- block.call
+ @metadata.with_scope_callbacks scope, &block
ensure
Thread.current[scope_with_prefix] = outer_container_or_nil
end
else
# not support nested scopes without block
Thread.current[scope_with_prefix] = container
+ @metadata.call_before_scope scope
end
end
def deactivate scope
raise_without_self "Only custom scopes can be deactivated!" if scope == :application or scope == :instance
scope_with_prefix = add_prefix(scope)
raise_without_self "Scope '#{scope}' not active!" unless container = Thread.current[scope_with_prefix]
- Thread.current[scope_with_prefix] = nil
+ @metadata.call_after_scope scope
+ Thread.current[scope_with_prefix] = nil
container
end
def active? scope
if scope == :application or scope == :instance
@@ -53,18 +59,18 @@
Thread.current.key?(add_prefix(scope))
end
end
def clear
- APPLILCATION_SYNC.synchronize{@application.clear}
+ SYNC.synchronize{@application.clear}
Thread.current.keys.each do |key|
Thread.current[key] = nil if key.to_s =~ /^mc_/
end
end
def empty?
- return false unless APPLILCATION_SYNC.synchronize{@application.empty?}
+ return false unless SYNC.synchronize{@application.empty?}
Thread.current.keys.each do |key|
return false if key.to_s =~ /^mc_/
end
return true
end
@@ -72,112 +78,162 @@
#
# Object Management
#
def include? key
- scope, initializer = nil
- REGISTRY_SYNC.synchronize{scope, initializer = @registry[key]}
+ scope = MSYNC.synchronize{@_r[key]}
case scope
when nil
- raise_without_self "The '#{key}' Name is not Managed!", Micon
+ raise_without_self "'#{key}' component not managed!", Micon
when :instance
true
when :application
- APPLILCATION_SYNC.synchronize do
+ SYNC.synchronize do
@application.include? key
end
else # custom
container = Thread.current[scope]
return false unless container
container.include? key
end
end
def [] key
- scope, initializer = nil
- REGISTRY_SYNC.synchronize{scope, initializer = @registry[key]}
-
+ scope = MSYNC.synchronize{@_r[key]}
+
case scope
when nil
- raise_without_self "The '#{key}' Name is not Managed!", Micon
- when :instance
- return initializer.call
+ raise_without_self "'#{key}' component not managed!", Micon
+ when :instance
+ return create_object(key)
when :application
- APPLILCATION_SYNC.synchronize do
- o = @application[key]
+ SYNC.synchronize do
+ o = @application[key]
unless o
- o = initializer.call
- @application[key] = o
+ return create_object(key, @application)
+ else
+ return o
end
- return o
end
- else # custom
+ else # custom
container = Thread.current[scope]
raise_without_self "Scope '#{remove_prefix(scope)}' not started!" unless container
o = container[key]
unless o
- o = initializer.call
- container[key] = o
+ return create_object(key, container)
+ else
+ return o
end
- return o
end
end
def []= key, value
- scope = nil
- REGISTRY_SYNC.synchronize{scope, initializer = @registry[key]}
+ scope = MSYNC.synchronize{@_r[key]}
case scope
when nil
- raise_without_self "The '#{key}' Name is not Managed!", Micon
+ raise_without_self "'#{key}' component not managed!", Micon
when :instance
raise_without_self "You can't outject variable with the 'instance' scope!"
when :application
- APPLILCATION_SYNC.synchronize{@application[key] = value}
+ SYNC.synchronize{@application[key] = value}
else # Custom
container = Thread.current[scope]
raise_without_self "Scope '#{remove_prefix(scope)}' not started!" unless container
container[key] = value
end
end
-
+
#
- # Registry
+ # Metadata
#
- def clear_registry
- REGISTRY_SYNC.synchronize{@registry.clear}
- end
-
- def register key, scope, &initializer
+ def register key, options = {}, &initializer
key.must_not_be.nil
- scope.must_not_be.nil
- scope = add_prefix(scope) unless scope == :application or scope == :instance
- registry_set key, [scope, (initializer || lambda{nil})]
+ options = options.symbolize_keys
+
+ scope = options.delete(:scope) || :application
+ scope = Micon.add_prefix(scope) unless scope == :application or scope == :instance
+ dependencies = Array.wrap(options.delete(:require) || options.delete(:depends_on))
+
+ options.each{|key| raise "Unknown option :#{key}!"}
+
+ MSYNC.synchronize do
+ @_r.object_id.must == @metadata.registry.object_id
+ @metadata.registry[key] = scope
+ @metadata.initializers[key] = [(initializer || lambda{nil}), dependencies]
+ end
end
def unregister key
- REGISTRY_SYNC.synchronize{@registry.delete key}
+ @metadata.delete key
end
- def registry_get key
- REGISTRY_SYNC.synchronize{@registry[key]}
+ def before scope_or_component, &block
+ @metadata.register_before scope_or_component, &block
end
- def registry_set key, value
- REGISTRY_SYNC.synchronize{@registry[key] = value}
+ def after scope_or_component, &block
+ @metadata.register_after scope_or_component, &block
end
- def registry_include? key
- REGISTRY_SYNC.synchronize{@registry.include? key}
+ # handy method, usually for test purposes
+ def swap_metadata metadata = nil
+ metadata ||= Metadata.new({}, MSYNC)
+ old = self.metadata
+
+ self.metadata = metadata
+ @_r = metadata.registry
+
+ old
end
+
+ protected
+ def create_object key, container = nil
+ initializer, dependencies = MSYNC.synchronize{@metadata.initializers[key]}
+ dependencies.each{|d| Micon[d]}
+ @metadata.call_before key
+
+ if container
+ unless o = container[key]
+ o = initializer.call
+ container[key] = o
+ else
+ # complex case, there's an circular dependency, and the 'o' already has been
+ # initialized in dependecies or callbacks
+ # here's the sample case:
+ #
+ # Micon.register :environment, :application do
+ # p :environment
+ # 'environment'
+ # end
+ #
+ # Micon.register :conveyors, :application, :depends_on => :environment do
+ # p :conveyors
+ # 'conveyors'
+ # end
+ #
+ # Micon.after :environment do
+ # Micon[:conveyors]
+ # end
+ #
+ # Micon[:conveyors]
+
+ o = container[key]
+ end
+ else
+ o = initializer.call
+ end
+
+ @metadata.call_after key, o
+ o
+ end
- protected
def add_prefix scope
:"mc_#{scope}"
end
-
+
def remove_prefix scope
scope.to_s.gsub(/^mc_/, '')
end
end
end
\ No newline at end of file