contextr 0.0.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.
@@ -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