micon 0.1.6 → 0.1.7

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