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,345 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
=begin rdoc
|
3
|
+
|
4
|
+
Name spacing of injectors
|
5
|
+
Author: LHA
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
Description of the difference between the different namespacing options of injectors:
|
10
|
+
|
11
|
+
injector :Main
|
12
|
+
vs.
|
13
|
+
injector :main
|
14
|
+
|
15
|
+
module X
|
16
|
+
injector :Some_Injector
|
17
|
+
vs.
|
18
|
+
injector :some_injector
|
19
|
+
end
|
20
|
+
|
21
|
+
class Y
|
22
|
+
injector :Other_Injector
|
23
|
+
vs.
|
24
|
+
injector :other_injector
|
25
|
+
end
|
26
|
+
|
27
|
+
=end
|
28
|
+
|
29
|
+
###########################
|
30
|
+
injector :Major
|
31
|
+
injector :minor
|
32
|
+
###########################
|
33
|
+
|
34
|
+
# upper case vs lower case: semantically equivalent
|
35
|
+
|
36
|
+
describe 'Major vs minor' do
|
37
|
+
|
38
|
+
|
39
|
+
example :Major do
|
40
|
+
Major{
|
41
|
+
lets(:bar) {|arg| arg * arg}
|
42
|
+
lets(:far) {|arg| arg * arg * arg}
|
43
|
+
}
|
44
|
+
|
45
|
+
class Object
|
46
|
+
inject Major()
|
47
|
+
end
|
48
|
+
|
49
|
+
Object.new.bar(3).should == 9
|
50
|
+
Object.new.far(2).should == 8
|
51
|
+
|
52
|
+
Object.eject Major()
|
53
|
+
end
|
54
|
+
|
55
|
+
example :minor do
|
56
|
+
minor do
|
57
|
+
lets(:bar) {|arg| arg * 3}
|
58
|
+
lets(:far) {|arg| arg * arg * 3}
|
59
|
+
end
|
60
|
+
|
61
|
+
class Object
|
62
|
+
inject minor
|
63
|
+
end
|
64
|
+
|
65
|
+
Object.new.bar(3).should == 9
|
66
|
+
Object.new.far(2).should == 12
|
67
|
+
|
68
|
+
Object.eject minor
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
###############################
|
75
|
+
module X
|
76
|
+
injector :Maxi # use sparringly
|
77
|
+
injector :mini
|
78
|
+
end
|
79
|
+
################################
|
80
|
+
|
81
|
+
# upper case vs lower case: semantically different
|
82
|
+
|
83
|
+
describe "X.Maxi vs X.mini" do
|
84
|
+
|
85
|
+
# scoped globally: no matter where defined or accessed
|
86
|
+
|
87
|
+
example 'X.Maxi' do
|
88
|
+
Maxi{
|
89
|
+
def bar
|
90
|
+
:bar
|
91
|
+
end
|
92
|
+
def far
|
93
|
+
:far
|
94
|
+
end
|
95
|
+
}
|
96
|
+
|
97
|
+
class MaxiTester
|
98
|
+
include Maxi()
|
99
|
+
end
|
100
|
+
|
101
|
+
MaxiTester.new.bar.should == :bar
|
102
|
+
MaxiTester.new.far.should == :far
|
103
|
+
end
|
104
|
+
|
105
|
+
# scoped to module/class
|
106
|
+
|
107
|
+
example 'X.mini' do
|
108
|
+
expect{
|
109
|
+
mini do
|
110
|
+
def bar
|
111
|
+
:barrr
|
112
|
+
end
|
113
|
+
def far
|
114
|
+
:farrr
|
115
|
+
end
|
116
|
+
end
|
117
|
+
}.to raise_error(NoMethodError) # not in scope
|
118
|
+
|
119
|
+
X.mini do
|
120
|
+
def bar
|
121
|
+
:barrr
|
122
|
+
end
|
123
|
+
def far
|
124
|
+
:farrr
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
expect{
|
129
|
+
class TesterFor_mini
|
130
|
+
include mini
|
131
|
+
end
|
132
|
+
}.to raise_error(NameError) # not in scope
|
133
|
+
|
134
|
+
class TesterFor_mini
|
135
|
+
include X.mini
|
136
|
+
end
|
137
|
+
|
138
|
+
TesterFor_mini.new.bar.should == :barrr
|
139
|
+
TesterFor_mini.new.far.should == :farrr
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
#############################
|
144
|
+
# Should follow the same line
|
145
|
+
#
|
146
|
+
class Y
|
147
|
+
injector :Other_Injector
|
148
|
+
# vs.
|
149
|
+
injector :other_injector
|
150
|
+
end
|
151
|
+
#
|
152
|
+
# No example needed!!
|
153
|
+
##############################
|
154
|
+
|
155
|
+
|
156
|
+
|
157
|
+
|
158
|
+
# Tied to the idea of Injector Name Spaces is the one of Injector Version Naming/Tagging. In order to make is easier to
|
159
|
+
# work with injector versions there is a need to tag/name the different versions for later use.
|
160
|
+
describe 'injector Tagging/Naming and its relationship to Injector Versioning' do
|
161
|
+
|
162
|
+
there 'is way to tag/name a particular version for later reuse' do
|
163
|
+
|
164
|
+
injector :Bar
|
165
|
+
|
166
|
+
Tag = Bar do
|
167
|
+
def foo_bar
|
168
|
+
'a bar and foo'
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
AnotherTag = Bar do
|
173
|
+
def foo_bar
|
174
|
+
'a foo and bar'
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
class TaggerOne
|
180
|
+
inject Tag # first version
|
181
|
+
end
|
182
|
+
TaggerOne.new.foo_bar.should == 'a bar and foo'
|
183
|
+
|
184
|
+
class TaggerTwo
|
185
|
+
inject AnotherTag # second version
|
186
|
+
end
|
187
|
+
TaggerTwo.new.foo_bar.should == 'a foo and bar'
|
188
|
+
|
189
|
+
|
190
|
+
class TaggerThree
|
191
|
+
inject Tag # first version
|
192
|
+
end
|
193
|
+
TaggerThree.new.foo_bar.should == 'a bar and foo'
|
194
|
+
|
195
|
+
class TaggerFour
|
196
|
+
inject AnotherTag # second version
|
197
|
+
end
|
198
|
+
TaggerFour.new.foo_bar.should == 'a foo and bar'
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
the 'following restriction applies to tags' do
|
203
|
+
|
204
|
+
# cannot redefine tags
|
205
|
+
|
206
|
+
SomeTag = Bar do
|
207
|
+
def foo
|
208
|
+
:foo
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
expect{
|
213
|
+
SomeTag do
|
214
|
+
def foo
|
215
|
+
end
|
216
|
+
end
|
217
|
+
}.to raise_error(NoMethodError)
|
218
|
+
expect{
|
219
|
+
SomeTag() {
|
220
|
+
def foo
|
221
|
+
end
|
222
|
+
}
|
223
|
+
}.to raise_error(NoMethodError)
|
224
|
+
|
225
|
+
# This is consistent with the notion of a version:
|
226
|
+
# the tag is a snapshot of the Injector at the point of creation
|
227
|
+
# . once defined it shouldn't be modified
|
228
|
+
# . to make modifications create a new version
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'does work this way however' do
|
233
|
+
|
234
|
+
OtherTag = Bar do # New Tag/version!!
|
235
|
+
def foo
|
236
|
+
:oof
|
237
|
+
end
|
238
|
+
end # Bar is a true method
|
239
|
+
|
240
|
+
class TagsTester
|
241
|
+
include SomeTag
|
242
|
+
end
|
243
|
+
|
244
|
+
TagsTester.new.foo.should == :foo
|
245
|
+
|
246
|
+
# update the version a target uses
|
247
|
+
|
248
|
+
class TagsTester
|
249
|
+
update OtherTag
|
250
|
+
end
|
251
|
+
|
252
|
+
TagsTester.new.foo.should == :oof
|
253
|
+
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
describe "tag scoping and naming" do
|
258
|
+
|
259
|
+
it 'passes' do
|
260
|
+
|
261
|
+
jack :M0
|
262
|
+
|
263
|
+
TopLevelTag = M0()
|
264
|
+
|
265
|
+
extend TopLevelTag
|
266
|
+
|
267
|
+
singleton_class.ancestors.to_s.should match(/TopLevelTag/)
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'also passes' do
|
272
|
+
|
273
|
+
module M1
|
274
|
+
jack :j1
|
275
|
+
end
|
276
|
+
|
277
|
+
module M2
|
278
|
+
Atag = M1.j1
|
279
|
+
end
|
280
|
+
|
281
|
+
class A4
|
282
|
+
include M2::Atag
|
283
|
+
end
|
284
|
+
|
285
|
+
A4.ancestors.to_s.should match(/Atag/)
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'also passes' do
|
290
|
+
|
291
|
+
module M3
|
292
|
+
module M4
|
293
|
+
AnotherTag = M1.j1
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
class B4
|
298
|
+
include M3::M4::AnotherTag
|
299
|
+
end
|
300
|
+
|
301
|
+
A4.ancestors.to_s.should match(/Atag/)
|
302
|
+
B4.ancestors.to_s.should match(/AnotherTag/)
|
303
|
+
|
304
|
+
end
|
305
|
+
|
306
|
+
describe "soft tags" do
|
307
|
+
|
308
|
+
it "is possible to create soft tags" do
|
309
|
+
|
310
|
+
jack :some_jack
|
311
|
+
|
312
|
+
some_jack :tag do
|
313
|
+
def mith
|
314
|
+
'King Arthur'
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
some_jack :tag do
|
319
|
+
def mith
|
320
|
+
'Leprechauns'
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
some_jack :tag do
|
325
|
+
def mith
|
326
|
+
'Tooth Fairy'
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
some_jack.tags.size.should == 3
|
331
|
+
|
332
|
+
$stdout.should_receive(:puts).with("King Arthur")
|
333
|
+
$stdout.should_receive(:puts).with("Leprechauns")
|
334
|
+
$stdout.should_receive(:puts).with("Tooth Fairy")
|
335
|
+
|
336
|
+
some_jack.tags.each { |t|
|
337
|
+
enrich t
|
338
|
+
puts mith
|
339
|
+
}
|
340
|
+
|
341
|
+
end
|
342
|
+
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
@@ -0,0 +1,847 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
=begin rdoc
|
3
|
+
|
4
|
+
Injector_spec
|
5
|
+
author: Lou Henry
|
6
|
+
|
7
|
+
Injectors are like modules; they can be extended or included in other modules, but
|
8
|
+
exhibit some additional functionality not present in modules.
|
9
|
+
|
10
|
+
=end
|
11
|
+
|
12
|
+
include Injectors
|
13
|
+
|
14
|
+
describe Injectors, :injectors do
|
15
|
+
|
16
|
+
describe :injector do
|
17
|
+
it 'creates the injector function' do
|
18
|
+
should respond_to :injector
|
19
|
+
class Bar
|
20
|
+
end
|
21
|
+
Bar.should respond_to :injector
|
22
|
+
end
|
23
|
+
it 'returns an instance of Injector' do
|
24
|
+
injector(:code_injector).class.should == Injector
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
#####
|
29
|
+
# Syntax differences
|
30
|
+
describe 'syntax differences: ' do
|
31
|
+
|
32
|
+
describe 'One of the main differences is in how injectors are declared using a method from the injectors
|
33
|
+
module and a block. Thsy can later be included or extended into a class or module. They can moreover
|
34
|
+
be used to #inject or #enrich into a class or module' do
|
35
|
+
|
36
|
+
it 'is declared like so...'do
|
37
|
+
|
38
|
+
expect {
|
39
|
+
|
40
|
+
injector :my_injector
|
41
|
+
|
42
|
+
# or...
|
43
|
+
|
44
|
+
Name = injector :name
|
45
|
+
|
46
|
+
# or even ...
|
47
|
+
|
48
|
+
injector :Name # capitalized method
|
49
|
+
|
50
|
+
}.to_not raise_error
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'allows the following expressions' do
|
55
|
+
|
56
|
+
expect{
|
57
|
+
|
58
|
+
# somewhere in your code
|
59
|
+
|
60
|
+
injector :my_injector # define the injector
|
61
|
+
|
62
|
+
# later on...
|
63
|
+
|
64
|
+
my_injector do
|
65
|
+
def foo
|
66
|
+
'a foo'
|
67
|
+
end
|
68
|
+
def bar
|
69
|
+
:a_bar
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# later on...
|
74
|
+
|
75
|
+
widget = Object.new
|
76
|
+
widget.extend my_injector
|
77
|
+
widget.bar
|
78
|
+
|
79
|
+
# or...
|
80
|
+
|
81
|
+
Mine = my_injector
|
82
|
+
|
83
|
+
class Target1
|
84
|
+
include Mine # apply the injector
|
85
|
+
extend Mine
|
86
|
+
end
|
87
|
+
|
88
|
+
Target1.new.bar
|
89
|
+
|
90
|
+
module Container
|
91
|
+
jack :some_jack # alternate term
|
92
|
+
|
93
|
+
class Contained
|
94
|
+
inject Container.some_jack
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# etc ...
|
99
|
+
|
100
|
+
}.to_not raise_error
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
the 'above generate the following target methods' do
|
105
|
+
|
106
|
+
Target1.bar.should == :a_bar
|
107
|
+
Target1.new.bar.should == :a_bar
|
108
|
+
|
109
|
+
Target1.foo.should == 'a foo'
|
110
|
+
Target1.new.foo.should == 'a foo'
|
111
|
+
|
112
|
+
Container.some_jack
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'follows the method lookup algorythm' do
|
117
|
+
|
118
|
+
injector :some_injector
|
119
|
+
|
120
|
+
expect{
|
121
|
+
class SomeReceiver
|
122
|
+
include some_injector
|
123
|
+
end
|
124
|
+
}.to raise_error(NameError)
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'allows you to follow the constant lookup algorythmn' do
|
129
|
+
|
130
|
+
Some_Injector = injector :some_injector
|
131
|
+
|
132
|
+
expect{
|
133
|
+
class Some_Receiver
|
134
|
+
inject Some_Injector
|
135
|
+
end
|
136
|
+
}.to_not raise_error
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'has alternate syntax and function through the inject/enrich keywords' do
|
141
|
+
|
142
|
+
# inject
|
143
|
+
|
144
|
+
injector :alternate do
|
145
|
+
def meth arg
|
146
|
+
arg < 3
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class AlternateClass
|
151
|
+
# ...
|
152
|
+
end
|
153
|
+
AlternateClass.inject alternate
|
154
|
+
|
155
|
+
AlternateClass.new.meth(2).should == true
|
156
|
+
|
157
|
+
# enrich
|
158
|
+
|
159
|
+
class Window
|
160
|
+
|
161
|
+
def draw
|
162
|
+
'window'
|
163
|
+
end
|
164
|
+
|
165
|
+
injector :scroll_bars do
|
166
|
+
def draw
|
167
|
+
super() + ' with scroll bars'
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
Window.new.enrich(Window.scroll_bars).draw.should == 'window with scroll bars'
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
# This toys with the ideas of modules as closures.'
|
180
|
+
describe 'another main difference is that Injectors are defined using a block.' do
|
181
|
+
there 'can be subsequent additions and definitions on the block' do
|
182
|
+
|
183
|
+
# declare the injector
|
184
|
+
Stuff = injector :stuff do
|
185
|
+
def far
|
186
|
+
'distant'
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# define containers and include the injector
|
191
|
+
class Something
|
192
|
+
include Stuff
|
193
|
+
end
|
194
|
+
|
195
|
+
# program works
|
196
|
+
some_thing = Something.new
|
197
|
+
some_thing.far.should == 'distant'
|
198
|
+
|
199
|
+
# define more stuff on the injector
|
200
|
+
stuff do
|
201
|
+
define_method :far do |miles|
|
202
|
+
'My stuff is: ' + miles.to_s + ' miles away'
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# program stiil works
|
207
|
+
some_thing.far(100).should == ('My stuff is: 100 miles away')
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'can have code dynamically injected into a receiver' do
|
212
|
+
|
213
|
+
Thing = Object.new
|
214
|
+
Thang = Object.new
|
215
|
+
|
216
|
+
injector :agent
|
217
|
+
|
218
|
+
Thing.extend agent
|
219
|
+
Thang.extend agent
|
220
|
+
|
221
|
+
agent do # on the fly definitions
|
222
|
+
define_method :capability do
|
223
|
+
'do the deed'
|
224
|
+
end
|
225
|
+
define_method :location do
|
226
|
+
'main building'
|
227
|
+
end
|
228
|
+
define_method :tester do
|
229
|
+
'some crap happened'
|
230
|
+
end
|
231
|
+
end
|
232
|
+
Thing.should respond_to :capability
|
233
|
+
Thing.capability.should == 'do the deed'
|
234
|
+
Thang.should respond_to :location
|
235
|
+
Thang.location.should == 'main building'
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
these 'blocks allow on the fly modules which can also include state from the surrounding context' do
|
240
|
+
|
241
|
+
class ClosureExpose
|
242
|
+
|
243
|
+
some_value = 'something'
|
244
|
+
|
245
|
+
injector :capture do
|
246
|
+
define_method :val do
|
247
|
+
some_value
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
inject capture
|
252
|
+
end
|
253
|
+
|
254
|
+
# the result
|
255
|
+
ClosureExpose.new.val.should == 'something'
|
256
|
+
|
257
|
+
class SecondClass
|
258
|
+
include ClosureExpose.capture
|
259
|
+
end
|
260
|
+
|
261
|
+
# the result
|
262
|
+
SecondClass.new.val.should == 'something'
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
these 'new blocks constitute new injector prolongations' do
|
267
|
+
|
268
|
+
AnotherName = injector :another_injector
|
269
|
+
|
270
|
+
class Target2
|
271
|
+
include AnotherName
|
272
|
+
end
|
273
|
+
|
274
|
+
#_____________________
|
275
|
+
# first prolongation
|
276
|
+
another_injector do
|
277
|
+
def meth arg
|
278
|
+
arg
|
279
|
+
end
|
280
|
+
|
281
|
+
def mith *args
|
282
|
+
return args
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
expect {
|
287
|
+
|
288
|
+
Target2.new.mith(1,2,3).should == [1,2,3]
|
289
|
+
Target2.new.meth(4).should == 4
|
290
|
+
|
291
|
+
}.to_not raise_error
|
292
|
+
|
293
|
+
#____________________
|
294
|
+
# another prolongation
|
295
|
+
another_injector do
|
296
|
+
def moth arg1, arg2
|
297
|
+
arg1 + arg2
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|
302
|
+
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
describe 'method definition on injectors' do
|
307
|
+
the 'methods of the injector closure can be defined using the def keyword' do
|
308
|
+
|
309
|
+
injector :MethodDefinitions
|
310
|
+
MethodDefinitions() do
|
311
|
+
def some_crazy_method
|
312
|
+
'I am %#&*@^%_crazy enough'
|
313
|
+
end
|
314
|
+
end
|
315
|
+
o = Object.new.enrich MethodDefinitions()
|
316
|
+
o.some_crazy_method.should == 'I am %#&*@^%_crazy enough'
|
317
|
+
|
318
|
+
end
|
319
|
+
|
320
|
+
the 'methods can be defined using the define_method method proc style' do
|
321
|
+
|
322
|
+
injector :MethodDefinitions
|
323
|
+
MethodDefinitions do
|
324
|
+
define_method :more_crazynex do |x, y|
|
325
|
+
x * y * x * y
|
326
|
+
end
|
327
|
+
end
|
328
|
+
enrich MethodDefinitions()
|
329
|
+
more_crazynex( 3, 4).should == 144
|
330
|
+
|
331
|
+
end
|
332
|
+
|
333
|
+
the 'methods can be defined using the define_method lambda style' do
|
334
|
+
|
335
|
+
injector :MethodDefinitions
|
336
|
+
MethodDefinitions do
|
337
|
+
define_method :gone_bonkers, lambda { |x,y,z| (x * y * z).split(':').join('#@')}
|
338
|
+
end
|
339
|
+
enrich MethodDefinitions()
|
340
|
+
gone_bonkers('errrr:rrrr', 2, 3).should == 'errrr#@rrrrerrrr#@rrrrerrrr#@rrrrerrrr#@rrrrerrrr#@rrrrerrrr#@rrrr'
|
341
|
+
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
describe 'the types of receivers' do
|
346
|
+
|
347
|
+
it 'can be a class INSTANCE' do
|
348
|
+
|
349
|
+
injector :class_instance_injector do
|
350
|
+
def new *args
|
351
|
+
puts "--done--"
|
352
|
+
super(*args)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
class SomeClassInstance
|
357
|
+
# ...
|
358
|
+
end
|
359
|
+
|
360
|
+
SomeClassInstance.enrich class_instance_injector
|
361
|
+
|
362
|
+
$stdout.should_receive(:puts).with("--done--")
|
363
|
+
iu = SomeClassInstance.new
|
364
|
+
|
365
|
+
end
|
366
|
+
|
367
|
+
it 'can be an actual class receiver' do
|
368
|
+
|
369
|
+
Layout = injector( :layout ){
|
370
|
+
def expand
|
371
|
+
end
|
372
|
+
}
|
373
|
+
Color = injector( :color ){
|
374
|
+
def tint
|
375
|
+
end
|
376
|
+
}
|
377
|
+
class Widget
|
378
|
+
|
379
|
+
include Layout
|
380
|
+
include Color
|
381
|
+
|
382
|
+
end
|
383
|
+
Widget.injectors.sym_list.should == [:layout, :color]
|
384
|
+
|
385
|
+
expect{Widget.new.expand}.to_not raise_error
|
386
|
+
expect{Widget.new.tint}.to_not raise_error
|
387
|
+
|
388
|
+
end
|
389
|
+
|
390
|
+
it 'can be an object instance receiver' do
|
391
|
+
|
392
|
+
injector :for_an_object do
|
393
|
+
def to_s
|
394
|
+
'oohooo'
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
o = Object.new
|
399
|
+
|
400
|
+
o.enrich for_an_object
|
401
|
+
|
402
|
+
o.to_s.should == 'oohooo'
|
403
|
+
|
404
|
+
end
|
405
|
+
|
406
|
+
end
|
407
|
+
|
408
|
+
describe 'introspection on receivers' do
|
409
|
+
|
410
|
+
a 'list of injections to an object is available from the objects themselves' do
|
411
|
+
|
412
|
+
class Coffee
|
413
|
+
def cost
|
414
|
+
1.50
|
415
|
+
end
|
416
|
+
end
|
417
|
+
injector :milk
|
418
|
+
injector :vanilla
|
419
|
+
|
420
|
+
cup = Coffee.new.enrich(milk).enrich(vanilla)
|
421
|
+
cup.injectors.sym_list.should == [:milk, :vanilla]
|
422
|
+
|
423
|
+
end
|
424
|
+
|
425
|
+
the 'list if also available on class injection' do
|
426
|
+
|
427
|
+
class SomeClass
|
428
|
+
inject injector :one
|
429
|
+
inject injector :two
|
430
|
+
end
|
431
|
+
SomeClass.injectors.sym_list.should == [:one, :two]
|
432
|
+
|
433
|
+
end
|
434
|
+
|
435
|
+
the 'same is true at the class instance level' do
|
436
|
+
|
437
|
+
# from above
|
438
|
+
SomeClassInstance.injectors.sym_list.should == [:class_instance_injector]
|
439
|
+
|
440
|
+
end
|
441
|
+
|
442
|
+
#
|
443
|
+
# more on this at the end of file
|
444
|
+
#
|
445
|
+
|
446
|
+
end
|
447
|
+
|
448
|
+
|
449
|
+
describe 'injectors can all be dynamically erased from context. In various ways:' do
|
450
|
+
describe 'An injector individually ejected to eliminate its function. All subsequent
|
451
|
+
method calls on the injectors methods may raise an error if there is nothing else up
|
452
|
+
the lookup chain' do
|
453
|
+
|
454
|
+
the 'functionality removed from the individual objects.
|
455
|
+
Note: applied using the enrich keyword on the object instance' do
|
456
|
+
|
457
|
+
class Coffee
|
458
|
+
def cost
|
459
|
+
1.00
|
460
|
+
end
|
461
|
+
end
|
462
|
+
injector :milk do
|
463
|
+
def cost
|
464
|
+
super() + 0.50
|
465
|
+
end
|
466
|
+
end
|
467
|
+
# expect{eject :milk}.to raise_error(NoMethodError, /eject/)
|
468
|
+
|
469
|
+
cup = Coffee.new.enrich(milk)
|
470
|
+
friends_cup = Coffee.new.enrich(milk)
|
471
|
+
|
472
|
+
cup.cost.should == 1.50
|
473
|
+
friends_cup.cost.should == 1.50
|
474
|
+
|
475
|
+
cup.eject :milk
|
476
|
+
cup.cost.should == 1.00
|
477
|
+
# friends cup didn't change price
|
478
|
+
friends_cup.cost.should == 1.50
|
479
|
+
|
480
|
+
end
|
481
|
+
|
482
|
+
the 'functionality removed from a class INSTANCE
|
483
|
+
Note: when applied via the enrich keywork on the class' do
|
484
|
+
|
485
|
+
injector :part do
|
486
|
+
def connector
|
487
|
+
'connected'
|
488
|
+
end
|
489
|
+
end
|
490
|
+
class Whole
|
491
|
+
end
|
492
|
+
Whole.extend part
|
493
|
+
|
494
|
+
Whole.connector.should == 'connected'
|
495
|
+
Whole.injectors.sym_list.should == [:part]
|
496
|
+
|
497
|
+
# eject the part
|
498
|
+
|
499
|
+
Whole.eject part
|
500
|
+
|
501
|
+
# result
|
502
|
+
|
503
|
+
Whole.injectors.should be_empty
|
504
|
+
expect{
|
505
|
+
Whole.eject part
|
506
|
+
}.to raise_error(ArgumentError)
|
507
|
+
|
508
|
+
end
|
509
|
+
|
510
|
+
the 'injector functionality removed from a class of objects.
|
511
|
+
Note: applied using the inject keyword on the class' do
|
512
|
+
|
513
|
+
# create the injection
|
514
|
+
class Home
|
515
|
+
include jack :layout do
|
516
|
+
def fractal
|
517
|
+
end
|
518
|
+
end
|
519
|
+
end
|
520
|
+
expect{Home.new.fractal}.to_not raise_error
|
521
|
+
|
522
|
+
# another injector
|
523
|
+
class Home
|
524
|
+
include jack :materials do
|
525
|
+
def plastic
|
526
|
+
end
|
527
|
+
end
|
528
|
+
end
|
529
|
+
Home.injectors.sym_list.should == [:layout, :materials ]
|
530
|
+
|
531
|
+
# build
|
532
|
+
my_home = Home.new
|
533
|
+
friends = Home.new
|
534
|
+
|
535
|
+
# eject the code
|
536
|
+
class Home
|
537
|
+
eject :layout
|
538
|
+
end
|
539
|
+
Home.injectors.sym_list.should == [:materials]
|
540
|
+
|
541
|
+
# the result
|
542
|
+
expect{my_home.fractal}.to raise_error(NoMethodError)
|
543
|
+
expect{friends.fractal}.to raise_error(NoMethodError)
|
544
|
+
expect{Home.new.fractal}.to raise_error(NoMethodError)
|
545
|
+
|
546
|
+
# eject the other injector
|
547
|
+
class Home
|
548
|
+
eject :materials
|
549
|
+
end
|
550
|
+
Home.injectors.sym_list.should be_empty
|
551
|
+
|
552
|
+
# the result
|
553
|
+
expect{my_home.plastic}.to raise_error(NoMethodError)
|
554
|
+
expect{friends.plastic}.to raise_error(NoMethodError)
|
555
|
+
expect{Home.new.plastic}.to raise_error(NoMethodError)
|
556
|
+
|
557
|
+
end
|
558
|
+
|
559
|
+
but 'the main injector is still available for re-injection' do
|
560
|
+
|
561
|
+
# re-inject the class
|
562
|
+
class Home
|
563
|
+
inject layout
|
564
|
+
end
|
565
|
+
|
566
|
+
# the result
|
567
|
+
expect{Home.new.fractal}.to_not raise_error
|
568
|
+
expect{Home.new.dup}.to_not raise_error
|
569
|
+
|
570
|
+
end
|
571
|
+
|
572
|
+
end
|
573
|
+
|
574
|
+
|
575
|
+
end
|
576
|
+
|
577
|
+
describe 'orthogonality of injectors with Jackbox' do
|
578
|
+
|
579
|
+
the 'following interdepent calls do not raise any errors' do
|
580
|
+
|
581
|
+
expect{
|
582
|
+
include Injectors
|
583
|
+
|
584
|
+
injector :tester1
|
585
|
+
|
586
|
+
tester1 do
|
587
|
+
extend self # extend self
|
588
|
+
# Note: you cannot self enrich an injector
|
589
|
+
def order weight
|
590
|
+
lets label =->(){"label for #{weight}"}
|
591
|
+
label[]
|
592
|
+
end
|
593
|
+
end
|
594
|
+
tester1.order(50).should == 'label for 50' # call method extended to self
|
595
|
+
|
596
|
+
|
597
|
+
tester1 do
|
598
|
+
decorate :order do |num| # decorate the same method
|
599
|
+
"#{self.to_s} " + super(num)
|
600
|
+
end
|
601
|
+
end
|
602
|
+
tester1.order(50).should match( /|tester1| label for 50/ ) # call decorated method extended to self
|
603
|
+
|
604
|
+
|
605
|
+
$stdout.should_receive(:puts).with("|tester1| label for 30")
|
606
|
+
with tester1 do # with self(tester1)
|
607
|
+
puts order 30 # execute method
|
608
|
+
def receive weight # define singleton method
|
609
|
+
"received #{weight}"
|
610
|
+
end
|
611
|
+
end
|
612
|
+
tester1.receive(90).should == 'received 90' # call singleton method
|
613
|
+
|
614
|
+
|
615
|
+
tester1 do
|
616
|
+
def more_tests arg # define instance method which depends on singleton method
|
617
|
+
receive 23 # call singleton method
|
618
|
+
"tested more #{arg}"
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
tester1.more_tests('of the api').should == 'tested more of the api'
|
623
|
+
|
624
|
+
}.to_not raise_error
|
625
|
+
|
626
|
+
end
|
627
|
+
|
628
|
+
it 'should also pass' do
|
629
|
+
|
630
|
+
expect{
|
631
|
+
|
632
|
+
injector :tester2
|
633
|
+
##################
|
634
|
+
tester2 do # enrich == extend
|
635
|
+
enrich self ##################
|
636
|
+
|
637
|
+
def meth arg # define method enriched on self
|
638
|
+
arg * arg
|
639
|
+
end
|
640
|
+
end
|
641
|
+
tester2.meth(4).should == 16 # call method enriched on self
|
642
|
+
|
643
|
+
|
644
|
+
tester2 do
|
645
|
+
decorate :meth do |arg| # re-define method enriched on self
|
646
|
+
super(arg) + 1
|
647
|
+
end
|
648
|
+
end
|
649
|
+
tester2.meth(3).should == 10 # call re-defined method
|
650
|
+
|
651
|
+
# splat
|
652
|
+
with tester2 do # with object self
|
653
|
+
meth(5).should == 26
|
654
|
+
def math arg1, arg2 # define singleton method on self
|
655
|
+
meth(arg1 + arg2)
|
656
|
+
end
|
657
|
+
end
|
658
|
+
tester2.math(3, 2).should == 26 # call singleton method on self
|
659
|
+
|
660
|
+
}.to_not raise_error
|
661
|
+
|
662
|
+
tester2 do # define new instance method depending on singleton method
|
663
|
+
def moth arg
|
664
|
+
math(arg, arg)
|
665
|
+
end
|
666
|
+
end
|
667
|
+
|
668
|
+
tester2.moth(4).should == 65
|
669
|
+
|
670
|
+
end
|
671
|
+
|
672
|
+
describe 'othogonality: equivalent injector forms' do
|
673
|
+
|
674
|
+
describe "equivalent inclusion" do
|
675
|
+
|
676
|
+
before do
|
677
|
+
class Injected
|
678
|
+
# ...
|
679
|
+
end
|
680
|
+
end
|
681
|
+
|
682
|
+
after do
|
683
|
+
|
684
|
+
end
|
685
|
+
|
686
|
+
the 'following class injection forms are all equivalent' do
|
687
|
+
|
688
|
+
injector :First do
|
689
|
+
def meth
|
690
|
+
:meth
|
691
|
+
end
|
692
|
+
end
|
693
|
+
Injected.inject First()
|
694
|
+
Injected.new.meth.should == :meth
|
695
|
+
|
696
|
+
Injected.eject First()
|
697
|
+
end
|
698
|
+
the 'following class injection forms are all equivalent' do
|
699
|
+
|
700
|
+
Injected.inject First() do
|
701
|
+
def meth
|
702
|
+
:meth
|
703
|
+
end
|
704
|
+
end
|
705
|
+
Injected.new.meth.should == :meth
|
706
|
+
|
707
|
+
Injected.eject First()
|
708
|
+
end
|
709
|
+
the 'following class injection forms are all equivalent' do
|
710
|
+
|
711
|
+
injector( :First ){
|
712
|
+
def meth
|
713
|
+
:meth
|
714
|
+
end
|
715
|
+
}
|
716
|
+
Injected.inject First()
|
717
|
+
Injected.new.meth.should == :meth
|
718
|
+
|
719
|
+
Injected.eject First()
|
720
|
+
end
|
721
|
+
the 'following class injection forms are all equivalent' do
|
722
|
+
|
723
|
+
Injected.inject injector( :First ){
|
724
|
+
def meth
|
725
|
+
:meth
|
726
|
+
end
|
727
|
+
}
|
728
|
+
Injected.new.meth.should == :meth
|
729
|
+
|
730
|
+
Injected.eject First()
|
731
|
+
end
|
732
|
+
end
|
733
|
+
|
734
|
+
describe 'equivalent enrichment' do
|
735
|
+
|
736
|
+
the 'following instance enrichment forms are all equvalent' do
|
737
|
+
|
738
|
+
injector :second do
|
739
|
+
def meth
|
740
|
+
:meth
|
741
|
+
end
|
742
|
+
end
|
743
|
+
enrich second
|
744
|
+
meth.should == :meth
|
745
|
+
|
746
|
+
eject second
|
747
|
+
end
|
748
|
+
the 'following instance enrichment forms are all equvalent' do
|
749
|
+
|
750
|
+
enrich injector :second do
|
751
|
+
def meth
|
752
|
+
:meth
|
753
|
+
end
|
754
|
+
end
|
755
|
+
meth.should == :meth
|
756
|
+
|
757
|
+
eject second
|
758
|
+
end
|
759
|
+
the 'following instance enrichment forms are all equvalent' do
|
760
|
+
|
761
|
+
injector( :second ){
|
762
|
+
def meth
|
763
|
+
:meth
|
764
|
+
end
|
765
|
+
}
|
766
|
+
enrich second
|
767
|
+
meth.should == :meth
|
768
|
+
|
769
|
+
eject second
|
770
|
+
end
|
771
|
+
the 'following instance enrichment forms are all equvalent' do
|
772
|
+
|
773
|
+
enrich injector( :second ){
|
774
|
+
def meth
|
775
|
+
:meth
|
776
|
+
end
|
777
|
+
}
|
778
|
+
meth.should == :meth
|
779
|
+
|
780
|
+
eject second
|
781
|
+
end
|
782
|
+
|
783
|
+
end
|
784
|
+
|
785
|
+
end
|
786
|
+
|
787
|
+
describe 'other forms of othogonlity' do
|
788
|
+
|
789
|
+
before do
|
790
|
+
injector :Ortho
|
791
|
+
end
|
792
|
+
|
793
|
+
after do
|
794
|
+
Ortho(:implode)
|
795
|
+
end
|
796
|
+
|
797
|
+
it 'uses #with in the following ways' do
|
798
|
+
|
799
|
+
with Ortho() do
|
800
|
+
def foo
|
801
|
+
end
|
802
|
+
end
|
803
|
+
|
804
|
+
Ortho().instance_methods.should include(:foo)
|
805
|
+
|
806
|
+
end
|
807
|
+
|
808
|
+
it "also works this way" do
|
809
|
+
|
810
|
+
class OrthoClass
|
811
|
+
end
|
812
|
+
|
813
|
+
with OrthoClass do
|
814
|
+
include Ortho()
|
815
|
+
extend Ortho(), Ortho()
|
816
|
+
end
|
817
|
+
|
818
|
+
with OrthoClass do
|
819
|
+
eject *injectors
|
820
|
+
end
|
821
|
+
|
822
|
+
OrthoClass.injectors.should be_empty
|
823
|
+
|
824
|
+
end
|
825
|
+
|
826
|
+
it 'works with #lets in this way' do
|
827
|
+
|
828
|
+
with Ortho() do
|
829
|
+
lets(:make){'Special Make'}
|
830
|
+
|
831
|
+
def print
|
832
|
+
puts make
|
833
|
+
end
|
834
|
+
end
|
835
|
+
|
836
|
+
enrich Ortho()
|
837
|
+
make.should == 'Special Make'
|
838
|
+
#...
|
839
|
+
|
840
|
+
end
|
841
|
+
|
842
|
+
end
|
843
|
+
end
|
844
|
+
|
845
|
+
end
|
846
|
+
|
847
|
+
|