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.
- data/COPYING.txt +340 -0
- data/History.txt +4 -0
- data/LICENSE.txt +57 -0
- data/Manifest.txt +32 -0
- data/README.txt +17 -0
- data/Rakefile +162 -0
- data/examples/education.rb +67 -0
- data/examples/fibonacci.rb +123 -0
- data/ext/dynamic.rb +73 -0
- data/ext/method_nature.rb +17 -0
- data/lib/contextr.rb +13 -0
- data/lib/contextr/contextr.rb +417 -0
- data/lib/contextr/version.rb +9 -0
- data/lib/core_ext/class.rb +24 -0
- data/lib/core_ext/module.rb +26 -0
- data/lib/core_ext/proc.rb +27 -0
- data/rake/group_spec_task.rb +7 -0
- data/rake/specific_group_spec_task.rb +7 -0
- data/scripts/txt2html +67 -0
- data/setup.rb +1585 -0
- data/spec/contextr/contextr_api_spec.rb +117 -0
- data/spec/core_ext/module_spec.rb +99 -0
- data/spec/core_ext/proc_spec.rb +61 -0
- data/spec/spec_helper.rb +1 -0
- data/tasks/annotations.rb +107 -0
- data/test/contextr/test_contextr.rb +165 -0
- data/test/test_helper.rb +2 -0
- data/website/index.html +180 -0
- data/website/index.txt +119 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +129 -0
- data/website/template.rhtml +48 -0
- metadata +79 -0
@@ -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,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
|