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