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 +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
|
+
|