jackbox 0.9.6.2
Sign up to get free protection for your applications and to get access to all the features.
- 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,334 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
include Injectors
|
4
|
+
|
5
|
+
describe 'Injector versioning:', :injectors do
|
6
|
+
# subsequent redefinitions of methods constitute another version of the injector. Injector
|
7
|
+
# versioning is the term used to identify a feature in the code that produces an artifact of injection which contains a
|
8
|
+
# certain set of methods and their associated outputs and represents a snapshot of that injector up until the point it
|
9
|
+
# gets applied to an object. From, that point on the object contains only that version of methods from that injector, and
|
10
|
+
# any subsequent overrides to those methods are only members of the "prolongation" of the injector and do not become part
|
11
|
+
# of the object of injection unless some form of reinjection occurs. Newer versions of methods only become part of newer
|
12
|
+
# objects or newer injections into existing targets'
|
13
|
+
|
14
|
+
describe 'injector versioning and its relationship to object instances' do
|
15
|
+
|
16
|
+
the 'injector versioning uses a snapshot of the injector`s existing methods up to the point of injection' do
|
17
|
+
# using only that version of methods as the object of injection. AA4ny overrides to methods in any subsequent injector prolongations
|
18
|
+
# do not take effect in this target. AA4ny new methods added to the injector do become available to the target, although
|
19
|
+
# may contain internal references to newer versions of the target methods. This ensures to keep everyting working correctly.'
|
20
|
+
|
21
|
+
#___________________
|
22
|
+
# injector declaration
|
23
|
+
injector :My_injector
|
24
|
+
|
25
|
+
#___________________
|
26
|
+
# injector first prolongation
|
27
|
+
My_injector do
|
28
|
+
def bar
|
29
|
+
:a_bar # version bar.1
|
30
|
+
end
|
31
|
+
def foo
|
32
|
+
# ...
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
object1 = Object.new
|
37
|
+
object1.enrich My_injector() # apply the injector --first snapshot
|
38
|
+
object1.bar.should == :a_bar # pass the test
|
39
|
+
|
40
|
+
#__________________
|
41
|
+
# injector second prolongation
|
42
|
+
My_injector do
|
43
|
+
def bar
|
44
|
+
:some_larger_bar # version bar.2 ... re-defines bar
|
45
|
+
end
|
46
|
+
def some_other_function
|
47
|
+
# ...
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
object2 = Object.new
|
52
|
+
object2.enrich My_injector() # apply the injector --second snapshot
|
53
|
+
object2.bar.should == :some_larger_bar
|
54
|
+
|
55
|
+
# result
|
56
|
+
###########
|
57
|
+
|
58
|
+
object1.bar.should == :a_bar # bar.1 is still the one
|
59
|
+
|
60
|
+
###########################################
|
61
|
+
# The object has kept its preferred version
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
the 're-injection changes ("updates if you will") versions of methods in use' do
|
66
|
+
|
67
|
+
#_________________
|
68
|
+
# re-injection
|
69
|
+
enrich My_injector() # re-injection on any object instance
|
70
|
+
bar.should == :some_larger_bar # bar.2 now available
|
71
|
+
|
72
|
+
expect{some_other_function}.to_not raise_error # some_other_function.1 is present
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
a 'different example with an explicit self' do
|
77
|
+
|
78
|
+
o = Object.new
|
79
|
+
|
80
|
+
injector :some_methods do
|
81
|
+
def meth arg
|
82
|
+
arg
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
o.enrich some_methods
|
87
|
+
o.meth('cha cha').should == 'cha cha'
|
88
|
+
|
89
|
+
some_methods do
|
90
|
+
def meth arg
|
91
|
+
arg * 2
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
o.meth('cha cha').should == 'cha cha'
|
96
|
+
|
97
|
+
oo = Object.new.enrich(some_methods)
|
98
|
+
oo.meth('cha cha').should == 'cha cha'+'cha cha'
|
99
|
+
|
100
|
+
o.meth('cha cha').should == 'cha cha'
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "injector versioning and its relationship to classes" do
|
107
|
+
# similar to object level versioning in that it uses a snapshot of the injector`s existing methods up to the point of injection, using only
|
108
|
+
# that version of methods as the object of injection. But, unlike object level versioning, because class injection is static, reinjection
|
109
|
+
# does not change the class unless we use the Strategy Pattern which completely changes the class's strategy of methods"
|
110
|
+
|
111
|
+
the "class injector versioning is similar to object injector versioning" do
|
112
|
+
|
113
|
+
#___________________
|
114
|
+
# injector declaration:
|
115
|
+
injector :Versions
|
116
|
+
|
117
|
+
#___________________
|
118
|
+
# injector first prolongation
|
119
|
+
Version1 = Versions do
|
120
|
+
def meth arg # version meth.1
|
121
|
+
arg ** arg
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class One
|
126
|
+
inject Version1 # apply --first snapshot
|
127
|
+
end
|
128
|
+
|
129
|
+
#_________________
|
130
|
+
# injector second prolongation
|
131
|
+
Version2 = Versions do
|
132
|
+
def meth arg1, arg2 # version meth.2 ... redefines meth.1
|
133
|
+
arg1 * arg2
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class Two
|
138
|
+
inject Version2 # apply --second snapshot
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
# result
|
143
|
+
#############################
|
144
|
+
Two.new.meth(2,4).should == 8 # meth.2
|
145
|
+
# two different injector versions
|
146
|
+
One.new.meth(3).should == 27 # meth.1
|
147
|
+
#############################
|
148
|
+
#
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
# we can update individual object instances but not classes directly
|
153
|
+
# this is unavailable because class injection is pervasive
|
154
|
+
the 'simple re-injection is different for classes: it fails ' do
|
155
|
+
|
156
|
+
# re-injection can happen on individual object instances
|
157
|
+
One.new.enrich(Version2).meth(4,5).should == 20 # meth.2 works!!
|
158
|
+
|
159
|
+
# re-injection applying version 2 to class One has no effect
|
160
|
+
One.inject Version2
|
161
|
+
expect{ One.new.meth(4,5) }.to raise_error(ArgumentError) # meth.2: fails!!
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
the 'way to class level injector updates is through the Strategy Pattern' do
|
166
|
+
|
167
|
+
# DO NOT want to necessarily update older clients of the class
|
168
|
+
# but possible if needed
|
169
|
+
|
170
|
+
class One
|
171
|
+
eject Version1 # eject version 1
|
172
|
+
|
173
|
+
inject Version2 # re-inject with prolonged injector -- can be any version
|
174
|
+
end
|
175
|
+
One.new.meth(4,5).should == 20 # meth.2 now available!!
|
176
|
+
|
177
|
+
################################################
|
178
|
+
# We feel this is the correct approach but has #
|
179
|
+
# a sllight syntactical handicap/ambiguity #
|
180
|
+
################################################
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
a 'preferred way as of late: private updates --could change' do
|
185
|
+
|
186
|
+
Versions do
|
187
|
+
def meth(*args)
|
188
|
+
args.inject(&:+)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class One
|
193
|
+
update Versions() # changes strategy for you
|
194
|
+
# by design a private method as sign of its delicate nature
|
195
|
+
end
|
196
|
+
|
197
|
+
# or ....
|
198
|
+
|
199
|
+
One.send :update, Versions()
|
200
|
+
One.new.meth( 3,4,5,6 ).should == 18 # new version now available!
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
a 'different use case' do
|
205
|
+
|
206
|
+
class Freak
|
207
|
+
|
208
|
+
injector :freaky
|
209
|
+
freaky do
|
210
|
+
def twitch
|
211
|
+
'_-=-_-=-_-=-_-=-_'
|
212
|
+
end
|
213
|
+
end
|
214
|
+
inject freaky
|
215
|
+
|
216
|
+
end
|
217
|
+
Freak.new.twitch.should == '_-=-_-=-_-=-_-=-_'
|
218
|
+
|
219
|
+
class Freak
|
220
|
+
|
221
|
+
freaky do
|
222
|
+
def twitch
|
223
|
+
'/\/\/\/\/\/\/\/\/\/'
|
224
|
+
end
|
225
|
+
end
|
226
|
+
update freaky
|
227
|
+
|
228
|
+
end
|
229
|
+
Freak.new.twitch.should == '/\/\/\/\/\/\/\/\/\/'
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
there 'is a different way todo global updates on Injectors: use define_method' do
|
234
|
+
|
235
|
+
SomeJack = jack :some_jack do
|
236
|
+
def foo_bar
|
237
|
+
'a foo and a bar'
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
class Client
|
242
|
+
inject SomeJack
|
243
|
+
end
|
244
|
+
|
245
|
+
Client.new.foo_bar.should == 'a foo and a bar' # expected
|
246
|
+
|
247
|
+
some_jack do
|
248
|
+
define_method :foo_bar do
|
249
|
+
'fooooo and barrrrr'
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
Client.new.foo_bar.should == 'fooooo and barrrrr' # different
|
254
|
+
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
describe "utility of injector versioning: " do
|
259
|
+
|
260
|
+
it 'allows to easily override methods without affecting other parts of your program' do
|
261
|
+
|
262
|
+
J1 = injector :j do
|
263
|
+
def meth(arg)
|
264
|
+
arg
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
class AA4
|
269
|
+
inject J1
|
270
|
+
end
|
271
|
+
AA4.new.meth(3).should == 3
|
272
|
+
|
273
|
+
J2 = j do
|
274
|
+
def meth(arg)
|
275
|
+
arg *arg
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
class BBB
|
280
|
+
inject J2
|
281
|
+
end
|
282
|
+
BBB.new.meth(3).should == 9
|
283
|
+
|
284
|
+
AA4.new.meth(3).should == 3
|
285
|
+
|
286
|
+
end
|
287
|
+
|
288
|
+
the "local binding of injectors" do
|
289
|
+
|
290
|
+
#_____________________
|
291
|
+
# injector declaration
|
292
|
+
VersionOne = injector :functionality do
|
293
|
+
def basic arg # version basic.1
|
294
|
+
arg * 2
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
o = Object.new.enrich VersionOne # apply --first snapshot
|
299
|
+
o.basic(1).should == 2 # basic.1
|
300
|
+
|
301
|
+
|
302
|
+
#_____________________
|
303
|
+
# injector prolongation
|
304
|
+
VersionTwo = functionality do
|
305
|
+
def basic arg # basic.2 ... basic.1 redefined
|
306
|
+
arg * 3
|
307
|
+
end
|
308
|
+
|
309
|
+
def compound # compound.1 --bound locally to basic.2
|
310
|
+
basic(3) + 2
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
p = Object.new.enrich VersionTwo # apply --second snapshot (like above)
|
315
|
+
p.basic(1).should == 3 # basic.2
|
316
|
+
p.compound.should == 11 # compound.1
|
317
|
+
|
318
|
+
|
319
|
+
# result
|
320
|
+
###################################
|
321
|
+
|
322
|
+
o.basic(1).should == 2 # basic.1
|
323
|
+
o.compound.should == 11 # compound.1 --local injector binding
|
324
|
+
|
325
|
+
###################################
|
326
|
+
# This ensures #compound.1 keeps bound
|
327
|
+
# to the right version #basic.2
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
|
@@ -0,0 +1,410 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
=begin rdoc
|
3
|
+
This file contains the specs for the GOF Decorator Pattern and
|
4
|
+
Strategy Pattern use cases for injectors.
|
5
|
+
=end
|
6
|
+
|
7
|
+
include Injectors
|
8
|
+
|
9
|
+
describe 'some use cases', :injectors do
|
10
|
+
|
11
|
+
describe 'GOF Decortors is one use of this codebase. Traditionally this is only partially solved in Ruby through PORO
|
12
|
+
decorators or the use of modules with the problems of loss of class identity for the former
|
13
|
+
or the limitations on the times it can be re-applied for the latter. We solve that!!' do
|
14
|
+
|
15
|
+
the 'GOF class decorator' do
|
16
|
+
|
17
|
+
class Coffee
|
18
|
+
def cost
|
19
|
+
1.50
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
injector :milk do
|
24
|
+
def cost
|
25
|
+
super() + 0.30
|
26
|
+
end
|
27
|
+
end
|
28
|
+
injector :vanilla do
|
29
|
+
def cost
|
30
|
+
super() + 0.15
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
cup = Coffee.new.enrich(milk).enrich(vanilla)
|
35
|
+
cup.should be_instance_of(Coffee)
|
36
|
+
cup.cost.should == 1.95
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
this 'can then be applied multiple times to the same receiver:' do
|
41
|
+
|
42
|
+
class Coffee
|
43
|
+
def cost
|
44
|
+
1.50
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
jack :milk do
|
49
|
+
def cost
|
50
|
+
super() + 0.30
|
51
|
+
end
|
52
|
+
end
|
53
|
+
jack :vanilla do
|
54
|
+
def cost
|
55
|
+
super() + 0.15
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
cup = Coffee.new.enrich(milk).enrich(vanilla).enrich(vanilla)
|
60
|
+
cup.injectors.sym_list.should == [:milk, :vanilla, :vanilla]
|
61
|
+
cup.cost.should == 2.10
|
62
|
+
cup.should be_instance_of(Coffee)
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
the 'a further workflow' do
|
67
|
+
|
68
|
+
class Coffee
|
69
|
+
def cost
|
70
|
+
1.50
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
jack :milk do
|
75
|
+
def cost
|
76
|
+
super() + 0.30
|
77
|
+
end
|
78
|
+
end
|
79
|
+
jack :vanilla do
|
80
|
+
def cost
|
81
|
+
super() + 0.15
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
cup = Coffee.new.enrich(milk).enrich(vanilla)
|
86
|
+
cup.should be_instance_of(Coffee)
|
87
|
+
cup.cost.should == 1.95
|
88
|
+
|
89
|
+
user_input = 'extra red vanilla'
|
90
|
+
vanilla do
|
91
|
+
define_method :appearance do
|
92
|
+
user_input
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
cup.enrich(vanilla)
|
97
|
+
cup.injectors.sym_list.should == [:milk, :vanilla, :vanilla]
|
98
|
+
cup.cost.should == 2.10
|
99
|
+
cup.appearance.should == 'extra red vanilla'
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
a 'bigger example' do
|
104
|
+
|
105
|
+
# some data
|
106
|
+
|
107
|
+
def database_content
|
108
|
+
%{car truck airplane boat}
|
109
|
+
end
|
110
|
+
|
111
|
+
# rendering helper controls
|
112
|
+
|
113
|
+
class MyWidgetClass
|
114
|
+
def initialize(content)
|
115
|
+
@content = content
|
116
|
+
end
|
117
|
+
|
118
|
+
def render
|
119
|
+
"<div id='MyWidget'>#{@content}</div>"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
injector :WidgetDecorator do
|
124
|
+
attr_reader :width, :height
|
125
|
+
|
126
|
+
def dim(width, heigth)
|
127
|
+
@width, @heigth = width, heigth
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
DesktopDecorator = WidgetDecorator do
|
132
|
+
def render
|
133
|
+
dim '600px', '200px'
|
134
|
+
%{
|
135
|
+
<style>
|
136
|
+
#MyWidget{
|
137
|
+
font: 14px, helvetica;
|
138
|
+
width:#{@width};
|
139
|
+
height: #{@heigth}
|
140
|
+
}
|
141
|
+
</style>
|
142
|
+
#{super()}
|
143
|
+
}
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
MobileDecorator = WidgetDecorator do
|
148
|
+
def render content
|
149
|
+
dim '200px', '600px'
|
150
|
+
%{
|
151
|
+
<style>
|
152
|
+
#MyWidget{
|
153
|
+
font: 10px, arial
|
154
|
+
width:#{@width}
|
155
|
+
height: #{@heigth}
|
156
|
+
}
|
157
|
+
</style>
|
158
|
+
#{super()}
|
159
|
+
}
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
# somewhere in a view
|
165
|
+
|
166
|
+
browser = 'Safari'
|
167
|
+
@content = database_content
|
168
|
+
|
169
|
+
my_widget = case browser
|
170
|
+
when match(/Safari|Firefox|IE/)
|
171
|
+
MyWidgetClass.new(@content).enrich(DesktopDecorator)
|
172
|
+
else
|
173
|
+
MyWidgetClass.new(@content).enrich(MobileDecorator)
|
174
|
+
end
|
175
|
+
expect(
|
176
|
+
|
177
|
+
my_widget.render.split.join).to eq( # split.join used for comparison
|
178
|
+
%{
|
179
|
+
<style>
|
180
|
+
#MyWidget {
|
181
|
+
font: 14px, helvetica;
|
182
|
+
width:600px;
|
183
|
+
height:200px
|
184
|
+
}
|
185
|
+
</style>
|
186
|
+
<div id='MyWidget'>car truck airplane boat</div>
|
187
|
+
}.split.join
|
188
|
+
)
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
describe 'strategy pattern.' do
|
195
|
+
|
196
|
+
this 'is a pattern with changes the guts of an object as opposed to just changing its face. Traditional
|
197
|
+
examples of this pattern use PORO component injection within constructors. Here is an alternate
|
198
|
+
implementation' do
|
199
|
+
|
200
|
+
class Coffee
|
201
|
+
|
202
|
+
attr_reader :strategy
|
203
|
+
def initialize
|
204
|
+
@strategy = nil
|
205
|
+
end
|
206
|
+
def cost
|
207
|
+
1.50
|
208
|
+
end
|
209
|
+
def brew
|
210
|
+
@strategy = 'normal-'
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
injector :Sweedish do
|
215
|
+
def brew
|
216
|
+
@strategy = 'sweedish-'
|
217
|
+
end
|
218
|
+
end
|
219
|
+
injector :French do
|
220
|
+
def brew
|
221
|
+
@strategy ='french-'
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
cup = Coffee.new
|
226
|
+
cup.brew
|
227
|
+
cup.strategy.should == 'normal-'
|
228
|
+
|
229
|
+
cup = Coffee.new.enrich(Sweedish()) # clobbers original strategy for this instance only
|
230
|
+
cup.brew
|
231
|
+
cup.strategy.should == ('sweedish-')
|
232
|
+
|
233
|
+
cup.enrich(French())
|
234
|
+
cup.brew
|
235
|
+
cup.strategy.should == ('french-')
|
236
|
+
end
|
237
|
+
|
238
|
+
this 'can be further enhanced by mixing it with the above decorator pattern' do
|
239
|
+
|
240
|
+
injector :Russian do
|
241
|
+
def brew
|
242
|
+
@strategy = super.to_s + 'vodka-'
|
243
|
+
end
|
244
|
+
end
|
245
|
+
injector :Scotish do
|
246
|
+
def brew
|
247
|
+
@strategy = super.to_s + 'wiskey-'
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
cup = Coffee.new
|
252
|
+
cup.enrich(Russian()).enrich(Scotish()).enrich(Russian())
|
253
|
+
cup.brew
|
254
|
+
cup.strategy.should == 'normal-vodka-wiskey-vodka-'
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'is even better or possible to have yet another implementation. This time we completely
|
259
|
+
replace the current strategy by actually ejecting it out of the class and then injecting
|
260
|
+
a new one' do
|
261
|
+
|
262
|
+
class Tea < Coffee # Tea is a type of coffee ;~Q)
|
263
|
+
injector :SpecialStrategy do
|
264
|
+
def brew
|
265
|
+
@strategy = 'special'
|
266
|
+
end
|
267
|
+
end
|
268
|
+
inject SpecialStrategy()
|
269
|
+
end
|
270
|
+
|
271
|
+
cup = Tea.new
|
272
|
+
cup.brew
|
273
|
+
cup.strategy.should == 'special'
|
274
|
+
friends_cup = Tea.new
|
275
|
+
friends_cup.strategy == 'special'
|
276
|
+
|
277
|
+
Tea.eject :SpecialStrategy
|
278
|
+
|
279
|
+
Tea.inject Sweedish()
|
280
|
+
cup.brew
|
281
|
+
friends_cup.brew
|
282
|
+
cup.strategy.should == 'sweedish-'
|
283
|
+
friends_cup.strategy.should == 'sweedish-'
|
284
|
+
|
285
|
+
Tea.eject Sweedish()
|
286
|
+
|
287
|
+
Tea.inject French()
|
288
|
+
cup.brew
|
289
|
+
friends_cup.brew
|
290
|
+
cup.strategy == 'french-'
|
291
|
+
friends_cup.strategy.should == 'french-'
|
292
|
+
|
293
|
+
end
|
294
|
+
|
295
|
+
end
|
296
|
+
|
297
|
+
describe "further Jackbox Injector workflows" do
|
298
|
+
|
299
|
+
it "allows adding decorators with function to be defined at later statge" do
|
300
|
+
|
301
|
+
class Widget
|
302
|
+
def cost
|
303
|
+
1
|
304
|
+
end
|
305
|
+
end
|
306
|
+
w = Widget.new
|
307
|
+
|
308
|
+
injector :decorator
|
309
|
+
w.enrich decorator, decorator, decorator, decorator
|
310
|
+
|
311
|
+
# user input
|
312
|
+
bid = 3.5
|
313
|
+
|
314
|
+
# define function
|
315
|
+
decorator do
|
316
|
+
# Global define
|
317
|
+
define_method :cost do
|
318
|
+
super() + bid
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
w.cost.should == 15
|
323
|
+
|
324
|
+
end
|
325
|
+
|
326
|
+
it "allows for the following workflow using super" do
|
327
|
+
|
328
|
+
jack :Superb
|
329
|
+
|
330
|
+
Superb do
|
331
|
+
def process string, additives, index
|
332
|
+
str = string.gsub('o', additives.slice!(index))
|
333
|
+
super(string, additives, index) + str rescue str
|
334
|
+
end
|
335
|
+
extend Superb(), Superb(), Superb()
|
336
|
+
end
|
337
|
+
|
338
|
+
Superb().process( 'food ', 'aeiu', 0 ).should == 'fuud fiid feed faad '
|
339
|
+
Superb(:implode)
|
340
|
+
|
341
|
+
end
|
342
|
+
|
343
|
+
it 'allows for the following strategy workflow using soft tags' do
|
344
|
+
|
345
|
+
###########################################################################
|
346
|
+
# For a specific example of what can be accomplished using this workflow #
|
347
|
+
# please refer to the examples directory under transformers spec #
|
348
|
+
# #
|
349
|
+
# #########################################################################
|
350
|
+
|
351
|
+
jack :Solution
|
352
|
+
|
353
|
+
Solution( :tag ) do
|
354
|
+
def solution
|
355
|
+
1
|
356
|
+
end
|
357
|
+
end
|
358
|
+
Solution( :tag ) do
|
359
|
+
def solution
|
360
|
+
2
|
361
|
+
end
|
362
|
+
end
|
363
|
+
Solution( :tag ) do
|
364
|
+
def solution
|
365
|
+
3
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
|
370
|
+
class Client
|
371
|
+
inject Solution()
|
372
|
+
|
373
|
+
def self.solve
|
374
|
+
Solution().tags.each { |e|
|
375
|
+
update e
|
376
|
+
puts new.solution rescue nil
|
377
|
+
}
|
378
|
+
|
379
|
+
# or...
|
380
|
+
|
381
|
+
solutions = Solution().tags.each
|
382
|
+
begin
|
383
|
+
update solutions.next
|
384
|
+
puts solved = new().solution()
|
385
|
+
end until solved
|
386
|
+
solved
|
387
|
+
end
|
388
|
+
|
389
|
+
end
|
390
|
+
|
391
|
+
$stdout.should_receive(:puts).with(1)
|
392
|
+
$stdout.should_receive(:puts).with(2)
|
393
|
+
$stdout.should_receive(:puts).with(3)
|
394
|
+
$stdout.should_receive(:puts).with(1)
|
395
|
+
|
396
|
+
Client.solve
|
397
|
+
|
398
|
+
end
|
399
|
+
|
400
|
+
end
|
401
|
+
|
402
|
+
# describe 'use in rails' do
|
403
|
+
#
|
404
|
+
# it 'allows replacing draper'
|
405
|
+
# it 'allows decorating anything not just models'
|
406
|
+
#
|
407
|
+
# end
|
408
|
+
|
409
|
+
end
|
410
|
+
|