madderlib 0.1.0
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.
- 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
|