From 9a3803cd8fb364156ab45f14de05810f35e11bcf Mon Sep 17 00:00:00 2001 From: alexeypetrushin Date: Fri, 1 Jul 2011 14:18:47 +0400 Subject: [PATCH] add configuration --- lib/micon.rb | 3 + lib/micon/config.rb | 43 ++++++++++++ lib/micon/core.rb | 70 ++++++++++--------- spec/config_spec.rb | 27 +++++++ .../basic/lib/components/logger.yml | 1 + .../order/app/config/object.production.yml | 1 + spec/config_spec/order/app/config/object.yml | 2 + .../lib/components/object.production.yml | 3 + .../order/lib/components/object.yml | 3 + spec/micelaneous_spec.rb | 20 +++++- 10 files changed, 138 insertions(+), 35 deletions(-) create mode 100644 lib/micon/config.rb create mode 100644 spec/config_spec.rb create mode 100644 spec/config_spec/basic/lib/components/logger.yml create mode 100644 spec/config_spec/order/app/config/object.production.yml create mode 100644 spec/config_spec/order/app/config/object.yml create mode 100644 spec/config_spec/order/lib/components/object.production.yml create mode 100644 spec/config_spec/order/lib/components/object.yml diff --git a/lib/micon.rb b/lib/micon.rb index 95138dd..77d6d5e 100644 --- a/lib/micon.rb +++ b/lib/micon.rb @@ -1,3 +1,5 @@ +require 'yaml' + module Micon end @@ -5,6 +7,7 @@ module Micon support metadata + config core helper diff --git a/lib/micon/config.rb b/lib/micon/config.rb new file mode 100644 index 0000000..36044a3 --- /dev/null +++ b/lib/micon/config.rb @@ -0,0 +1,43 @@ +class Micon::Config + attr_reader :micon, :name + def initialize micon, name + @micon, @name = micon, name + end + + def load + files = [] + files << find_file(config_path(name, nil), $LOAD_PATH) + files << find_file(config_path(name, micon.mode_name), $LOAD_PATH) if micon.mode_name + if micon.runtime_path + files << find_file(runtime_config_path(name, nil), [micon.runtime_path]) + files << find_file(runtime_config_path(name, micon.mode_name), [micon.runtime_path]) if micon.mode_name + end + + config = {} + files.compact.each do |f| + c = YAML.load_file(f) + next unless c + raise "component config must be a Hash (#{f})!" unless c.is_a? Hash + c.each{|k, v| config[:"#{k}="] = v} + end + + config.empty? ? nil : config + end + + protected + def config_path name, mode + fs_name = name.to_s.gsub(/::/, '/') + mode ? "/components/#{fs_name}.#{mode}.yml" : "/components/#{fs_name}.yml" + end + + def runtime_config_path name, mode + fs_name = name.to_s.gsub(/::/, '/') + mode ? "/config/#{fs_name}.#{mode}.yml" : "/config/#{fs_name}.yml" + end + + def find_file path, directories + files = directories.collect{|dir| "#{dir}#{path}"}.select{|f| File.exist? f} + raise "multiple configs for :#{name} component" if files.size > 1 + files.first + end +end \ No newline at end of file diff --git a/lib/micon/core.rb b/lib/micon/core.rb index e5fe8ea..0fd6996 100644 --- a/lib/micon/core.rb +++ b/lib/micon/core.rb @@ -320,6 +320,11 @@ def deinitialize! # @loaded_classes.clear end + # override it in Your framework if You need it + # - 'app/runtime' + # - :development, :test, :production + attr_accessor :runtime_path, :mode_name + protected def autoload_component_definition key, bang = true begin @@ -332,45 +337,35 @@ def autoload_component_definition key, bang = true end def create_object key, container = nil - initializer, dependencies = @metadata.initializers[key] + initializer, dependencies, config = @metadata.initializers[key] raise "no initializer for :#{key} component!" unless initializer - raise "component :#{key} used before it's initialization is finished!" if @stack.include? key - @stack[key] = true + raise "component :#{key} used before it's initialization is finished!" if @stack.include? key begin dependencies.each{|d| self[d]} @metadata.call_before key - - if container - unless o = container[key] - o = initializer.call - container[key] = o + + # we need to check container first, in complex cases (circullar dependency) + # the object already may be initialized. + # See "should allow to use circullar dependency in :after callback". + @stack[key] = true + o = (container && container[key]) || initializer.call + + unless config == false + unless config + # loading and caching config + config = get_config key + config = false unless config # we use false to differentiate from nil + @metadata.initializers[key] = [initializer, dependencies, config] + + apply_config o, config if config else - # complex case, there's an circular dependency, and the 'o' already has been - # initialized in dependecies or callbacks - # here's the sample case: - # - # app.register :environment, :application do - # p :environment - # 'environment' - # end - # - # app.register :conveyors, :application, depends_on: :environment do - # p :conveyors - # 'conveyors' - # end - # - # app.after :environment do - # app[:conveyors] - # end - # - # app[:conveyors] - - o = container[key] - end - else - o = initializer.call + apply_config o, config + end end + + container[key] = o if container + raise "initializer for component :#{key} returns nill!" unless o ensure @stack.delete key @@ -378,7 +373,16 @@ def create_object key, container = nil @metadata.call_after key, o o - end + end + + def get_config key + ::Micon::Config.new(self, key).load + end + + def apply_config component, config + # config already have keys like "#{k}=" + config.each{|k, v| component.send(k, v)} + end def name_hack namespace if namespace diff --git a/spec/config_spec.rb b/spec/config_spec.rb new file mode 100644 index 0000000..499a148 --- /dev/null +++ b/spec/config_spec.rb @@ -0,0 +1,27 @@ +def p; end +require 'spec_helper' + +describe "Configuration" do + before{self.micon = Micon::Core.new} + after{micon.runtime_path = nil} + + it "should configure component if config provided" do + micon.register(:logger){OpenStruct.new} + with_load_path "#{spec_dir}/basic/lib" do + micon[:logger].level.should == :info + end + end + + it "should merge in order: conf <- conf.mode <- runtime <- runtime.mode" do + micon.register(:object){OpenStruct.new} + with_load_path "#{spec_dir}/order/lib" do + micon.runtime_path = "#{spec_dir}/order/app" + micon.mode_name = :production + micon[:object].tap do |o| + o.a.should == 'object.production.yml' + o.b.should == 'runtime.object.yml' + o.c.should == 'runtime.object.production.yml' + end + end + end +end \ No newline at end of file diff --git a/spec/config_spec/basic/lib/components/logger.yml b/spec/config_spec/basic/lib/components/logger.yml new file mode 100644 index 0000000..534ce2d --- /dev/null +++ b/spec/config_spec/basic/lib/components/logger.yml @@ -0,0 +1 @@ +level: :info \ No newline at end of file diff --git a/spec/config_spec/order/app/config/object.production.yml b/spec/config_spec/order/app/config/object.production.yml new file mode 100644 index 0000000..df38c2e --- /dev/null +++ b/spec/config_spec/order/app/config/object.production.yml @@ -0,0 +1 @@ +c: 'runtime.object.production.yml' \ No newline at end of file diff --git a/spec/config_spec/order/app/config/object.yml b/spec/config_spec/order/app/config/object.yml new file mode 100644 index 0000000..db1cfa9 --- /dev/null +++ b/spec/config_spec/order/app/config/object.yml @@ -0,0 +1,2 @@ +b: 'runtime.object.yml' +c: 'runtime.object.yml' \ No newline at end of file diff --git a/spec/config_spec/order/lib/components/object.production.yml b/spec/config_spec/order/lib/components/object.production.yml new file mode 100644 index 0000000..c1d2a22 --- /dev/null +++ b/spec/config_spec/order/lib/components/object.production.yml @@ -0,0 +1,3 @@ +a: 'object.production.yml' +b: 'object.production.yml' +c: 'object.production.yml' \ No newline at end of file diff --git a/spec/config_spec/order/lib/components/object.yml b/spec/config_spec/order/lib/components/object.yml new file mode 100644 index 0000000..2142f74 --- /dev/null +++ b/spec/config_spec/order/lib/components/object.yml @@ -0,0 +1,3 @@ +a: 'object.yml' +b: 'object.yml' +c: 'object.yml' \ No newline at end of file diff --git a/spec/micelaneous_spec.rb b/spec/micelaneous_spec.rb index c48a140..118f765 100644 --- a/spec/micelaneous_spec.rb +++ b/spec/micelaneous_spec.rb @@ -18,7 +18,7 @@ # end end - describe "complex circullar dependencies" do + describe "complex circullar dependencies" do it "should not initialize twice (from error)" do micon.register :kit do micon[:kit] @@ -37,7 +37,7 @@ 'router' end - -> {micon[:router]}.should raise_error(/component :router used before it's initialization is finished/) + -> {micon[:router]}.should raise_error(/component .* used before it's initialization is finished/) end it "should allow to use circullar dependency in :after callback" do @@ -52,6 +52,22 @@ end micon[:kit].should == 'kit' end + + it "should allow circullar dependencies in :after callback" do + micon.register :environment do + 'environment' + end + + micon.register :router, depends_on: :environment do + 'router' + end + + micon.after :environment do + micon[:router] + end + + micon[:router] + end end it "helper method generation" do