micon 0.1.1

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 ADDED
@@ -0,0 +1,56 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+
4
+ Dir.chdir File.dirname(__FILE__)
5
+
6
+ # Specs
7
+ task :default => :spec
8
+
9
+ Spec::Rake::SpecTask.new('spec') do |t|
10
+ t.spec_files = FileList["spec/**/*_spec.rb"].select{|f| f !~ /\/_/}
11
+ t.libs = ['lib'].collect{|f| "#{File.dirname __FILE__}/#{f}"}
12
+ end
13
+
14
+ # Gem
15
+ require 'rake/clean'
16
+ require 'rake/gempackagetask'
17
+ require 'fileutils'
18
+
19
+ spec = Gem::Specification.new do |s|
20
+ s.name = "micon"
21
+ s.version = "0.1.1"
22
+ s.summary = "Micro Container assembles and manages pieces of your application"
23
+ s.description = "Micro Container assembles and manages pieces of your application"
24
+ s.author = "Alexey Petrushin"
25
+ s.homepage = "http://github.com/alexeypetrushin/micon"
26
+ s.platform = Gem::Platform::RUBY
27
+ s.has_rdoc = true
28
+ s.files = (%w{Rakefile readme.md} + Dir.glob("{lib,spec}/**/*"))
29
+ # s.add_dependency "ruby-ext"
30
+ s.require_path = "lib"
31
+ end
32
+
33
+ PACKAGE_DIR = "#{File.expand_path File.dirname(__FILE__)}/build"
34
+
35
+ Rake::GemPackageTask.new(spec) do |p|
36
+ package_dir = PACKAGE_DIR
37
+ # FileUtils.mkdir package_dir unless File.exist? package_dir
38
+ p.need_tar = true if RUBY_PLATFORM !~ /mswin/
39
+ p.need_zip = true
40
+ p.package_dir = package_dir
41
+ end
42
+
43
+ # CLEAN.include [ 'pkg', '*.gem']
44
+
45
+ task :push do
46
+ dir = Dir.chdir PACKAGE_DIR do
47
+ gem_file = Dir.glob("micon*.gem").first
48
+ system "gem push #{gem_file}"
49
+ end
50
+ end
51
+
52
+ task :clean do
53
+ system "rm -r #{PACKAGE_DIR}"
54
+ end
55
+
56
+ task :release => [:gem, :push, :clean]
data/lib/micon.rb ADDED
@@ -0,0 +1,9 @@
1
+ %w{
2
+ support
3
+
4
+ metadata
5
+ micon
6
+
7
+ module
8
+ class
9
+ }.each{|f| require "micon/#{f}"}
@@ -0,0 +1,7 @@
1
+ class Class
2
+ # register_as :session, :scope => :request
3
+ # register_as :loggger
4
+ def register_as *args
5
+ ::Micon.register(*args){self.new}
6
+ end
7
+ end
@@ -0,0 +1,132 @@
1
+ #
2
+ # This class intentially made using "wired and not clear code", to provide better performance.
3
+ #
4
+ module Micon
5
+ class Metadata
6
+ attr_accessor :registry, :initializers, :before, :after
7
+
8
+ def initialize registry, sync
9
+ @registry, @sync = registry, sync
10
+ @before, @after, @before_scope, @after_scope, @initializers = {}, {}, {}, {}, {}
11
+ end
12
+
13
+ def clear
14
+ @sync.synchronize do
15
+ @registry.clear
16
+ @initializers.clear
17
+ @before.clear
18
+ @after.clear
19
+ end
20
+ end
21
+
22
+ def delete key
23
+ @sync.synchronize do
24
+ @registry.delete key
25
+ @initializers.delete key
26
+ @before.delete key
27
+ @after.delete key
28
+ end
29
+ end
30
+
31
+
32
+ #
33
+ # Registry
34
+ #
35
+ def [] key
36
+ @sync.synchronize{@registry[key]}
37
+ end
38
+
39
+ # def []= key, value
40
+ # @sync.synchronize{@registry[key] = value}
41
+ # end
42
+
43
+ def include? key
44
+ @sync.synchronize{@registry.include? key}
45
+ end
46
+
47
+
48
+ #
49
+ # Callbacks
50
+ #
51
+ def register_before key, &block
52
+ @sync.synchronize do
53
+ raise "you should provide block!" unless block
54
+ (@before[key] ||= []) << block
55
+ end
56
+ end
57
+
58
+ def register_after key, &block
59
+ @sync.synchronize do
60
+ raise "you should provide block!" unless block
61
+ (@after[key] ||= []) << block
62
+ end
63
+ end
64
+
65
+ def call_before key
66
+ if callbacks = @before[key]
67
+ callbacks.each{|c| c.call}
68
+ end
69
+ end
70
+
71
+ def call_after key, object
72
+ if callbacks = @after[key]
73
+ callbacks.each{|c| c.call(object)}
74
+ end
75
+ end
76
+
77
+
78
+ #
79
+ # Scope callbacks
80
+ #
81
+ def register_before_scope key, &block
82
+ @sync.synchronize do
83
+ raise "you should provide block!" unless block
84
+ (@before_scope[key] ||= []) << block
85
+ end
86
+ end
87
+
88
+ def register_after_scope key, &block
89
+ @sync.synchronize do
90
+ raise "you should provide block!" unless block
91
+ (@after_scope[key] ||= []) << block
92
+ end
93
+ end
94
+
95
+ def call_before_scope key
96
+ if callbacks = @before_scope[key]
97
+ callbacks.each{|c| c.call}
98
+ end
99
+ end
100
+
101
+ def call_after_scope key
102
+ if callbacks = @after_scope[key]
103
+ callbacks.each{|c| c.call}
104
+ end
105
+ end
106
+
107
+ def with_scope_callbacks key, &block
108
+ call_before_scope key
109
+ result = block.call
110
+ call_after_scope key
111
+ result
112
+ end
113
+
114
+
115
+ #
116
+ # Other
117
+ #
118
+ # def inspect
119
+ # "Registry: " + self.registry.keys.inspect
120
+ # end
121
+
122
+ # def deep_clone
123
+ # m = Metadata.new @sync
124
+ # m.registry = {}
125
+ # registry.each do |k, v|
126
+ # m.registry[k] = v
127
+ # end
128
+ # p m
129
+ # m
130
+ # end
131
+ end
132
+ end
@@ -0,0 +1,250 @@
1
+ # Predefined scopes are: :application | :session | :thread | :instance | :"custom_name"
2
+ #
3
+ # Micons :"custom_name" are managed by 'scope_begin' / 'scope_end' methods
4
+ #
5
+ # :"custom_name" can't be nested (it will destroy old and start new one) and always should be explicitly started!.
6
+
7
+ module Micon
8
+ SYNC, MSYNC = Monitor.new, Monitor.new
9
+
10
+ # quick access to Metadata inner variable.
11
+ # I intentially broke the Metadata incapsulation to provide better performance, don't refactor it.
12
+ @_r = {}
13
+
14
+ @application, @metadata = {}, Metadata.new(@_r, MSYNC)
15
+
16
+ class << self
17
+ attr_accessor :metadata
18
+
19
+ #
20
+ # Scope Management
21
+ #
22
+ def activate scope, container, &block
23
+ raise_without_self "Only custom scopes can be activated!" if scope == :application or scope == :instance
24
+ raise "container should have type of Hash but has #{container.class.name}" unless container.is_a? Hash
25
+
26
+ scope_with_prefix = add_prefix(scope)
27
+ raise_without_self "Scope '#{remove_prefix(scope)}' already active!" if !block and Thread.current[scope_with_prefix]
28
+
29
+ if block
30
+ begin
31
+ outer_container_or_nil = Thread.current[scope_with_prefix]
32
+ Thread.current[scope_with_prefix] = container
33
+ @metadata.with_scope_callbacks scope, &block
34
+ ensure
35
+ Thread.current[scope_with_prefix] = outer_container_or_nil
36
+ end
37
+ else
38
+ # not support nested scopes without block
39
+ Thread.current[scope_with_prefix] = container
40
+ @metadata.call_before_scope scope
41
+ end
42
+ end
43
+
44
+ def deactivate scope
45
+ raise_without_self "Only custom scopes can be deactivated!" if scope == :application or scope == :instance
46
+
47
+ scope_with_prefix = add_prefix(scope)
48
+ raise_without_self "Scope '#{scope}' not active!" unless container = Thread.current[scope_with_prefix]
49
+
50
+ @metadata.call_after_scope scope
51
+ Thread.current[scope_with_prefix] = nil
52
+ container
53
+ end
54
+
55
+ def active? scope
56
+ if scope == :application or scope == :instance
57
+ true
58
+ else
59
+ Thread.current.key?(add_prefix(scope))
60
+ end
61
+ end
62
+
63
+ def clear
64
+ SYNC.synchronize{@application.clear}
65
+ Thread.current.keys.each do |key|
66
+ Thread.current[key] = nil if key.to_s =~ /^mc_/
67
+ end
68
+ end
69
+
70
+ def empty?
71
+ return false unless SYNC.synchronize{@application.empty?}
72
+ Thread.current.keys.each do |key|
73
+ return false if key.to_s =~ /^mc_/
74
+ end
75
+ return true
76
+ end
77
+
78
+
79
+ #
80
+ # Object Management
81
+ #
82
+ def include? key
83
+ scope = MSYNC.synchronize{@_r[key]}
84
+
85
+ case scope
86
+ when nil
87
+ raise_without_self "'#{key}' component not managed!"
88
+ when :instance
89
+ true
90
+ when :application
91
+ SYNC.synchronize do
92
+ @application.include? key
93
+ end
94
+ else # custom
95
+ container = Thread.current[scope]
96
+ return false unless container
97
+ container.include? key
98
+ end
99
+ end
100
+
101
+ def [] key
102
+ scope = MSYNC.synchronize{@_r[key]}
103
+
104
+ case scope
105
+ when nil
106
+ raise_without_self "'#{key}' component not managed!"
107
+ when :instance
108
+ return create_object(key)
109
+ when :application
110
+ SYNC.synchronize do
111
+ o = @application[key]
112
+ unless o
113
+ return create_object(key, @application)
114
+ else
115
+ return o
116
+ end
117
+ end
118
+ else # custom
119
+ container = Thread.current[scope]
120
+ raise_without_self "Scope '#{remove_prefix(scope)}' not started!" unless container
121
+ o = container[key]
122
+ unless o
123
+ return create_object(key, container)
124
+ else
125
+ return o
126
+ end
127
+ end
128
+ end
129
+
130
+ def []= key, value
131
+ scope = MSYNC.synchronize{@_r[key]}
132
+
133
+ case scope
134
+ when nil
135
+ raise_without_self "'#{key}' component not managed!"
136
+ when :instance
137
+ raise_without_self "You can't outject variable with the 'instance' scope!"
138
+ when :application
139
+ SYNC.synchronize{@application[key] = value}
140
+ else # Custom
141
+ container = Thread.current[scope]
142
+ raise_without_self "Scope '#{remove_prefix(scope)}' not started!" unless container
143
+ container[key] = value
144
+ end
145
+ end
146
+
147
+
148
+ #
149
+ # Metadata
150
+ #
151
+ def register key, options = {}, &initializer
152
+ raise "key should not be nil or false value!" unless key
153
+ options = options.symbolize_keys
154
+
155
+ scope = options.delete(:scope) || :application
156
+ scope = Micon.add_prefix(scope) unless scope == :application or scope == :instance
157
+ dependencies = Array(options.delete(:require) || options.delete(:depends_on))
158
+
159
+ options.each{|key| raise "Unknown option :#{key}!"}
160
+
161
+ MSYNC.synchronize do
162
+ unless @_r.object_id == @metadata.registry.object_id
163
+ raise "internal error, reference to registry aren't equal to actual registry!"
164
+ end
165
+ @metadata.registry[key] = scope
166
+ @metadata.initializers[key] = [(initializer || lambda{nil}), dependencies]
167
+ end
168
+ end
169
+
170
+ def unregister key
171
+ @metadata.delete key
172
+ end
173
+
174
+ def before component, &block
175
+ @metadata.register_before component, &block
176
+ end
177
+
178
+ def after component, &block
179
+ @metadata.register_after component, &block
180
+ end
181
+
182
+ def before_scope scope, &block
183
+ @metadata.register_before_scope scope, &block
184
+ end
185
+
186
+ def after_scope scope, &block
187
+ @metadata.register_after_scope scope, &block
188
+ end
189
+
190
+ # handy method, usually for test purposes
191
+ def swap_metadata metadata = nil
192
+ metadata ||= Metadata.new({}, MSYNC)
193
+ old = self.metadata
194
+
195
+ self.metadata = metadata
196
+ @_r = metadata.registry
197
+
198
+ old
199
+ end
200
+
201
+ protected
202
+ def create_object key, container = nil
203
+ initializer, dependencies = MSYNC.synchronize{@metadata.initializers[key]}
204
+ dependencies.each{|d| Micon[d]}
205
+ @metadata.call_before key
206
+
207
+ if container
208
+ unless o = container[key]
209
+ o = initializer.call
210
+ container[key] = o
211
+ else
212
+ # complex case, there's an circular dependency, and the 'o' already has been
213
+ # initialized in dependecies or callbacks
214
+ # here's the sample case:
215
+ #
216
+ # Micon.register :environment, :application do
217
+ # p :environment
218
+ # 'environment'
219
+ # end
220
+ #
221
+ # Micon.register :conveyors, :application, :depends_on => :environment do
222
+ # p :conveyors
223
+ # 'conveyors'
224
+ # end
225
+ #
226
+ # Micon.after :environment do
227
+ # Micon[:conveyors]
228
+ # end
229
+ #
230
+ # Micon[:conveyors]
231
+
232
+ o = container[key]
233
+ end
234
+ else
235
+ o = initializer.call
236
+ end
237
+
238
+ @metadata.call_after key, o
239
+ o
240
+ end
241
+
242
+ def add_prefix scope
243
+ :"mc_#{scope}"
244
+ end
245
+
246
+ def remove_prefix scope
247
+ scope.to_s.gsub(/^mc_/, '')
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,27 @@
1
+ class Module
2
+ # inject :attribute => :session
3
+ def inject attributes
4
+ Micon.raise_without_self "Invalid argument!" unless attributes.is_a? Hash
5
+ attributes.each do |name, specificator|
6
+ Micon.raise_without_self "Attribute name should be a Symbol!" unless name.is_a? Symbol
7
+
8
+ if [Class, Module].include? specificator.class
9
+ specificator = specificator.name
10
+ elsif specificator.is_a? Symbol
11
+ specificator = ":#{specificator}"
12
+ else
13
+ specificator = "\"#{specificator}\""
14
+ end
15
+
16
+ script = %{\
17
+ def #{name}
18
+ ::Micon[#{specificator}]
19
+ end
20
+
21
+ def #{name}= value
22
+ ::Micon[#{specificator}] = value
23
+ end}
24
+ self.class_eval script, __FILE__, __LINE__
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ require 'monitor'
2
+
3
+ module Micon
4
+ def self.raise_without_self message
5
+ raise RuntimeError, message, caller.select{|path| path !~ /\/lib\/micon\//}
6
+ end
7
+ end
8
+
9
+ unless {}.respond_to? :symbolize_keys
10
+ class Hash
11
+ def symbolize_keys
12
+ r = {}
13
+ each{|k, v| r[k.to_sym] = v}
14
+ r
15
+ end
16
+ end
17
+ end
data/readme.md ADDED
@@ -0,0 +1,79 @@
1
+ # Micro Container - assembles and manages pieces of your application
2
+
3
+ Micon is infrastructural component, invisible to user and it's main goal is to simplify development. It reduces complex monolithic application to set of simple low coupled components.
4
+
5
+ Concentrate on business logic and interfaces and Micon will provide automatic configuration, life cycle management and dependency resolving.
6
+
7
+ Technically it's like [IoC][ioc] container managing components, callbacks, scopes and bijections, inspired by Spring and JBoss Seam.
8
+
9
+ ## Usage
10
+
11
+ Let's suppose you are building the Ruby on Rails clone, there are lots of modules let's try to deal with them
12
+
13
+ require 'micon'
14
+ def app; Micon end
15
+
16
+ # static (singleton) components
17
+ class Environment
18
+ register_as :environment
19
+ end
20
+
21
+ class Logger
22
+ register_as :logger
23
+
24
+ def info msg; end
25
+ end
26
+
27
+ class Router
28
+ register_as :router
29
+
30
+ def parse rote_filename
31
+ # do something
32
+ end
33
+ end
34
+
35
+ # callbacks, we need to parse routes right after environment will be initialized
36
+ app.after :environment do
37
+ app[:router].parse '/config/routes.rb'
38
+ end
39
+
40
+ # dynamic components, will be created and destroyed for every request
41
+ class Request
42
+ register_as :request, :scope => :request
43
+ end
44
+
45
+ class Application
46
+ # injecting components into attributes
47
+ inject :request => :request, :logger => :logger
48
+
49
+ def do_business
50
+ # now we can use injected component
51
+ logger.info "routes parsed"
52
+ do_something_with request
53
+ logger.info 'well done'
54
+ end
55
+
56
+ def do_something_with request; end
57
+ end
58
+
59
+ # Web Server / Rack Adapter
60
+ class RackAdapter
61
+ def call env
62
+ # activating new request scope, the session component will be created and destroyed automatically
63
+ app.activate :request, {} do
64
+ Application.new.do_business
65
+ end
66
+ end
67
+ end
68
+
69
+ RackAdapter.new.call({})
70
+
71
+ For actual code go to spec/overview_spec.rb
72
+
73
+ ## Installation
74
+
75
+ $ sudo gem install micon
76
+
77
+ Copyright (c) 2009 Alexey Petrushin [http://bos-tec.com](http://bos-tec.com), released under the MIT license.
78
+
79
+ [ioc]: http://en.wikipedia.org/wiki/Inversion_of_control
@@ -0,0 +1,56 @@
1
+ dir = File.expand_path(File.dirname(__FILE__))
2
+ require "#{dir}/helper"
3
+
4
+ describe "Callbacks" do
5
+ before :each do
6
+ Micon.clear
7
+ Micon.metadata.clear
8
+ end
9
+
10
+ describe "components callbacs" do
11
+ it "basic" do
12
+ Micon.register(:the_object){"The Object"}
13
+
14
+ check = mock
15
+ check.should_receive(:done)
16
+ Micon.before :the_object do
17
+ check.done
18
+ end
19
+
20
+ Micon.after :the_object do |o|
21
+ o << " updated"
22
+ end
23
+ Micon.after :the_object do |o|
24
+ o << " even more updated"
25
+ end
26
+
27
+ Micon[:the_object].should == "The Object updated even more updated"
28
+ end
29
+
30
+ it "should be able reference to the component itself inside of after filter (cycle reference)" do
31
+ Micon.register(:the_object){"The Object"}
32
+ check = nil
33
+ Micon.after :the_object do
34
+ check = Micon[:the_object]
35
+ end
36
+ Micon[:the_object]
37
+ check.should == "The Object"
38
+ end
39
+ end
40
+
41
+ describe "custom scope callbacks" do
42
+ it "scope :before and :after callbacks" do
43
+ check = mock
44
+ check.should_receive(:before).ordered
45
+ check.should_receive(:run).ordered
46
+ check.should_receive(:after).ordered
47
+ check.should_receive(:after2).ordered
48
+
49
+ Micon.before_scope(:custom){check.before}
50
+ Micon.after_scope(:custom){check.after}
51
+ Micon.after_scope(:custom){check.after2}
52
+
53
+ Micon.activate(:custom, {}){check.run}
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,76 @@
1
+ dir = File.expand_path(File.dirname(__FILE__))
2
+ require "#{dir}/helper"
3
+
4
+ describe "Micon custom scope" do
5
+ before :each do
6
+ Micon.clear
7
+ Micon.metadata.clear
8
+ end
9
+
10
+ it "activate" do
11
+ container = {}
12
+ Micon.should_not be_active(:custom)
13
+ Micon.activate :custom, container
14
+ Micon.should be_active(:custom)
15
+
16
+ lambda{Micon.activate :custom, container}.should raise_error(/active/)
17
+
18
+ Micon.deactivate :custom
19
+ lambda{Micon.deactivate :custom}.should raise_error(/not active/)
20
+
21
+ Micon.should_not be_active(:custom)
22
+ Micon.activate :custom, container do
23
+ Micon.should be_active(:custom)
24
+ end
25
+ end
26
+
27
+ it "check" do
28
+ Micon.register(:value, :scope => :custom){"The Object"}
29
+ lambda{Micon[:value]}.should raise_error(/not started/)
30
+ lambda{Micon[:value] = nil}.should raise_error(/not started/)
31
+ end
32
+
33
+ it "get" do
34
+ Micon.register(:value, :scope => :custom){"The Object"}
35
+ container, the_object = {}, nil
36
+
37
+ Micon.activate :custom, container do
38
+ Micon[:value].should == "The Object"
39
+ the_object = Micon[:value]
40
+ end
41
+
42
+ Micon.activate :custom, {} do
43
+ Micon[:value].object_id.should_not == the_object.object_id
44
+ end
45
+
46
+ Micon.activate :custom, container do
47
+ Micon[:value].object_id.should == the_object.object_id
48
+ end
49
+
50
+ container.size.should == 1
51
+ container[:value].should == the_object
52
+ end
53
+
54
+ it "set" do
55
+ Micon.register(:value, :scope => :custom){"The Object"}
56
+ container = {}
57
+
58
+ Micon.activate :custom, container do
59
+ Micon[:value].should == "The Object"
60
+ Micon[:value] = "Another Object"
61
+ the_object = Micon[:value]
62
+ end
63
+
64
+ Micon.activate :custom, {} do
65
+ Micon[:value].should == "The Object"
66
+ end
67
+
68
+ Micon.activate :custom, container do
69
+ Micon[:value].should == "Another Object"
70
+ end
71
+ end
72
+
73
+ it "scope should return block value (from error)" do
74
+ Micon.activate(:custom, {}){'value'}.should == 'value'
75
+ end
76
+ end
data/spec/helper.rb ADDED
@@ -0,0 +1,6 @@
1
+ lib_dir = "#{File.expand_path("#{File.dirname(__FILE__)}/..")}/lib"
2
+ $LOAD_PATH << lib_dir if lib_dir
3
+
4
+ require "micon"
5
+
6
+ require 'spec'
@@ -0,0 +1,52 @@
1
+ dir = File.expand_path(File.dirname(__FILE__))
2
+ require "#{dir}/helper"
3
+
4
+ describe "Micon Managed" do
5
+ before :all do
6
+ Micon.metadata.clear
7
+
8
+ class ManagedObject
9
+ register_as :managed_object
10
+ inject :object => :object_key
11
+
12
+ class << self
13
+ inject :object => :object_key
14
+ end
15
+ end
16
+ end
17
+
18
+ before :each do
19
+ Micon.clear
20
+ end
21
+
22
+ it "scope" do
23
+ scope = Micon.metadata[:managed_object]
24
+ initializer, dependencies = Micon.metadata.initializers[:managed_object]
25
+ scope.should == :application
26
+ initializer.call.should be_a(ManagedObject)
27
+ end
28
+
29
+ it "injection" do
30
+ the_object = "The Object"
31
+ Micon.register(:object_key){the_object}
32
+
33
+ ManagedObject.object.should == the_object
34
+ o = ManagedObject.new
35
+ o.object.should == the_object
36
+ end
37
+
38
+ it "outjection" do
39
+ the_object = "The Object"
40
+ Micon.register(:object_key)
41
+
42
+ ManagedObject.object.should be_nil
43
+ ManagedObject.object = the_object
44
+ ManagedObject.object.should == the_object
45
+ end
46
+
47
+ it "empty?" do
48
+ Micon.should be_empty
49
+ Micon[:managed_object]
50
+ Micon.should_not be_empty
51
+ end
52
+ end
@@ -0,0 +1,53 @@
1
+ dir = File.expand_path(File.dirname(__FILE__))
2
+ require "#{dir}/helper"
3
+
4
+ describe "Micelaneous" do
5
+ before :each do
6
+ Micon.clear
7
+ Micon.metadata.clear
8
+ end
9
+
10
+ it "swap_metadata" do
11
+ Micon.register :the_object
12
+ Micon.metadata[:the_object].should_not be_nil
13
+ Micon.instance_variable_get("@_r").should include(:the_object)
14
+
15
+ old_metadat = Micon.swap_metadata
16
+
17
+ Micon.metadata[:the_object].should be_nil
18
+ Micon.instance_variable_get("@_r").should_not include(:the_object)
19
+
20
+ Micon.swap_metadata old_metadat
21
+ Micon.metadata[:the_object].should_not be_nil
22
+ Micon.instance_variable_get("@_r").should include(:the_object)
23
+ end
24
+
25
+ it "dependencies" do
26
+ Micon.register :another_object, :depends_on => :the_object
27
+ lambda{Micon[:another_object]}.should raise_error(/the_object/)
28
+ Micon.register :the_object
29
+ Micon[:another_object]
30
+ end
31
+
32
+ it "should not initialize twice (from error)" do
33
+ check = mock
34
+ check.should_receive(:environment).once.ordered
35
+ check.should_receive(:router).once.ordered
36
+
37
+ Micon.register :environment do
38
+ check.environment
39
+ 'environment'
40
+ end
41
+
42
+ Micon.register :router, :depends_on => :environment do
43
+ check.router
44
+ 'router'
45
+ end
46
+ Micon.after :environment do
47
+ # some code that needs :router
48
+ Micon[:router]
49
+ end
50
+
51
+ Micon[:router]
52
+ end
53
+ end
@@ -0,0 +1,32 @@
1
+ dir = File.expand_path(File.dirname(__FILE__))
2
+ require "#{dir}/helper"
3
+
4
+ describe "Micon nested custom scope" do
5
+ before :each do
6
+ Micon.clear
7
+ Micon.metadata.clear
8
+ end
9
+
10
+ it "with block" do
11
+ Micon.register :value, :scope => :custom
12
+
13
+ custom_a = {}
14
+ Micon.activate :custom, custom_a do
15
+ Micon[:value] = 'value a'
16
+
17
+ custom_b = {}
18
+ Micon.activate :custom, custom_b do
19
+ Micon[:value].should be_nil
20
+ Micon[:value] = 'value b'
21
+ Micon[:value].should == 'value b'
22
+ end
23
+
24
+ Micon[:value].should == 'value a'
25
+ end
26
+ end
27
+
28
+ it "should not support nested scopes without block" do
29
+ Micon.activate :custom, {}
30
+ lambda{Micon.activate :custom, {}}.should raise_error(/active/)
31
+ end
32
+ end
@@ -0,0 +1,65 @@
1
+ dir = File.expand_path(File.dirname(__FILE__))
2
+ require "#{dir}/helper"
3
+
4
+ describe "Micon Overview" do
5
+ it "sample" do
6
+ class Object
7
+ def app; Micon end
8
+ end
9
+
10
+ # static (singleton) components
11
+ class Environment
12
+ register_as :environment
13
+ end
14
+
15
+ class Logger
16
+ register_as :logger
17
+
18
+ def info msg; end
19
+ end
20
+
21
+ class Router
22
+ register_as :router
23
+
24
+ def parse rote_filename
25
+ # do something
26
+ end
27
+ end
28
+
29
+ # callbacks, we need to parse routes right after environment will be initialized
30
+ app.after :environment do
31
+ app[:router].parse '/config/routes.rb'
32
+ end
33
+
34
+ # dynamic components, will be created and destroyed for every request
35
+ class Request
36
+ register_as :request, :scope => :request
37
+ end
38
+
39
+ class Application
40
+ # injecting components into attributes
41
+ inject :request => :request, :logger => :logger
42
+
43
+ def do_business
44
+ # now we can use injected component
45
+ logger.info "routes parsed"
46
+ do_something_with request
47
+ logger.info 'well done'
48
+ end
49
+
50
+ def do_something_with request; end
51
+ end
52
+
53
+ # Web Server / Rack Adapter
54
+ class RackAdapter
55
+ def call env
56
+ # activating new request scope, the session component will be created and destroyed automatically
57
+ app.activate :request, {} do
58
+ Application.new.do_business
59
+ end
60
+ end
61
+ end
62
+
63
+ RackAdapter.new.call({})
64
+ end
65
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --reverse
@@ -0,0 +1,59 @@
1
+ dir = File.expand_path(File.dirname(__FILE__))
2
+ require "#{dir}/helper"
3
+
4
+ describe "Application and Instance scopes" do
5
+ before :each do
6
+ Micon.clear
7
+ Micon.metadata.clear
8
+ end
9
+
10
+ it "instance scope" do
11
+ Micon.register(:value, :scope => :instance){"The Object"}
12
+
13
+ Micon[:value].should == "The Object"
14
+ Micon[:value].object_id.should_not == Micon[:value].object_id
15
+ end
16
+
17
+ it "application scope" do
18
+ Micon.register(:value){"The Object"}
19
+
20
+ Micon[:value].should == "The Object"
21
+ Micon[:value].object_id.should == Micon[:value].object_id
22
+ end
23
+
24
+ it "application scope, outjection" do
25
+ the_object = "The Object"
26
+ Micon.register :value
27
+
28
+ Micon[:value].should be_nil
29
+ Micon[:value] = the_object
30
+ Micon[:value].object_id.should == the_object.object_id
31
+ end
32
+
33
+ it "cycle reference" do
34
+ class CycleB; end
35
+
36
+ class CycleA
37
+ register_as :cycle_a
38
+ inject :b => :cycle_b
39
+ end
40
+
41
+ class CycleB
42
+ register_as :cycle_b
43
+ inject :a => :cycle_a
44
+ end
45
+
46
+ a = Micon[:cycle_a]
47
+ b = Micon[:cycle_b]
48
+ a.b.equal?(b).should be_true
49
+ b.a.equal?(a).should be_true
50
+ end
51
+
52
+ it "unregister" do
53
+ Micon.register(:value){"The Object"}
54
+ Micon[:value].should == "The Object"
55
+
56
+ Micon.unregister :value
57
+ lambda{Micon[:value]}.should raise_error(/component not managed/)
58
+ end
59
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: micon
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 1
10
+ version: 0.1.1
11
+ platform: ruby
12
+ authors:
13
+ - Alexey Petrushin
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-11 00:00:00 +04:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Micro Container assembles and manages pieces of your application
23
+ email:
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - Rakefile
32
+ - readme.md
33
+ - lib/micon/class.rb
34
+ - lib/micon/metadata.rb
35
+ - lib/micon/micon.rb
36
+ - lib/micon/module.rb
37
+ - lib/micon/support.rb
38
+ - lib/micon.rb
39
+ - spec/callbacks_spec.rb
40
+ - spec/custom_scope_spec.rb
41
+ - spec/helper.rb
42
+ - spec/managed_spec.rb
43
+ - spec/micelaneous_spec.rb
44
+ - spec/nested_custom_scope_spec.rb
45
+ - spec/overview_spec.rb
46
+ - spec/spec.opts
47
+ - spec/static_scope_spec.rb
48
+ has_rdoc: true
49
+ homepage: http://github.com/alexeypetrushin/micon
50
+ licenses: []
51
+
52
+ post_install_message:
53
+ rdoc_options: []
54
+
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ hash: 3
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ requirements: []
76
+
77
+ rubyforge_project:
78
+ rubygems_version: 1.3.7
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Micro Container assembles and manages pieces of your application
82
+ test_files: []
83
+