micon 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rake_ext'
2
2
 
3
3
  project(
4
4
  name: "micon",
5
- version: "0.1.7",
5
+ gem: true,
6
6
  summary: "Assembles and Manages Components of Your Application",
7
7
 
8
8
  author: "Alexey Petrushin",
@@ -1,3 +1,5 @@
1
+ require 'yaml'
2
+
1
3
  module Micon
2
4
  end
3
5
 
@@ -5,6 +7,7 @@ end
5
7
  support
6
8
 
7
9
  metadata
10
+ config
8
11
  core
9
12
  helper
10
13
 
@@ -0,0 +1,46 @@
1
+ class Micon::Config
2
+ attr_reader :micon, :name
3
+ def initialize micon, name
4
+ @micon, @name = micon, name
5
+ end
6
+
7
+ def load
8
+ files = []
9
+ files.push *config_paths.collect{|path| find_file(path, $LOAD_PATH)}
10
+ files.push *runtime_config_paths.collect{|path| find_file(path, [micon.runtime_path])} if micon.runtime_path?
11
+
12
+ config = {}
13
+ files.compact.each do |f|
14
+ c = YAML.load_file(f)
15
+ next unless c
16
+ raise "component config must be a Hash (#{f})!" unless c.is_a? Hash
17
+ c.each{|k, v| config[:"#{k}="] = v}
18
+ end
19
+
20
+ config.empty? ? nil : config
21
+ end
22
+
23
+ protected
24
+ def config_paths
25
+ fs_name = name.to_s.gsub(/::/, '/')
26
+ paths = ["/components/#{fs_name}.yml"]
27
+ paths << "/components/#{fs_name}.#{micon.mode}.yml" if micon.mode?
28
+ paths
29
+ end
30
+
31
+ def runtime_config_paths
32
+ fs_name = name.to_s.gsub(/::/, '/')
33
+ paths = ["/config/#{fs_name}.yml"]
34
+ if micon.mode?
35
+ paths << "/config/#{fs_name}.#{micon.mode}.yml"
36
+ paths << "/config/#{micon.mode}/#{fs_name}.yml"
37
+ end
38
+ paths
39
+ end
40
+
41
+ def find_file path, directories
42
+ files = directories.collect{|dir| "#{dir}#{path}"}.select{|f| File.exist? f}
43
+ raise "multiple configs for :#{name} component" if files.size > 1
44
+ files.first
45
+ end
46
+ end
@@ -107,66 +107,6 @@ class Micon::Core
107
107
  end
108
108
  end
109
109
 
110
- # def get_constant_component key
111
- # sname = @registry[key] || autoload_component_definition(key, false)
112
- #
113
- # case sname
114
- # when nil
115
- # nil
116
- # when :instance
117
- # must_be.never_called
118
- # when :application
119
- # return nil unless @constants.include? key
120
- #
121
- # o = @application[key]
122
- # unless o
123
- # return create_object(key, @application)
124
- # else
125
- # return o
126
- # end
127
- # else # custom
128
- # must_be.never_called
129
- # end
130
- # end
131
- #
132
- # def get_constant namespace, const
133
- # original_namespace = namespace
134
- # namespace = nil if namespace == Object or namespace == Module
135
- # target_namespace = namespace
136
- #
137
- # # Name hack (for anonymous classes)
138
- # namespace = eval "#{name_hack(namespace)}" if namespace
139
- #
140
- # class_name = namespace ? "#{namespace.name}::#{const}" : const
141
- #
142
- # simple_also_tried = false
143
- # begin
144
- # simple_also_tried = (namespace == nil)
145
- #
146
- # if result = get_constant_component(class_name.to_sym)
147
- # if @loaded_classes.include?(class_name)
148
- # raise_without_self "something wrong is goin on, constant '#{const}' in '#{original_namespace}' namespace already has been defined!"
149
- # end
150
- #
151
- # real_namespace = namespace ? namespace : Object
152
- # if real_namespace.const_defined?(const)
153
- # raise_without_self "component trying to redefine constant '#{const}' that already defined in '#{real_namespace}'!"
154
- # end
155
- #
156
- # real_namespace.const_set const, result
157
- #
158
- # @loaded_classes[class_name] = [real_namespace, const]
159
- #
160
- # return result
161
- # elsif namespace
162
- # namespace = Module.namespace_for(namespace.name)
163
- # class_name = namespace ? "#{namespace.name}::#{const}" : const
164
- # end
165
- # end until simple_also_tried
166
- #
167
- # return nil
168
- # end
169
-
170
110
  def []= key, value
171
111
  raise "can't assign nill as :#{key} component!" unless value
172
112
 
@@ -296,6 +236,7 @@ class Micon::Core
296
236
  # I intentially broke the Metadata incapsulation to provide better performance, don't refactor it.
297
237
  @registry = {} # @loaded_classes, @constants = {}, {}
298
238
  @metadata = Micon::Metadata.new(@registry)
239
+ @stack = {}
299
240
 
300
241
  @application, @custom_scopes = {}, {}
301
242
 
@@ -319,59 +260,85 @@ class Micon::Core
319
260
  # @loaded_classes.clear
320
261
  end
321
262
 
263
+ #
264
+ # :mode, :runtime_path, used in component configuration
265
+ # - 'app/runtime'
266
+ # - :development, :test, :production
267
+ #
268
+ def runtime_path; @runtime_path || raise(":runtime_path not defined!") end
269
+ def runtime_path= runtime_path
270
+ runtime_path, force = runtime_path
271
+ raise "some components has been already initialized before You set :runtime_path!" unless empty? or force
272
+ @runtime_path = runtime_path
273
+ end
274
+ def runtime_path?; !!@runtime_path end
275
+
276
+ def mode; @mode || raise(":mode not defined!") end
277
+ def mode= mode
278
+ mode, force = mode
279
+ raise "some components has been already initialized before You set :mode!" unless empty? or force
280
+ @mode = mode
281
+ end
282
+ def mode?; !!@mode end
283
+
322
284
  protected
323
285
  def autoload_component_definition key, bang = true
324
286
  begin
325
287
  load "components/#{key.to_s.gsub(/::/, '/')}.rb"
326
288
  rescue LoadError
327
- end
289
+ end
328
290
  sname = @registry[key]
329
291
  raise_without_self "'#{key}' component not managed!" if bang and !sname
330
292
  sname
331
293
  end
332
294
 
333
295
  def create_object key, container = nil
334
- initializer, dependencies = @metadata.initializers[key]
296
+ initializer, dependencies, config = @metadata.initializers[key]
335
297
  raise "no initializer for :#{key} component!" unless initializer
336
-
337
- dependencies.each{|d| self[d]}
338
- @metadata.call_before key
339
-
340
- if container
341
- unless o = container[key]
342
- o = initializer.call
343
- container[key] = o
344
- else
345
- # complex case, there's an circular dependency, and the 'o' already has been
346
- # initialized in dependecies or callbacks
347
- # here's the sample case:
348
- #
349
- # app.register :environment, :application do
350
- # p :environment
351
- # 'environment'
352
- # end
353
- #
354
- # app.register :conveyors, :application, depends_on: :environment do
355
- # p :conveyors
356
- # 'conveyors'
357
- # end
358
- #
359
- # app.after :environment do
360
- # app[:conveyors]
361
- # end
362
- #
363
- # app[:conveyors]
364
-
365
- o = container[key]
298
+
299
+ raise "component :#{key} used before it's initialization is finished!" if @stack.include? key
300
+ begin
301
+ dependencies.each{|d| self[d]}
302
+ @metadata.call_before key
303
+
304
+ # we need to check container first, in complex cases (circullar dependency)
305
+ # the object already may be initialized.
306
+ # See "should allow to use circullar dependency in :after callback".
307
+ @stack[key] = true
308
+ o = (container && container[key]) || initializer.call
309
+
310
+ unless config == false
311
+ unless config
312
+ # loading and caching config
313
+ config = get_config key
314
+ config = false unless config # we use false to differentiate from nil
315
+ @metadata.initializers[key] = [initializer, dependencies, config]
316
+
317
+ apply_config o, config if config
318
+ else
319
+ apply_config o, config
320
+ end
366
321
  end
367
- else
368
- o = initializer.call
322
+
323
+ container[key] = o if container
324
+
325
+ raise "initializer for component :#{key} returns nill!" unless o
326
+ ensure
327
+ @stack.delete key
369
328
  end
370
- raise "initializer for component :#{key} returns nill!" unless o
371
-
329
+
372
330
  @metadata.call_after key, o
373
- o
374
- end
331
+ o
332
+ end
333
+
334
+ def get_config key
335
+ ::Micon::Config.new(self, key).load
336
+ end
337
+
338
+ def apply_config component, config
339
+ # config already have keys like "#{k}="
340
+ config.each{|k, v| component.send(k, v)}
341
+ end
375
342
 
376
343
  def name_hack namespace
377
344
  if namespace
data/readme.md CHANGED
@@ -6,6 +6,8 @@ Concentrate on business logic and interfaces and Micon will provide automatic co
6
6
 
7
7
  Technically it's [IoC][ioc] like framework with components, callbacks, scopes and bijections, inspired by Spring and JBoss Seam.
8
8
 
9
+ Is it usefull, is there any real-life application? - I'm using it as a heart of my [web framework][rad_core], this site http://ruby-lang.info for example powered with it.
10
+
9
11
  ## Usage
10
12
 
11
13
  Let's suppose you are building the Ruby on Rails clone, there are lots of modules let's try to deal with them
@@ -89,4 +91,5 @@ there's no multithreading in ruby.
89
91
 
90
92
  Copyright (c) Alexey Petrushin [http://4ire.net](http://4ire.net), released under the MIT license.
91
93
 
92
- [ioc]: http://en.wikipedia.org/wiki/Inversion_of_control
94
+ [ioc]: http://en.wikipedia.org/wiki/Inversion_of_control
95
+ [rad_core]: https://github.com/alexeypetrushin/rad_core
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Configuration" do
4
+ before{self.micon = Micon::Core.new}
5
+
6
+ it "should configure component if config provided" do
7
+ micon.register(:logger){OpenStruct.new}
8
+ with_load_path "#{spec_dir}/basic/lib" do
9
+ micon[:logger].level.should == :info
10
+ end
11
+ end
12
+
13
+ it "should merge in order: conf <- conf.mode <- runtime <- runtime.mode" do
14
+ micon.register(:object){OpenStruct.new}
15
+ with_load_path "#{spec_dir}/order/lib" do
16
+ micon.runtime_path = "#{spec_dir}/order/app"
17
+ micon.mode = :production
18
+ micon[:object].tap do |o|
19
+ o.a.should == 'object.production.yml'
20
+ o.b.should == 'runtime.object.yml'
21
+ o.c.should == 'runtime.object.production.yml'
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1 @@
1
+ c: 'runtime.object.production.yml'
@@ -0,0 +1,2 @@
1
+ b: 'runtime.object.yml'
2
+ c: 'runtime.object.yml'
@@ -0,0 +1,3 @@
1
+ a: 'object.production.yml'
2
+ b: 'object.production.yml'
3
+ c: 'object.production.yml'
@@ -0,0 +1,3 @@
1
+ a: 'object.yml'
2
+ b: 'object.yml'
3
+ c: 'object.yml'
@@ -3,13 +3,8 @@ require 'spec_helper'
3
3
  describe "Micelaneous" do
4
4
  with_load_path "#{spec_dir}/autoload/lib"
5
5
 
6
- before do
7
- self.micon = Micon::Core.new
8
- end
9
-
10
- after do
11
- remove_constants :TheRouter, :TheRad
12
- end
6
+ before{self.micon = Micon::Core.new}
7
+ after{remove_constants :TheRouter, :TheRad}
13
8
 
14
9
  describe "autoloading" do
15
10
  it "should autoload component definition" do
@@ -22,27 +17,57 @@ describe "Micelaneous" do
22
17
  # TheRad::TheView.should == "TheView"
23
18
  # end
24
19
  end
25
-
26
- it "should not initialize twice (from error)" do
27
- check = mock
28
- check.should_receive(:environment).once.ordered
29
- check.should_receive(:router).once.ordered
20
+
21
+ describe "complex circullar dependencies" do
22
+ it "should not initialize twice (from error)" do
23
+ micon.register :kit do
24
+ micon[:kit]
25
+ 'kit'
26
+ end
27
+ lambda{micon[:kit]}.should raise_error(/component :kit used before it's initialization is finished/)
28
+ end
30
29
 
31
- micon.register :environment do
32
- check.environment
33
- 'environment'
30
+ it "should not initialize twice if called from dependency (from error)" do
31
+ micon.register :environment do
32
+ micon[:router]
33
+ 'environment'
34
+ end
35
+
36
+ micon.register :router, depends_on: :environment do
37
+ 'router'
38
+ end
39
+
40
+ -> {micon[:router]}.should raise_error(/component .* used before it's initialization is finished/)
34
41
  end
35
42
 
36
- micon.register :router, depends_on: :environment do
37
- check.router
38
- 'router'
43
+ it "should allow to use circullar dependency in :after callback" do
44
+ check = mock
45
+ check.should_receive(:initialized).once
46
+ micon.register :kit do
47
+ check.initialized
48
+ 'kit'
49
+ end
50
+ micon.after :kit do
51
+ micon[:kit]
52
+ end
53
+ micon[:kit].should == 'kit'
39
54
  end
40
- micon.after :environment do
41
- # some code that needs :router
42
- micon[:router]
43
- end
44
55
 
45
- micon[:router]
56
+ it "should allow circullar dependencies in :after callback" do
57
+ micon.register :environment do
58
+ 'environment'
59
+ end
60
+
61
+ micon.register :router, depends_on: :environment do
62
+ 'router'
63
+ end
64
+
65
+ micon.after :environment do
66
+ micon[:router]
67
+ end
68
+
69
+ micon[:router]
70
+ end
46
71
  end
47
72
 
48
73
  it "helper method generation" do
@@ -1,13 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe "Application and Instance scopes" do
4
- before do
5
- self.micon = Micon::Core.new
6
- end
4
+ before{self.micon = Micon::Core.new}
7
5
 
8
6
  it "dependencies" do
9
7
  micon.register(:another_object, depends_on: :the_object){"another_object"}
10
- -> {micon[:another_object]}.should raise_error(/the_object/)
8
+ -> {micon[:another_object]}.should raise_error(/the_object.*not managed/)
11
9
  micon.register(:the_object){"the_object"}
12
10
  micon[:another_object]
13
11
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: micon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-06-17 00:00:00.000000000 +04:00
12
+ date: 2011-07-04 00:00:00.000000000 +04:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
  description:
@@ -21,6 +21,7 @@ files:
21
21
  - Rakefile
22
22
  - readme.md
23
23
  - lib/micon/class.rb
24
+ - lib/micon/config.rb
24
25
  - lib/micon/core.rb
25
26
  - lib/micon/helper.rb
26
27
  - lib/micon/metadata.rb
@@ -30,6 +31,12 @@ files:
30
31
  - lib/micon/support.rb
31
32
  - lib/micon.rb
32
33
  - spec/callbacks_spec.rb
34
+ - spec/config_spec/basic/lib/components/logger.yml
35
+ - spec/config_spec/order/app/config/object.production.yml
36
+ - spec/config_spec/order/app/config/object.yml
37
+ - spec/config_spec/order/lib/components/object.production.yml
38
+ - spec/config_spec/order/lib/components/object.yml
39
+ - spec/config_spec.rb
33
40
  - spec/constants_spec/get_constant_component/lib/components/TheController.rb
34
41
  - spec/constants_spec.rb
35
42
  - spec/custom_scope_spec.rb