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.
Files changed (40) hide show
  1. data/CHANGELOG +3 -0
  2. data/LICENSE +22 -0
  3. data/README.rdoc +173 -0
  4. data/Rakefile +134 -0
  5. data/lib/madderlib.rb +27 -0
  6. data/lib/madderlib/builder.rb +659 -0
  7. data/lib/madderlib/conditional/allowed.rb +144 -0
  8. data/lib/madderlib/conditional/helper.rb +135 -0
  9. data/lib/madderlib/conditional/likely.rb +162 -0
  10. data/lib/madderlib/conditional/recur.rb +103 -0
  11. data/lib/madderlib/conditional/registry.rb +56 -0
  12. data/lib/madderlib/conditional/repeat.rb +130 -0
  13. data/lib/madderlib/context.rb +140 -0
  14. data/lib/madderlib/core.rb +217 -0
  15. data/lib/madderlib/extensions.rb +40 -0
  16. data/lib/madderlib/instruction.rb +171 -0
  17. data/lib/madderlib/phrase.rb +284 -0
  18. data/lib/madderlib/sequencer.rb +337 -0
  19. data/madderlib.gemspec +72 -0
  20. data/spec/benchmark_spec.rb +69 -0
  21. data/spec/builder_spec.rb +321 -0
  22. data/spec/builder_to_other_spec.rb +47 -0
  23. data/spec/builder_to_sequencer_spec.rb +388 -0
  24. data/spec/conditional_allowed_spec.rb +130 -0
  25. data/spec/conditional_helper_spec.rb +131 -0
  26. data/spec/conditional_likely_spec.rb +138 -0
  27. data/spec/conditional_recur_spec.rb +102 -0
  28. data/spec/conditional_registry_spec.rb +94 -0
  29. data/spec/conditional_repeat_spec.rb +88 -0
  30. data/spec/doc_spec.rb +550 -0
  31. data/spec/error_spec.rb +33 -0
  32. data/spec/examples_spec.rb +151 -0
  33. data/spec/extensions_spec.rb +47 -0
  34. data/spec/grammar_spec.rb +101 -0
  35. data/spec/instruction_spec.rb +133 -0
  36. data/spec/kernel_spec.rb +58 -0
  37. data/spec/phrase_spec.rb +7 -0
  38. data/spec/sequencer_spec.rb +317 -0
  39. data/spec/spec_helper.rb +54 -0
  40. metadata +98 -0
@@ -0,0 +1,94 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+
4
+
5
+ module MadderLib
6
+ module Spec
7
+ class Phrase
8
+ class << self
9
+ include Conditional::Registry::Static
10
+ end
11
+ include Conditional::Registry::Instance
12
+
13
+ def instructions
14
+ @instructions ||= []
15
+ end
16
+ end
17
+
18
+ class Instruction
19
+ class << self
20
+ include Conditional::Registry::Static
21
+ end
22
+ include Conditional::Registry::Instance
23
+ end
24
+ end
25
+ end
26
+
27
+
28
+
29
+ describe MadderLib::Conditional::Registry do
30
+ it "accumulates phrase preprations" do
31
+ hits = []
32
+
33
+ # how to prepare each instruction
34
+ MadderLib::Spec::Instruction.add_prepare { hits << :instruction_bare }
35
+ MadderLib::Spec::Instruction.add_prepare {|context| hits << :instruction_context }
36
+
37
+ # how to prepare the phrase
38
+ MadderLib::Spec::Phrase.add_prepare { hits << :phrase_bare }
39
+ MadderLib::Spec::Phrase.add_prepare {|context| hits << :phrase_context }
40
+
41
+ # just confirming ...
42
+ MadderLib::Spec::Phrase.new.methods.include?('instructions').should be_true
43
+
44
+ # add 2 instructions
45
+ instance = MadderLib::Spec::Phrase.new
46
+ instance.instructions << MadderLib::Spec::Instruction.new
47
+ instance.instructions << MadderLib::Spec::Instruction.new
48
+ instance.instructions.should have(2).instructions
49
+
50
+ instance.prepare(MadderLib::Context::EMPTY)
51
+
52
+ hits.should eql([
53
+ :phrase_bare, :phrase_context,
54
+ :instruction_bare, :instruction_context,
55
+ :instruction_bare, :instruction_context,
56
+ ])
57
+ end
58
+
59
+ it "accumulates test preprations" do
60
+ context = MadderLib::Context::EMPTY
61
+ hits = []
62
+ barely, contextual = true, true
63
+
64
+ # how to test each instruction
65
+ MadderLib::Spec::Instruction.add_test do |instance|
66
+ hits << :instruction_bare
67
+ barely
68
+ end
69
+ MadderLib::Spec::Instruction.add_test do |instance, context|
70
+ hits << :instruction_context
71
+ contextual
72
+ end
73
+
74
+ # all clear
75
+ hits.clear
76
+ instance = MadderLib::Spec::Instruction.new
77
+ instance.test(context).should be_true
78
+ hits.should eql([:instruction_bare, :instruction_context])
79
+
80
+ # bare terminate
81
+ hits.clear
82
+ barely = false
83
+ instance = MadderLib::Spec::Instruction.new
84
+ instance.test(context).should be_false
85
+ hits.should eql([:instruction_bare])
86
+
87
+ # contextual terminate
88
+ hits.clear
89
+ barely, contextual = true, false
90
+ instance = MadderLib::Spec::Instruction.new
91
+ instance.test(context).should be_false
92
+ hits.should eql([:instruction_bare, :instruction_context])
93
+ end
94
+ end
@@ -0,0 +1,88 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+
4
+
5
+ describe MadderLib::Conditional::Repeat do
6
+
7
+ it "no repeat = once" do
8
+ builder = madderlib do
9
+ say(:one)
10
+ say(:two)
11
+ end
12
+
13
+ builder.words.should eql(%w{ one two })
14
+ end
15
+
16
+ it "supports simple repeating" do
17
+ builder = madderlib do
18
+ say(:once)
19
+ say(:twice).repeat(2)
20
+ say(:thrice).times(3)
21
+ end
22
+
23
+ builder.words.should eql(%w{ once twice twice thrice thrice thrice })
24
+ end
25
+
26
+ it "supports a 0-repeat, which blocks the instruction" do
27
+ builder = madderlib do
28
+ say(:one)
29
+ say(:two).times(0)
30
+ end
31
+
32
+ builder.words.should eql(%w{ one })
33
+ end
34
+
35
+ it "supports Ranged repeating" do
36
+ builder = madderlib do
37
+ say(:once)
38
+ say(:few).repeat(Range.new(3, 5))
39
+ say(:couple).times(3, 5)
40
+ end
41
+
42
+ pound_on do
43
+ words = builder.words
44
+ words.shift.should eql('once')
45
+
46
+ repeat = []
47
+ repeat << words.shift while words.first == 'few'
48
+ repeat.size.should be_close(4, 1.1)
49
+
50
+ repeat = []
51
+ repeat << words.shift while words.first == 'couple'
52
+ repeat.size.should be_close(4, 1.1)
53
+ end
54
+ end
55
+
56
+
57
+
58
+ it "supports arbitrary repeating, simple boolean tests" do
59
+ men = [:fred, :barney]
60
+ women = [:wilma, :betty]
61
+
62
+ builder = madderlib :repeat_exhausting do
63
+ say(:intro)
64
+ a(:man).says { men.shift }.repeat { ! men.empty? }
65
+ a(:woman).says { women.shift }.while { ! women.empty? }
66
+ end
67
+
68
+ builder.words.should eql(%w{ intro fred barney wilma betty })
69
+
70
+ # the next pass should be exhausted
71
+ builder.words.should eql(%w{ intro })
72
+ end
73
+
74
+
75
+
76
+ it "stops repeating after getting a nil value" do
77
+ values = [ :a, :b, nil, :c, :d, '', :e, :f ]
78
+ builder = madderlib :repeat_exhausting do
79
+ say { values.shift }.repeat { ! values.empty? }
80
+ end
81
+
82
+ builder.words.should eql(%w{ a b })
83
+ builder.words.should eql(%w{ c d })
84
+ builder.words.should eql(%w{ e f })
85
+ builder.words.should eql([])
86
+ end
87
+
88
+ end
data/spec/doc_spec.rb ADDED
@@ -0,0 +1,550 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+
4
+
5
+ describe MadderLib, "tests from the documentation" do
6
+
7
+ it "Builder.initialize" do
8
+ builder = MadderLib::Builder.new
9
+ builder.id.should be_nil
10
+ builder.should have(0).phrases
11
+ #
12
+ builder = MadderLib::Builder.new :id
13
+ builder.id.should equal(:id)
14
+ builder.should have(0).phrases
15
+ #
16
+ builder = MadderLib::Builder.new do
17
+ say 'no id'
18
+ end
19
+ builder.id.should be_nil
20
+ builder.sentence.should eql('no id')
21
+ #
22
+ builder = MadderLib::Builder.new :id do
23
+ say {|context| context.builder.id }
24
+ end
25
+ builder.sentence.should eql('id')
26
+ end
27
+
28
+ it "Builder.append" do
29
+ builder = MadderLib::Builder.new { say 'construct' }
30
+ builder.append { say 'extended' }
31
+ builder.extend { say 'appending' }
32
+ builder.words.should eql(%w{ construct extended appending })
33
+ end
34
+
35
+ it "Builder.clone" do
36
+ original = MadderLib::Builder.new do
37
+ meta[:meta] = :original
38
+ say 'original'
39
+ and_then(:shared).say('initial').likely(1)
40
+ end
41
+ #
42
+ cloned = original.clone
43
+ cloned[:meta] = :cloned
44
+ cloned.extend { say 'cloned' }
45
+ #
46
+ done = :original
47
+ cloned.setup { done = :cloned }
48
+ #
49
+ shared = original.phrases.find {|phrase| phrase.id == :shared }
50
+ shared.instructions.first.words << 'added'
51
+ #
52
+ original[:meta].should equal(:original)
53
+ original.sentence.should eql('original initial added')
54
+ done.should equal(:original)
55
+ #
56
+ cloned[:meta].should equal(:cloned)
57
+ cloned.sentence.should eql('original initial added cloned')
58
+ done.should equal(:cloned)
59
+ end
60
+
61
+ it "Builder.setup" do
62
+ builder = MadderLib::Builder.new do
63
+ say {|context| context[:setup] }
64
+ end
65
+ builder.setup {|context| context[:setup] << 2 }
66
+ builder.setup {|context| context[:setup] << 3 }
67
+ builder.setup(:first) {|context| context[:setup] = [1] }
68
+ #
69
+ builder.sentence.should eql('1 2 3')
70
+ end
71
+
72
+ it "Builder.teardown" do
73
+ builder = MadderLib::Builder.new do
74
+ say 'teardown'
75
+ end
76
+ markers = []
77
+ builder.teardown {|context| markers << 2 }
78
+ builder.teardown {|context| markers << 3 }
79
+ builder.teardown(:first) {|context| markers = [1] }
80
+ #
81
+ builder.sentence.should eql('teardown')
82
+ markers.should eql([1, 2, 3])
83
+ end
84
+
85
+ it "Builder.meta accessors" do
86
+ builder = MadderLib::Builder.new do
87
+ meta[:key] = :value
88
+ end
89
+ builder[:key].should equal(:value)
90
+ end
91
+
92
+ it "Builder.phrase" do
93
+ builder = MadderLib::Builder.new do
94
+ say 'yes'
95
+ phrase.if {|context| context.builder[:activated] == true }
96
+ it.repeat(3)
97
+ end
98
+ #
99
+ builder.should have(1).phrases
100
+ builder.phrase.should have(1).instructions
101
+ #
102
+ builder.should have(0).words
103
+ #
104
+ builder[:activated] = true
105
+ builder.sentence.should eql('yes yes yes')
106
+ end
107
+
108
+ it "Builder.and_then" do
109
+ builder = MadderLib::Builder.new do
110
+ say 'first'
111
+ and_then.say 'and_then'
112
+ also.say 'also'
113
+ end
114
+ builder.and.say 'and'
115
+ builder.then.say 'then'
116
+
117
+ builder.sentence.should eql('first and_then also and then')
118
+ end
119
+
120
+ it "Builder.an" do
121
+ builder = MadderLib::Builder.new do
122
+ say 'first'
123
+ a(:second).says 'second'
124
+ an(:other).says 'other'
125
+ end
126
+
127
+ builder.sentence.should eql('first second other')
128
+ end
129
+
130
+ it "Builder.first" do
131
+ builder = MadderLib::Builder.new do
132
+ say 'something'
133
+ first.say 'say'
134
+ end
135
+ builder.sentence.should eql('say something')
136
+
137
+ builder.first.say 'first'
138
+ builder.sentence.should eql('first say something')
139
+ end
140
+
141
+ it "Builder.last" do
142
+ builder = MadderLib::Builder.new do
143
+ last.say 'said'
144
+ say 'something'
145
+ end
146
+ builder.sentence.should eql('something said')
147
+
148
+ builder.last.say 'last'
149
+ builder.sentence.should eql('something said last')
150
+ end
151
+
152
+ it "Builder.anywhere" do
153
+ builder = MadderLib::Builder.new do
154
+ say 'top'
155
+ say 'here'
156
+ say 'there'
157
+ say 'bottom'
158
+ end
159
+ builder.anywhere.say 'anywhere'
160
+
161
+ words = builder.words
162
+ words.should have(5).words
163
+ words.find_all {|word| word == 'anywhere'}.should have(1).word
164
+
165
+ builder.it.recurs(2)
166
+
167
+ words = builder.words
168
+ words.should have(6).words
169
+ words.find_all {|word| word == 'anywhere'}.should have(2).word
170
+ end
171
+
172
+ it "Builder.before" do
173
+ builder = MadderLib::Builder.new do
174
+ an(:always).says 'always'
175
+ a(:sometimes).says('sometimes').if {|context| context.builder[:sometimes] == true }
176
+ before(:always).say 'before-always'
177
+ before(:sometimes, :depends).say 'before-sometimes'
178
+ before(:depends).say 'depends'
179
+ end
180
+
181
+ builder.sentence.should eql('before-always always')
182
+
183
+ builder[:sometimes] = true
184
+
185
+ builder.sentence.should eql('before-always always depends before-sometimes sometimes')
186
+ end
187
+
188
+ it "Builder.after" do
189
+ builder = MadderLib::Builder.new do
190
+ an(:always).says 'always'
191
+ a(:sometimes).says('sometimes').if {|context| context.builder[:sometimes] == true }
192
+ after(:always).say 'after-always'
193
+ after(:sometimes, :depends).say 'after-sometimes'
194
+ after(:depends).say 'depends'
195
+ end
196
+
197
+ builder.sentence.should eql('always after-always')
198
+
199
+ builder[:sometimes] = true
200
+
201
+ builder.sentence.should eql('always after-always sometimes after-sometimes depends')
202
+ end
203
+
204
+ it "Builder.say" do
205
+ builder = MadderLib::Builder.new do
206
+ says 'word'
207
+ say :symbol
208
+ say { 'lambda' }
209
+ end
210
+ builder.should have(3).phrases
211
+ builder.sentence.should eql('word symbol lambda')
212
+ end
213
+
214
+ it "Builder.alternately" do
215
+ builder = MadderLib::Builder.new do
216
+ says 'word'
217
+ alternately.says :symbol
218
+ end
219
+ builder.or.say { 'lambda' }
220
+
221
+ builder.should have(1).phrases
222
+ builder.phrase.should have(3).instructions
223
+ %w{ word symbol lambda}.include?(builder.sentence).should be_true
224
+ end
225
+
226
+ it "Build.words" do
227
+ builder = MadderLib::Builder.new do
228
+ says 'word'
229
+ say :symbol, [:with, :hierarchy]
230
+ say { 'lambda' }
231
+ end
232
+ builder.words.should eql(%w{ word symbol with hierarchy lambda })
233
+ end
234
+
235
+ it "Build.sentence" do
236
+ builder = MadderLib::Builder.new do
237
+ says 'word'
238
+ say :symbol, [:with, :hierarchy]
239
+ say { 'lambda' }
240
+ end
241
+ builder.sentence.should eql('word symbol with hierarchy lambda')
242
+ end
243
+
244
+
245
+
246
+ it "Context.state" do
247
+ context = MadderLib::Context.new
248
+ state = context.state(:state)
249
+ state.should_not be_nil
250
+
251
+ state[:key] = :value
252
+ context.state(:state)[:key].should equal(:value)
253
+ end
254
+
255
+ it "Context.data accessors" do
256
+ context = MadderLib::Context.new
257
+ context.data[:key] = :value
258
+
259
+ context[:key].should equal(:value)
260
+ end
261
+
262
+ it "Context::EMPTY" do
263
+ MadderLib::Context::EMPTY.frozen?.should be_true
264
+
265
+ lambda { MadderLib::Context::EMPTY.state(:immutable) }.should raise_error TypeError
266
+ lambda { MadderLib::Context::EMPTY[:immutable] = true }.should raise_error TypeError
267
+ end
268
+
269
+
270
+
271
+ it "KernelMethods.madderlib" do
272
+ builder = madderlib do
273
+ say 'no id'
274
+ end
275
+ madderlib_grammar.builders.include?(builder).should be_true
276
+ madderlib_grammar.builder_map.values.include?(builder).should_not be_true
277
+
278
+ builder = madderlib :id do
279
+ say 'has id'
280
+ end
281
+ madderlib_grammar.builders.include?(builder).should be_true
282
+ madderlib_grammar.builder_map.values.include?(builder).should be_true
283
+ end
284
+
285
+ it "KernelMethods.madderlib" do
286
+ builder = madderlib do
287
+ say 'no id'
288
+ end
289
+ madderlib_grammar.builders.include?(builder).should be_true
290
+ madderlib_grammar.builder_map.values.include?(builder).should_not be_true
291
+
292
+ builder = madderlib :id do
293
+ say 'has id'
294
+ end
295
+ madderlib_grammar.builders.include?(builder).should be_true
296
+ madderlib_grammar.builder_map.values.include?(builder).should be_true
297
+ end
298
+
299
+
300
+
301
+ it "Grammar.new_instance" do
302
+ current = MadderLib::Grammar.new_instance
303
+ current.should have(0).builders
304
+ current.should equal(MadderLib::Grammar.get_instance)
305
+
306
+ one = madderlib { say 'one' }
307
+ current.should have(1).builders
308
+ current.builders.include?(one).should be_true
309
+
310
+ fresh = MadderLib::Grammar.new_instance
311
+ fresh.should equal(MadderLib::Grammar.get_instance)
312
+
313
+ two = madderlib { say 'two' }
314
+ fresh.should have(1).builders
315
+ fresh.builders.include?(two).should be_true
316
+
317
+ current.should_not equal(MadderLib::Grammar.get_instance)
318
+ current.builders.include?(two).should_not be_true
319
+ end
320
+
321
+ it "Grammar.add" do
322
+ grammar = MadderLib::Grammar.new_instance
323
+
324
+ builder = madderlib { say 'exists' }
325
+ x = grammar.add(builder)
326
+ x.should equal(builder)
327
+ grammar.should have(1).builders
328
+ grammar.builder_map.should have(0).keys
329
+
330
+ builder = grammar.add { say 'no id' }
331
+ grammar.should have(2).builders
332
+ grammar.builder_map.should have(0).keys
333
+ builder.sentence.should eql('no id')
334
+
335
+ builder = grammar << :id
336
+ grammar.should have(3).builders
337
+ grammar.builder_map.values.include?(builder).should be_true
338
+ builder.sentence.should eql('')
339
+ end
340
+
341
+ it "Grammar.builder_map accessors" do
342
+ grammar = MadderLib::Grammar.new_instance
343
+
344
+ builder = grammar.add(:id) { say 'has id' }
345
+ grammar[:id].should equal(builder)
346
+ end
347
+
348
+
349
+
350
+ it "Instruction.speak" do
351
+ builder = madderlib do
352
+ say nil
353
+ say ''
354
+ end
355
+ builder.words.should eql([])
356
+
357
+ builder = madderlib do
358
+ say 'one'
359
+ say :two
360
+ say 3
361
+ end
362
+ builder.words.should eql(%w{ one two 3 })
363
+
364
+ builder = madderlib do
365
+ say []
366
+ say [ nil, 'one' ]
367
+ say [ :two, [ '', 3 ]]
368
+ end
369
+ builder.words.should eql(%w{ one two 3 })
370
+
371
+ builder = madderlib do
372
+ say madderlib { say 'one' }
373
+ say madderlib {
374
+ say madderlib { say :two }
375
+ say madderlib { say 3 }
376
+ }
377
+ end
378
+ builder.words.should eql(%w{ one two 3 })
379
+
380
+ words = [ 'one', lambda { :two }, madderlib { say 3 } ]
381
+ builder = madderlib do
382
+ say { words.shift }.repeat { ! words.empty? }
383
+ end
384
+ builder.words.should eql(%w{ one two 3 })
385
+ end
386
+
387
+
388
+
389
+ it "Phrase.alternately" do
390
+ builder = madderlib do
391
+ say('barnard').or.say('bryn mawr')
392
+ alternately(2).say('mount holyoke').alternately(2).say('radcliffe')
393
+ it.alternately(4).says('smith').or(4).says('vassar')
394
+ end
395
+ builder.phrase.says('wellesley').or(5).nothing
396
+
397
+ usage = {}
398
+ 200.times do
399
+ key = builder.sentence
400
+ usage[key] = (usage[key] || 0) + 1
401
+ end
402
+
403
+ # if proportions were accurately reproducible:
404
+ # ['barnard', 'bryn mawr', 'wellesley'].each {|name| usage[name].should eql(10) }
405
+ # ['mount holyoke', 'radcliffe'].each {|name| usage[name].should eql(20) }
406
+ # ['smith', 'vassar'].each {|name| usage[name].should eql(40) }
407
+ # [''].each {|name| usage[name].should eql(50) }
408
+ end
409
+
410
+
411
+
412
+ it "AnywherePhrase.before" do
413
+ flag = true
414
+ builder = madderlib do
415
+ say 'top'
416
+ also(:limit).say('middle').if { flag }
417
+ say 'bottom'
418
+
419
+ anywhere.say('hello').before(:limit)
420
+ end
421
+
422
+ 10.times do
423
+ words = builder.words
424
+ words.index('hello').should eql(1)
425
+ end
426
+
427
+ flag = false
428
+ 10.times do
429
+ words = builder.words
430
+ (words.index('hello') < 2).should be_true
431
+ end
432
+ end
433
+
434
+ it "AnywherePhrase.before" do
435
+ flag = true
436
+ builder = madderlib do
437
+ say 'top'
438
+ also(:limit).say('middle').if { flag }
439
+ say 'bottom'
440
+
441
+ anywhere.say('hello').after(:limit)
442
+ end
443
+
444
+ 10.times do
445
+ words = builder.words
446
+ words.index('hello').should eql(2)
447
+ end
448
+
449
+ flag = false
450
+ 10.times do
451
+ words = builder.words
452
+ (words.index('hello') > 0).should be_true
453
+ end
454
+ end
455
+
456
+ it "AnywherePhrase.between" do
457
+ builder = madderlib do
458
+ say 'top'
459
+ also(:upper).say('upper')
460
+ also(:lower).say('lower')
461
+ say 'bottom'
462
+
463
+ anywhere.say('hello').between(:upper, :lower)
464
+ end
465
+
466
+ 10.times do
467
+ words = builder.words
468
+ words.index('hello').should eql(2)
469
+ end
470
+ end
471
+
472
+
473
+
474
+
475
+ it "Instruction.assuming" do
476
+ switch = false
477
+ builder = madderlib do
478
+ an(:on).says('on').assuming { switch }
479
+ an(:off).says('off').if { ! switch }
480
+ say('bright').if :on
481
+ say('dark').if :off
482
+ end
483
+
484
+ builder.sentence.should eql('off dark')
485
+ switch = true
486
+ builder.sentence.should eql('on bright')
487
+ end
488
+
489
+ it "Instruction.forbidding" do
490
+ switch = false
491
+ builder = madderlib do
492
+ an(:on).says('on').forbidding { ! switch }
493
+ an(:off).says('off').unless { switch }
494
+ say('bright').unless :off
495
+ say('dark').unless :on
496
+ end
497
+
498
+ builder.sentence.should eql('off dark')
499
+ switch = true
500
+ builder.sentence.should eql('on bright')
501
+ end
502
+
503
+ it "Instruction.likely" do
504
+ builder = madderlib do
505
+ say('parsley').likely(4)
506
+ alternately(3).say('sage')
507
+ alternately.say('rosemary').weighted(2).or.say('thyme')
508
+ end
509
+
510
+ usage = {}
511
+ 60.times do
512
+ key = builder.sentence
513
+ usage[key] = (usage[key] || 0) + 1
514
+ end
515
+
516
+ # if proportions were accurately reproducible:
517
+ # usage['parsley'].should eql(20)
518
+ # usage['sage'].should eql(15)
519
+ # usage['rosemary'].should eql(10)
520
+ # usage['thyme'].should eql(5)
521
+ end
522
+
523
+ it "Phrase.recurs" do
524
+ builder = madderlib do
525
+ say(:start)
526
+ say(:end)
527
+ anytime.recurring(2).say(:any)
528
+ anytime.recurring {|count| count < 2 }.say(:also)
529
+ end
530
+
531
+ words = builder.words
532
+ words.find_all {|word| word == 'any' }.should have(2).items
533
+ words.find_all {|word| word == 'also' }.should have(2).items
534
+ end
535
+
536
+ it "Phrase.repeat" do
537
+ builder = madderlib do
538
+ say(:twice).times(2)
539
+ say(:couple).repeats(1, 2)
540
+ say(:thrice).while {|count| count < 3 }
541
+ end
542
+
543
+ words = builder.words
544
+ words.find_all {|word| word == 'twice' }.should have(2).items
545
+ words.find_all {|word| word == 'thrice' }.should have(3).items
546
+ count = words.find_all {|word| word == 'couple' }.size
547
+ (count >= 1 && count <= 2).should be_true
548
+ end
549
+
550
+ end