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,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
|
+
|