jackbox 0.9.6.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.
- checksums.yaml +7 -0
- data/.yardopts +5 -0
- data/CHANGES.txt +108 -0
- data/LICENSE.lic +0 -0
- data/LICENSE.txt +13 -0
- data/README.md +1395 -0
- data/Rakefile +6 -0
- data/bin/jackup +248 -0
- data/jackbox.gemspec +27 -0
- data/jackbox.jpg +0 -0
- data/lib/.document +0 -0
- data/lib/jackbox.rb +2 -0
- data/lib/jackbox/examples/dir.rb +80 -0
- data/lib/jackbox/examples/dx.rb +182 -0
- data/lib/jackbox/examples/transformers.rb +101 -0
- data/lib/jackbox/injectors.rb +2 -0
- data/lib/jackbox/rake.rb +2 -0
- data/lib/jackbox/tools/prefs.rb +2 -0
- data/lib/jackbox/version.rb +4 -0
- data/rgloader/loader.rb +23 -0
- data/rgloader/rgloader.darwin.bundle +0 -0
- data/rgloader/rgloader.freebsd.so +0 -0
- data/rgloader/rgloader.freebsd.x86_64.so +0 -0
- data/rgloader/rgloader.linux.so +0 -0
- data/rgloader/rgloader.linux.x86_64.so +0 -0
- data/rgloader/rgloader.mingw.so +0 -0
- data/rgloader/rgloader19.darwin.bundle +0 -0
- data/rgloader/rgloader19.freebsd.so +0 -0
- data/rgloader/rgloader19.freebsd.x86_64.so +0 -0
- data/rgloader/rgloader19.linux.so +0 -0
- data/rgloader/rgloader19.linux.x86_64.so +0 -0
- data/rgloader/rgloader19.mingw.so +0 -0
- data/rgloader/rgloader191.mingw.so +0 -0
- data/rgloader/rgloader192.darwin.bundle +0 -0
- data/rgloader/rgloader192.freebsd.so +0 -0
- data/rgloader/rgloader192.freebsd.x86_64.so +0 -0
- data/rgloader/rgloader192.linux.so +0 -0
- data/rgloader/rgloader192.linux.x86_64.so +0 -0
- data/rgloader/rgloader192.mingw.so +0 -0
- data/rgloader/rgloader193.darwin.bundle +0 -0
- data/rgloader/rgloader193.freebsd.so +0 -0
- data/rgloader/rgloader193.freebsd.x86_64.so +0 -0
- data/rgloader/rgloader193.linux.so +0 -0
- data/rgloader/rgloader193.linux.x86_64.so +0 -0
- data/rgloader/rgloader193.mingw.so +0 -0
- data/rgloader/rgloader20.darwin.bundle +0 -0
- data/rgloader/rgloader20.freebsd.so +0 -0
- data/rgloader/rgloader20.freebsd.x86_64.so +0 -0
- data/rgloader/rgloader20.linux.so +0 -0
- data/rgloader/rgloader20.linux.x86_64.so +0 -0
- data/rgloader/rgloader20.mingw.so +0 -0
- data/rgloader/rgloader20.mingw.x64.so +0 -0
- data/rgloader/rgloader21.darwin.bundle +0 -0
- data/rgloader/rgloader21.freebsd.so +0 -0
- data/rgloader/rgloader21.freebsd.x86_64.so +0 -0
- data/rgloader/rgloader21.linux.so +0 -0
- data/rgloader/rgloader21.linux.x86_64.so +0 -0
- data/rgloader/rgloader21.mingw.so +0 -0
- data/rgloader/rgloader21.mingw.x64.so +0 -0
- data/rgloader/rgloader22.darwin.bundle +0 -0
- data/rgloader/rgloader22.freebsd.so +0 -0
- data/rgloader/rgloader22.linux.so +0 -0
- data/rgloader/rgloader22.linux.x86_64.so +0 -0
- data/rgloader/rgloader22.mingw.so +0 -0
- data/rgloader/rgloader22.mingw.x64.so +0 -0
- data/spec/bin/jackup_cmd_shared.rb +176 -0
- data/spec/bin/jackup_cmd_spec.rb +292 -0
- data/spec/lib/abtract_spec.rb +56 -0
- data/spec/lib/jackbox/examples/dir_spec.rb +112 -0
- data/spec/lib/jackbox/examples/dx_spec.rb +346 -0
- data/spec/lib/jackbox/examples/result.xml +15 -0
- data/spec/lib/jackbox/examples/source1.xml +11 -0
- data/spec/lib/jackbox/examples/source2.xml +15 -0
- data/spec/lib/jackbox/examples/source3.xml +11 -0
- data/spec/lib/jackbox/examples/trasnformers_spec.rb +35 -0
- data/spec/lib/jackbox/injector_composition_spec.rb +950 -0
- data/spec/lib/jackbox/injector_directives_spec.rb +266 -0
- data/spec/lib/jackbox/injector_inheritance_spec.rb +799 -0
- data/spec/lib/jackbox/injector_introspection_spec.rb +614 -0
- data/spec/lib/jackbox/injector_namespacing_spec.rb +345 -0
- data/spec/lib/jackbox/injector_spec.rb +847 -0
- data/spec/lib/jackbox/injector_versioning_spec.rb +334 -0
- data/spec/lib/jackbox/patterns_spec.rb +410 -0
- data/spec/lib/jackbox/prefs_spec.rb +212 -0
- data/spec/lib/jackbox/reclassing_spec.rb +394 -0
- data/spec/lib/jackbox_spec.rb +595 -0
- data/spec/spec_helper.rb +139 -0
- metadata +218 -0
@@ -0,0 +1,266 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
include Injectors
|
4
|
+
|
5
|
+
describe 'Injector Directives: ' do
|
6
|
+
|
7
|
+
describe "<injector> :implode, the entire injector and all instances eliminated. Produces different results than
|
8
|
+
ejecting individual injectors, or from using <injector> :collapse and then restored using <injector> :rebuild" do
|
9
|
+
|
10
|
+
an 'example of complete injector implosion' do
|
11
|
+
|
12
|
+
class Model
|
13
|
+
def feature
|
14
|
+
'a standard feature'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
injector :extras do
|
19
|
+
def feature
|
20
|
+
super() + ' plus some extras'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
car = Model.new.enrich(extras)
|
25
|
+
car.feature.should == 'a standard feature plus some extras'
|
26
|
+
|
27
|
+
extras :implode
|
28
|
+
|
29
|
+
# total implosion
|
30
|
+
expect{extras}.to raise_error(NameError, /extras/)
|
31
|
+
car.feature.should == 'a standard feature'
|
32
|
+
|
33
|
+
expect{
|
34
|
+
new_car = Model.new.enrich(extras)
|
35
|
+
}.to raise_error(NameError, /extras/)
|
36
|
+
|
37
|
+
expect{
|
38
|
+
extras do
|
39
|
+
def foo
|
40
|
+
end
|
41
|
+
end
|
42
|
+
}.to raise_error(NameError, /extras/)
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'difference between injector ejection/implosion' do
|
47
|
+
|
48
|
+
the 'Injector reconstitution after ejection is possible through reinjection
|
49
|
+
but reconstitution after injector implosion is NOT AVAILABLE' do
|
50
|
+
|
51
|
+
# code defined
|
52
|
+
class Job
|
53
|
+
injector :agent do
|
54
|
+
def call
|
55
|
+
end
|
56
|
+
end
|
57
|
+
inject agent
|
58
|
+
end
|
59
|
+
Job.injectors.sym_list.should == [:agent]
|
60
|
+
|
61
|
+
# normal use
|
62
|
+
expect{Job.new.call}.to_not raise_error
|
63
|
+
|
64
|
+
# code ejection
|
65
|
+
Job.eject :agent
|
66
|
+
|
67
|
+
# code extended and re-injected
|
68
|
+
class Job
|
69
|
+
inject agent
|
70
|
+
agent do
|
71
|
+
def sms
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#normal use
|
77
|
+
expect{Job.new.call}.to_not raise_error
|
78
|
+
expect{Job.new.sms}.to_not raise_error
|
79
|
+
|
80
|
+
# code imlossion
|
81
|
+
Job.agent :implode
|
82
|
+
|
83
|
+
# Unavailable !!!
|
84
|
+
expect{
|
85
|
+
class Job
|
86
|
+
inject :agent
|
87
|
+
end
|
88
|
+
}.to raise_error(TypeError)
|
89
|
+
|
90
|
+
# Unavailable !!!
|
91
|
+
expect{
|
92
|
+
class Job
|
93
|
+
agent do
|
94
|
+
def something
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end }.to raise_error(NoMethodError)
|
98
|
+
|
99
|
+
# Unavailable!!!
|
100
|
+
expect{Job.new.call}.to raise_error(NoMethodError)
|
101
|
+
expect{Job.new.sms}.to raise_error(NoMethodError)
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '<injector> :collapse. Injectors can be silenced. This description produces similar results to
|
110
|
+
the previous except that further injector method calls DO NOT raise an error they just quietly return nil' do
|
111
|
+
|
112
|
+
the 'case with objects' do
|
113
|
+
|
114
|
+
injector :copiable do
|
115
|
+
def object_copy
|
116
|
+
'a dubious copy'
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
o1 = Object.new.enrich(copiable)
|
121
|
+
o2 = Object.new.enrich(copiable)
|
122
|
+
|
123
|
+
o1.object_copy.should == 'a dubious copy'
|
124
|
+
o2.object_copy.should == 'a dubious copy'
|
125
|
+
|
126
|
+
copiable :silence
|
127
|
+
|
128
|
+
o1.object_copy.should == nil
|
129
|
+
o2.object_copy.should == nil
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
the 'case with classes' do
|
134
|
+
|
135
|
+
class SomeClass
|
136
|
+
injector :code do
|
137
|
+
def tester
|
138
|
+
'boo'
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
inject code
|
143
|
+
end
|
144
|
+
|
145
|
+
# collapse
|
146
|
+
SomeClass.code :collapse
|
147
|
+
|
148
|
+
# build
|
149
|
+
a = SomeClass.new
|
150
|
+
b = SomeClass.new
|
151
|
+
|
152
|
+
a.tester.should == nil
|
153
|
+
b.tester.should == nil
|
154
|
+
|
155
|
+
# further
|
156
|
+
SomeClass.eject :code
|
157
|
+
expect{ a.tester }.to raise_error(NoMethodError)
|
158
|
+
expect{ b.tester }.to raise_error(NoMethodError)
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
the 'class members are not affected by the collapse' do
|
163
|
+
|
164
|
+
# define container
|
165
|
+
class BumperCar
|
166
|
+
def fun
|
167
|
+
'this is fun'
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
injector :somecode do # share member name with container
|
172
|
+
def fun
|
173
|
+
super + ' and more fun'
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
bc = BumperCar.new.enrich(somecode).enrich(somecode) # decorator pattern
|
178
|
+
bc.fun.should == 'this is fun and more fun and more fun'
|
179
|
+
|
180
|
+
somecode :collapse # collapse the injector
|
181
|
+
|
182
|
+
bc.fun.should == 'this is fun' # class memeber foo intact
|
183
|
+
|
184
|
+
# eject all injectors
|
185
|
+
bc.injectors.sym_list.each { |ij| bc.eject ij } # same as before
|
186
|
+
bc.fun.should == 'this is fun'
|
187
|
+
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe '<injector> :rebuild. Quieted injectors restored without having
|
192
|
+
to re-inject them into every object they modify' do
|
193
|
+
|
194
|
+
the 'case with objects' do
|
195
|
+
|
196
|
+
injector :reenforcer do
|
197
|
+
def thick_walls
|
198
|
+
'===== ====='
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
o1 = Object.new.enrich(reenforcer)
|
203
|
+
o2 = Object.new.enrich(reenforcer)
|
204
|
+
|
205
|
+
reenforcer :collapse
|
206
|
+
|
207
|
+
o1.thick_walls.should == nil
|
208
|
+
o2.thick_walls.should == nil
|
209
|
+
|
210
|
+
reenforcer :rebuild
|
211
|
+
|
212
|
+
o1.thick_walls.should == '===== ====='
|
213
|
+
o2.thick_walls.should == '===== ====='
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
the 'case with classes' do
|
218
|
+
|
219
|
+
class SomeBloatedObject
|
220
|
+
injector :ThinFunction do
|
221
|
+
def perform
|
222
|
+
'do the deed'
|
223
|
+
end
|
224
|
+
end
|
225
|
+
inject ThinFunction()
|
226
|
+
end
|
227
|
+
ThinFunction :silence
|
228
|
+
|
229
|
+
tester = SomeBloatedObject.new
|
230
|
+
tester.perform.should == nil
|
231
|
+
|
232
|
+
ThinFunction :active
|
233
|
+
tester.perform.should == 'do the deed'
|
234
|
+
|
235
|
+
end
|
236
|
+
|
237
|
+
the 'rebuild reconstructs the entire injector and all applications' do
|
238
|
+
|
239
|
+
class BumperCar
|
240
|
+
def fun
|
241
|
+
'this is fun'
|
242
|
+
end
|
243
|
+
end
|
244
|
+
injector :othercode do # share memeber name with container
|
245
|
+
def fun
|
246
|
+
super + ' and more fun'
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
bc = BumperCar.new.enrich(othercode).enrich(othercode) # decorator pattern
|
251
|
+
bc.fun.should == 'this is fun and more fun and more fun'
|
252
|
+
|
253
|
+
othercode :silence # declare injector silence
|
254
|
+
|
255
|
+
bc.fun.should == 'this is fun' # class member un-affected
|
256
|
+
|
257
|
+
othercode :active
|
258
|
+
bc.fun.should == 'this is fun and more fun and more fun' # restores all decorations !!!
|
259
|
+
|
260
|
+
# restore the original
|
261
|
+
bc.injectors.sym_list.each { |ij| bc.eject ij } # same as before
|
262
|
+
bc.fun.should == 'this is fun'
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
@@ -0,0 +1,799 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
=begin rdoc
|
3
|
+
|
4
|
+
Specifies the behavior of Injectors under inheritance
|
5
|
+
|
6
|
+
. First we se how ancestors are treated
|
7
|
+
. Next we treat Injectors under a class hierarchy
|
8
|
+
. Then we see a few special cases in the realm of multiple inheritance
|
9
|
+
|
10
|
+
lha
|
11
|
+
|
12
|
+
=end
|
13
|
+
|
14
|
+
include Injectors
|
15
|
+
|
16
|
+
describe "ancestor chains" do
|
17
|
+
|
18
|
+
it 'updates the ancestor chains accordingly based on: injections and ejections' do
|
19
|
+
|
20
|
+
injector :Parent # capitalized method
|
21
|
+
class Child
|
22
|
+
include Parent()
|
23
|
+
end
|
24
|
+
Child.ancestors.to_s.should match( /Child, \(.*\|Parent\|\), Object.*BasicObject/ )
|
25
|
+
|
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
|
+
|
30
|
+
c.eject Parent()
|
31
|
+
# Parent is ejected from the object metaclass and ancestor chain reverts back
|
32
|
+
c.singleton_class.ancestors.to_s.should match( /Child, \(.*\|Parent\|\), Object.*BasicObject/ )
|
33
|
+
|
34
|
+
Child.eject Parent()
|
35
|
+
# Parent is ejected from the class and ancestors chain is empty
|
36
|
+
Child.ancestors.to_s.should match( /Child, Object.*BasicObject/ )
|
37
|
+
# cannot update empty injector
|
38
|
+
expect{ Child.send :update, Child.parent }.to raise_error(NoMethodError)
|
39
|
+
|
40
|
+
c.enrich Parent()
|
41
|
+
c.enrich Parent()
|
42
|
+
# object metaclass is extended with Parent twice like in the case of multiple decorators
|
43
|
+
c.singleton_class.ancestors.to_s.should match( /\(.*\|Parent\|\), \(.*\|Parent\|\), Child, Object.*BasicObject/ )
|
44
|
+
|
45
|
+
c.eject Parent()
|
46
|
+
c.eject Parent()
|
47
|
+
# object is reverted back to no Injectors
|
48
|
+
c.singleton_class.ancestors.to_s.should match( /Child, Object.*BasicObject/ )
|
49
|
+
|
50
|
+
Child.inject Parent()
|
51
|
+
# class is re-injected with Parent and ancestors is updated
|
52
|
+
Child.ancestors.to_s.should match( /Child, \(.*\|Parent\|\), Object.*BasicObject/ )
|
53
|
+
c.singleton_class.ancestors.to_s.should match( /Child, \(.*\|Parent\|\), Object.*BasicObject/ )
|
54
|
+
|
55
|
+
c.eject Parent()
|
56
|
+
# class level Injector ejected from single object and ancestor for the object updated but not for class
|
57
|
+
c.singleton_class.ancestors.to_s.should match( /Child, Object.*BasicObject/ )
|
58
|
+
Child.ancestors.to_s.should match( /Child, \(.*\|Parent\|\), Object.*BasicObject/ )
|
59
|
+
|
60
|
+
Child.send :update, Parent()
|
61
|
+
# class level Injector update re-introduces it to objects that have ejected it locally and ancestors is updated
|
62
|
+
c.singleton_class.ancestors.to_s.should match( /Child, \(.*\|Parent\|\), Object.*BasicObject/ )
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "the inheritance behavior of injectors" do
|
68
|
+
|
69
|
+
example "works accross class hierarchies" do
|
70
|
+
|
71
|
+
|
72
|
+
###########################################
|
73
|
+
# injection
|
74
|
+
###########################################
|
75
|
+
injector :j
|
76
|
+
|
77
|
+
class C
|
78
|
+
end
|
79
|
+
C.inject j { # #foo pre-defined at time of injection
|
80
|
+
def foo
|
81
|
+
'foo'
|
82
|
+
end
|
83
|
+
}
|
84
|
+
C.injectors.sym_list.should == [:j]
|
85
|
+
C.new.injectors.sym_list.should == [:j]
|
86
|
+
|
87
|
+
injector :k
|
88
|
+
|
89
|
+
C.inject k { # #faa pre-defined at injection
|
90
|
+
def faa
|
91
|
+
'faa'
|
92
|
+
end
|
93
|
+
}
|
94
|
+
C.injectors.sym_list.should == [:j, :k]
|
95
|
+
C.new.injectors.sym_list.should == [:j, :k]
|
96
|
+
|
97
|
+
C.new.foo.should == 'foo'
|
98
|
+
C.new.faa.should == 'faa'
|
99
|
+
c = C.new
|
100
|
+
|
101
|
+
|
102
|
+
# D inherits from C
|
103
|
+
|
104
|
+
class D < C # methods are inherited from j and k
|
105
|
+
end
|
106
|
+
C.injectors.sym_list.should == [:j, :k]
|
107
|
+
C.new.injectors.sym_list.should == [:j, :k]
|
108
|
+
D.injectors.sym_list.should == []
|
109
|
+
D.new.injectors.sym_list.should == []
|
110
|
+
|
111
|
+
# New Objects
|
112
|
+
C.new.foo.should == 'foo'
|
113
|
+
C.new.faa.should == 'faa'
|
114
|
+
D.new.foo.should == 'foo'
|
115
|
+
D.new.faa.should == 'faa'
|
116
|
+
# Existing Objects
|
117
|
+
d = D.new
|
118
|
+
c.foo.should == 'foo'
|
119
|
+
c.faa.should == 'faa'
|
120
|
+
|
121
|
+
|
122
|
+
# inject D and override C
|
123
|
+
|
124
|
+
D.inject j { # new version of j pre-defined at injection
|
125
|
+
def foo
|
126
|
+
'foooo'
|
127
|
+
end
|
128
|
+
}
|
129
|
+
C.injectors.sym_list.should == [:j, :k]
|
130
|
+
C.new.injectors.sym_list.should == [:j, :k]
|
131
|
+
D.injectors.sym_list.should == [:j]
|
132
|
+
D.new.injectors.sym_list.should == [:j]
|
133
|
+
|
134
|
+
# New Objects
|
135
|
+
D.new.foo.should == 'foooo' # new version of #foo
|
136
|
+
# still the same
|
137
|
+
D.new.faa.should == 'faa'
|
138
|
+
C.new.foo.should == 'foo'
|
139
|
+
C.new.faa.should == 'faa'
|
140
|
+
# Existing Objects
|
141
|
+
c.foo.should == 'foo'
|
142
|
+
c.faa.should == 'faa'
|
143
|
+
d.foo.should == 'foooo'
|
144
|
+
d.faa.should == 'faa'
|
145
|
+
|
146
|
+
|
147
|
+
# D class overrides j
|
148
|
+
|
149
|
+
class D < C
|
150
|
+
def foo # overrides foo from j
|
151
|
+
'fuu'
|
152
|
+
end
|
153
|
+
end
|
154
|
+
C.injectors.sym_list.should == [:j, :k]
|
155
|
+
C.new.injectors.sym_list.should == [:j, :k]
|
156
|
+
D.injectors.sym_list.should == [:j]
|
157
|
+
D.new.injectors.sym_list.should == [:j]
|
158
|
+
|
159
|
+
# New Objects
|
160
|
+
D.new.foo.should == 'fuu' # overrided last version from j
|
161
|
+
# still the same
|
162
|
+
D.new.faa.should == 'faa'
|
163
|
+
C.new.foo.should == 'foo'
|
164
|
+
C.new.faa.should == 'faa'
|
165
|
+
# Existing Objects
|
166
|
+
c.foo.should == 'foo'
|
167
|
+
c.faa.should == 'faa'
|
168
|
+
d.foo.should == 'fuu' # overrided
|
169
|
+
d.faa.should == 'faa'
|
170
|
+
|
171
|
+
|
172
|
+
# update C and inherit into D
|
173
|
+
|
174
|
+
C.send :update, k { # new version of k pre-defined at update
|
175
|
+
def faa # -- no other version of k in hierarchy
|
176
|
+
'faaxx'
|
177
|
+
end
|
178
|
+
}
|
179
|
+
C.injectors.sym_list.should == [:j, :k]
|
180
|
+
C.new.injectors.sym_list.should == [:j, :k]
|
181
|
+
D.injectors.sym_list.should == [:j]
|
182
|
+
D.new.injectors.sym_list.should == [:j]
|
183
|
+
|
184
|
+
# New Objects
|
185
|
+
C.new.faa.should == 'faaxx' # new version of #faa
|
186
|
+
D.new.faa.should == 'faaxx'
|
187
|
+
# still the same
|
188
|
+
C.new.foo.should == 'foo'
|
189
|
+
D.new.foo.should == 'fuu'
|
190
|
+
# Existing Objects
|
191
|
+
c.foo.should == 'foo'
|
192
|
+
c.faa.should == 'faaxx'
|
193
|
+
d.foo.should == 'fuu'
|
194
|
+
d.faa.should == 'faaxx'
|
195
|
+
|
196
|
+
|
197
|
+
# E inherits from D
|
198
|
+
|
199
|
+
class E < D # methods are inherited from j at C, D and k update at C
|
200
|
+
end
|
201
|
+
C.injectors.sym_list.should == [:j, :k]
|
202
|
+
C.new.injectors.sym_list.should == [:j, :k]
|
203
|
+
D.injectors.sym_list.should == [:j]
|
204
|
+
D.new.injectors.sym_list.should == [:j]
|
205
|
+
E.injectors.sym_list.should == []
|
206
|
+
E.new.injectors.sym_list.should == []
|
207
|
+
|
208
|
+
# New Objects
|
209
|
+
C.new.foo.should == 'foo'
|
210
|
+
C.new.faa.should == 'faaxx'
|
211
|
+
D.new.foo.should == 'fuu'
|
212
|
+
D.new.faa.should == 'faaxx' # new objects pass
|
213
|
+
E.new.foo.should == 'fuu'
|
214
|
+
E.new.faa.should == 'faaxx'
|
215
|
+
# Existing Objects
|
216
|
+
e = E.new
|
217
|
+
c.foo.should == 'foo'
|
218
|
+
c.faa.should == 'faaxx' # existing objects pass
|
219
|
+
d.foo.should == 'fuu'
|
220
|
+
d.faa.should == 'faaxx'
|
221
|
+
|
222
|
+
|
223
|
+
# E overrides D
|
224
|
+
|
225
|
+
class E < D
|
226
|
+
def foo # overrides #foo from j at C, D
|
227
|
+
'fuuuu'
|
228
|
+
end
|
229
|
+
end
|
230
|
+
C.injectors.sym_list.should == [:j, :k]
|
231
|
+
C.new.injectors.sym_list.should == [:j, :k]
|
232
|
+
D.injectors.sym_list.should == [:j]
|
233
|
+
D.new.injectors.sym_list.should == [:j]
|
234
|
+
E.injectors.sym_list.should == []
|
235
|
+
E.new.injectors.sym_list.should == []
|
236
|
+
|
237
|
+
# New Objects
|
238
|
+
C.new.foo.should == 'foo'
|
239
|
+
C.new.faa.should == 'faaxx'
|
240
|
+
D.new.foo.should == 'fuu'
|
241
|
+
D.new.faa.should == 'faaxx'
|
242
|
+
E.new.foo.should == 'fuuuu'
|
243
|
+
E.new.faa.should == 'faaxx'
|
244
|
+
# Existing Objects
|
245
|
+
c.foo.should == 'foo'
|
246
|
+
c.faa.should == 'faaxx'
|
247
|
+
d.foo.should == 'fuu'
|
248
|
+
d.faa.should == 'faaxx'
|
249
|
+
e.foo.should == 'fuuuu'
|
250
|
+
e.faa.should == 'faaxx'
|
251
|
+
|
252
|
+
|
253
|
+
#######################################
|
254
|
+
# ejection
|
255
|
+
#######################################
|
256
|
+
C.eject :j # eject j from C
|
257
|
+
|
258
|
+
C.new.injectors.sym_list.should == [:k]
|
259
|
+
C.injectors.sym_list.should == [:k]
|
260
|
+
D.new.injectors.sym_list.should == [:j]
|
261
|
+
D.injectors.sym_list.should == [:j]
|
262
|
+
E.new.injectors.sym_list.should == []
|
263
|
+
E.injectors.sym_list.should == []
|
264
|
+
|
265
|
+
# New Objects
|
266
|
+
expect{ C.new.foo.should == 'foo'}.to raise_error(NoMethodError) # #foo errors out on C
|
267
|
+
C.new.faa.should == 'faaxx'
|
268
|
+
D.new.foo.should == 'fuu'
|
269
|
+
D.new.faa.should == 'faaxx' # all else is the same...
|
270
|
+
E.new.foo.should == 'fuuuu'
|
271
|
+
E.new.faa.should == 'faaxx'
|
272
|
+
# Existing Objects
|
273
|
+
expect{c.foo.should == 'foo'}.to raise_error(NoMethodError)
|
274
|
+
c.faa.should == 'faaxx'
|
275
|
+
d.foo.should == 'fuu'
|
276
|
+
d.faa.should == 'faaxx'
|
277
|
+
e.foo.should == 'fuuuu'
|
278
|
+
e.faa.should == 'faaxx'
|
279
|
+
|
280
|
+
|
281
|
+
C.eject :k # eject the only k
|
282
|
+
|
283
|
+
C.new.injectors.sym_list.should == []
|
284
|
+
C.injectors.sym_list.should == []
|
285
|
+
D.new.injectors.sym_list.should == [:j]
|
286
|
+
D.injectors.sym_list.should == [:j]
|
287
|
+
E.new.injectors.sym_list.should == []
|
288
|
+
E.injectors.sym_list.should == []
|
289
|
+
|
290
|
+
# New Objects
|
291
|
+
expect{ C.new.foo.should == 'foo'}.to raise_error(NoMethodError)
|
292
|
+
expect{ C.new.faa.should == 'faaxx'}.to raise_error(NoMethodError) # # faa errors out
|
293
|
+
D.new.foo.should == 'fuu'
|
294
|
+
expect{ D.new.faa.should == 'faaxx'}.to raise_error(NoMethodError) #faa errors out
|
295
|
+
E.new.foo.should == 'fuuuu'
|
296
|
+
expect{ E.new.faa.should == 'faaxx'}.to raise_error(NoMethodError) #faa was only available thru k at C
|
297
|
+
# Existing Objects
|
298
|
+
expect{c.foo.should == 'foo'}.to raise_error(NoMethodError)
|
299
|
+
expect{c.faa.should == 'faaxx'}.to raise_error(NoMethodError)
|
300
|
+
d.foo.should == 'fuu' # same thing for pre-existing objects
|
301
|
+
expect{d.faa.should == 'faaxx'}.to raise_error(NoMethodError)
|
302
|
+
e.foo.should == 'fuuuu'
|
303
|
+
expect{e.faa.should == 'faaxx'}.to raise_error(NoMethodError)
|
304
|
+
|
305
|
+
|
306
|
+
D.eject :j # eject j from D: the only one remaining
|
307
|
+
# -- everything should revert back to class level
|
308
|
+
C.injectors.sym_list.should == []
|
309
|
+
C.new.injectors.sym_list.should == []
|
310
|
+
D.injectors.sym_list.should == []
|
311
|
+
D.new.injectors.sym_list.should == []
|
312
|
+
E.injectors.sym_list.should == []
|
313
|
+
E.new.injectors.sym_list.should == []
|
314
|
+
# New Objects
|
315
|
+
expect{ C.new.foo.should == 'foo'}.to raise_error(NoMethodError) # no actual #foo on class
|
316
|
+
expect{ C.new.faa.should == 'faaxx'}.to raise_error(NoMethodError) # ''
|
317
|
+
D.new.foo.should == 'fuu' # retains overrides from D
|
318
|
+
expect{ D.new.faa.should == 'faaxx'}.to raise_error(NoMethodError)
|
319
|
+
E.new.foo.should == 'fuuuu' # retains overrides from E
|
320
|
+
expect{ E.new.faa.should == 'faaxx'}.to raise_error(NoMethodError)
|
321
|
+
# Existing Objects
|
322
|
+
expect{c.foo.should == 'foo'}.to raise_error(NoMethodError)
|
323
|
+
expect{c.faa.should == 'faaxx'}.to raise_error(NoMethodError)
|
324
|
+
d.foo.should == 'fuu' # same for pre-existing objects
|
325
|
+
expect{d.faa.should == 'faaxx'}.to raise_error(NoMethodError)
|
326
|
+
e.foo.should == 'fuuuu'
|
327
|
+
expect{e.faa.should == 'faaxx'}.to raise_error(NoMethodError)
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
end
|
332
|
+
|
333
|
+
describe "some special cases and circumstances" do
|
334
|
+
|
335
|
+
it 'passes on this case when we apply a <blank> Injector and then define the methods on it' do
|
336
|
+
|
337
|
+
class C1
|
338
|
+
end
|
339
|
+
|
340
|
+
# Define a Blank Injector
|
341
|
+
|
342
|
+
injector :multi_levelA
|
343
|
+
|
344
|
+
C1.inject multi_levelA # apply blank injector
|
345
|
+
|
346
|
+
|
347
|
+
# Define function after the application
|
348
|
+
|
349
|
+
multi_levelA do
|
350
|
+
def m1
|
351
|
+
'm1'
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
|
356
|
+
# C1 calls
|
357
|
+
|
358
|
+
C1.new.m1.should == 'm1' # call
|
359
|
+
|
360
|
+
|
361
|
+
# D1 inherits from C1
|
362
|
+
|
363
|
+
class D1 < C1
|
364
|
+
end
|
365
|
+
D1.new.m1.should == 'm1' # inherited call
|
366
|
+
|
367
|
+
|
368
|
+
# E1 inherits from D1
|
369
|
+
|
370
|
+
class E1 < D1
|
371
|
+
end
|
372
|
+
E1.new.m1.should == 'm1' # inherited call
|
373
|
+
|
374
|
+
|
375
|
+
# Re-define Method Cache
|
376
|
+
# -- previously un-applied methods
|
377
|
+
|
378
|
+
multi_levelA do
|
379
|
+
def m1
|
380
|
+
'm1xx'
|
381
|
+
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
|
+
|
391
|
+
|
392
|
+
# Apply the <full> Injector onto E1
|
393
|
+
|
394
|
+
E1.inject multi_levelA # Attaches this version only onto E1
|
395
|
+
|
396
|
+
# calls
|
397
|
+
|
398
|
+
C1.new.m1.should == 'm1xx' # => from cache
|
399
|
+
D1.new.m1.should == 'm1xx' # ''
|
400
|
+
|
401
|
+
E1.new.m1.should == 'm1xx' # => from applied version
|
402
|
+
|
403
|
+
|
404
|
+
# Re-define cached injector methods
|
405
|
+
|
406
|
+
multi_levelA do
|
407
|
+
def m1
|
408
|
+
'-----'
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
|
413
|
+
# calls
|
414
|
+
|
415
|
+
C1.new.m1.should == '-----' # re-defined!!
|
416
|
+
D1.new.m1.should == '-----' # ''
|
417
|
+
|
418
|
+
E1.new.m1.should == 'm1xx' # NOT REDEFINED!!
|
419
|
+
# -- attached with version
|
420
|
+
|
421
|
+
end
|
422
|
+
|
423
|
+
it 'also passes on this case' do
|
424
|
+
|
425
|
+
class C2
|
426
|
+
end
|
427
|
+
|
428
|
+
# Define a blanck Injector
|
429
|
+
|
430
|
+
injector :multi_levelB
|
431
|
+
|
432
|
+
|
433
|
+
# Fill-in function as you apply
|
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
|
441
|
+
|
442
|
+
|
443
|
+
# DD inherits from CC
|
444
|
+
|
445
|
+
class D2 < C2
|
446
|
+
end # call on Injector
|
447
|
+
D2.new.m1.should == 'm1'
|
448
|
+
|
449
|
+
|
450
|
+
# EE inherits from DD
|
451
|
+
|
452
|
+
class E2 < D2
|
453
|
+
end # call on Injector
|
454
|
+
E2.new.m1.should == 'm1'
|
455
|
+
|
456
|
+
|
457
|
+
# Re-define methods
|
458
|
+
|
459
|
+
multi_levelB do # NEW VERSION! because previous had an application
|
460
|
+
def m1 # -- was applied to C2 in the hierarchy
|
461
|
+
'm1xx'
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
# Calls un-affected !! # have existing version
|
466
|
+
|
467
|
+
C2.new.m1.should == 'm1'
|
468
|
+
D2.new.m1.should == 'm1'
|
469
|
+
E2.new.m1.should == 'm1'
|
470
|
+
|
471
|
+
E2.inject multi_levelB # apply to hierarchy on E
|
472
|
+
|
473
|
+
C2.new.m1.should == 'm1' # same
|
474
|
+
D2.new.m1.should == 'm1' # same
|
475
|
+
E2.new.m1.should == 'm1xx' # changed
|
476
|
+
|
477
|
+
end
|
478
|
+
|
479
|
+
it 'also passes on this case' do
|
480
|
+
|
481
|
+
class C3
|
482
|
+
end
|
483
|
+
|
484
|
+
# Define a blank Injector
|
485
|
+
|
486
|
+
injector :multi_levelC
|
487
|
+
|
488
|
+
|
489
|
+
# Apply <full> Injector definition to an ancestor
|
490
|
+
|
491
|
+
Object.inject multi_levelC do
|
492
|
+
def m1
|
493
|
+
'm1xx'
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
|
498
|
+
# Call on the Injector
|
499
|
+
|
500
|
+
C3.new.m1.should == 'm1xx' # call Injector
|
501
|
+
|
502
|
+
|
503
|
+
# Re-define a new Injector version
|
504
|
+
|
505
|
+
multi_levelC do
|
506
|
+
def m1
|
507
|
+
'm1'
|
508
|
+
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
|
+
|
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
|
+
|
532
|
+
C3.inject multi_levelC
|
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
|
540
|
+
|
541
|
+
|
542
|
+
# Call on ancestor the same
|
543
|
+
|
544
|
+
Object.new.m1.should == 'm1xx' # previous version
|
545
|
+
|
546
|
+
end
|
547
|
+
|
548
|
+
it 'carries current methods onto sub-versions' do
|
549
|
+
|
550
|
+
# Define injector
|
551
|
+
|
552
|
+
jack :bounce
|
553
|
+
|
554
|
+
bounce do
|
555
|
+
def sound
|
556
|
+
'splat splat'
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
|
561
|
+
# Define Tag with new methods
|
562
|
+
|
563
|
+
TagOne = bounce do
|
564
|
+
def bounce
|
565
|
+
'boing boing'
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
|
570
|
+
# Apply Tag
|
571
|
+
|
572
|
+
class Ball
|
573
|
+
inject TagOne
|
574
|
+
end
|
575
|
+
Ball.new.bounce.should == 'boing boing'
|
576
|
+
|
577
|
+
|
578
|
+
class Spring
|
579
|
+
inject TagOne
|
580
|
+
end
|
581
|
+
Spring.new.bounce.should == 'boing boing'
|
582
|
+
|
583
|
+
|
584
|
+
# Inherited methods from :bounce
|
585
|
+
|
586
|
+
Ball.new.sound.should == 'splat splat'
|
587
|
+
Spring.new.sound.should == 'splat splat'
|
588
|
+
|
589
|
+
end
|
590
|
+
|
591
|
+
a "more complex example: effectively working Ruby's multiple inheritance" do
|
592
|
+
|
593
|
+
jack :player do
|
594
|
+
def sound
|
595
|
+
'Lets make some music'
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
TapePlayer = player do
|
600
|
+
def play
|
601
|
+
return 'Tape playing...' + sound()
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
CDPlayer = player do
|
606
|
+
def play
|
607
|
+
return 'CD playing...' + sound()
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
class BoomBox
|
612
|
+
include TapePlayer
|
613
|
+
|
614
|
+
def on
|
615
|
+
play
|
616
|
+
end
|
617
|
+
end
|
618
|
+
|
619
|
+
class JukeBox < BoomBox
|
620
|
+
inject CDPlayer
|
621
|
+
end
|
622
|
+
|
623
|
+
BoomBox.new.on.should == 'Tape playing...Lets make some music'
|
624
|
+
JukeBox.new.on.should == 'CD playing...Lets make some music'
|
625
|
+
|
626
|
+
|
627
|
+
jack :speakers
|
628
|
+
|
629
|
+
Bass = speakers do
|
630
|
+
def sound
|
631
|
+
super + '...boom boom boom...'
|
632
|
+
end
|
633
|
+
end
|
634
|
+
JukeBox.inject Bass
|
635
|
+
|
636
|
+
#...
|
637
|
+
|
638
|
+
JukeBox.new.on.should == 'CD playing...Lets make some music...boom boom boom...'
|
639
|
+
|
640
|
+
end
|
641
|
+
|
642
|
+
it 'works accross compound injectors' do
|
643
|
+
|
644
|
+
jack :S1
|
645
|
+
|
646
|
+
S1 do
|
647
|
+
include jack :s2 do
|
648
|
+
def s2m1
|
649
|
+
end
|
650
|
+
include jack :s3 do
|
651
|
+
def s3m1
|
652
|
+
end
|
653
|
+
include jack :s4 do
|
654
|
+
def s4m1
|
655
|
+
end
|
656
|
+
end
|
657
|
+
end
|
658
|
+
end
|
659
|
+
end
|
660
|
+
S1().s2.s3.s4 # injector pipeline!
|
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
|
675
|
+
|
676
|
+
|
677
|
+
# Add methods to the method cache of s3
|
678
|
+
|
679
|
+
S1().s2.s3 do
|
680
|
+
def s3m2
|
681
|
+
end
|
682
|
+
end
|
683
|
+
CompoundContainer.new.s3m2
|
684
|
+
|
685
|
+
|
686
|
+
# Create a version Tag
|
687
|
+
|
688
|
+
AccessTag = S1()
|
689
|
+
|
690
|
+
|
691
|
+
# Apply the Tag
|
692
|
+
|
693
|
+
class SecondContainer
|
694
|
+
include AccessTag
|
695
|
+
end
|
696
|
+
|
697
|
+
|
698
|
+
# Call on tag methods
|
699
|
+
|
700
|
+
SecondContainer.new.s3m2
|
701
|
+
|
702
|
+
|
703
|
+
# Add methdos to the s2 method cache
|
704
|
+
|
705
|
+
S1().s2 do
|
706
|
+
def s2m2
|
707
|
+
end
|
708
|
+
end
|
709
|
+
|
710
|
+
|
711
|
+
# The current cache is available to all containers
|
712
|
+
|
713
|
+
CompoundContainer.new.s2m2
|
714
|
+
SecondContainer.new.s2m2
|
715
|
+
|
716
|
+
end
|
717
|
+
|
718
|
+
it 'pases' do
|
719
|
+
|
720
|
+
injector :M1
|
721
|
+
injector :M2
|
722
|
+
injector :M3
|
723
|
+
|
724
|
+
class AA1
|
725
|
+
end
|
726
|
+
M1 do
|
727
|
+
include M2()
|
728
|
+
end
|
729
|
+
M2 do
|
730
|
+
def mM2
|
731
|
+
end
|
732
|
+
end
|
733
|
+
M1 do
|
734
|
+
def mM1
|
735
|
+
end
|
736
|
+
end
|
737
|
+
class AA1
|
738
|
+
include M1()
|
739
|
+
end
|
740
|
+
AA1.new.mM2
|
741
|
+
|
742
|
+
end
|
743
|
+
|
744
|
+
it 'passes' do
|
745
|
+
|
746
|
+
injector :J1
|
747
|
+
injector :J2
|
748
|
+
injector :J3
|
749
|
+
|
750
|
+
J1 do
|
751
|
+
include J2() do
|
752
|
+
def mj2
|
753
|
+
end
|
754
|
+
end
|
755
|
+
end
|
756
|
+
class AA2
|
757
|
+
include J1()
|
758
|
+
end
|
759
|
+
AA2.new.mj2
|
760
|
+
J2 do
|
761
|
+
include J3() do
|
762
|
+
def mj3
|
763
|
+
end
|
764
|
+
end
|
765
|
+
end
|
766
|
+
AA2.new.mj3
|
767
|
+
|
768
|
+
end
|
769
|
+
|
770
|
+
it 'also passes' do
|
771
|
+
|
772
|
+
injector :K1
|
773
|
+
injector :K2
|
774
|
+
injector :K3
|
775
|
+
|
776
|
+
K1 do
|
777
|
+
include K2() do
|
778
|
+
def mk2
|
779
|
+
end
|
780
|
+
end
|
781
|
+
end
|
782
|
+
class AA3
|
783
|
+
include K1()
|
784
|
+
end
|
785
|
+
AA3.new.mk2
|
786
|
+
K2 do
|
787
|
+
include K3()
|
788
|
+
end
|
789
|
+
K3 do
|
790
|
+
def mk3
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
794
|
+
AA3.new.mk3
|
795
|
+
|
796
|
+
end
|
797
|
+
|
798
|
+
end
|
799
|
+
|