micon 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,10 +1,10 @@
1
1
  require 'rake_ext'
2
2
 
3
3
  project(
4
- :name => "micon",
5
- :version => "0.1.6",
6
- :summary => "Assembles and manages pieces of your application",
4
+ name: "micon",
5
+ version: "0.1.7",
6
+ summary: "Assembles and Manages Components of Your Application",
7
7
 
8
- :author => "Alexey Petrushin",
9
- :homepage => "http://github.com/alexeypetrushin/micon"
8
+ author: "Alexey Petrushin",
9
+ homepage: "http://github.com/alexeypetrushin/micon"
10
10
  )
@@ -1,8 +1,12 @@
1
+ module Micon
2
+ end
3
+
1
4
  %w{
2
5
  support
3
6
 
4
7
  metadata
5
- micon
8
+ core
9
+ helper
6
10
 
7
11
  module
8
12
  class
@@ -1,7 +1,7 @@
1
1
  class Class
2
- # register_as :session, :scope => :request
2
+ # register_as :session, scope: :request
3
3
  # register_as :loggger
4
4
  def register_as *args
5
- ::Micon.register(*args){self.new}
5
+ ::MICON.register(*args){self.new}
6
6
  end
7
7
  end
@@ -0,0 +1,408 @@
1
+ # Predefined scopes are: :application | :session | :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
+ class Micon::Core
7
+ #
8
+ # Scope Management
9
+ #
10
+ attr_accessor :custom_scopes
11
+
12
+ def activate sname, container, &block
13
+ raise_without_self "Only custom scopes can be activated!" if sname == :application or sname == :instance
14
+ raise "container should have type of Hash but has #{container.class.name}" unless container.is_a? Hash
15
+
16
+ raise_without_self "Scope '#{sname}' already active!" if !block and @custom_scopes[sname]
17
+
18
+ if block
19
+ begin
20
+ outer_container_or_nil = @custom_scopes[sname]
21
+ @custom_scopes[sname] = container
22
+ @metadata.with_scope_callbacks sname, container, &block
23
+ ensure
24
+ if outer_container_or_nil
25
+ @custom_scopes[sname] = outer_container_or_nil
26
+ else
27
+ @custom_scopes.delete sname
28
+ end
29
+ end
30
+ else
31
+ # not support nested scopes without block
32
+ @custom_scopes[sname] = container
33
+ @metadata.call_before_scope sname, container
34
+ end
35
+ end
36
+
37
+ def deactivate sname
38
+ raise_without_self "Only custom scopes can be deactivated!" if sname == :application or sname == :instance
39
+
40
+ raise_without_self "Scope '#{sname}' not active!" unless container = @custom_scopes[sname]
41
+
42
+ @metadata.call_after_scope sname, container
43
+ @custom_scopes.delete sname
44
+ container
45
+ end
46
+
47
+ def active? sname
48
+ if sname == :application or sname == :instance
49
+ true
50
+ else
51
+ @custom_scopes.include? sname
52
+ end
53
+ end
54
+
55
+ def clear
56
+ @application.clear
57
+ @custom_scopes.clear
58
+ end
59
+
60
+ def empty?
61
+ @application.empty? and @custom_scopes.empty?
62
+ end
63
+
64
+
65
+ #
66
+ # Object Management
67
+ #
68
+ def include? key
69
+ sname = @registry[key]
70
+
71
+ case sname
72
+ when nil
73
+ false
74
+ when :instance
75
+ true
76
+ when :application
77
+ @application.include? key
78
+ else # custom
79
+ container = @custom_scopes[sname]
80
+ return false unless container
81
+ container.include? key
82
+ end
83
+ end
84
+
85
+ def [] key
86
+ sname = @registry[key] || autoload_component_definition(key)
87
+
88
+ case sname
89
+ when :instance
90
+ return create_object(key)
91
+ when :application
92
+ o = @application[key]
93
+ unless o
94
+ return create_object(key, @application)
95
+ else
96
+ return o
97
+ end
98
+ else # custom
99
+ container = @custom_scopes[sname]
100
+ raise_without_self "Scope '#{sname}' not started!" unless container
101
+ o = container[key]
102
+ unless o
103
+ return create_object(key, container)
104
+ else
105
+ return o
106
+ end
107
+ end
108
+ end
109
+
110
+ # def get_constant_component key
111
+ # sname = @registry[key] || autoload_component_definition(key, false)
112
+ #
113
+ # case sname
114
+ # when nil
115
+ # nil
116
+ # when :instance
117
+ # must_be.never_called
118
+ # when :application
119
+ # return nil unless @constants.include? key
120
+ #
121
+ # o = @application[key]
122
+ # unless o
123
+ # return create_object(key, @application)
124
+ # else
125
+ # return o
126
+ # end
127
+ # else # custom
128
+ # must_be.never_called
129
+ # end
130
+ # end
131
+ #
132
+ # def get_constant namespace, const
133
+ # original_namespace = namespace
134
+ # namespace = nil if namespace == Object or namespace == Module
135
+ # target_namespace = namespace
136
+ #
137
+ # # Name hack (for anonymous classes)
138
+ # namespace = eval "#{name_hack(namespace)}" if namespace
139
+ #
140
+ # class_name = namespace ? "#{namespace.name}::#{const}" : const
141
+ #
142
+ # simple_also_tried = false
143
+ # begin
144
+ # simple_also_tried = (namespace == nil)
145
+ #
146
+ # if result = get_constant_component(class_name.to_sym)
147
+ # if @loaded_classes.include?(class_name)
148
+ # raise_without_self "something wrong is goin on, constant '#{const}' in '#{original_namespace}' namespace already has been defined!"
149
+ # end
150
+ #
151
+ # real_namespace = namespace ? namespace : Object
152
+ # if real_namespace.const_defined?(const)
153
+ # raise_without_self "component trying to redefine constant '#{const}' that already defined in '#{real_namespace}'!"
154
+ # end
155
+ #
156
+ # real_namespace.const_set const, result
157
+ #
158
+ # @loaded_classes[class_name] = [real_namespace, const]
159
+ #
160
+ # return result
161
+ # elsif namespace
162
+ # namespace = Module.namespace_for(namespace.name)
163
+ # class_name = namespace ? "#{namespace.name}::#{const}" : const
164
+ # end
165
+ # end until simple_also_tried
166
+ #
167
+ # return nil
168
+ # end
169
+
170
+ def []= key, value
171
+ raise "can't assign nill as :#{key} component!" unless value
172
+
173
+ sname = @registry[key] || autoload_component_definition(key)
174
+
175
+ value = case sname
176
+ when :instance
177
+ raise_without_self "You can't outject variable with the 'instance' sname!"
178
+ when :application
179
+ @application[key] = value
180
+ else # custom
181
+ container = @custom_scopes[sname]
182
+ raise_without_self "Scope '#{sname}' not started!" unless container
183
+ container[key] = value
184
+ end
185
+
186
+ @metadata.call_after key, value
187
+
188
+ value
189
+ end
190
+
191
+ def delete key
192
+ sname = @registry[key] # || autoload_component_definition(key)
193
+
194
+ case sname
195
+ when nil
196
+ when :instance
197
+ raise_without_self "You can't outject variable with the 'instance' scope!"
198
+ when :application
199
+ @application.delete key
200
+ else # Custom
201
+ container = @custom_scopes[sname]
202
+ # raise_without_self "Scope '#{sname}' not started!" unless container
203
+ container.delete key if container
204
+ end
205
+ end
206
+
207
+ def delete_all key
208
+ metadata.delete key
209
+ delete key
210
+ end
211
+
212
+ def reset key
213
+ delete key
214
+ self[key]
215
+ end
216
+
217
+ #
218
+ # Metadata
219
+ #
220
+ attr_accessor :metadata
221
+
222
+ def register key, options = {}, &initializer
223
+ raise "key should not be nil or false value!" unless key
224
+ options = options.symbolize_keys
225
+
226
+ sname = options.delete(:scope) || :application
227
+ dependencies = Array(options.delete(:require) || options.delete(:depends_on))
228
+ # constant = options.delete(:constant) || false
229
+
230
+ raise "unknown options :#{options.keys.join(', :')}!" unless options.empty?
231
+
232
+ unless @registry.object_id == @metadata.registry.object_id
233
+ raise "internal error, reference to registry aren't equal to actual registry!"
234
+ end
235
+ @metadata.registry[key] = sname
236
+ @metadata.initializers[key] = [initializer, dependencies] #, constant]
237
+ # if constant
238
+ # raise "component '#{key}' defined as constant must be a symbol!" unless key.is_a? Symbol
239
+ # raise "component '#{key}' defined as constant can have only :application scope!" unless sname == :application
240
+ # @constants[key] = true
241
+ # end
242
+ end
243
+
244
+ def unregister key
245
+ @metadata.delete key
246
+ # @constants.delete key
247
+ end
248
+
249
+ def before component, options = {}, &block
250
+ options[:bang] = true unless options.include? :bang
251
+ raise_without_self "component :#{component} already created!" if options[:bang] and include?(component)
252
+ @metadata.register_before component, &block
253
+ end
254
+
255
+ def after component, options = {}, &block
256
+ options[:bang] = true unless options.include? :bang
257
+ if include? component
258
+ if options[:bang]
259
+ raise_without_self "component :#{component} already created!"
260
+ else
261
+ block.call self[component]
262
+ end
263
+ end
264
+ @metadata.register_after component, &block
265
+ end
266
+
267
+ def before_scope sname, options = {}, &block
268
+ options[:bang] = true unless options.include? :bang
269
+ raise_without_self "scope :#{sname} already started!" if options[:bang] and active?(sname)
270
+ @metadata.register_before_scope sname, &block
271
+ end
272
+
273
+ def after_scope sname, options = {}, &block
274
+ options[:bang] = true unless options.include? :bang
275
+ raise_without_self "scope :#{sname} already started!" if options[:bang] and active?(sname)
276
+ @metadata.register_after_scope sname, &block
277
+ end
278
+
279
+
280
+
281
+ def clone
282
+ another = super
283
+ %w(@metadata @application @custom_scopes).each do |name| # @loaded_classes, @constants
284
+ value = instance_variable_get name
285
+ another.instance_variable_set name, value.clone
286
+ end
287
+ another.instance_variable_set '@registry', another.metadata.registry
288
+ another.instance_variable_set '@initialized', another.instance_variable_get('@initialized')
289
+ another
290
+ end
291
+ alias_method :deep_clone, :clone
292
+
293
+ def initialize!
294
+ unless @initialized
295
+ # quick access to Metadata inner variable.
296
+ # I intentially broke the Metadata incapsulation to provide better performance, don't refactor it.
297
+ @registry = {} # @loaded_classes, @constants = {}, {}
298
+ @metadata = Micon::Metadata.new(@registry)
299
+
300
+ @application, @custom_scopes = {}, {}
301
+
302
+ @initialized = true
303
+ end
304
+
305
+ # Micon::Core is independent itself and there can be multiple Cores simultaneously.
306
+ # But some of it's extensions can work only with one global instance, and them need to know how to get it,
307
+ # the MICON constant references this global instance.
308
+ Object.send(:remove_const, :MICON) if Object.const_defined?(:MICON)
309
+ Object.const_set :MICON, self
310
+ end
311
+
312
+ def deinitialize!
313
+ Object.send(:remove_const, :MICON) if Object.const_defined?(:MICON)
314
+
315
+ # @loaded_classes.each do |class_name, tuple|
316
+ # namespace, const = tuple
317
+ # namespace.send(:remove_const, const)
318
+ # end
319
+ # @loaded_classes.clear
320
+ end
321
+
322
+ protected
323
+ def autoload_component_definition key, bang = true
324
+ begin
325
+ load "components/#{key.to_s.gsub(/::/, '/')}.rb"
326
+ rescue LoadError
327
+ end
328
+ sname = @registry[key]
329
+ raise_without_self "'#{key}' component not managed!" if bang and !sname
330
+ sname
331
+ end
332
+
333
+ def create_object key, container = nil
334
+ initializer, dependencies = @metadata.initializers[key]
335
+ raise "no initializer for :#{key} component!" unless initializer
336
+
337
+ dependencies.each{|d| self[d]}
338
+ @metadata.call_before key
339
+
340
+ if container
341
+ unless o = container[key]
342
+ o = initializer.call
343
+ container[key] = o
344
+ else
345
+ # complex case, there's an circular dependency, and the 'o' already has been
346
+ # initialized in dependecies or callbacks
347
+ # here's the sample case:
348
+ #
349
+ # app.register :environment, :application do
350
+ # p :environment
351
+ # 'environment'
352
+ # end
353
+ #
354
+ # app.register :conveyors, :application, depends_on: :environment do
355
+ # p :conveyors
356
+ # 'conveyors'
357
+ # end
358
+ #
359
+ # app.after :environment do
360
+ # app[:conveyors]
361
+ # end
362
+ #
363
+ # app[:conveyors]
364
+
365
+ o = container[key]
366
+ end
367
+ else
368
+ o = initializer.call
369
+ end
370
+ raise "initializer for component :#{key} returns nill!" unless o
371
+
372
+ @metadata.call_after key, o
373
+ o
374
+ end
375
+
376
+ def name_hack namespace
377
+ if namespace
378
+ namespace.to_s.gsub("#<Class:", "").gsub(">", "")
379
+ else
380
+ ""
381
+ end
382
+ # Namespace Hack description
383
+ # Module.name doesn't works correctly for Anonymous classes.
384
+ # try to execute this code:
385
+ #
386
+ #class Module
387
+ # def const_missing const
388
+ # p self.to_s
389
+ # end
390
+ #end
391
+ #
392
+ #class A
393
+ # class << self
394
+ # def a
395
+ # p self
396
+ # MissingConst
397
+ # end
398
+ # end
399
+ #end
400
+ #
401
+ #A.a
402
+ #
403
+ # the output will be:
404
+ # A
405
+ # "#<Class:A>"
406
+ #
407
+ end
408
+ end
@@ -0,0 +1,26 @@
1
+ #
2
+ # Generates helper methods for Micon,
3
+ # so you can use micon.config instead of micon[:config]
4
+ #
5
+ module Micon::Helper
6
+ def method_missing m, *args, &block
7
+ super if args.size > 1 or block
8
+
9
+ key = m.to_s.sub(/[?=]$/, '').to_sym
10
+ self.class.class_eval do
11
+ define_method key do
12
+ self[key]
13
+ end
14
+
15
+ define_method "#{key}=" do |value|
16
+ self[key] = value
17
+ end
18
+
19
+ define_method "#{key}?" do
20
+ include? key
21
+ end
22
+ end
23
+
24
+ send m, *args
25
+ end
26
+ end