contextr 0.0.1 → 0.0.2
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/History.txt +8 -0
- data/LICENSE.txt +2 -2
- data/Manifest.txt +4 -2
- data/README.txt +36 -3
- data/examples/education.rb +19 -15
- data/lib/contextr.rb +4 -2
- data/lib/contextr/contextr.rb +57 -384
- data/lib/contextr/layer.rb +405 -0
- data/lib/contextr/version.rb +1 -1
- data/lib/core_ext/class.rb +6 -1
- data/lib/core_ext/module.rb +39 -3
- data/lib/core_ext/proc.rb +29 -3
- data/{ext → lib/ext}/dynamic.rb +10 -0
- data/lib/ext/method_nature.rb +80 -0
- data/spec/contextr/contextr_api_spec.rb +0 -39
- data/spec/contextr/contextr_functional_spec.rb +294 -0
- data/test/contextr/test_contextr.rb +2 -160
- data/website/index.html +16 -8
- data/website/index.txt +16 -7
- metadata +6 -5
- data/ext/method_nature.rb +0 -17
data/lib/core_ext/proc.rb
CHANGED
@@ -1,22 +1,48 @@
|
|
1
1
|
class Proc
|
2
|
+
|
3
|
+
# Returns the Proc converted to an UnboundMethod within the the given Class
|
4
|
+
#
|
5
|
+
# class A
|
6
|
+
# def a
|
7
|
+
# "a"
|
8
|
+
# end
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# um_a = A.instance_method( :a ) # => #<UnboundMethod: A#a>
|
12
|
+
# um_a.bind( A.new ).call # => "a"
|
13
|
+
#
|
14
|
+
# b = lambda do "b" end
|
15
|
+
# um_b = b.to_unbound_method( A ) # => #<UnboundMethod: A#_um_from_proc>
|
16
|
+
# um_b.bind( A.new ).call # => "b"
|
17
|
+
#
|
2
18
|
def to_unbound_method( klass )
|
3
19
|
raise ArgumentError.new( "Only class objects allowed in parameter list"
|
4
20
|
) unless klass.kind_of?( Class )
|
5
21
|
|
6
22
|
proc_object = self
|
7
23
|
klass.class_eval do
|
8
|
-
define_method( :
|
24
|
+
define_method( :_um_from_proc, &proc_object )
|
9
25
|
end
|
10
26
|
|
11
|
-
unbound_method = klass.instance_method( :
|
27
|
+
unbound_method = klass.instance_method( :_um_from_proc )
|
12
28
|
|
13
29
|
klass.class_eval do
|
14
|
-
undef_method( :
|
30
|
+
undef_method( :_um_from_proc )
|
15
31
|
end
|
16
32
|
|
17
33
|
unbound_method
|
18
34
|
end
|
19
35
|
|
36
|
+
# joins to blocks into a new one, that forwards on excution the given
|
37
|
+
# arguments.
|
38
|
+
#
|
39
|
+
# a = lambda do print "a" end
|
40
|
+
# b = lambda do print "b" end
|
41
|
+
#
|
42
|
+
# ab = a + b
|
43
|
+
#
|
44
|
+
# ab.call # => "ab"
|
45
|
+
#
|
20
46
|
def +( other_proc )
|
21
47
|
this_proc = self
|
22
48
|
lambda do | *arguments |
|
data/{ext → lib/ext}/dynamic.rb
RENAMED
@@ -1,3 +1,13 @@
|
|
1
|
+
# This library was created by Christian Neukirchen in the context of
|
2
|
+
# EuRuKo 2005 and is licensed under the same terms as Ruby.
|
3
|
+
#
|
4
|
+
# It provides dynamically scoped variables. It is used within ContextR to
|
5
|
+
# store the current, thread-wide activated layers.
|
6
|
+
#
|
7
|
+
# For more information see the corresponding slides at
|
8
|
+
# http://chneukirchen.org/talks/euruko-2005/chneukirchen-euruko2005-contextr.pdf
|
9
|
+
#
|
10
|
+
# (c) 2005 - Christian Neukirchen - http://chneukirchen.org
|
1
11
|
module Dynamic
|
2
12
|
class << self
|
3
13
|
Thread.main[:DYNAMIC] = Hash.new { |hash, key|
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# This class was created by Christian Neukirchen in the context of
|
2
|
+
# EuRuKo 2005 and is licensed under the same terms as Ruby.
|
3
|
+
#
|
4
|
+
# It is used within ContextR to provide an interface to manipulate the calling
|
5
|
+
# context within a method wrapper.
|
6
|
+
#
|
7
|
+
# For more information see the corresponding slides at
|
8
|
+
# http://chneukirchen.org/talks/euruko-2005/chneukirchen-euruko2005-contextr.pdf
|
9
|
+
#
|
10
|
+
# (c) 2005 - Christian Neukirchen - http://chneukirchen.org
|
11
|
+
#
|
12
|
+
# ---
|
13
|
+
#
|
14
|
+
# It provides access
|
15
|
+
# [+arguments+] to read and manipulate method's arguments.
|
16
|
+
# [+return_value+] to read and manipulate the method's return value
|
17
|
+
#
|
18
|
+
class MethodNature < Struct.new(:arguments, :return_value, :break, :block)
|
19
|
+
|
20
|
+
# stops the execution of the following method wrappers and potentially the
|
21
|
+
# core method.
|
22
|
+
#
|
23
|
+
# class A
|
24
|
+
# def a
|
25
|
+
# "a"
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# layer :foo
|
29
|
+
#
|
30
|
+
# foo.pre :a do | method_nature |
|
31
|
+
# method_nature.break! "b"
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# A.new.a # => "a"
|
36
|
+
# ContextR::with_layers :foo do
|
37
|
+
# A.new.a # => "b"
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# If it is called without parameter, the return value will not be changed.
|
41
|
+
#
|
42
|
+
# :call-seq:
|
43
|
+
# break!
|
44
|
+
# break!( return_value )
|
45
|
+
#
|
46
|
+
def break!( *value )
|
47
|
+
self.break = true
|
48
|
+
self.return_value = value.first unless value.empty?
|
49
|
+
end
|
50
|
+
|
51
|
+
# calls the next wrapper with an around method. It corresponds to a super call
|
52
|
+
# in an inheritance hierarchy.
|
53
|
+
#
|
54
|
+
# The example attaches to each method in the class A an around wrapper which
|
55
|
+
# logs access.
|
56
|
+
#
|
57
|
+
# class A
|
58
|
+
# def a
|
59
|
+
# "a"
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# layer :log
|
63
|
+
#
|
64
|
+
# instance_methods.each do | method |
|
65
|
+
#
|
66
|
+
# log.around method.to_sym do | method_nature |
|
67
|
+
# logger.info "before #{self.class}##{method}"
|
68
|
+
#
|
69
|
+
# method_nature.call_next
|
70
|
+
#
|
71
|
+
# logger.info "after #{self.class}##{method}"
|
72
|
+
# end
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
def call_next
|
78
|
+
block.call( *arguments )
|
79
|
+
end
|
80
|
+
end
|
@@ -76,42 +76,3 @@ context 'Each layer in a class' do
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
-
context "An instance of a contextified class" do
|
80
|
-
setup do
|
81
|
-
@instance = ContextRApiFoo.new
|
82
|
-
end
|
83
|
-
|
84
|
-
specify "should run a simple method " +
|
85
|
-
"*normally* when all layers are deactivated" do
|
86
|
-
@instance.non_contextified_method.should == "non_contextified_method"
|
87
|
-
end
|
88
|
-
|
89
|
-
specify "should run a simple method " +
|
90
|
-
"*normally* when any layer is activated" do
|
91
|
-
ContextR.with_layers( :bar ) do
|
92
|
-
@instance.non_contextified_method.should == "non_contextified_method"
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
specify "should run a contextified method " +
|
97
|
-
"*normally* when all layers are deactivated" do
|
98
|
-
@instance.foo.should == "foo"
|
99
|
-
end
|
100
|
-
|
101
|
-
specify "should run a contextified method " +
|
102
|
-
"*normally* when any layer is activated" do
|
103
|
-
ContextR.with_layers( :baz ) do
|
104
|
-
@instance.foo.should == "foo"
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
specify "should run a contextified method with " +
|
109
|
-
"additional behaviour when a specific layer is activated" do
|
110
|
-
ContextR.with_layers( :bar ) do
|
111
|
-
@instance.foo.should == "foo"
|
112
|
-
end
|
113
|
-
@instance.instance_variable_get( :@pre_visited ).should == true
|
114
|
-
@instance.instance_variable_get( :@post_visited ).should == true
|
115
|
-
@instance.instance_variable_get( :@around_visited ).should == true
|
116
|
-
end
|
117
|
-
end
|
@@ -0,0 +1,294 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
class SimpleWrapperClass
|
4
|
+
def non_contextified_method
|
5
|
+
"non_contextified_method"
|
6
|
+
end
|
7
|
+
def pre_wrapped_method
|
8
|
+
"pre_wrapped_method"
|
9
|
+
end
|
10
|
+
def post_wrapped_method
|
11
|
+
"post_wrapped_method"
|
12
|
+
end
|
13
|
+
def around_wrapped_method
|
14
|
+
"around_wrapped_method"
|
15
|
+
end
|
16
|
+
|
17
|
+
layer :simple_wrappers, :dummy
|
18
|
+
|
19
|
+
simple_wrappers.pre :pre_wrapped_method do
|
20
|
+
@pre_wrapped_method_called = true
|
21
|
+
end
|
22
|
+
simple_wrappers.post :post_wrapped_method do
|
23
|
+
@post_wrapped_method_called = true
|
24
|
+
end
|
25
|
+
simple_wrappers.around :around_wrapped_method do | n |
|
26
|
+
@around_wrapped_method_called = true
|
27
|
+
n.call_next
|
28
|
+
end
|
29
|
+
end
|
30
|
+
context "An instance of a contextified class" do
|
31
|
+
setup do
|
32
|
+
@instance = SimpleWrapperClass.new
|
33
|
+
end
|
34
|
+
|
35
|
+
specify "should run a simple method " +
|
36
|
+
"*normally* when all layers are deactivated" do
|
37
|
+
@instance.non_contextified_method.should == "non_contextified_method"
|
38
|
+
end
|
39
|
+
|
40
|
+
specify "should run a simple method " +
|
41
|
+
"*normally* when any layer is activated" do
|
42
|
+
ContextR.with_layers( :simple_wrappers ) do
|
43
|
+
@instance.non_contextified_method.should == "non_contextified_method"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
%w{pre post around}.each do | qualifier |
|
48
|
+
specify "should run a #{qualifier}-ed method " +
|
49
|
+
"*normally* when all layers are deactivated" do
|
50
|
+
@instance.send( "#{qualifier}_wrapped_method" ).should ==
|
51
|
+
"#{qualifier}_wrapped_method"
|
52
|
+
@instance.instance_variables.should_not_include(
|
53
|
+
"@#{qualifier}_wrapped_method_called" )
|
54
|
+
end
|
55
|
+
|
56
|
+
specify "should run a #{qualifier}-ed method " +
|
57
|
+
"*normally* when any layer is activated" do
|
58
|
+
ContextR.with_layers( :dummy ) do
|
59
|
+
@instance.send( "#{qualifier}_wrapped_method" ).should ==
|
60
|
+
"#{qualifier}_wrapped_method"
|
61
|
+
@instance.instance_variables.should_not_include(
|
62
|
+
"@#{qualifier}_wrapped_method_called" )
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
specify "should run a #{qualifier}-ed method with " +
|
67
|
+
"additional behaviour when a specific layer is activated" do
|
68
|
+
ContextR.with_layers( :simple_wrappers ) do
|
69
|
+
@instance.send( "#{qualifier}_wrapped_method" ).should ==
|
70
|
+
"#{qualifier}_wrapped_method"
|
71
|
+
@instance.instance_variables.should_include(
|
72
|
+
"@#{qualifier}_wrapped_method_called" )
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
specify "should run a #{qualifier}-ed method without additional " +
|
77
|
+
"behaviour when a specific layer is activated and afterwards " +
|
78
|
+
"deactivated" do
|
79
|
+
ContextR.with_layers( :simple_wrappers ) do
|
80
|
+
ContextR.without_layers( :simple_wrappers ) do
|
81
|
+
@instance.send( "#{qualifier}_wrapped_method" ).should ==
|
82
|
+
"#{qualifier}_wrapped_method"
|
83
|
+
@instance.instance_variables.should_not_include(
|
84
|
+
"@#{qualifier}_wrapped_method_called" )
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class NestedLayerActivationClass
|
92
|
+
attr_accessor :execution
|
93
|
+
def initialize
|
94
|
+
@execution = []
|
95
|
+
end
|
96
|
+
|
97
|
+
def preed_method
|
98
|
+
@execution << "core"
|
99
|
+
"preed_method"
|
100
|
+
end
|
101
|
+
|
102
|
+
def posted_method
|
103
|
+
@execution << "core"
|
104
|
+
"posted_method"
|
105
|
+
end
|
106
|
+
|
107
|
+
def arounded_method
|
108
|
+
@execution << "core"
|
109
|
+
"arounded_method"
|
110
|
+
end
|
111
|
+
|
112
|
+
layer :outer_layer, :inner_layer
|
113
|
+
|
114
|
+
inner_layer.pre :preed_method do
|
115
|
+
@execution << "inner_layer"
|
116
|
+
end
|
117
|
+
|
118
|
+
outer_layer.pre :preed_method do
|
119
|
+
@execution << "outer_layer"
|
120
|
+
end
|
121
|
+
|
122
|
+
inner_layer.post :posted_method do
|
123
|
+
@execution << "inner_layer"
|
124
|
+
end
|
125
|
+
|
126
|
+
outer_layer.post :posted_method do
|
127
|
+
@execution << "outer_layer"
|
128
|
+
end
|
129
|
+
|
130
|
+
inner_layer.around :arounded_method do | n |
|
131
|
+
@execution << "inner_layer_pre"
|
132
|
+
n.call_next
|
133
|
+
@execution << "inner_layer_post"
|
134
|
+
end
|
135
|
+
|
136
|
+
outer_layer.around :arounded_method do | n |
|
137
|
+
@execution << "outer_layer_pre"
|
138
|
+
n.call_next
|
139
|
+
@execution << "outer_layer_post"
|
140
|
+
end
|
141
|
+
|
142
|
+
def contextualized_method
|
143
|
+
@execution << "core"
|
144
|
+
"contextualized_method"
|
145
|
+
end
|
146
|
+
|
147
|
+
layer :break_in_pre, :break_in_post, :break_in_around
|
148
|
+
layer :other_layer
|
149
|
+
|
150
|
+
break_in_pre.pre :contextualized_method do | n |
|
151
|
+
n.return_value = "contextualized_method"
|
152
|
+
@execution << "breaking_pre"
|
153
|
+
n.break!
|
154
|
+
end
|
155
|
+
break_in_post.post :contextualized_method do | n |
|
156
|
+
@execution << "breaking_post"
|
157
|
+
n.break!
|
158
|
+
end
|
159
|
+
break_in_around.around :contextualized_method do | n |
|
160
|
+
@execution << "breaking_around"
|
161
|
+
n.return_value = "contextualized_method"
|
162
|
+
n.break!
|
163
|
+
n.call_next
|
164
|
+
end
|
165
|
+
|
166
|
+
other_layer.pre :contextualized_method do | n |
|
167
|
+
@execution << "other_pre"
|
168
|
+
end
|
169
|
+
other_layer.post :contextualized_method do | n |
|
170
|
+
@execution << "other_post"
|
171
|
+
end
|
172
|
+
other_layer.around :contextualized_method do | n |
|
173
|
+
@execution << "other_around"
|
174
|
+
n.call_next
|
175
|
+
end
|
176
|
+
|
177
|
+
layer :multiple_pres, :multiple_posts, :multiple_arounds
|
178
|
+
multiple_pres.pre :contextualized_method do
|
179
|
+
@execution << "first_pre"
|
180
|
+
end
|
181
|
+
multiple_pres.pre :contextualized_method do
|
182
|
+
@execution << "second_pre"
|
183
|
+
end
|
184
|
+
|
185
|
+
multiple_posts.post :contextualized_method do
|
186
|
+
@execution << "first_post"
|
187
|
+
end
|
188
|
+
multiple_posts.post :contextualized_method do
|
189
|
+
@execution << "second_post"
|
190
|
+
end
|
191
|
+
|
192
|
+
multiple_arounds.around :contextualized_method do | n |
|
193
|
+
@execution << "first_around_pre"
|
194
|
+
n.call_next
|
195
|
+
@execution << "first_around_post"
|
196
|
+
end
|
197
|
+
multiple_arounds.around :contextualized_method do | n |
|
198
|
+
@execution << "second_around_pre"
|
199
|
+
n.call_next
|
200
|
+
@execution << "second_around_post"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
pre_spec = lambda do | instance |
|
205
|
+
instance.execution.shift.should == "outer_layer"
|
206
|
+
instance.execution.shift.should == "inner_layer"
|
207
|
+
instance.execution.shift.should == "core"
|
208
|
+
end
|
209
|
+
post_spec = lambda do | instance |
|
210
|
+
instance.execution.shift.should == "core"
|
211
|
+
instance.execution.shift.should == "inner_layer"
|
212
|
+
instance.execution.shift.should == "outer_layer"
|
213
|
+
end
|
214
|
+
around_spec = lambda do | instance |
|
215
|
+
instance.execution.shift.should == "outer_layer_pre"
|
216
|
+
instance.execution.shift.should == "inner_layer_pre"
|
217
|
+
instance.execution.shift.should == "core"
|
218
|
+
instance.execution.shift.should == "inner_layer_post"
|
219
|
+
instance.execution.shift.should == "outer_layer_post"
|
220
|
+
end
|
221
|
+
|
222
|
+
pre_break_spec = lambda do | instance |
|
223
|
+
instance.execution.shift.should == "breaking_pre"
|
224
|
+
end
|
225
|
+
post_break_spec = lambda do | instance |
|
226
|
+
instance.execution.shift.should == "other_pre"
|
227
|
+
instance.execution.shift.should == "other_around"
|
228
|
+
instance.execution.shift.should == "core"
|
229
|
+
instance.execution.shift.should == "other_post"
|
230
|
+
instance.execution.shift.should == "breaking_post"
|
231
|
+
end
|
232
|
+
around_break_spec = lambda do | instance |
|
233
|
+
instance.execution.shift.should == "other_pre"
|
234
|
+
instance.execution.shift.should == "breaking_around"
|
235
|
+
end
|
236
|
+
|
237
|
+
pre_multiple_spec = lambda do | instance |
|
238
|
+
instance.execution.shift.should == "first_pre"
|
239
|
+
instance.execution.shift.should == "second_pre"
|
240
|
+
instance.execution.shift.should == "core"
|
241
|
+
end
|
242
|
+
post_multiple_spec = lambda do | instance |
|
243
|
+
instance.execution.shift.should == "core"
|
244
|
+
instance.execution.shift.should == "second_post"
|
245
|
+
instance.execution.shift.should == "first_post"
|
246
|
+
end
|
247
|
+
around_multiple_spec = lambda do | instance |
|
248
|
+
instance.execution.shift.should == "first_around_pre"
|
249
|
+
instance.execution.shift.should == "second_around_pre"
|
250
|
+
instance.execution.shift.should == "core"
|
251
|
+
instance.execution.shift.should == "second_around_post"
|
252
|
+
instance.execution.shift.should == "first_around_post"
|
253
|
+
end
|
254
|
+
|
255
|
+
%w{pre post around}.each do | qualifier |
|
256
|
+
context "#{qualifier.capitalize} wrappers within a method" do
|
257
|
+
setup do
|
258
|
+
@instance = NestedLayerActivationClass.new
|
259
|
+
end
|
260
|
+
specify "should run in the sequence of nesting, when using nested " +
|
261
|
+
"activation" do
|
262
|
+
ContextR::with_layers :outer_layer do
|
263
|
+
ContextR::with_layers :inner_layer do
|
264
|
+
@instance.send( "#{qualifier}ed_method" ).should ==
|
265
|
+
"#{qualifier}ed_method"
|
266
|
+
end
|
267
|
+
end
|
268
|
+
eval("#{qualifier}_spec").call( @instance )
|
269
|
+
end
|
270
|
+
specify "should run in the sequence of nesting, when using simultaneous " +
|
271
|
+
"activation" do
|
272
|
+
ContextR::with_layers :outer_layer, :inner_layer do
|
273
|
+
@instance.send( "#{qualifier}ed_method" ).should ==
|
274
|
+
"#{qualifier}ed_method"
|
275
|
+
end
|
276
|
+
eval("#{qualifier}_spec").call( @instance )
|
277
|
+
end
|
278
|
+
|
279
|
+
specify "should run in the sequence of definition within the same layer" do
|
280
|
+
ContextR::with_layers "multiple_#{qualifier}s".to_sym do
|
281
|
+
@instance.contextualized_method.should == "contextualized_method"
|
282
|
+
end
|
283
|
+
eval("#{qualifier}_multiple_spec").call( @instance )
|
284
|
+
end
|
285
|
+
|
286
|
+
specify "should be able to stop the execution with `break!`" do
|
287
|
+
ContextR::with_layers "break_in_#{qualifier}".to_sym, :other_layer do
|
288
|
+
@instance.contextualized_method.should == "contextualized_method"
|
289
|
+
end
|
290
|
+
eval("#{qualifier}_break_spec").call( @instance )
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|