jackbox 0.9.6.2 → 0.9.6.3
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.
- checksums.yaml +13 -5
- data/CHANGES.txt +41 -1
- data/LICENSE.lic +0 -0
- data/README.md +244 -149
- data/Rakefile +6 -6
- data/jackbox.gemspec +3 -2
- data/lib/jackbox.rb +1 -1
- data/lib/jackbox/injectors.rb +1 -1
- data/lib/jackbox/rake.rb +1 -1
- data/lib/jackbox/tools/prefs.rb +1 -1
- data/lib/jackbox/version.rb +1 -1
- data/spec/lib/abtract_spec.rb +6 -6
- data/spec/lib/jackbox/injector_composition_spec.rb +106 -88
- data/spec/lib/jackbox/injector_directives_spec.rb +46 -0
- data/spec/lib/jackbox/injector_inheritance_spec.rb +980 -410
- data/spec/lib/jackbox/injector_introspection_spec.rb +333 -208
- data/spec/lib/jackbox/injector_spec.rb +28 -28
- data/spec/lib/jackbox/injector_versioning_spec.rb +1 -0
- data/spec/lib/jackbox/patterns_spec.rb +146 -14
- data/spec/lib/jackbox/reclassing_spec.rb +165 -78
- data/spec/lib/jackbox/vmc_spec.rb +246 -0
- metadata +32 -28
@@ -264,3 +264,49 @@ to re-inject them into every object they modify' do
|
|
264
264
|
end
|
265
265
|
|
266
266
|
end
|
267
|
+
|
268
|
+
describe 'more interesting uses' do
|
269
|
+
|
270
|
+
it 'allows the following' do
|
271
|
+
|
272
|
+
facet :PreFunction do
|
273
|
+
def pre_function
|
274
|
+
puts '++++++++++'
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
facet :PosFunction do
|
279
|
+
def pos_function
|
280
|
+
puts '=========='
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
class Model
|
285
|
+
|
286
|
+
inject PreFunction(:silence)
|
287
|
+
inject PosFunction(:silence)
|
288
|
+
|
289
|
+
def meth arg
|
290
|
+
pre_function
|
291
|
+
puts arg * arg
|
292
|
+
pos_function
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
obj = Model.new
|
297
|
+
|
298
|
+
$stdout.should_receive(:puts).with(4)
|
299
|
+
obj.meth( 2 )
|
300
|
+
|
301
|
+
PreFunction(:active)
|
302
|
+
PosFunction(:active)
|
303
|
+
|
304
|
+
$stdout.should_receive(:puts).with('++++++++++')
|
305
|
+
$stdout.should_receive(:puts).with(4)
|
306
|
+
$stdout.should_receive(:puts).with('==========')
|
307
|
+
obj.meth( 2 )
|
308
|
+
|
309
|
+
end
|
310
|
+
|
311
|
+
|
312
|
+
end
|
@@ -13,60 +13,89 @@ require "spec_helper"
|
|
13
13
|
|
14
14
|
include Injectors
|
15
15
|
|
16
|
+
|
17
|
+
####################### IMPORTANT ##############################
|
18
|
+
# NOTE: Once again, some of these examples are long on purpose.
|
19
|
+
# We are trying to show what happens to Injectors as
|
20
|
+
# a result of various lifecycle change events.
|
21
|
+
# ##############################################################
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
# RubyProf.start
|
16
26
|
describe "ancestor chains" do
|
17
27
|
|
18
28
|
it 'updates the ancestor chains accordingly based on: injections and ejections' do
|
19
|
-
|
29
|
+
|
30
|
+
# LifeCycle Start
|
31
|
+
|
20
32
|
injector :Parent # capitalized method
|
33
|
+
|
21
34
|
class Child
|
22
35
|
include Parent()
|
23
36
|
end
|
24
|
-
|
37
|
+
c = Child.new
|
38
|
+
|
39
|
+
# Parent is ancestor of the class and instance
|
40
|
+
Child.ancestors.to_s.should match( /Child, \(\|Parent\|.*\), Object.*BasicObject/ )
|
41
|
+
c.singleton_class.ancestors.to_s.should match(/Child, \(\|Parent\|.*\), Object.*BasicObject/ )
|
42
|
+
|
43
|
+
# Parent is ancestor of the class and of the instance metaclass
|
44
|
+
c.enrich Parent()
|
45
|
+
c.singleton_class.ancestors.to_s.should match( /\(\|Parent\|.*\), Child, \(\|Parent\|.*\), Object.*BasicObject/ )
|
25
46
|
|
26
|
-
c = Child.new.enrich Parent()
|
27
|
-
# Parent is ancestor of the class and of the metaclass
|
28
|
-
c.singleton_class.ancestors.to_s.should match( /\(.*\|Parent\|\), Child, \(.*\|Parent\|\), Object.*BasicObject/ )
|
29
47
|
|
30
|
-
c.eject Parent()
|
31
48
|
# Parent is ejected from the object metaclass and ancestor chain reverts back
|
32
|
-
c.
|
49
|
+
c.eject Parent()
|
50
|
+
c.singleton_class.ancestors.to_s.should match( /Child, \(\|Parent\|.*\), Object.*BasicObject/ )
|
51
|
+
|
33
52
|
|
34
|
-
Child.eject Parent()
|
35
53
|
# Parent is ejected from the class and ancestors chain is empty
|
54
|
+
Child.eject Parent()
|
36
55
|
Child.ancestors.to_s.should match( /Child, Object.*BasicObject/ )
|
37
|
-
|
56
|
+
|
57
|
+
|
58
|
+
# Note: cannot update empty injector
|
38
59
|
expect{ Child.send :update, Child.parent }.to raise_error(NoMethodError)
|
39
60
|
|
61
|
+
|
62
|
+
# Instance metaclass is extended with Parent twice like in the case of multiple decorators
|
40
63
|
c.enrich Parent()
|
41
64
|
c.enrich Parent()
|
42
|
-
|
43
|
-
|
65
|
+
c.singleton_class.ancestors.to_s.should match( /\(\|Parent\|.*\), \(\|Parent\|.*\), Child, Object.*BasicObject/ )
|
66
|
+
|
44
67
|
|
68
|
+
# Instance is reverted back to no Injectors
|
45
69
|
c.eject Parent()
|
46
70
|
c.eject Parent()
|
47
|
-
# object is reverted back to no Injectors
|
48
71
|
c.singleton_class.ancestors.to_s.should match( /Child, Object.*BasicObject/ )
|
49
72
|
|
73
|
+
|
74
|
+
# LyfeCycle Restart
|
75
|
+
|
76
|
+
# class is re-injected with Parent and becomes ancestor of the class and instance
|
50
77
|
Child.inject Parent()
|
51
|
-
|
52
|
-
Child.ancestors.to_s.should match( /Child, \(
|
53
|
-
c.singleton_class.ancestors.to_s.should match( /Child, \(
|
78
|
+
|
79
|
+
Child.ancestors.to_s.should match( /Child, \(\|Parent\|.*\), Object.*BasicObject/ )
|
80
|
+
c.singleton_class.ancestors.to_s.should match( /Child, \(\|Parent\|.*\), Object.*BasicObject/ )
|
54
81
|
|
82
|
+
|
83
|
+
# Parent is eject from the instance
|
55
84
|
c.eject Parent()
|
56
|
-
# class level Injector ejected from single object and ancestor for the object updated but not for class
|
57
85
|
c.singleton_class.ancestors.to_s.should match( /Child, Object.*BasicObject/ )
|
58
|
-
Child.ancestors.to_s.should match( /Child, \(
|
86
|
+
Child.ancestors.to_s.should match( /Child, \(\|Parent\|.*\), Object.*BasicObject/ )
|
87
|
+
|
59
88
|
|
89
|
+
# Class-level Injector update re-introduces it to objects that have ejected it locally and ancestors is updated
|
60
90
|
Child.send :update, Parent()
|
61
|
-
|
62
|
-
c.singleton_class.ancestors.to_s.should match( /Child, \(.*\|Parent\|\), Object.*BasicObject/ )
|
91
|
+
c.singleton_class.ancestors.to_s.should match( /Child, \(\|Parent\|.*\), Object.*BasicObject/ )
|
63
92
|
|
64
93
|
end
|
65
94
|
end
|
66
95
|
|
67
|
-
describe "the
|
96
|
+
describe "the behavior of injectors under class inheritance" do
|
68
97
|
|
69
|
-
example "
|
98
|
+
example "working accross class hierarchies and the effects of ejection" do
|
70
99
|
|
71
100
|
|
72
101
|
###########################################
|
@@ -77,7 +106,7 @@ describe "the inheritance behavior of injectors" do
|
|
77
106
|
class C
|
78
107
|
end
|
79
108
|
C.inject j { # #foo pre-defined at time of injection
|
80
|
-
def
|
109
|
+
def m1
|
81
110
|
'foo'
|
82
111
|
end
|
83
112
|
}
|
@@ -87,167 +116,177 @@ describe "the inheritance behavior of injectors" do
|
|
87
116
|
injector :k
|
88
117
|
|
89
118
|
C.inject k { # #faa pre-defined at injection
|
90
|
-
def
|
119
|
+
def m2
|
91
120
|
'faa'
|
92
121
|
end
|
93
122
|
}
|
94
|
-
C.injectors.sym_list.should == [:
|
95
|
-
C.new.injectors.sym_list.should == [:
|
123
|
+
C.injectors.sym_list.should == [:k, :j]
|
124
|
+
C.new.injectors.sym_list.should == [:k, :j]
|
96
125
|
|
97
|
-
C.new.
|
98
|
-
C.new.
|
126
|
+
C.new.m1.should == 'foo'
|
127
|
+
C.new.m2.should == 'faa'
|
99
128
|
c = C.new
|
100
129
|
|
101
130
|
|
131
|
+
########################################
|
102
132
|
# D inherits from C
|
103
|
-
|
133
|
+
########################################
|
104
134
|
class D < C # methods are inherited from j and k
|
105
135
|
end
|
106
|
-
C.injectors.sym_list.should == [:
|
107
|
-
C.new.injectors.sym_list.should == [:
|
136
|
+
C.injectors.sym_list.should == [:k, :j]
|
137
|
+
C.new.injectors.sym_list.should == [:k, :j]
|
108
138
|
D.injectors.sym_list.should == []
|
109
139
|
D.new.injectors.sym_list.should == []
|
110
140
|
|
111
141
|
# New Objects
|
112
|
-
C.new.
|
113
|
-
C.new.
|
114
|
-
D.new.
|
115
|
-
D.new.
|
142
|
+
C.new.m1.should == 'foo'
|
143
|
+
C.new.m2.should == 'faa'
|
144
|
+
D.new.m1.should == 'foo'
|
145
|
+
D.new.m2.should == 'faa'
|
116
146
|
# Existing Objects
|
117
147
|
d = D.new
|
118
|
-
c.
|
119
|
-
c.
|
148
|
+
c.m1.should == 'foo'
|
149
|
+
c.m2.should == 'faa'
|
120
150
|
|
121
151
|
|
152
|
+
########################################
|
122
153
|
# inject D and override C
|
123
|
-
|
154
|
+
########################################
|
124
155
|
D.inject j { # new version of j pre-defined at injection
|
125
|
-
def
|
156
|
+
def m1
|
126
157
|
'foooo'
|
127
158
|
end
|
128
159
|
}
|
129
|
-
C.injectors.sym_list.should == [:
|
130
|
-
C.new.injectors.sym_list.should == [:
|
160
|
+
C.injectors.sym_list.should == [:k, :j]
|
161
|
+
C.new.injectors.sym_list.should == [:k, :j]
|
131
162
|
D.injectors.sym_list.should == [:j]
|
132
163
|
D.new.injectors.sym_list.should == [:j]
|
133
164
|
|
134
165
|
# New Objects
|
135
|
-
D.new.
|
166
|
+
D.new.m1.should == 'foooo' # new version of #foo
|
136
167
|
# still the same
|
137
|
-
D.new.
|
138
|
-
C.new.
|
139
|
-
C.new.
|
168
|
+
D.new.m2.should == 'faa'
|
169
|
+
C.new.m1.should == 'foo'
|
170
|
+
C.new.m2.should == 'faa'
|
140
171
|
# Existing Objects
|
141
|
-
c.
|
142
|
-
c.
|
143
|
-
d.
|
144
|
-
d.
|
172
|
+
c.m1.should == 'foo'
|
173
|
+
c.m2.should == 'faa'
|
174
|
+
d.m1.should == 'foooo'
|
175
|
+
d.m2.should == 'faa'
|
145
176
|
|
146
177
|
|
178
|
+
########################################
|
147
179
|
# D class overrides j
|
148
|
-
|
180
|
+
########################################
|
149
181
|
class D < C
|
150
|
-
def
|
182
|
+
def m1 # overrides foo from j
|
151
183
|
'fuu'
|
152
184
|
end
|
153
185
|
end
|
154
|
-
C.injectors.sym_list.should == [:
|
155
|
-
C.new.injectors.sym_list.should == [:
|
186
|
+
C.injectors.sym_list.should == [:k, :j]
|
187
|
+
C.new.injectors.sym_list.should == [:k, :j]
|
156
188
|
D.injectors.sym_list.should == [:j]
|
157
189
|
D.new.injectors.sym_list.should == [:j]
|
158
190
|
|
159
191
|
# New Objects
|
160
|
-
D.new.
|
192
|
+
D.new.m1.should == 'fuu' # overrided last version from j
|
161
193
|
# still the same
|
162
|
-
D.new.
|
163
|
-
C.new.
|
164
|
-
C.new.
|
194
|
+
D.new.m2.should == 'faa'
|
195
|
+
C.new.m1.should == 'foo'
|
196
|
+
C.new.m2.should == 'faa'
|
165
197
|
# Existing Objects
|
166
|
-
c.
|
167
|
-
c.
|
168
|
-
d.
|
169
|
-
d.
|
198
|
+
c.m1.should == 'foo'
|
199
|
+
c.m2.should == 'faa'
|
200
|
+
d.m1.should == 'fuu' # overrided
|
201
|
+
d.m2.should == 'faa'
|
170
202
|
|
171
203
|
|
204
|
+
########################################
|
172
205
|
# update C and inherit into D
|
173
|
-
|
206
|
+
########################################
|
174
207
|
C.send :update, k { # new version of k pre-defined at update
|
175
|
-
def
|
208
|
+
def m2 # -- no other version of k in hierarchy
|
176
209
|
'faaxx'
|
177
210
|
end
|
178
211
|
}
|
179
|
-
|
180
|
-
|
212
|
+
# can also be written
|
213
|
+
# class C
|
214
|
+
# update k # providing k is in scope
|
215
|
+
# end
|
216
|
+
C.injectors.sym_list.should == [:k, :j]
|
217
|
+
C.new.injectors.sym_list.should == [:k, :j]
|
181
218
|
D.injectors.sym_list.should == [:j]
|
182
219
|
D.new.injectors.sym_list.should == [:j]
|
183
220
|
|
184
221
|
# New Objects
|
185
|
-
C.new.
|
186
|
-
D.new.
|
222
|
+
C.new.m2.should == 'faaxx' # new version of #faa
|
223
|
+
D.new.m2.should == 'faaxx'
|
187
224
|
# still the same
|
188
|
-
C.new.
|
189
|
-
D.new.
|
225
|
+
C.new.m1.should == 'foo'
|
226
|
+
D.new.m1.should == 'fuu'
|
190
227
|
# Existing Objects
|
191
|
-
c.
|
192
|
-
c.
|
193
|
-
d.
|
194
|
-
d.
|
228
|
+
c.m1.should == 'foo'
|
229
|
+
c.m2.should == 'faaxx'
|
230
|
+
d.m1.should == 'fuu'
|
231
|
+
d.m2.should == 'faaxx'
|
195
232
|
|
196
233
|
|
234
|
+
########################################
|
197
235
|
# E inherits from D
|
198
|
-
|
236
|
+
########################################
|
199
237
|
class E < D # methods are inherited from j at C, D and k update at C
|
200
238
|
end
|
201
|
-
C.injectors.sym_list.should == [:
|
202
|
-
C.new.injectors.sym_list.should == [:
|
239
|
+
C.injectors.sym_list.should == [:k, :j]
|
240
|
+
C.new.injectors.sym_list.should == [:k, :j]
|
203
241
|
D.injectors.sym_list.should == [:j]
|
204
242
|
D.new.injectors.sym_list.should == [:j]
|
205
243
|
E.injectors.sym_list.should == []
|
206
244
|
E.new.injectors.sym_list.should == []
|
207
245
|
|
208
246
|
# New Objects
|
209
|
-
C.new.
|
210
|
-
C.new.
|
211
|
-
D.new.
|
212
|
-
D.new.
|
213
|
-
E.new.
|
214
|
-
E.new.
|
247
|
+
C.new.m1.should == 'foo'
|
248
|
+
C.new.m2.should == 'faaxx'
|
249
|
+
D.new.m1.should == 'fuu'
|
250
|
+
D.new.m2.should == 'faaxx' # new objects pass
|
251
|
+
E.new.m1.should == 'fuu'
|
252
|
+
E.new.m2.should == 'faaxx'
|
215
253
|
# Existing Objects
|
216
254
|
e = E.new
|
217
|
-
c.
|
218
|
-
c.
|
219
|
-
d.
|
220
|
-
d.
|
255
|
+
c.m1.should == 'foo'
|
256
|
+
c.m2.should == 'faaxx' # existing objects pass
|
257
|
+
d.m1.should == 'fuu'
|
258
|
+
d.m2.should == 'faaxx'
|
221
259
|
|
222
260
|
|
261
|
+
########################################
|
223
262
|
# E overrides D
|
224
|
-
|
263
|
+
########################################
|
225
264
|
class E < D
|
226
|
-
def
|
265
|
+
def m1 # overrides #foo from j at C, D
|
227
266
|
'fuuuu'
|
228
267
|
end
|
229
268
|
end
|
230
|
-
C.injectors.sym_list.should == [:
|
231
|
-
C.new.injectors.sym_list.should == [:
|
269
|
+
C.injectors.sym_list.should == [:k, :j]
|
270
|
+
C.new.injectors.sym_list.should == [:k, :j]
|
232
271
|
D.injectors.sym_list.should == [:j]
|
233
272
|
D.new.injectors.sym_list.should == [:j]
|
234
273
|
E.injectors.sym_list.should == []
|
235
274
|
E.new.injectors.sym_list.should == []
|
236
275
|
|
237
276
|
# New Objects
|
238
|
-
C.new.
|
239
|
-
C.new.
|
240
|
-
D.new.
|
241
|
-
D.new.
|
242
|
-
E.new.
|
243
|
-
E.new.
|
277
|
+
C.new.m1.should == 'foo'
|
278
|
+
C.new.m2.should == 'faaxx'
|
279
|
+
D.new.m1.should == 'fuu'
|
280
|
+
D.new.m2.should == 'faaxx'
|
281
|
+
E.new.m1.should == 'fuuuu'
|
282
|
+
E.new.m2.should == 'faaxx'
|
244
283
|
# Existing Objects
|
245
|
-
c.
|
246
|
-
c.
|
247
|
-
d.
|
248
|
-
d.
|
249
|
-
e.
|
250
|
-
e.
|
284
|
+
c.m1.should == 'foo'
|
285
|
+
c.m2.should == 'faaxx'
|
286
|
+
d.m1.should == 'fuu'
|
287
|
+
d.m2.should == 'faaxx'
|
288
|
+
e.m1.should == 'fuuuu'
|
289
|
+
e.m2.should == 'faaxx'
|
251
290
|
|
252
291
|
|
253
292
|
#######################################
|
@@ -263,21 +302,24 @@ describe "the inheritance behavior of injectors" do
|
|
263
302
|
E.injectors.sym_list.should == []
|
264
303
|
|
265
304
|
# New Objects
|
266
|
-
expect{ C.new.
|
267
|
-
C.new.
|
268
|
-
D.new.
|
269
|
-
D.new.
|
270
|
-
E.new.
|
271
|
-
E.new.
|
305
|
+
expect{ C.new.m1.should == 'foo'}.to raise_error(NoMethodError) # m1 errors out on C
|
306
|
+
C.new.m2.should == 'faaxx'
|
307
|
+
D.new.m1.should == 'fuu'
|
308
|
+
D.new.m2.should == 'faaxx' # all else is the same...
|
309
|
+
E.new.m1.should == 'fuuuu'
|
310
|
+
E.new.m2.should == 'faaxx'
|
272
311
|
# Existing Objects
|
273
|
-
expect{c.
|
274
|
-
c.
|
275
|
-
d.
|
276
|
-
d.
|
277
|
-
e.
|
278
|
-
e.
|
279
|
-
|
280
|
-
|
312
|
+
expect{c.m1.should == 'foo'}.to raise_error(NoMethodError) # m1 errors out on C
|
313
|
+
c.m2.should == 'faaxx'
|
314
|
+
d.m1.should == 'fuu'
|
315
|
+
d.m2.should == 'faaxx'
|
316
|
+
e.m1.should == 'fuuuu'
|
317
|
+
e.m2.should == 'faaxx'
|
318
|
+
|
319
|
+
|
320
|
+
########################################
|
321
|
+
# more ejection
|
322
|
+
########################################
|
281
323
|
C.eject :k # eject the only k
|
282
324
|
|
283
325
|
C.new.injectors.sym_list.should == []
|
@@ -288,21 +330,24 @@ describe "the inheritance behavior of injectors" do
|
|
288
330
|
E.injectors.sym_list.should == []
|
289
331
|
|
290
332
|
# New Objects
|
291
|
-
expect{ C.new.
|
292
|
-
expect{ C.new.
|
293
|
-
D.new.
|
294
|
-
expect{ D.new.
|
295
|
-
E.new.
|
296
|
-
expect{ E.new.
|
333
|
+
expect{ C.new.m1.should == 'foo'}.to raise_error(NoMethodError)
|
334
|
+
expect{ C.new.m2.should == 'faaxx'}.to raise_error(NoMethodError) # # faa errors out
|
335
|
+
D.new.m1.should == 'fuu'
|
336
|
+
expect{ D.new.m2.should == 'faaxx'}.to raise_error(NoMethodError) #faa errors out
|
337
|
+
E.new.m1.should == 'fuuuu'
|
338
|
+
expect{ E.new.m2.should == 'faaxx'}.to raise_error(NoMethodError) #faa was only available thru k at C
|
297
339
|
# Existing Objects
|
298
|
-
expect{c.
|
299
|
-
expect{c.
|
300
|
-
d.
|
301
|
-
expect{d.
|
302
|
-
e.
|
303
|
-
expect{e.
|
340
|
+
expect{c.m1.should == 'foo'}.to raise_error(NoMethodError)
|
341
|
+
expect{c.m2.should == 'faaxx'}.to raise_error(NoMethodError)
|
342
|
+
d.m1.should == 'fuu' # same thing for pre-existing objects
|
343
|
+
expect{d.m2.should == 'faaxx'}.to raise_error(NoMethodError)
|
344
|
+
e.m1.should == 'fuuuu'
|
345
|
+
expect{e.m2.should == 'faaxx'}.to raise_error(NoMethodError)
|
304
346
|
|
305
347
|
|
348
|
+
########################################
|
349
|
+
# more ejection
|
350
|
+
########################################
|
306
351
|
D.eject :j # eject j from D: the only one remaining
|
307
352
|
# -- everything should revert back to class level
|
308
353
|
C.injectors.sym_list.should == []
|
@@ -311,241 +356,263 @@ describe "the inheritance behavior of injectors" do
|
|
311
356
|
D.new.injectors.sym_list.should == []
|
312
357
|
E.injectors.sym_list.should == []
|
313
358
|
E.new.injectors.sym_list.should == []
|
359
|
+
|
314
360
|
# New Objects
|
315
|
-
expect{ C.new.
|
316
|
-
expect{ C.new.
|
317
|
-
D.new.
|
318
|
-
expect{ D.new.
|
319
|
-
E.new.
|
320
|
-
expect{ E.new.
|
361
|
+
expect{ C.new.m1.should == 'foo'}.to raise_error(NoMethodError) # no actual #foo on class
|
362
|
+
expect{ C.new.m2.should == 'faaxx'}.to raise_error(NoMethodError) # ''
|
363
|
+
D.new.m1.should == 'fuu' # retains overrides from D
|
364
|
+
expect{ D.new.m2.should == 'faaxx'}.to raise_error(NoMethodError)
|
365
|
+
E.new.m1.should == 'fuuuu' # retains overrides from E
|
366
|
+
expect{ E.new.m2.should == 'faaxx'}.to raise_error(NoMethodError)
|
321
367
|
# Existing Objects
|
322
|
-
expect{c.
|
323
|
-
expect{c.
|
324
|
-
d.
|
325
|
-
expect{d.
|
326
|
-
e.
|
327
|
-
expect{e.
|
368
|
+
expect{c.m1.should == 'foo'}.to raise_error(NoMethodError)
|
369
|
+
expect{c.m2.should == 'faaxx'}.to raise_error(NoMethodError)
|
370
|
+
d.m1.should == 'fuu' # same for pre-existing objects
|
371
|
+
expect{d.m2.should == 'faaxx'}.to raise_error(NoMethodError)
|
372
|
+
e.m1.should == 'fuuuu'
|
373
|
+
expect{e.m2.should == 'faaxx'}.to raise_error(NoMethodError)
|
328
374
|
|
329
375
|
end
|
330
376
|
|
331
|
-
|
377
|
+
describe 'some special cases' do
|
378
|
+
|
379
|
+
the 'behavior when re-applying a new version of same Injector further down the line' do
|
332
380
|
|
333
|
-
|
381
|
+
class C1
|
382
|
+
end
|
334
383
|
|
335
|
-
|
384
|
+
# Define a blanck Injector
|
336
385
|
|
337
|
-
|
338
|
-
end
|
386
|
+
injector :j1
|
339
387
|
|
340
|
-
# Define a Blank Injector
|
341
388
|
|
342
|
-
|
389
|
+
# Apply to hierarchy root as defined
|
343
390
|
|
344
|
-
|
391
|
+
C1.inject j1 do # apply definitions
|
392
|
+
def m1
|
393
|
+
'm1'
|
394
|
+
end
|
395
|
+
end
|
396
|
+
C1.new.m1.should == 'm1' # call on Injector
|
345
397
|
|
346
398
|
|
347
|
-
|
399
|
+
# DD inherits from CC
|
348
400
|
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
end
|
353
|
-
end
|
401
|
+
class D1 < C1
|
402
|
+
end # call on Injector
|
403
|
+
D1.new.m1.should == 'm1'
|
354
404
|
|
355
405
|
|
356
|
-
|
406
|
+
# EE inherits from DD
|
357
407
|
|
358
|
-
|
408
|
+
class E1 < D1
|
409
|
+
end # call on Injector
|
410
|
+
E1.new.m1.should == 'm1'
|
359
411
|
|
360
412
|
|
361
|
-
|
413
|
+
############################
|
414
|
+
# Re-define methods
|
415
|
+
############################
|
416
|
+
j1 do # NEW VERSION! because previous had an application
|
417
|
+
def m1 # -- was applied to C1 in the hierarchy
|
418
|
+
'm1xx' # Methods only defined in the Virtual Method Cache
|
419
|
+
end
|
420
|
+
end
|
362
421
|
|
363
|
-
|
364
|
-
|
365
|
-
|
422
|
+
# Calls un-affected !! # have existing version
|
423
|
+
|
424
|
+
C1.new.m1.should == 'm1'
|
425
|
+
D1.new.m1.should == 'm1'
|
426
|
+
E1.new.m1.should == 'm1'
|
366
427
|
|
367
428
|
|
368
|
-
|
429
|
+
E1.inject j1 # apply to hierarchy on E
|
430
|
+
|
431
|
+
C1.new.m1.should == 'm1' # same
|
432
|
+
D1.new.m1.should == 'm1' # same
|
433
|
+
E1.new.m1.should == 'm1xx' # changed
|
369
434
|
|
370
|
-
class E1 < D1
|
371
435
|
end
|
372
|
-
E1.new.m1.should == 'm1' # inherited call
|
373
436
|
|
437
|
+
it 'also passes on this case' do
|
374
438
|
|
375
|
-
|
376
|
-
|
439
|
+
class C2
|
440
|
+
end
|
377
441
|
|
378
|
-
|
379
|
-
|
380
|
-
|
442
|
+
# Define a blank Injector
|
443
|
+
|
444
|
+
injector :j2
|
445
|
+
|
446
|
+
|
447
|
+
# Apply <full> Injector definition to an ancestor
|
448
|
+
|
449
|
+
Object.inject j2 do
|
450
|
+
def m1
|
451
|
+
'm1xx'
|
452
|
+
end
|
381
453
|
end
|
382
|
-
end
|
383
|
-
|
384
|
-
|
385
|
-
# Calls are also re-defined
|
386
|
-
|
387
|
-
C1.new.m1.should == 'm1xx' # call is redefined
|
388
|
-
D1.new.m1.should == 'm1xx' # ''
|
389
|
-
E1.new.m1.should == 'm1xx' # ''
|
390
454
|
|
391
455
|
|
392
|
-
|
456
|
+
# Call on the Injector
|
393
457
|
|
394
|
-
|
395
|
-
|
396
|
-
# calls
|
458
|
+
C2.new.m1.should == 'm1xx' # call Injector
|
397
459
|
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
def m1
|
408
|
-
'-----'
|
460
|
+
|
461
|
+
############################
|
462
|
+
# Re-define methods
|
463
|
+
############################
|
464
|
+
|
465
|
+
j2 do # NEW VERSION! because previous had an application
|
466
|
+
def m1 # -- was applied to Object in the hierarchy
|
467
|
+
'm1' # Methods only defined in the Virtual Method Cache
|
468
|
+
end
|
409
469
|
end
|
410
|
-
end
|
411
|
-
|
412
470
|
|
413
|
-
|
471
|
+
# Calls un-afffected
|
414
472
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
473
|
+
C2.new.m1.should == 'm1xx' # still using the previous version --no changes
|
474
|
+
|
475
|
+
|
476
|
+
# Inherit
|
477
|
+
|
478
|
+
class D2 < C2
|
479
|
+
end
|
480
|
+
D2.new.m1.should == 'm1xx' # using previous version
|
481
|
+
|
482
|
+
|
483
|
+
# Inherit
|
422
484
|
|
423
|
-
|
485
|
+
class E2 < D2
|
486
|
+
end
|
487
|
+
E2.new.m1.should == 'm1xx' # using previous version
|
488
|
+
|
489
|
+
|
490
|
+
###########################
|
491
|
+
# Finally apply new version
|
492
|
+
###########################
|
493
|
+
|
494
|
+
C2.inject j2
|
495
|
+
|
496
|
+
|
497
|
+
# Calls changed from C on up
|
498
|
+
|
499
|
+
C2.new.m1.should == 'm1'
|
500
|
+
D2.new.m1.should == 'm1'
|
501
|
+
E2.new.m1.should == 'm1' # new version of #m1
|
502
|
+
|
503
|
+
|
504
|
+
# Call on ancestor the same
|
505
|
+
|
506
|
+
Object.new.m1.should == 'm1xx' # previous version
|
507
|
+
|
508
|
+
|
509
|
+
# back to normal
|
510
|
+
|
511
|
+
Object.eject j2 # so we do no interfere with other tests!!
|
424
512
|
|
425
|
-
class C2
|
426
513
|
end
|
427
514
|
|
428
|
-
|
429
|
-
|
430
|
-
injector :multi_levelB
|
431
|
-
|
515
|
+
it 'acts differently when using the Virtual Method Cache (VMC)' do
|
432
516
|
|
433
|
-
|
434
|
-
|
435
|
-
C2.inject multi_levelB do # apply definitions
|
436
|
-
def m1
|
437
|
-
'm1'
|
438
|
-
end
|
439
|
-
end
|
440
|
-
C2.new.m1.should == 'm1' # call on Injector
|
517
|
+
class C3
|
518
|
+
end
|
441
519
|
|
520
|
+
# Define a Blank Injector
|
442
521
|
|
443
|
-
|
522
|
+
injector :j3
|
444
523
|
|
445
|
-
class D2 < C2
|
446
|
-
end # call on Injector
|
447
|
-
D2.new.m1.should == 'm1'
|
448
524
|
|
525
|
+
# Apply the blank injector
|
449
526
|
|
450
|
-
|
527
|
+
C3.inject j3
|
451
528
|
|
452
|
-
class E2 < D2
|
453
|
-
end # call on Injector
|
454
|
-
E2.new.m1.should == 'm1'
|
455
529
|
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
530
|
+
# Define function after the application
|
531
|
+
|
532
|
+
j3 do # NEW VERSION!
|
533
|
+
def m1 # -- never applied to Object in the hierarchy
|
534
|
+
'm1' # Methods only defined in the Virtual Method Cache
|
535
|
+
end
|
462
536
|
end
|
463
|
-
end
|
464
537
|
|
465
|
-
# Calls un-affected !! # have existing version
|
466
538
|
|
467
|
-
|
468
|
-
D2.new.m1.should == 'm1'
|
469
|
-
E2.new.m1.should == 'm1'
|
539
|
+
# C3 calls
|
470
540
|
|
471
|
-
|
541
|
+
C3.new.m1.should == 'm1' # call works as normal from VMC
|
472
542
|
|
473
|
-
C2.new.m1.should == 'm1' # same
|
474
|
-
D2.new.m1.should == 'm1' # same
|
475
|
-
E2.new.m1.should == 'm1xx' # changed
|
476
543
|
|
477
|
-
|
544
|
+
# D3 inherits from C3
|
545
|
+
|
546
|
+
class D3 < C3
|
547
|
+
end
|
548
|
+
D3.new.m1.should == 'm1' # inherited call
|
478
549
|
|
479
|
-
it 'also passes on this case' do
|
480
|
-
|
481
|
-
class C3
|
482
|
-
end
|
483
550
|
|
484
|
-
|
485
|
-
|
486
|
-
injector :multi_levelC
|
551
|
+
# E3 inherits from D3
|
487
552
|
|
488
|
-
|
489
|
-
# Apply <full> Injector definition to an ancestor
|
490
|
-
|
491
|
-
Object.inject multi_levelC do
|
492
|
-
def m1
|
493
|
-
'm1xx'
|
553
|
+
class E3 < D3
|
494
554
|
end
|
495
|
-
|
496
|
-
|
555
|
+
E3.new.m1.should == 'm1' # inherited call
|
497
556
|
|
498
|
-
# Call on the Injector
|
499
|
-
|
500
|
-
C3.new.m1.should == 'm1xx' # call Injector
|
501
557
|
|
558
|
+
###################################
|
559
|
+
# Re-define Virtual Method Cache
|
560
|
+
# -- previously un-applied methods
|
561
|
+
###################################
|
502
562
|
|
503
|
-
|
563
|
+
j3 do # NEW VERSION!
|
564
|
+
def m1 # -- never applied to Object in the hierarchy
|
565
|
+
'm1xx' # Methods only defined in the Virtual Method Cache
|
566
|
+
end
|
567
|
+
end
|
504
568
|
|
505
|
-
|
506
|
-
|
507
|
-
|
569
|
+
|
570
|
+
# Calls are also re-defined
|
571
|
+
|
572
|
+
C3.new.m1.should == 'm1xx' # call is redefined
|
573
|
+
D3.new.m1.should == 'm1xx' # ''
|
574
|
+
E3.new.m1.should == 'm1xx' # ''
|
575
|
+
|
576
|
+
|
577
|
+
# Apply the <full> Injector onto E3
|
578
|
+
|
579
|
+
E3.inject j3 # Attaches this version onto E only!!
|
580
|
+
|
581
|
+
# calls
|
582
|
+
|
583
|
+
C3.new.m1.should == 'm1xx' # => from cache
|
584
|
+
D3.new.m1.should == 'm1xx' # ''
|
585
|
+
|
586
|
+
E3.new.m1.should == 'm1xx' # => from applied version
|
587
|
+
|
588
|
+
|
589
|
+
################################
|
590
|
+
# Re-define cached methods
|
591
|
+
#################################
|
592
|
+
|
593
|
+
j3 do # NEW VERSION!
|
594
|
+
def m1 # -- never applied to Object in the hierarchy
|
595
|
+
'-----' # Methods only defined in the Virtual Method Cache
|
596
|
+
end
|
508
597
|
end
|
509
|
-
end
|
510
|
-
|
511
|
-
# Calls un-afffected
|
512
|
-
|
513
|
-
C3.new.m1.should == 'm1xx' # still using the previous version --no changes
|
514
|
-
|
515
|
-
|
516
|
-
# Inherit
|
517
|
-
|
518
|
-
class D3 < C3
|
519
|
-
end
|
520
|
-
D3.new.m1.should == 'm1xx' # using previous version
|
521
|
-
|
522
598
|
|
523
|
-
# Inherit
|
524
|
-
|
525
|
-
class E3 < D3
|
526
|
-
end
|
527
|
-
E3.new.m1.should == 'm1xx' # using previous version
|
528
|
-
|
529
|
-
|
530
|
-
# Finally apply new version
|
531
599
|
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
# Calls changed from C on up
|
536
|
-
|
537
|
-
C3.new.m1.should == 'm1'
|
538
|
-
D3.new.m1.should == 'm1'
|
539
|
-
E3.new.m1.should == 'm1' # new version of #m1
|
600
|
+
# calls
|
540
601
|
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
602
|
+
C3.new.m1.should == '-----' # re-defined!!
|
603
|
+
D3.new.m1.should == '-----' # ''
|
604
|
+
|
605
|
+
E3.new.m1.should == 'm1xx' # NOT REDEFINED!!
|
606
|
+
# from applied version
|
607
|
+
|
608
|
+
end
|
545
609
|
|
546
610
|
end
|
611
|
+
end
|
612
|
+
|
613
|
+
describe "regular Injector internal inheritance" do
|
547
614
|
|
548
|
-
it 'carries current methods onto
|
615
|
+
it 'carries current methods onto Injector Versions/Tags' do
|
549
616
|
|
550
617
|
# Define injector
|
551
618
|
|
@@ -623,6 +690,7 @@ describe "some special cases and circumstances" do
|
|
623
690
|
BoomBox.new.on.should == 'Tape playing...Lets make some music'
|
624
691
|
JukeBox.new.on.should == 'CD playing...Lets make some music'
|
625
692
|
|
693
|
+
#...
|
626
694
|
|
627
695
|
jack :speakers
|
628
696
|
|
@@ -630,170 +698,672 @@ describe "some special cases and circumstances" do
|
|
630
698
|
def sound
|
631
699
|
super + '...boom boom boom...'
|
632
700
|
end
|
633
|
-
end
|
701
|
+
end
|
702
|
+
|
634
703
|
JukeBox.inject Bass
|
635
|
-
|
636
|
-
#...
|
637
|
-
|
638
704
|
JukeBox.new.on.should == 'CD playing...Lets make some music...boom boom boom...'
|
639
705
|
|
640
706
|
end
|
641
707
|
|
708
|
+
|
642
709
|
it 'works accross compound injectors' do
|
643
710
|
|
644
|
-
|
711
|
+
jack :S1
|
645
712
|
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
include jack :s3 do
|
651
|
-
def s3m1
|
713
|
+
S1 do
|
714
|
+
include jack :s2 do
|
715
|
+
def s2m1
|
716
|
+
:s2m1
|
652
717
|
end
|
653
|
-
include jack :
|
654
|
-
def
|
718
|
+
include jack :s3 do
|
719
|
+
def s3m1
|
720
|
+
:s3m1
|
721
|
+
end
|
722
|
+
include jack :s4 do
|
723
|
+
def s4m1
|
724
|
+
:s4m1
|
725
|
+
end
|
655
726
|
end
|
656
727
|
end
|
657
728
|
end
|
658
729
|
end
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
# Apply the injectors
|
664
|
-
|
665
|
-
class CompoundContainer
|
666
|
-
include S1()
|
667
|
-
end
|
668
|
-
|
669
|
-
|
670
|
-
# Call on the Injectors
|
671
|
-
|
672
|
-
CompoundContainer.new.s2m1
|
673
|
-
CompoundContainer.new.s3m1
|
674
|
-
CompoundContainer.new.s4m1
|
730
|
+
|
731
|
+
S1().s2.s3.s4 # injector pipeline!
|
675
732
|
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
733
|
+
|
734
|
+
# Apply the injectors
|
735
|
+
|
736
|
+
class CompoundContainer
|
737
|
+
include S1()
|
681
738
|
end
|
682
|
-
end
|
683
|
-
CompoundContainer.new.s3m2
|
684
|
-
|
685
739
|
|
686
|
-
|
740
|
+
CompoundContainer.new.s2m1.should == :s2m1
|
741
|
+
CompoundContainer.new.s3m1.should == :s3m1
|
742
|
+
CompoundContainer.new.s4m1.should == :s4m1
|
743
|
+
|
744
|
+
|
745
|
+
# Add methods to the VMC of s3
|
746
|
+
|
747
|
+
S1().s2.s3 do
|
748
|
+
def s3m2
|
749
|
+
:s3m2
|
750
|
+
end
|
751
|
+
end
|
752
|
+
|
753
|
+
CompoundContainer.new.s3m2.should == :s3m2
|
754
|
+
|
755
|
+
|
756
|
+
# Create a version Tag
|
757
|
+
|
758
|
+
AccessTag = S1()
|
759
|
+
|
760
|
+
class SecondContainer
|
761
|
+
include AccessTag
|
762
|
+
end
|
763
|
+
|
764
|
+
SecondContainer.new.s3m2.should == :s3m2
|
765
|
+
|
766
|
+
end
|
767
|
+
|
768
|
+
it 'works accross the VMC' do
|
769
|
+
|
770
|
+
expect{
|
771
|
+
|
772
|
+
injector :J1
|
773
|
+
injector :J2
|
774
|
+
injector :J3
|
775
|
+
|
776
|
+
class AA1
|
777
|
+
end
|
778
|
+
J1 do
|
779
|
+
include J2()
|
780
|
+
end
|
781
|
+
J2 do
|
782
|
+
def mJ2 # virtual cache method
|
783
|
+
:mJ2
|
784
|
+
end
|
785
|
+
end
|
786
|
+
J1 do
|
787
|
+
def mJ1 # applied method
|
788
|
+
:mJ1
|
789
|
+
end
|
790
|
+
end
|
791
|
+
class AA1
|
792
|
+
include J1()
|
793
|
+
end
|
794
|
+
|
795
|
+
AA1.new.mJ2.should == :mJ2
|
796
|
+
AA1.new.mJ1.should == :mJ1
|
797
|
+
|
798
|
+
}.not_to raise_error
|
687
799
|
|
688
|
-
|
800
|
+
end
|
801
|
+
|
802
|
+
a 'different example' do
|
803
|
+
|
804
|
+
expect{
|
805
|
+
|
806
|
+
injector :K1
|
807
|
+
injector :K2
|
808
|
+
injector :K3
|
809
|
+
|
810
|
+
K1 do
|
811
|
+
include K2() do
|
812
|
+
def mj2 # applied method
|
813
|
+
:mj2
|
814
|
+
end
|
815
|
+
end
|
816
|
+
end
|
817
|
+
class AA2
|
818
|
+
include K1()
|
819
|
+
end
|
820
|
+
AA2.new.mj2.should == :mj2
|
821
|
+
K2 do
|
822
|
+
include K3() do
|
823
|
+
def mj3 # virtual cache method with another indirect
|
824
|
+
:mj3
|
825
|
+
end
|
826
|
+
end
|
827
|
+
end
|
828
|
+
AA2.new.mj3.should == :mj3
|
829
|
+
|
830
|
+
}.not_to raise_error
|
689
831
|
|
832
|
+
end
|
833
|
+
|
834
|
+
a 'yet different one' do
|
835
|
+
|
836
|
+
expect{
|
837
|
+
|
838
|
+
injector :M1
|
839
|
+
injector :M2
|
840
|
+
injector :M3
|
841
|
+
|
842
|
+
M1 do
|
843
|
+
include M2() do
|
844
|
+
def mk2 # applied method
|
845
|
+
:mk2
|
846
|
+
end
|
847
|
+
end
|
848
|
+
end
|
849
|
+
class AA3
|
850
|
+
include M1()
|
851
|
+
end
|
852
|
+
AA3.new.mk2.should == :mk2
|
853
|
+
M2 do
|
854
|
+
include M3()
|
855
|
+
end
|
856
|
+
M3 do
|
857
|
+
def mk3 # virtual cache method
|
858
|
+
:mk3
|
859
|
+
end
|
860
|
+
end
|
861
|
+
AA3.new.mk3.should == :mk3
|
862
|
+
|
863
|
+
}.not_to raise_error
|
690
864
|
|
691
|
-
|
865
|
+
end
|
866
|
+
|
867
|
+
describe 'jit inheriatnce' do
|
868
|
+
|
869
|
+
before do
|
870
|
+
injector :Tagger
|
871
|
+
|
872
|
+
end
|
692
873
|
|
693
|
-
|
694
|
-
|
874
|
+
after do
|
875
|
+
Tagger(:implode)
|
876
|
+
|
695
877
|
end
|
696
878
|
|
697
879
|
|
698
|
-
# Call on tag methods
|
699
880
|
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
# Add methdos to the s2 method cache
|
881
|
+
it "follows a just-in-time inheritance policy" do
|
704
882
|
|
705
|
-
|
706
|
-
|
883
|
+
#
|
884
|
+
# Our Modular Closure
|
885
|
+
#
|
886
|
+
Tag1 = Tagger do
|
887
|
+
def m1
|
888
|
+
1
|
889
|
+
end
|
890
|
+
|
891
|
+
def m2
|
892
|
+
:m2
|
893
|
+
end
|
894
|
+
end
|
895
|
+
|
896
|
+
expect(Tag1.ancestors).to eql( [Tag1] )
|
897
|
+
expect(Tagger().ancestors).to eql( [Tagger()] )
|
898
|
+
|
899
|
+
|
900
|
+
#
|
901
|
+
# Normal Injector inheritance
|
902
|
+
#
|
903
|
+
Tagger do
|
904
|
+
def other # No overrides No inheritance
|
905
|
+
'other' # -- same ancestors as before
|
906
|
+
end # -- normal injector inheritance
|
907
|
+
end
|
908
|
+
|
909
|
+
# test it
|
910
|
+
|
911
|
+
expect(Tag1.ancestors).to eql( [Tag1] )
|
912
|
+
expect(Tagger().ancestors).to eql( [Tagger()] )
|
913
|
+
|
914
|
+
o = Object.new.extend(Tagger())
|
915
|
+
|
916
|
+
# inherited
|
917
|
+
o.m1.should == 1
|
918
|
+
o.m2.should == :m2
|
919
|
+
|
920
|
+
# current
|
921
|
+
o.other.should == 'other'
|
922
|
+
|
923
|
+
|
924
|
+
#
|
925
|
+
# JIT inheritance
|
926
|
+
#
|
927
|
+
Tag2 = Tagger do
|
928
|
+
def m1 # The :m1 override invokes JIT inheritance
|
929
|
+
super + 1 # -- Tag1 is added as ancestor
|
930
|
+
end # -- allows the use of super
|
931
|
+
|
932
|
+
def m3
|
933
|
+
'em3'
|
934
|
+
end
|
935
|
+
end
|
936
|
+
|
937
|
+
# test it
|
938
|
+
|
939
|
+
expect(Tagger().ancestors).to eql( [Tagger(), Tag1] )
|
940
|
+
expect(Tag2.ancestors).to eql( [Tag2, Tag1] )
|
941
|
+
|
942
|
+
p = Object.new.extend(Tag2)
|
943
|
+
|
944
|
+
# JIT inherited
|
945
|
+
p.m1.should == 2
|
946
|
+
|
947
|
+
# regular inheritance
|
948
|
+
p.m2.should == :m2
|
949
|
+
p.m3.should == 'em3'
|
950
|
+
p.other.should == 'other'
|
951
|
+
|
952
|
+
|
953
|
+
#
|
954
|
+
# Under Inclusion
|
955
|
+
#
|
956
|
+
class AA6
|
957
|
+
inject Tag2
|
958
|
+
end
|
959
|
+
aa6 = AA6.new
|
960
|
+
|
961
|
+
# test it
|
962
|
+
|
963
|
+
aa6.m1.should == 2
|
964
|
+
aa6.m2.should == :m2
|
965
|
+
aa6.m3.should == 'em3'
|
966
|
+
|
967
|
+
|
968
|
+
#
|
969
|
+
# One more level: mixed level inheritance
|
970
|
+
#
|
971
|
+
Tag3 = Tagger() do
|
972
|
+
def m1
|
973
|
+
super * 2 # second override to #m1
|
974
|
+
end # -- Tag2 added as ancestor
|
975
|
+
def m3
|
976
|
+
super * 2 # first override to #m3
|
977
|
+
end
|
978
|
+
end
|
979
|
+
|
980
|
+
# test it
|
981
|
+
|
982
|
+
expect(Tagger().ancestors).to eql( [Tagger(), Tag2, Tag1] )
|
983
|
+
expect(Tag3.ancestors).to eql( [Tag3, Tag2, Tag1] )
|
984
|
+
expect(Tag2.ancestors).to eql( [Tag2, Tag1] )
|
985
|
+
|
986
|
+
class AA7
|
987
|
+
inject Tag3
|
988
|
+
end
|
989
|
+
aa7 = AA7.new
|
990
|
+
|
991
|
+
# JIT inheritance
|
992
|
+
aa7.m1.should == 4
|
993
|
+
aa7.m3.should == 'em3em3'
|
994
|
+
|
995
|
+
# regular inheritance
|
996
|
+
aa7.m2.should == :m2
|
997
|
+
aa7.other.should == 'other'
|
998
|
+
|
999
|
+
|
1000
|
+
#
|
1001
|
+
# Another version: back to basics
|
1002
|
+
#
|
1003
|
+
Tagger() do
|
1004
|
+
def m1 # another override but no call to #super
|
1005
|
+
:m1 # -- ancestor added
|
1006
|
+
end
|
1007
|
+
end
|
1008
|
+
n = Object.new.extend(Tagger())
|
1009
|
+
|
1010
|
+
# test it
|
1011
|
+
|
1012
|
+
expect(Tagger().ancestors).to eql( [Tagger(), Tag3, Tag2, Tag1] )
|
1013
|
+
expect(Tag3.ancestors).to eql( [Tag3, Tag2, Tag1] )
|
1014
|
+
expect(Tag2.ancestors).to eql( [Tag2, Tag1] )
|
1015
|
+
|
1016
|
+
|
1017
|
+
n.m1.should == :m1 # new version of #m1
|
1018
|
+
|
1019
|
+
# JIT
|
1020
|
+
n.m3.should == 'em3em3'
|
1021
|
+
|
1022
|
+
# regular
|
1023
|
+
n.m2.should == :m2
|
1024
|
+
|
1025
|
+
|
1026
|
+
#
|
1027
|
+
# Test previous versions
|
1028
|
+
#
|
1029
|
+
aa6.m1.should == 2
|
1030
|
+
aa6.m2.should == :m2
|
1031
|
+
aa6.m3.should == 'em3'
|
1032
|
+
|
1033
|
+
aa66 = AA6.new
|
1034
|
+
|
1035
|
+
aa66.m1.should == 2
|
1036
|
+
aa66.m2.should == :m2
|
1037
|
+
aa66.m3.should == 'em3'
|
1038
|
+
|
1039
|
+
aa7.m1.should == 4
|
1040
|
+
aa7.m2.should == :m2
|
1041
|
+
aa7.m3.should == 'em3em3'
|
1042
|
+
|
1043
|
+
aa77 = AA7.new
|
1044
|
+
|
1045
|
+
aa77.m1.should == 4
|
1046
|
+
aa77.m2.should == :m2
|
1047
|
+
aa77.m3.should == 'em3em3'
|
1048
|
+
|
1049
|
+
|
1050
|
+
#
|
1051
|
+
# other clients
|
1052
|
+
#
|
1053
|
+
class AA6B
|
1054
|
+
inject Tag2
|
1055
|
+
end
|
1056
|
+
aa6b = AA6B.new
|
1057
|
+
|
1058
|
+
aa6b.m1.should == 2
|
1059
|
+
aa6b.m2.should == :m2
|
1060
|
+
aa6b.m3.should == 'em3'
|
1061
|
+
|
1062
|
+
#
|
1063
|
+
# VMC (Virtual Method Cache) method
|
1064
|
+
#
|
1065
|
+
Tagger() do
|
1066
|
+
def m4
|
1067
|
+
:m4
|
1068
|
+
end
|
707
1069
|
end
|
1070
|
+
|
1071
|
+
# test it
|
1072
|
+
|
1073
|
+
expect(Tagger().ancestors).to eql([Tagger(), Tag3, Tag2, Tag1])
|
1074
|
+
expect(Tag3.ancestors).to eql( [Tag3, Tag2, Tag1] )
|
1075
|
+
expect(Tag2.ancestors).to eql( [Tag2, Tag1] )
|
1076
|
+
|
1077
|
+
aa6b.m1.should == 2
|
1078
|
+
aa6b.m2.should == :m2
|
1079
|
+
aa6b.m3.should == 'em3'
|
1080
|
+
aa6b.m4.should == :m4 # vmc method
|
1081
|
+
|
1082
|
+
|
1083
|
+
# Total Tags
|
1084
|
+
|
1085
|
+
Tagger().tags.should == [Tag1, Tag2, Tag3]
|
1086
|
+
|
708
1087
|
end
|
709
1088
|
|
1089
|
+
it "also allows further ancestor injection" do
|
1090
|
+
|
1091
|
+
Tag4 = Tagger do
|
1092
|
+
def m1
|
1093
|
+
1
|
1094
|
+
end
|
1095
|
+
def m2
|
1096
|
+
:m2
|
1097
|
+
end
|
1098
|
+
end
|
710
1099
|
|
711
|
-
|
1100
|
+
module Mod1
|
1101
|
+
def m1
|
1102
|
+
'one'
|
1103
|
+
end
|
1104
|
+
end
|
712
1105
|
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
1106
|
+
Tag5 = Tagger(Mod1) do
|
1107
|
+
|
1108
|
+
# include Mod1 # alternatively
|
1109
|
+
|
1110
|
+
def m1
|
1111
|
+
super * 2 # invoking inheritance for Tag5 !!
|
1112
|
+
end
|
1113
|
+
def m3
|
1114
|
+
:m3
|
1115
|
+
end
|
1116
|
+
end
|
717
1117
|
|
718
|
-
|
1118
|
+
expect(Tagger().ancestors).to eql([Tagger(), Mod1, Tag4])
|
1119
|
+
expect(Tag4.ancestors).to eql([Tag4])
|
1120
|
+
expect(Tag5.ancestors).to eql([Tag5, Mod1, Tag4])
|
719
1121
|
|
720
|
-
|
721
|
-
injector :M2
|
722
|
-
injector :M3
|
1122
|
+
# test it
|
723
1123
|
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
1124
|
+
Object.new.extend(Tag5).m1.should == 'oneone'
|
1125
|
+
Object.new.extend(Tag5).m2.should == :m2
|
1126
|
+
Object.new.extend(Tag5).m3.should == :m3
|
1127
|
+
|
1128
|
+
# other prolongation
|
1129
|
+
|
1130
|
+
Object.new.extend(Tagger(){
|
1131
|
+
def m4
|
1132
|
+
:m4
|
1133
|
+
end
|
1134
|
+
}).m4.should == :m4
|
1135
|
+
|
728
1136
|
end
|
729
|
-
|
730
|
-
|
1137
|
+
|
1138
|
+
it 'also works like this' do
|
1139
|
+
|
1140
|
+
Tag6 = Tagger do
|
1141
|
+
def m1
|
1142
|
+
super() * 2
|
1143
|
+
end
|
1144
|
+
def m2
|
1145
|
+
:m2
|
1146
|
+
end
|
731
1147
|
end
|
732
|
-
|
733
|
-
|
734
|
-
|
1148
|
+
|
1149
|
+
module Mod2
|
1150
|
+
def m1
|
1151
|
+
3
|
1152
|
+
end
|
735
1153
|
end
|
1154
|
+
|
1155
|
+
Object.new.extend(Tagger(Mod2)).m1.should == 6
|
1156
|
+
Object.new.extend(Tagger(Mod2)).m2.should == :m2
|
1157
|
+
|
736
1158
|
end
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
1159
|
+
|
1160
|
+
this "also possible" do
|
1161
|
+
|
1162
|
+
Tag7 = Tagger do
|
1163
|
+
def m1
|
1164
|
+
1
|
1165
|
+
end
|
1166
|
+
def m2
|
1167
|
+
:m2
|
1168
|
+
end
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
Tag8 = Tagger do
|
1172
|
+
def m1
|
1173
|
+
super * 2
|
1174
|
+
end
|
1175
|
+
def m3
|
1176
|
+
:m3
|
1177
|
+
end
|
1178
|
+
end
|
1179
|
+
|
1180
|
+
# test it
|
1181
|
+
|
1182
|
+
Object.new.extend(Tag8).m1.should == 2
|
745
1183
|
|
746
|
-
injector :J1
|
747
|
-
injector :J2
|
748
|
-
injector :J3
|
749
1184
|
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
1185
|
+
#
|
1186
|
+
# On the fly overrides
|
1187
|
+
#
|
1188
|
+
obj = Object.new.extend(
|
1189
|
+
Tagger {
|
1190
|
+
def m1
|
1191
|
+
super + 3
|
1192
|
+
end
|
1193
|
+
def m4
|
1194
|
+
:m4
|
1195
|
+
end
|
1196
|
+
})
|
1197
|
+
obj.m1.should == 5
|
1198
|
+
obj.m2.should == :m2
|
1199
|
+
obj.m3.should == :m3
|
1200
|
+
obj.m4.should == :m4
|
1201
|
+
|
765
1202
|
end
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
def mk2
|
1203
|
+
|
1204
|
+
this "one is a bit tricky" do
|
1205
|
+
|
1206
|
+
Tag9 = Tagger do
|
1207
|
+
def m1
|
1208
|
+
1
|
1209
|
+
end
|
1210
|
+
end
|
1211
|
+
|
1212
|
+
module Mod1
|
1213
|
+
def m1
|
1214
|
+
'one'
|
779
1215
|
end
|
780
1216
|
end
|
1217
|
+
|
1218
|
+
Tag10 = Tagger() do
|
1219
|
+
|
1220
|
+
include Mod1
|
1221
|
+
|
1222
|
+
def m1
|
1223
|
+
super * 2
|
1224
|
+
end
|
1225
|
+
end
|
1226
|
+
|
1227
|
+
# test it
|
1228
|
+
|
1229
|
+
Object.new.extend(Tag10).m1.should == 'oneone'
|
1230
|
+
Object.new.extend(Tagger()).m1.should == 'oneone'
|
1231
|
+
|
1232
|
+
Tag10.ancestors.should == [Tag10, Mod1, Tag9]
|
1233
|
+
|
781
1234
|
end
|
782
|
-
|
783
|
-
|
1235
|
+
|
1236
|
+
this 'further trick' do
|
1237
|
+
|
1238
|
+
Tag11 = Tagger do
|
1239
|
+
def m1
|
1240
|
+
1
|
1241
|
+
end
|
1242
|
+
def m2 # original definition
|
1243
|
+
2
|
1244
|
+
end
|
1245
|
+
end
|
1246
|
+
Tag12 = Tagger do
|
1247
|
+
def m1
|
1248
|
+
'm1'
|
1249
|
+
end # skipped #m2
|
1250
|
+
end
|
1251
|
+
Tag13 = Tagger do
|
1252
|
+
def m1
|
1253
|
+
super * 2
|
1254
|
+
end
|
1255
|
+
def m2
|
1256
|
+
super * 2 # override # m2 two levels down
|
1257
|
+
end
|
1258
|
+
end
|
1259
|
+
class AA10
|
1260
|
+
inject Tag13
|
1261
|
+
end
|
1262
|
+
|
1263
|
+
# test it
|
1264
|
+
|
1265
|
+
AA10.new.m1.should == 'm1m1'
|
1266
|
+
AA10.new.m2 == 4
|
1267
|
+
|
1268
|
+
Tag13.ancestors.should == [Tag13, Tag12, Tag11]
|
1269
|
+
|
784
1270
|
end
|
785
|
-
|
786
|
-
|
787
|
-
|
1271
|
+
|
1272
|
+
it 'rebases' do
|
1273
|
+
|
1274
|
+
Tag14 = Tagger do
|
1275
|
+
def m1
|
1276
|
+
1
|
1277
|
+
end
|
1278
|
+
end
|
1279
|
+
|
1280
|
+
class AA11
|
1281
|
+
inject Tagger() do
|
1282
|
+
def m1
|
1283
|
+
super + 1
|
1284
|
+
end
|
1285
|
+
end
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
# test it
|
1289
|
+
|
1290
|
+
AA11.new.m1.should == 2
|
1291
|
+
|
1292
|
+
|
1293
|
+
Tag15 = Tagger do
|
1294
|
+
def m1
|
1295
|
+
5 # rebase m1
|
1296
|
+
end
|
1297
|
+
end
|
1298
|
+
|
1299
|
+
class BB11
|
1300
|
+
inject Tagger() do
|
1301
|
+
def m1
|
1302
|
+
super * 2 # new override
|
1303
|
+
end
|
1304
|
+
end
|
1305
|
+
end
|
1306
|
+
|
1307
|
+
# test it
|
1308
|
+
|
1309
|
+
BB11.new.m1.should == 10
|
1310
|
+
|
1311
|
+
|
788
1312
|
end
|
789
|
-
|
790
|
-
|
1313
|
+
|
1314
|
+
it 'takes Injector Directives' do
|
1315
|
+
|
1316
|
+
Tag16 = Tagger do
|
1317
|
+
def m1
|
1318
|
+
1
|
1319
|
+
end
|
1320
|
+
end
|
1321
|
+
|
1322
|
+
class AA12
|
1323
|
+
inject Tagger() do
|
1324
|
+
def m1
|
1325
|
+
super + 1
|
1326
|
+
end
|
1327
|
+
end
|
791
1328
|
end
|
1329
|
+
|
1330
|
+
AA12.new.m1.should == 2
|
1331
|
+
|
1332
|
+
|
1333
|
+
Tag17 = Tagger do
|
1334
|
+
def m1
|
1335
|
+
5 # rebase m1
|
1336
|
+
end
|
1337
|
+
end
|
1338
|
+
|
1339
|
+
class BB12
|
1340
|
+
inject Tagger() do
|
1341
|
+
def m1
|
1342
|
+
super * 2 # new override
|
1343
|
+
end
|
1344
|
+
end
|
1345
|
+
end
|
1346
|
+
|
1347
|
+
BB12.new.m1.should == 10
|
1348
|
+
|
1349
|
+
# test directives
|
1350
|
+
|
1351
|
+
Tagger(:silence)
|
1352
|
+
|
1353
|
+
AA12.new.m1.should == nil # both bases affected
|
1354
|
+
BB12.new.m1.should == nil
|
1355
|
+
|
1356
|
+
Tagger(:active)
|
1357
|
+
|
1358
|
+
AA12.new.m1.should == 2 # both bases restored
|
1359
|
+
BB12.new.m1.should == 10
|
1360
|
+
|
792
1361
|
end
|
793
1362
|
|
794
|
-
AA3.new.mk3
|
795
|
-
|
796
1363
|
end
|
797
|
-
|
798
1364
|
end
|
799
1365
|
|
1366
|
+
# profile = RubyProf.stop
|
1367
|
+
# RubyProf::FlatPrinter.new(profile).print(STDOUT)
|
1368
|
+
# RubyProf::GraphHtmlPrinter.new(profile).print(open('profile.html', 'w+'))
|
1369
|
+
# RubyProf::CallStackPrinter.new(profile).print(open('profile.html', 'w+'))
|