madderlib 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -0
- data/LICENSE +22 -0
- data/README.rdoc +173 -0
- data/Rakefile +134 -0
- data/lib/madderlib.rb +27 -0
- data/lib/madderlib/builder.rb +659 -0
- data/lib/madderlib/conditional/allowed.rb +144 -0
- data/lib/madderlib/conditional/helper.rb +135 -0
- data/lib/madderlib/conditional/likely.rb +162 -0
- data/lib/madderlib/conditional/recur.rb +103 -0
- data/lib/madderlib/conditional/registry.rb +56 -0
- data/lib/madderlib/conditional/repeat.rb +130 -0
- data/lib/madderlib/context.rb +140 -0
- data/lib/madderlib/core.rb +217 -0
- data/lib/madderlib/extensions.rb +40 -0
- data/lib/madderlib/instruction.rb +171 -0
- data/lib/madderlib/phrase.rb +284 -0
- data/lib/madderlib/sequencer.rb +337 -0
- data/madderlib.gemspec +72 -0
- data/spec/benchmark_spec.rb +69 -0
- data/spec/builder_spec.rb +321 -0
- data/spec/builder_to_other_spec.rb +47 -0
- data/spec/builder_to_sequencer_spec.rb +388 -0
- data/spec/conditional_allowed_spec.rb +130 -0
- data/spec/conditional_helper_spec.rb +131 -0
- data/spec/conditional_likely_spec.rb +138 -0
- data/spec/conditional_recur_spec.rb +102 -0
- data/spec/conditional_registry_spec.rb +94 -0
- data/spec/conditional_repeat_spec.rb +88 -0
- data/spec/doc_spec.rb +550 -0
- data/spec/error_spec.rb +33 -0
- data/spec/examples_spec.rb +151 -0
- data/spec/extensions_spec.rb +47 -0
- data/spec/grammar_spec.rb +101 -0
- data/spec/instruction_spec.rb +133 -0
- data/spec/kernel_spec.rb +58 -0
- data/spec/phrase_spec.rb +7 -0
- data/spec/sequencer_spec.rb +317 -0
- data/spec/spec_helper.rb +54 -0
- metadata +98 -0
@@ -0,0 +1,321 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
describe MadderLib::Builder, "building" do
|
6
|
+
before(:each) do
|
7
|
+
@builder = MadderLib::Builder.new
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
it "doesn't require an id" do
|
13
|
+
@builder.id.should be_nil
|
14
|
+
|
15
|
+
@builder = MadderLib::Builder.new :id
|
16
|
+
@builder.id.should equal(:id)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "transfers a phrase into a Sequence" do
|
20
|
+
@builder.should have(0).phrases
|
21
|
+
@builder.phrase.should be_nil
|
22
|
+
|
23
|
+
phrase = @builder.then
|
24
|
+
phrase.should_not be_nil
|
25
|
+
phrase.id.should be_nil
|
26
|
+
|
27
|
+
@builder.should have(1).phrases
|
28
|
+
@builder.phrases.last.should equal(phrase)
|
29
|
+
@builder.phrase.should equal(phrase)
|
30
|
+
|
31
|
+
# one more
|
32
|
+
phrase = @builder.and_then
|
33
|
+
|
34
|
+
@builder.should have(2).phrases
|
35
|
+
@builder.phrases.last.should equal(phrase)
|
36
|
+
@builder.phrase.should equal(phrase)
|
37
|
+
|
38
|
+
# one more, with an id
|
39
|
+
phrase = @builder.also(:id)
|
40
|
+
phrase.id.should equal(:id)
|
41
|
+
|
42
|
+
@builder.should have(3).phrases
|
43
|
+
@builder.phrases.last.should equal(phrase)
|
44
|
+
@builder.phrase.should equal(phrase)
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
it "has id support for some phrase construction methods, not for others" do
|
50
|
+
[
|
51
|
+
:a, :an,
|
52
|
+
:then, :and_then, :also,
|
53
|
+
:first, :last, :lastly,
|
54
|
+
:anytime,
|
55
|
+
].each do |method|
|
56
|
+
# the method takes just an id
|
57
|
+
phrase = @builder.__send__ method, method
|
58
|
+
phrase.id.should equal(method)
|
59
|
+
end
|
60
|
+
|
61
|
+
[
|
62
|
+
:before, :after,
|
63
|
+
].each do |method|
|
64
|
+
# the method takes a reference, then the id
|
65
|
+
phrase = @builder.__send__ method, :ref, method
|
66
|
+
phrase.id.should equal(method)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it "#it is an alias for #phrase" do
|
71
|
+
@builder.it.should equal(@builder.phrase)
|
72
|
+
@builder.it.should be_nil
|
73
|
+
|
74
|
+
@builder.then
|
75
|
+
@builder.should have(1).phrases
|
76
|
+
@builder.it.should equal(@builder.phrase)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "#a & #and are aliases for #and_then, requiring an id" do
|
80
|
+
@builder.should have(0).phrases
|
81
|
+
|
82
|
+
phrase = @builder.phrase
|
83
|
+
@builder.a(:new).should_not equal(phrase)
|
84
|
+
@builder.should have(1).phrases
|
85
|
+
|
86
|
+
phrase = @builder.phrase
|
87
|
+
|
88
|
+
@builder.an(:other).should_not equal(phrase)
|
89
|
+
@builder.should have(2).phrases
|
90
|
+
end
|
91
|
+
|
92
|
+
it "will not permit duplicate phrase ids" do
|
93
|
+
# this will apply to all id-capable methods
|
94
|
+
@builder.a(:first)
|
95
|
+
@builder.a(:second)
|
96
|
+
lambda { @builder.a(:first) }.should raise_error(MadderLib::Error)
|
97
|
+
lambda { @builder.a(:second) }.should raise_error(MadderLib::Error)
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
it "has a 'say' shortcut" do
|
103
|
+
@builder.should have(0).phrases
|
104
|
+
|
105
|
+
phrase = @builder.say('hello')
|
106
|
+
phrase.should_not be_nil
|
107
|
+
phrase.id.should be_nil
|
108
|
+
|
109
|
+
@builder.should have(1).phrases
|
110
|
+
@builder.phrases.last.should equal(phrase)
|
111
|
+
|
112
|
+
# builds a new phrase
|
113
|
+
# even with the same words, it's different
|
114
|
+
phrase = @builder.say('hello')
|
115
|
+
|
116
|
+
@builder.should have(2).phrases
|
117
|
+
|
118
|
+
# we do not try to pull an id out of the say
|
119
|
+
# use and_then, or a similar explicit phrasing method
|
120
|
+
phrase = @builder.say(:it, 'goodbye')
|
121
|
+
phrase.id.should be_nil
|
122
|
+
|
123
|
+
@builder.should have(3).phrases
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
|
128
|
+
# best to test this once we have 'say' in the mix
|
129
|
+
#
|
130
|
+
it "'extend' operates in the context of the builder" do
|
131
|
+
words = %w{ hello goodbye }
|
132
|
+
|
133
|
+
@builder.extend do
|
134
|
+
say words.shift
|
135
|
+
and_then.say words.shift
|
136
|
+
end
|
137
|
+
|
138
|
+
@builder.should have(2).phrases
|
139
|
+
end
|
140
|
+
|
141
|
+
it "automatic 'extend' during construction" do
|
142
|
+
words = %w{ hello goodbye }
|
143
|
+
|
144
|
+
@builder = MadderLib::Builder.new do
|
145
|
+
say 'hello'
|
146
|
+
also.say 'goodbye'
|
147
|
+
end
|
148
|
+
|
149
|
+
@builder.should have(2).phrases
|
150
|
+
|
151
|
+
# with an id
|
152
|
+
@builder = MadderLib::Builder.new(:id) do
|
153
|
+
say 'hello'
|
154
|
+
# just testing syntactic sugar
|
155
|
+
an(:other).says 'goodbye'
|
156
|
+
end
|
157
|
+
|
158
|
+
@builder.id.should equal(:id)
|
159
|
+
@builder.should have(2).phrases
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
|
164
|
+
it "has an 'or' shortcut" do
|
165
|
+
@builder.say 'hello'
|
166
|
+
@builder.should have(1).phrases
|
167
|
+
|
168
|
+
@builder.or.say 'aloha'
|
169
|
+
@builder.should have(1).phrases
|
170
|
+
|
171
|
+
@builder.append do
|
172
|
+
alternately.say 'konnichiwa'
|
173
|
+
end
|
174
|
+
@builder.should have(1).phrases
|
175
|
+
end
|
176
|
+
|
177
|
+
it "the 'or' shortcut requires a pre-existing phrase" do
|
178
|
+
lambda { @builder.or }.should raise_error(MadderLib::Error)
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
|
183
|
+
it "categorizes orderings" do
|
184
|
+
@builder.say 'middle'
|
185
|
+
phrase = @builder.first.say 'beginning'
|
186
|
+
|
187
|
+
@builder.should have(2).phrases
|
188
|
+
@builder.should have(1).orderings
|
189
|
+
@builder.should have(0).dependencies
|
190
|
+
|
191
|
+
model = @builder.orderings.last
|
192
|
+
model.type.should equal(:first)
|
193
|
+
model.phrase.should equal(phrase)
|
194
|
+
|
195
|
+
phrase = @builder.last.say 'end'
|
196
|
+
|
197
|
+
@builder.should have(3).phrases
|
198
|
+
@builder.should have(2).orderings
|
199
|
+
|
200
|
+
model = @builder.orderings.last
|
201
|
+
model.type.should equal(:last)
|
202
|
+
model.phrase.should equal(phrase)
|
203
|
+
|
204
|
+
phrase = @builder.anytime.say 'illustration'
|
205
|
+
|
206
|
+
@builder.should have(4).phrases
|
207
|
+
@builder.should have(3).orderings
|
208
|
+
|
209
|
+
model = @builder.orderings.last
|
210
|
+
model.type.should equal(:anytime)
|
211
|
+
model.phrase.should equal(phrase)
|
212
|
+
|
213
|
+
phrase = @builder.anytime.say 'table'
|
214
|
+
@builder.orderings.last.phrase.should equal(phrase)
|
215
|
+
|
216
|
+
phrase = @builder.last.say 'appendix'
|
217
|
+
@builder.orderings.last.phrase.should equal(phrase)
|
218
|
+
|
219
|
+
phrase = @builder.first.say 'contents'
|
220
|
+
@builder.orderings.last.phrase.should equal(phrase)
|
221
|
+
end
|
222
|
+
|
223
|
+
it "has dependencies which require reference ids" do
|
224
|
+
lambda { @builder.before.say 'no id' }.should raise_error
|
225
|
+
lambda { @builder.after.say 'no id' }.should raise_error
|
226
|
+
end
|
227
|
+
|
228
|
+
it "categorizes dependencies" do
|
229
|
+
phrase = @builder.before(:use).say 'open'
|
230
|
+
|
231
|
+
@builder.should have(1).phrases
|
232
|
+
@builder.should have(0).orderings
|
233
|
+
@builder.should have(1).dependencies
|
234
|
+
|
235
|
+
dep = @builder.dependencies.last
|
236
|
+
dep.ref.should equal(:use)
|
237
|
+
dep.phrase.should equal(phrase)
|
238
|
+
dep.type.should equal(:before)
|
239
|
+
|
240
|
+
phrase = @builder.after(:use).say 'close'
|
241
|
+
|
242
|
+
@builder.should have(2).phrases
|
243
|
+
@builder.should have(2).dependencies
|
244
|
+
|
245
|
+
dep = @builder.dependencies.last
|
246
|
+
dep.ref.should equal(:use)
|
247
|
+
dep.phrase.should equal(phrase)
|
248
|
+
dep.type.should equal(:after)
|
249
|
+
|
250
|
+
phrase = @builder.before(:use).say 'destroy'
|
251
|
+
@builder.dependencies.last.phrase.should equal(phrase)
|
252
|
+
|
253
|
+
phrase = @builder.before(:use).say 'init'
|
254
|
+
@builder.dependencies.last.phrase.should equal(phrase)
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
|
259
|
+
it "can be cloned and extended" do
|
260
|
+
builder = madderlib :clone_original do
|
261
|
+
an(:early).says 'early'
|
262
|
+
a(:late).says 'late'
|
263
|
+
end
|
264
|
+
builder.id.should equal(:clone_original)
|
265
|
+
|
266
|
+
# clone
|
267
|
+
extended = builder.clone(:clone_extended).extend do
|
268
|
+
before(:early).say "it's"
|
269
|
+
after(:late).say 'bird'
|
270
|
+
end
|
271
|
+
extended[:meta] = :extended
|
272
|
+
metas = nil
|
273
|
+
block = lambda {|context| metas << context.builder[:meta] }
|
274
|
+
extended.setup &block
|
275
|
+
extended.teardown &block
|
276
|
+
|
277
|
+
# modify a shared Phrase
|
278
|
+
shared = builder.phrases.find {|phrase| phrase.id == :late }
|
279
|
+
shared.instructions.first.words << 'later'
|
280
|
+
|
281
|
+
# new id, in the registry
|
282
|
+
extended.id.should equal(:clone_extended)
|
283
|
+
extended.should equal(madderlib_grammar[:clone_extended])
|
284
|
+
|
285
|
+
# setup, teardown, meta, and extended content
|
286
|
+
metas = []
|
287
|
+
extended.sentence.should eql("it's early late later bird")
|
288
|
+
metas.should eql([:extended, :extended])
|
289
|
+
|
290
|
+
# original is not impacted
|
291
|
+
# except for the shared Phrase
|
292
|
+
metas = []
|
293
|
+
builder.sentence.should eql('early late later')
|
294
|
+
builder[:meta].should be_nil
|
295
|
+
metas.should have(0).items
|
296
|
+
end
|
297
|
+
|
298
|
+
it "adds the clone to the active Grammar if its original is there" do
|
299
|
+
grammar = MadderLib::Grammar.get_instance
|
300
|
+
|
301
|
+
original = MadderLib::Builder.new
|
302
|
+
grammar.builders.include?(original).should_not be_true
|
303
|
+
|
304
|
+
cloned = original.clone
|
305
|
+
|
306
|
+
grammar.add original
|
307
|
+
added = original.clone
|
308
|
+
grammar.builders.include?(added).should be_true
|
309
|
+
end
|
310
|
+
|
311
|
+
|
312
|
+
|
313
|
+
it "holds metadata for you" do
|
314
|
+
builder = madderlib do
|
315
|
+
meta[:cow] = :cow
|
316
|
+
builder.meta[:dog] = :dog
|
317
|
+
|
318
|
+
[:cow, :dog].each {|item| builder[item].should equal(item) }
|
319
|
+
end
|
320
|
+
|
321
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
describe MadderLib::Builder, "has component parts" do
|
6
|
+
WORDS = %w{ one two three }
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
it "is iterable" do
|
11
|
+
words = []
|
12
|
+
|
13
|
+
(madderlib do
|
14
|
+
WORDS.each {|word| say word }
|
15
|
+
end).each do |word|
|
16
|
+
words << word
|
17
|
+
end
|
18
|
+
|
19
|
+
words.should eql(WORDS)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "converts to an Array" do
|
23
|
+
array = (madderlib do
|
24
|
+
WORDS.each {|word| say word }
|
25
|
+
end).to_a
|
26
|
+
|
27
|
+
array.should eql(WORDS)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "converts to a String" do
|
31
|
+
s = (madderlib do
|
32
|
+
WORDS.each {|word| say word }
|
33
|
+
end).to_s
|
34
|
+
|
35
|
+
# default separator
|
36
|
+
s.should eql(WORDS.join(' '))
|
37
|
+
end
|
38
|
+
|
39
|
+
it "converts to a String, with separator" do
|
40
|
+
s = (madderlib do
|
41
|
+
WORDS.each {|word| say word }
|
42
|
+
end).to_s("\t")
|
43
|
+
|
44
|
+
# explicit separator
|
45
|
+
s.should eql(WORDS.join("\t"))
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,388 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
describe MadderLib::Builder, "to Sequencer" do
|
6
|
+
|
7
|
+
it "turns an empty builder into an empty sequencer" do
|
8
|
+
sequencer = MadderLib::Builder.new.to_sequencer
|
9
|
+
|
10
|
+
sequencer.should have(0).steps
|
11
|
+
sequencer.should have(0).anytimes
|
12
|
+
sequencer.should have(0).befores
|
13
|
+
sequencer.should have(0).afters
|
14
|
+
end
|
15
|
+
|
16
|
+
it "handles a single phrase" do
|
17
|
+
builder = MadderLib::Builder.new()
|
18
|
+
phrase = builder.say('puh-TAY-to').or.say('puh-TAH-to')
|
19
|
+
sequencer = builder.to_sequencer
|
20
|
+
|
21
|
+
sequencer.should have(1).steps
|
22
|
+
sequencer.should have(0).anytimes
|
23
|
+
sequencer.should have(0).befores
|
24
|
+
sequencer.should have(0).afters
|
25
|
+
|
26
|
+
sequencer.steps.last.should equal(phrase)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "requires valid ids for befores and afters" do
|
30
|
+
builder = madderlib do
|
31
|
+
before(:missing).say('uh')
|
32
|
+
end
|
33
|
+
lambda { builder.to_sequencer }.should raise_error(MadderLib::Error)
|
34
|
+
|
35
|
+
builder = madderlib do
|
36
|
+
after(:missing).say('whoa')
|
37
|
+
end
|
38
|
+
lambda { builder.to_sequencer }.should raise_error(MadderLib::Error)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "reminder... Builder#say doesn't derive a Phrase id" do
|
42
|
+
phrase = nil
|
43
|
+
builder = madderlib do
|
44
|
+
phrase = say(:it, 'words')
|
45
|
+
before(:it).say('something')
|
46
|
+
end
|
47
|
+
|
48
|
+
phrase.id.should be_nil
|
49
|
+
lambda { builder.to_sequencer }.should raise_error(MadderLib::Error)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
it "properly sequences firsts" do
|
55
|
+
builder = madderlib :sequence_befores do
|
56
|
+
says('loud')
|
57
|
+
first.say('too')
|
58
|
+
first.say('yer')
|
59
|
+
end
|
60
|
+
|
61
|
+
sequencer = builder.to_sequencer
|
62
|
+
sequencer.should have(3).steps
|
63
|
+
|
64
|
+
ids = []
|
65
|
+
sequencer.each_phrase do |phrase|
|
66
|
+
phrase.should have(1).instructions
|
67
|
+
words = phrase.instructions.last.words
|
68
|
+
words.should have(1).items
|
69
|
+
|
70
|
+
ids << words.last
|
71
|
+
end
|
72
|
+
ids.should eql(%w{ yer too loud })
|
73
|
+
end
|
74
|
+
|
75
|
+
it "properly sequences lasts" do
|
76
|
+
builder = madderlib do
|
77
|
+
last.say('too')
|
78
|
+
lastly.say('big')
|
79
|
+
say('feet')
|
80
|
+
end
|
81
|
+
|
82
|
+
sequencer = builder.to_sequencer
|
83
|
+
sequencer.should have(3).steps
|
84
|
+
|
85
|
+
ids = []
|
86
|
+
sequencer.each_phrase do |phrase|
|
87
|
+
phrase.should have(1).instructions
|
88
|
+
words = phrase.instructions.last.words
|
89
|
+
words.should have(1).items
|
90
|
+
|
91
|
+
ids << words.last
|
92
|
+
end
|
93
|
+
ids.should eql(%w{ feet too big })
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
it "before and after require referenceable ids" do
|
99
|
+
# doesn't complain on build
|
100
|
+
builder = madderlib do
|
101
|
+
before(:say).say 'before'
|
102
|
+
say 'saying'
|
103
|
+
end
|
104
|
+
# but during validation, etc.
|
105
|
+
lambda { builder.validate }.should raise_error MadderLib::Error
|
106
|
+
|
107
|
+
builder = madderlib do
|
108
|
+
after(:say).say 'after'
|
109
|
+
say 'saying'
|
110
|
+
end
|
111
|
+
lambda { builder.validate }.should raise_error MadderLib::Error
|
112
|
+
end
|
113
|
+
|
114
|
+
it "moves befores and afters into their own little worlds" do
|
115
|
+
builder = madderlib do
|
116
|
+
after(:say, :after).say 'after'
|
117
|
+
before(:say, :before).say 'before'
|
118
|
+
a(:say).says 'says'
|
119
|
+
end
|
120
|
+
|
121
|
+
sequencer = builder.to_sequencer
|
122
|
+
sequencer.should have(1).steps
|
123
|
+
|
124
|
+
# before
|
125
|
+
sequencer.should have(1).befores
|
126
|
+
dep = sequencer.befores[:say]
|
127
|
+
dep.should have(1).items
|
128
|
+
dep.last.instructions.last.words.last.should eql('before')
|
129
|
+
|
130
|
+
# after
|
131
|
+
sequencer.should have(1).afters
|
132
|
+
dep = sequencer.afters[:say]
|
133
|
+
dep.should have(1).items
|
134
|
+
dep.last.instructions.last.words.last.should eql('after')
|
135
|
+
|
136
|
+
# some more, to prove ordering
|
137
|
+
builder.append do
|
138
|
+
after(:say).say 'end'
|
139
|
+
before(:say).say 'begin'
|
140
|
+
end
|
141
|
+
|
142
|
+
sequencer = builder.to_sequencer
|
143
|
+
|
144
|
+
# before
|
145
|
+
words = []
|
146
|
+
sequencer.befores[:say].each do |before|
|
147
|
+
words << before.instructions.last.words.last
|
148
|
+
end
|
149
|
+
words.should eql(['begin', 'before'])
|
150
|
+
|
151
|
+
# after
|
152
|
+
words = []
|
153
|
+
sequencer.afters[:say].each do |after|
|
154
|
+
words << after.instructions.last.words.last
|
155
|
+
end
|
156
|
+
words.should eql(['after', 'end'])
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
|
161
|
+
it "anytime requires referenceable ids" do
|
162
|
+
# doesn't complain on build
|
163
|
+
builder = madderlib do
|
164
|
+
anytime.say('dipsy').before(:say)
|
165
|
+
say 'doodle'
|
166
|
+
end
|
167
|
+
# but during validation, etc.
|
168
|
+
lambda { builder.validate }.should raise_error MadderLib::Error
|
169
|
+
|
170
|
+
builder = madderlib do
|
171
|
+
anytime.say('doodle').after(:say)
|
172
|
+
say 'dipsy'
|
173
|
+
end
|
174
|
+
lambda { builder.validate }.should raise_error MadderLib::Error
|
175
|
+
|
176
|
+
builder = madderlib do
|
177
|
+
anytime.say('zzz').between(:night, :day)
|
178
|
+
say 'ni-night'
|
179
|
+
end
|
180
|
+
lambda { builder.validate }.should raise_error MadderLib::Error
|
181
|
+
end
|
182
|
+
|
183
|
+
it "anytimes go into their own little world" do
|
184
|
+
builder = madderlib :sequence_befores do
|
185
|
+
anytime(:b).before(:say).say('before')
|
186
|
+
anytime(:a).after(:say).say('after')
|
187
|
+
anytime(:ab).say('somewhere').after(:b).before(:a)
|
188
|
+
anytime(:t).between(:b, :a).say('tween')
|
189
|
+
a(:say).says 'hello'
|
190
|
+
end
|
191
|
+
|
192
|
+
sequencer = builder.to_sequencer
|
193
|
+
sequencer.should have(1).steps
|
194
|
+
|
195
|
+
sequencer.should have(4).anytimes
|
196
|
+
|
197
|
+
ids = []
|
198
|
+
sequencer.anytimes.each do |anytime|
|
199
|
+
id = anytime.id
|
200
|
+
ids << id
|
201
|
+
anytime.before.should_not be_nil unless :a == id
|
202
|
+
anytime.after.should_not be_nil unless :b == id
|
203
|
+
end
|
204
|
+
|
205
|
+
ids.should eql([:b, :a, :ab, :t])
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
|
210
|
+
it "blends everything together perfectly" do
|
211
|
+
builder = madderlib :sequence_befores do
|
212
|
+
last(:late).say('4')
|
213
|
+
first(:early).say('2')
|
214
|
+
last.say('5')
|
215
|
+
first.say('1')
|
216
|
+
|
217
|
+
before(:late).say('3.9')
|
218
|
+
after(:early).say('2.1')
|
219
|
+
before(:early).say('1.9')
|
220
|
+
after(:late).say('4.1')
|
221
|
+
|
222
|
+
anytime.between(:early, :late).say('imaginary')
|
223
|
+
anytime.say('random')
|
224
|
+
|
225
|
+
says('3')
|
226
|
+
end
|
227
|
+
|
228
|
+
sequencer = builder.to_sequencer
|
229
|
+
sequencer.should have(5).steps
|
230
|
+
|
231
|
+
marks = []
|
232
|
+
sequencer.steps.each do |phrase|
|
233
|
+
phrase.should have(1).instructions
|
234
|
+
words = phrase.instructions.last.words
|
235
|
+
words.should have(1).items
|
236
|
+
|
237
|
+
marks << words.last
|
238
|
+
end
|
239
|
+
marks.should eql(%w{ 1 2 3 4 5 })
|
240
|
+
|
241
|
+
sequencer.befores[:early].last.instructions.last.words.last.should eql('1.9')
|
242
|
+
sequencer.afters[:early].last.instructions.last.words.last.should eql('2.1')
|
243
|
+
sequencer.befores[:late].last.instructions.last.words.last.should eql('3.9')
|
244
|
+
sequencer.afters[:late].last.instructions.last.words.last.should eql('4.1')
|
245
|
+
|
246
|
+
marks = []
|
247
|
+
sequencer.anytimes.each do |phrase|
|
248
|
+
marks << phrase.instructions.last.words.last
|
249
|
+
end
|
250
|
+
marks.should eql(%w{ imaginary random })
|
251
|
+
end
|
252
|
+
|
253
|
+
|
254
|
+
|
255
|
+
it "supports setup and teardown blocks" do
|
256
|
+
holder = []
|
257
|
+
|
258
|
+
sequencer = (madderlib do
|
259
|
+
# takes context, uses data, get and set local scope
|
260
|
+
# multiple are handled sequentially
|
261
|
+
setup {|context| holder << :setup }
|
262
|
+
setup {|context| context.data[:word] = holder.first }
|
263
|
+
setup {|context| holder << :a_s }
|
264
|
+
setup(:first) {|context| holder << :b_s }
|
265
|
+
|
266
|
+
# takes context
|
267
|
+
say {|context| context.data[:word] }
|
268
|
+
|
269
|
+
# doesn't need context, set local scope
|
270
|
+
# multiple are handled sequentially
|
271
|
+
teardown { holder << :teardown }
|
272
|
+
teardown(:first) { holder << :b_t }
|
273
|
+
teardown { holder << :a_t }
|
274
|
+
end).to_sequencer
|
275
|
+
|
276
|
+
# due to execution sequence...
|
277
|
+
sequencer.words.should eql(%w{ b_s })
|
278
|
+
|
279
|
+
holder.should eql([:b_s, :setup, :a_s, :b_t, :teardown, :a_t])
|
280
|
+
end
|
281
|
+
|
282
|
+
|
283
|
+
|
284
|
+
it "can collect the executed context" do
|
285
|
+
builder = madderlib :outer do
|
286
|
+
say madderlib(:inner_1) { say 'inner' }
|
287
|
+
say 'plain'
|
288
|
+
# tried a do .. end block here, wasn't seen
|
289
|
+
# using { .. } does work
|
290
|
+
say madderlib(:inner_2) {
|
291
|
+
say madderlib(:deep_1) { say 'deep' }
|
292
|
+
say madderlib(:deep_2) { say 'deeper' }
|
293
|
+
}
|
294
|
+
end
|
295
|
+
|
296
|
+
# capture the context
|
297
|
+
# setup or teardown, not important
|
298
|
+
context, count = nil, 0
|
299
|
+
words = builder.words do |ctx|
|
300
|
+
context = ctx
|
301
|
+
count = ctx.spoken.size
|
302
|
+
end
|
303
|
+
words.should eql(%w{ inner plain deep deeper })
|
304
|
+
|
305
|
+
# it'll be called before the context is used
|
306
|
+
count.should eql(0)
|
307
|
+
|
308
|
+
# and here's what you get back
|
309
|
+
context.should_not be_nil
|
310
|
+
context.spoken.should have(3).phrases
|
311
|
+
context.silent.should have(0).phrases
|
312
|
+
context.instructions.should have(3).instructions
|
313
|
+
|
314
|
+
# the :flat approach (default)
|
315
|
+
context.contexts.should have(4).contexts
|
316
|
+
|
317
|
+
# just the sub-contexts, not the outer builder
|
318
|
+
ids = []
|
319
|
+
context.contexts.each {|ctx| ids << ctx.builder.id }
|
320
|
+
|
321
|
+
ids.should have(4).ids
|
322
|
+
ids.should eql([ :inner_1, :inner_2, :deep_1, :deep_2 ])
|
323
|
+
|
324
|
+
# the :tree approach
|
325
|
+
# just the inner ones
|
326
|
+
context.contexts(:tree).should have(2).contexts
|
327
|
+
|
328
|
+
# hierarchical traversal
|
329
|
+
# this traversal provides a full tree
|
330
|
+
# including he the outer builder
|
331
|
+
ids = []
|
332
|
+
traverse = lambda do |ctx|
|
333
|
+
ids << ctx.builder.id
|
334
|
+
ctx.contexts(:tree).each {|sub| traverse.call(sub) }
|
335
|
+
end
|
336
|
+
traverse.call(context)
|
337
|
+
|
338
|
+
ids.should have(5).ids
|
339
|
+
ids.should eql([:outer, :inner_1, :inner_2, :deep_1, :deep_2 ])
|
340
|
+
end
|
341
|
+
|
342
|
+
it "can inject data into the context" do
|
343
|
+
builder = madderlib :inject_data do
|
344
|
+
say '('
|
345
|
+
say {|context| context[:text] }
|
346
|
+
say ')'
|
347
|
+
|
348
|
+
# text is nil, so doesn't appear
|
349
|
+
builder.words.should eql(['(', ')'])
|
350
|
+
|
351
|
+
# round trip with simple value
|
352
|
+
# can't pull out the context with enumerator-based methods
|
353
|
+
# but you can still inject data
|
354
|
+
context = nil
|
355
|
+
|
356
|
+
words = builder.words(:text => 'words') {|ctx| context = ctx }
|
357
|
+
context[:text].should eql('words')
|
358
|
+
words.should eql(['(', context[:text], ')'])
|
359
|
+
|
360
|
+
s = ''
|
361
|
+
builder.each_word(:text => 'each_word') {|word| s << word }
|
362
|
+
s.should eql('(each_word)')
|
363
|
+
|
364
|
+
# proper handling of data / separator mixing
|
365
|
+
builder.sentence.should eql('( )')
|
366
|
+
builder.sentence(:text => 'sentence').should eql('( sentence )')
|
367
|
+
builder.sentence('', :text => 'sentence').should eql('(sentence)')
|
368
|
+
map = { :text => 'sentence' }
|
369
|
+
builder.sentence(map, '.').should eql('(.sentence.)')
|
370
|
+
|
371
|
+
# so, let's try an indirection through the sequence
|
372
|
+
# we're testing by omission
|
373
|
+
# we won't see a phrase if the text is nil
|
374
|
+
sequencer = builder.to_sequencer
|
375
|
+
|
376
|
+
sequencer.phrases.should have(2).phrases
|
377
|
+
sequencer.phrases(:text => 'phrase').should have(3).phrases
|
378
|
+
|
379
|
+
phrases = []
|
380
|
+
sequencer.each_phrase {|phrase| phrases << phrase }
|
381
|
+
phrases.should have(2).phrases
|
382
|
+
|
383
|
+
phrases = []
|
384
|
+
sequencer.each_phrase(:text => 'phrase') {|phrase| phrases << phrase }
|
385
|
+
phrases.should have(3).phrases
|
386
|
+
end
|
387
|
+
|
388
|
+
end
|