micon 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +56 -0
- data/lib/micon.rb +9 -0
- data/lib/micon/class.rb +7 -0
- data/lib/micon/metadata.rb +132 -0
- data/lib/micon/micon.rb +250 -0
- data/lib/micon/module.rb +27 -0
- data/lib/micon/support.rb +17 -0
- data/readme.md +79 -0
- data/spec/callbacks_spec.rb +56 -0
- data/spec/custom_scope_spec.rb +76 -0
- data/spec/helper.rb +6 -0
- data/spec/managed_spec.rb +52 -0
- data/spec/micelaneous_spec.rb +53 -0
- data/spec/nested_custom_scope_spec.rb +32 -0
- data/spec/overview_spec.rb +65 -0
- data/spec/spec.opts +2 -0
- data/spec/static_scope_spec.rb +59 -0
- metadata +83 -0
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
data/lib/micon/class.rb
ADDED
@@ -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
|
data/lib/micon/micon.rb
ADDED
@@ -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
|
data/lib/micon/module.rb
ADDED
@@ -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,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,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
|
+
|