contextr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,417 @@
1
+ module ContextR
2
+ @@genid = 0
3
+ class << self
4
+ def gensym( name, kind = "", postfix = "_%05d_" )
5
+ @@genid += 1
6
+ ( "_#{name}_#{kind}#{postfix}" % @@genid ).intern
7
+ end
8
+
9
+ def symbolize( layer_klass )
10
+ layer_klass.namespace_free_name.gsub( "Layer", "" ).downcase.to_sym
11
+ end
12
+
13
+ def layerize( layer_symbol )
14
+ "#{layer_symbol}_layer".camelize
15
+ end
16
+
17
+ def with_layers( *layer_symbols, &block )
18
+ layers = layer_symbols.collect do | layer_symbol |
19
+ ContextR.layer_by_name( ContextR.layerize( layer_symbol ) )
20
+ end
21
+ Dynamic.let( { :layers => Dynamic[:layers] | layers }, &block )
22
+ end
23
+
24
+ def without_layers( *layer_symbols, &block )
25
+ layers = layer_symbols.collect do | layer_symbol |
26
+ ContextR.layer_by_name( ContextR.layerize( layer_symbol ) )
27
+ end
28
+ Dynamic.let( { :layers => Dynamic[:layers] - layers }, &block )
29
+ end
30
+
31
+ def layer_by_symbol( layer_symbol )
32
+ layer_by_name( layerize( layer_symbol ) )
33
+ end
34
+
35
+ def layer_by_name( layer_name )
36
+ unless ContextR.const_defined?( layer_name )
37
+ ContextR::module_eval(
38
+ "class #{layer_name} < Layer; end", __FILE__, __LINE__ )
39
+ # ContextR.const_set( layer_name, Class.new( ContextR::Layer ) )
40
+ end
41
+ ContextR.const_get( layer_name )
42
+ end
43
+
44
+ def current_layer
45
+ Layer.compose( Dynamic[:layers] )
46
+ end
47
+ end
48
+
49
+
50
+ class Layer # its role as base instance of all layers
51
+ class << self
52
+ attr_accessor_with_default_setter :base_layers, :combined_layers do
53
+ Hash.new
54
+ end
55
+
56
+ def core_methods
57
+ @core_methods ||= Hash.new do | hash, extended_class |
58
+ add_redefine_callback( extended_class )
59
+ hash[extended_class] = Hash.new do | class_hash, method_name |
60
+ um = extended_class.instance_method( method_name )
61
+ replace_core_method( extended_class, method_name )
62
+ class_hash[method_name] = um
63
+ end
64
+ end
65
+ end
66
+
67
+ def inherited( new_base_layer )
68
+ unless new_base_layer.name.empty?
69
+ base_layers[ContextR::symbolize( new_base_layer )] = new_base_layer
70
+ end
71
+ end
72
+
73
+ def compose( layers )
74
+ # TODO: Add better caching
75
+ combined_layers[ layers ] ||=
76
+ layers.reverse.inject( nil ) do | akku, layer |
77
+ layer + akku
78
+ end
79
+ end
80
+
81
+ protected
82
+ def replace_core_method( extended_class, method_name )
83
+ num_of_args = extended_class.instance_method( method_name ).arity
84
+ arg_signature = case num_of_args <=> 0
85
+ when 0
86
+ ""
87
+ when 1
88
+ "%s" % Array.new( num_of_args ) { |i| "arg%d" % i }.join( ", " )
89
+ else
90
+ "*arguments"
91
+ end
92
+ arg_call = arg_signature.empty? ? "" : ", " + arg_signature
93
+
94
+ extended_class.class_eval( %Q{
95
+ remove_method :#{method_name}
96
+ def #{method_name} #{arg_signature}
97
+ ContextR::current_layer.extended( self ).send(
98
+ :#{method_name}#{arg_call} )
99
+ end
100
+ }, __FILE__, __LINE__ )
101
+ end
102
+
103
+ def add_redefine_callback( extended_class )
104
+ (class << extended_class; self; end).instance_eval do
105
+ define_method( :method_added ) do | method_name |
106
+ if ContextR::Layer.core_methods[extended_class].
107
+ include?( method_name )
108
+ warn( caller.first + " : ContextR - Redefining already wrapped methods is not supported yet. Your changes _may_ have no effect." )
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ class Layer # its role as base class for all other layers
117
+ class << self
118
+ attr_accessor_with_default_setter :extended_classes do
119
+ Hash.new do | classes, extended_class |
120
+ classes[extended_class] = Hash.new do | hash, key |
121
+ hash[key] = ContextualizedMethod.new(
122
+ ContextR::Layer.core_methods[ extended_class ][ key ] )
123
+ end
124
+ end
125
+ end
126
+ attr_accessor :extended_objects
127
+
128
+ def methods_of( extended_class )
129
+ self.extended_classes[extended_class]
130
+ end
131
+
132
+ def extended( object )
133
+ self.extended_objects ||= Hash.new do | cache, object |
134
+ cache[ object ] =
135
+ ExtendedObject.new( object, self.methods_of( object.class ) )
136
+ end
137
+ self.extended_objects[object]
138
+ end
139
+
140
+ def + other_layer
141
+ if other_layer.nil?
142
+ self
143
+ else
144
+ combined_layer = Class.new( Layer )
145
+ combined_layer.extended_classes = self.merge_extended_classes_with(
146
+ other_layer.extended_classes )
147
+ combined_layer
148
+ end
149
+ end
150
+
151
+ protected
152
+
153
+ def merge_extended_classes_with( other_ec )
154
+ extended_classes.merge( other_ec ) do | extended_c, my_ms, other_ms |
155
+ my_ms.merge( other_ms ) do | method_name, my_cm, other_cm |
156
+ my_cm + other_cm
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ class LayerInClass # its public interface
164
+ attr_accessor :contextualized_class
165
+ attr_accessor :layer
166
+
167
+ def initialize( contextualized_class, layer )
168
+ self.contextualized_class = contextualized_class
169
+ self.layer = layer
170
+ end
171
+
172
+ def pre( method_name, &block )
173
+ layer.methods_of( self.contextualized_class )[method_name].pres <<
174
+ block.to_unbound_method( self.contextualized_class )
175
+ nil
176
+ end
177
+ def post( method_name, &block )
178
+ layer.methods_of( self.contextualized_class )[method_name].posts <<
179
+ block.to_unbound_method( self.contextualized_class )
180
+ nil
181
+ end
182
+ def around( method_name, &block )
183
+ layer.methods_of( self.contextualized_class )[method_name].arounds <<
184
+ block.to_unbound_method( self.contextualized_class )
185
+ nil
186
+ end
187
+ alias :wrap :around
188
+ end
189
+
190
+ class ExtendedObject
191
+ attr_accessor :proxied_object
192
+ attr_accessor :extended_methods
193
+ attr_accessor :behaviours
194
+
195
+ def initialize( proxied_object, extended_methods )
196
+ self.proxied_object = proxied_object
197
+ self.extended_methods = extended_methods
198
+ self.behaviours = {}
199
+ end
200
+
201
+ def send( method_name, *arguments )
202
+ ( self.behaviours[method_name] ||=
203
+ self.extended_methods[method_name].behaviour( self.proxied_object )
204
+ ).call( *arguments )
205
+ end
206
+ end
207
+
208
+ class ContextualizedMethod
209
+ attr_accessor :core
210
+ attr_accessor :behaviour_cache
211
+
212
+ attr_accessor_with_default_setter :pres, :posts, :arounds do
213
+ Array.new
214
+ end
215
+
216
+ def initialize( unbound_core_method )
217
+ self.core = unbound_core_method
218
+ self.behaviour_cache = {}
219
+ end
220
+
221
+ def + other_cm
222
+ new_cm = ContextualizedMethod.new( self.core )
223
+ new_cm.pres = self.pres + other_cm.pres
224
+ new_cm.posts = self.posts + other_cm.posts
225
+ new_cm.arounds = self.arounds + other_cm.arounds
226
+ new_cm
227
+ end
228
+
229
+ def behaviour( instance )
230
+ self.behaviour_cache[instance] ||=
231
+ self.send( self.behaviour_name, instance )
232
+ end
233
+
234
+ def behaviour_name
235
+ wrappers = []
236
+ wrappers << "pres" unless self.pres.empty?
237
+ wrappers << "arounds" unless self.arounds.empty?
238
+ wrappers << "posts" unless self.posts.empty?
239
+
240
+ "behaviour_" + ( wrappers.empty? ? "without_wrappers" :
241
+ "with_" + wrappers.join( "_and_" ) )
242
+ end
243
+
244
+ def behaviour_without_wrappers( instance )
245
+ self.core.bind( instance )
246
+ end
247
+
248
+ def behaviour_with_pres( instance )
249
+ combined_pres = self.combine_pres( instance )
250
+ bound_core = self.bind_core( instance )
251
+
252
+ lambda do | *arguments |
253
+ nature = MethodNature.new( arguments, nil, false )
254
+
255
+ combined_pres.call( nature )
256
+ unless nature.break
257
+ bound_core.call( *nature.arguments )
258
+ else
259
+ nature.return_value
260
+ end
261
+ end
262
+ end
263
+
264
+ def behaviour_with_posts( instance )
265
+ bound_core = self.bind_core( instance )
266
+ combined_posts = self.combine_posts( instance )
267
+
268
+ lambda do | *arguments |
269
+ nature = MethodNature.new( arguments, nil, false )
270
+
271
+ nature.return_value = bound_core.call( *arguments )
272
+ combined_posts.call( nature )
273
+
274
+ nature.return_value
275
+ end
276
+ end
277
+
278
+ def behaviour_with_pres_and_posts( instance )
279
+ combined_pres = self.combine_pres( instance )
280
+ bound_core = self.bind_core( instance )
281
+ combined_posts = self.combine_posts( instance )
282
+
283
+ lambda do | *arguments |
284
+ nature = MethodNature.new( arguments, nil, false )
285
+
286
+ combined_pres.call( nature )
287
+ unless nature.break
288
+ nature.return_value = bound_core.call( *nature.arguments )
289
+ combined_posts.call( nature )
290
+ end
291
+ nature.return_value
292
+ end
293
+ end
294
+
295
+ def behaviour_with_arounds( instance )
296
+ bound_core = self.bind_core( instance )
297
+ bound_arounds = self.bind_arounds( instance )
298
+
299
+ lambda do | *arguments |
300
+ working_arounds = bound_arounds.clone
301
+ nature = MethodNature.new( arguments, nil, false)
302
+ nature.block = around_block( nature, working_arounds, bound_core )
303
+
304
+ catch( :break_in_around ) do
305
+ working_arounds.pop.call( nature )
306
+ end
307
+ nature.return_value
308
+ end
309
+ end
310
+
311
+ def behaviour_with_pres_and_arounds( instance )
312
+ combined_pres = self.combine_pres( instance )
313
+ bound_core = self.bind_core( instance )
314
+ bound_arounds = self.bind_arounds( instance )
315
+
316
+ lambda do | *arguments |
317
+ nature = MethodNature.new( arguments, nil, false )
318
+
319
+ combined_pres.call( nature )
320
+ unless nature.break
321
+ working_arounds = bound_arounds.clone
322
+ nature.block = around_block( nature, working_arounds, bound_core )
323
+ catch( :break_in_around ) do
324
+ working_arounds.pop.call( nature )
325
+ end
326
+ end
327
+ nature.return_value
328
+ end
329
+ end
330
+
331
+ def behaviour_with_arounds_and_posts( instance )
332
+ bound_core = self.bind_core( instance )
333
+ bound_arounds = self.bind_arounds( instance )
334
+ combined_posts = self.combine_posts( instance )
335
+
336
+ lambda do | *arguments |
337
+ working_arounds = bound_arounds.clone
338
+ nature = MethodNature.new( arguments, nil, false,
339
+ around_block( nature, working_arounds, bound_core ) )
340
+
341
+ catch( :break_in_around ) do
342
+ working_arounds.pop.call( nature )
343
+ end
344
+ combinded_posts.call( nature ) unless nature.break
345
+
346
+ nature.return_value
347
+ end
348
+ end
349
+
350
+ def behaviour_with_pres_and_arounds_and_posts( instance )
351
+ combined_pres = self.combine_pres( instance )
352
+ bound_core = self.bind_core( instance )
353
+ bound_arounds = self.bind_arounds( instance )
354
+ combined_posts = self.combine_posts( instance )
355
+
356
+ lambda do | *arguments |
357
+ nature = MethodNature.new( arguments, nil, false )
358
+
359
+ combined_pres.call( nature )
360
+ unless nature.break
361
+ working_arounds = bound_arounds.clone
362
+ nature.block = around_block( nature, working_arounds, bound_core )
363
+ catch( :break_in_around ) do
364
+ working_arounds.pop.call( nature )
365
+ end
366
+ unless nature.break
367
+ combined_posts.call( nature )
368
+ end
369
+ end
370
+ nature.return_value
371
+ end
372
+ end
373
+
374
+
375
+ # helpers
376
+ def combine_pres( instance )
377
+ bound_pres = self.pres.collect { | p | p.bind( instance ) }
378
+ lambda do | nature |
379
+ bound_pres.reverse.each do | bound_pre |
380
+ bound_pre.call( nature )
381
+ break if nature.break
382
+ end
383
+ end
384
+ end
385
+
386
+ def bind_arounds( instance )
387
+ self.arounds.collect { | a | a.bind( instance ) }
388
+ end
389
+
390
+ def around_block( nature, bound_arounds, bound_core )
391
+ lambda do
392
+ unless bound_arounds.empty?
393
+ bound_arounds.pop.call( nature )
394
+ throw( :break_in_around ) if nature.break
395
+ else
396
+ nature.return_value = bound_core.call( *nature.arguments )
397
+ end
398
+ end
399
+ end
400
+
401
+ def bind_core( instance )
402
+ self.core.bind( instance )
403
+ end
404
+
405
+ def combine_posts( instance )
406
+ bound_posts = self.posts.collect { | p | p.bind( instance ) }
407
+ lambda do | nature |
408
+ bound_posts.each do | bound_post |
409
+ bound_post.call( nature )
410
+ break if nature.break
411
+ end
412
+ end
413
+ end
414
+ end
415
+
416
+ class DefaultLayer < Layer; end
417
+ end
@@ -0,0 +1,9 @@
1
+ module ContextR #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 1
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ class Class
2
+ def layer( *layer_keys )
3
+ layer_keys.each do | layer_key |
4
+ layer_key = layer_key.to_s.downcase.to_sym
5
+ layer_name = ContextR::layerize( layer_key )
6
+ layer = ContextR.layer_by_name( layer_name )
7
+ layer_in_class = ContextR::LayerInClass.new( self, layer )
8
+
9
+ define_private_class_method( layer_key ) do
10
+ layer_in_class
11
+ end
12
+ end
13
+
14
+ nil
15
+ end
16
+
17
+ protected
18
+ def define_private_class_method( symbol, &block )
19
+ (class << self; self; end).instance_eval do
20
+ define_method( symbol, block )
21
+ private symbol
22
+ end
23
+ end
24
+ end