micon 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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
+