madderlib 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,130 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+
4
+
5
+ describe MadderLib::Conditional::Allowed do
6
+
7
+ it "supports single assumption" do
8
+ one, two = nil, nil
9
+
10
+ builder = madderlib do
11
+ say(:one).assuming {|test| one }
12
+ say(:two).presuming {|test| two }
13
+ say(:three).if { false }
14
+ end
15
+
16
+ [nil, true].each do |t|
17
+ two = t
18
+ [false, true].each do |o|
19
+ one = o
20
+
21
+ words = []
22
+ words << 'one' if one
23
+ words << 'two' if two
24
+
25
+ builder.words.should eql(words)
26
+ end
27
+ end
28
+ end
29
+
30
+ it "supports multiple assumptions" do
31
+ one, two = nil, nil
32
+
33
+ builder = madderlib do
34
+ say(:something).if {|test| one }.assuming {|test| two }
35
+ end
36
+
37
+ [nil, true].each do |t|
38
+ two = t
39
+ [false, true].each do |o|
40
+ one = o
41
+
42
+ words = []
43
+ words << 'something' if one && two
44
+
45
+ builder.words.should eql(words)
46
+ end
47
+ end
48
+ end
49
+
50
+ it "supports id-based assumption" do
51
+ # will say first = yes
52
+ builder = madderlib do
53
+ say(:yes).assuming(:spoken)
54
+ first(:spoken).say(:said)
55
+ end
56
+
57
+ builder.words.should eql(%w{ said yes })
58
+
59
+ # will say last = no
60
+ builder = madderlib do
61
+ say(:yes).if(:spoken)
62
+ last(:spoken).say(:said)
63
+ end
64
+
65
+ builder.words.should eql(%w{ said })
66
+ end
67
+
68
+
69
+
70
+ it "supports single forbiddance" do
71
+ one, two = nil, nil
72
+
73
+ builder = madderlib do
74
+ say(:one).forbidding {|test| one }
75
+ say(:two).forbidding {|test| two }
76
+ say(:three).unless { true }
77
+ end
78
+
79
+ [nil, true].each do |t|
80
+ two = t
81
+ [false, true].each do |o|
82
+ one = o
83
+
84
+ words = []
85
+ words << 'one' unless one
86
+ words << 'two' unless two
87
+
88
+ builder.words.should eql(words)
89
+ end
90
+ end
91
+ end
92
+
93
+ it "supports multiple forbiddance" do
94
+ one, two = nil, nil
95
+
96
+ builder = madderlib do
97
+ say(:something).unless {|test| one }.forbidding {|test| two }
98
+ end
99
+
100
+ [nil, true].each do |t|
101
+ two = t
102
+ [false, true].each do |o|
103
+ one = o
104
+
105
+ words = []
106
+ words << 'something' unless one || two
107
+
108
+ builder.words.should eql(words)
109
+ end
110
+ end
111
+ end
112
+
113
+ it "supports id-based forbiddance" do
114
+ # will say first = no
115
+ builder = madderlib do
116
+ say(:yes).forbidding(:spoken)
117
+ first(:spoken).say(:said)
118
+ end
119
+
120
+ builder.words.should eql(%w{ said })
121
+
122
+ # will say last = yes
123
+ builder = madderlib do
124
+ say(:yes).unless(:spoken)
125
+ last(:spoken).say(:said)
126
+ end
127
+
128
+ builder.words.should eql(%w{ yes said })
129
+ end
130
+ end
@@ -0,0 +1,131 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+
4
+
5
+ describe MadderLib::Conditional::Helper do
6
+
7
+ it "a TestBlock fails with invalid arguments" do
8
+ # arg or block, not both
9
+ lambda {
10
+ MadderLib::Conditional::Helper::TestBlock.new(1) {|count| count < 1 }
11
+ }.should raise_error MadderLib::Error
12
+
13
+ # unsupported
14
+ lambda {
15
+ MadderLib::Conditional::Helper::TestBlock.new(:symbol)
16
+ }.should raise_error MadderLib::Error
17
+ end
18
+
19
+ it "a TestBlock with a block" do
20
+ tester = MadderLib::Conditional::Helper::TestBlock.new {|count| count < 1 }
21
+
22
+ built = tester.block
23
+
24
+ built.call(0).should be_true
25
+
26
+ tester.invoke(0, :ignore).should be_true
27
+ tester.invoke(1, :ignore).should_not be_true
28
+ end
29
+
30
+ it "a TestBlock with a fixed limit" do
31
+ tester = MadderLib::Conditional::Helper::TestBlock.new(2)
32
+
33
+ built = tester.block
34
+
35
+ built.call(1).should be_true
36
+ built.call(2).should_not be_true
37
+
38
+ tester.invoke(1, :ignore).should be_true
39
+ tester.invoke(2, :ignore).should_not be_true
40
+ end
41
+
42
+ it "a TestBlock with a Range" do
43
+ tester = MadderLib::Conditional::Helper::TestBlock.new(2, 4)
44
+
45
+ pound_on do
46
+ built = tester.block
47
+
48
+ built.call(1).should be_true
49
+ built.call(4).should_not be_true
50
+ end
51
+
52
+ tester.invoke(1, :ignore).should be_true
53
+ tester.invoke(4, :ignore).should_not be_true
54
+
55
+ tester = MadderLib::Conditional::Helper::TestBlock.new(Range.new(3, 5))
56
+
57
+ pound_on do
58
+ built = tester.block
59
+
60
+ built.call(2).should be_true
61
+ built.call(5).should_not be_true
62
+ end
63
+
64
+ tester.invoke(2, :ignore).should be_true
65
+ tester.invoke(5, :ignore).should_not be_true
66
+ end
67
+
68
+
69
+
70
+ it "a TestBlock in minutes" do
71
+ tester = MadderLib::Conditional::Helper::TestBlock.new(2, :minute)
72
+
73
+ pound_on do
74
+ built = tester.block
75
+
76
+ # 0, 1 ...
77
+ built.call(1).should be_true
78
+ built.call(2).should_not be_true
79
+ end
80
+
81
+ tester = MadderLib::Conditional::Helper::TestBlock.new(2, 4, :minute)
82
+
83
+ pound_on do
84
+ built = tester.block
85
+
86
+ # 0, 1 ...
87
+ built.call(1).should be_true
88
+ built.call(4).should_not be_true
89
+ end
90
+ end
91
+
92
+
93
+
94
+ it "can convert a TestBlock criterion into an integer" do
95
+ context = MadderLib::Context.new
96
+
97
+ tester = MadderLib::Conditional::Helper::TestBlock.new(1)
98
+ tester.to_i(context).should eql(1)
99
+
100
+ tester = MadderLib::Conditional::Helper::TestBlock.new(1, 2)
101
+ tester.to_i(context).should eql(2)
102
+
103
+ tester = MadderLib::Conditional::Helper::TestBlock.new(Range.new(3, 4))
104
+ tester.to_i(context).should eql(4)
105
+
106
+ tester = MadderLib::Conditional::Helper::TestBlock.new { 2 + 2 }
107
+ tester.to_i(context).should eql(4)
108
+
109
+ context.data[:value] = 5
110
+ tester = MadderLib::Conditional::Helper::TestBlock.new {|context| context.data[:value] }
111
+ tester.to_i(context).should eql(5)
112
+ end
113
+
114
+ it "invokes the contained block, or one explicitly provided" do
115
+ # arity < 1
116
+ tester = MadderLib::Conditional::Helper::TestBlock.new { 2 }
117
+ tester.invoke.should eql(2)
118
+ tester.invoke { 0 }.should eql(0)
119
+
120
+ # arity = 1
121
+ tester = MadderLib::Conditional::Helper::TestBlock.new {|value| value + 1 }
122
+ tester.invoke(1).should eql(2)
123
+ tester.invoke(1) {|value| value - 1 }.should eql(0)
124
+
125
+ # arity = 2, discard ignored
126
+ tester = MadderLib::Conditional::Helper::TestBlock.new {|a, b| a + b }
127
+ tester.invoke(1, 1, :ignored).should eql(2)
128
+ tester.invoke(1, 1, :ignored) {|a, b| a - b }.should eql(0)
129
+ end
130
+
131
+ end
@@ -0,0 +1,138 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+
4
+
5
+ describe MadderLib::Conditional::Likely do
6
+ def distribution(words, map=nil)
7
+ map ||= {}
8
+ words.each do |word|
9
+ map[word] = (map[word] || 0) + 1
10
+ end
11
+
12
+ return map
13
+ end
14
+
15
+
16
+
17
+ it "by default has a 50/50 split" do
18
+ builder = madderlib do
19
+ say 'buffalo'
20
+ it.alternately.says 'fish'
21
+ end
22
+
23
+ refs = %w{ buffalo fish }
24
+ map = nil
25
+ pound_on do
26
+ map = distribution(builder.words, map)
27
+ end
28
+
29
+ # now that'we re done
30
+ # should have hit each once
31
+ # and should have a reasonable distribution (though who can tell)
32
+ map.should have(refs.size).items
33
+ map.keys.each {|key| refs.include?(key).should be_true }
34
+
35
+ map['buffalo'].should be_close(50, 20)
36
+ map['fish'].should be_close(50, 20)
37
+ end
38
+
39
+ it "supports nothing" do
40
+ builder = madderlib do
41
+ say 'something nice'
42
+ alternately.nothing
43
+ end
44
+
45
+ map = nil
46
+ pound_on(100) do
47
+ map = distribution(builder.words, map)
48
+ end
49
+
50
+ # now that'we re done
51
+ # should have hit each once
52
+ # and should have a reasonable distribution (though who can tell)
53
+ map.should have(1).items
54
+ map['something nice'].should_not be_nil
55
+
56
+ map['something nice'].should be_close(50, 20)
57
+ end
58
+
59
+
60
+
61
+ it "getting a 2/1 split" do
62
+ # testing out various alias formats, etc
63
+ builder = madderlib :split_2_1 do
64
+ say 'buffalo'
65
+ it.alternately.says('fish').likely(2)
66
+ end
67
+
68
+ refs = %w{ buffalo fish }
69
+ map = nil
70
+ pound_on do
71
+ words = builder.words
72
+ map = distribution(words, map)
73
+ end
74
+
75
+ # now that'we re done
76
+ # should have hit each once
77
+ # and should have a reasonable distribution (though who can tell)
78
+ map.should have(refs.size).items
79
+ map.keys.each {|key| refs.include?(key).should be_true }
80
+
81
+ map['buffalo'].should be_close(33, 20)
82
+ map['fish'].should be_close(66, 20)
83
+ end
84
+
85
+ it "getting a 3/1 split" do
86
+ # testing out various alias formats, etcnext
87
+ builder = madderlib :split_3_1 do
88
+ say 'buffalo'
89
+ it.alternately.says('fish').weighing { 2 + 1 }
90
+ end
91
+
92
+ refs = %w{ buffalo fish }
93
+ map = nil
94
+ pound_on do
95
+ words = builder.words
96
+ map = distribution(words, map)
97
+ end
98
+
99
+ # now that'we re done
100
+ # should have hit each once
101
+ # and should have a reasonable distribution (though who can tell)
102
+ map.should have(refs.size).items
103
+ map.keys.each {|key| refs.include?(key).should be_true }
104
+
105
+ map['buffalo'].should be_close(25, 20)
106
+ map['fish'].should be_close(75, 20)
107
+ end
108
+
109
+
110
+
111
+ it "getting a 3/2/1 split" do
112
+ # testing out transferrence from 'or' operation to likelihood
113
+ builder = madderlib :split_3_2_1 do
114
+ say 'faith'
115
+ alternately(2).says('hope')
116
+ # range is supported, but not recommended
117
+ it.or(0..3).says('charity')
118
+ end
119
+
120
+ refs = %w{ faith hope charity }
121
+ map = nil
122
+ pound_on 150 do
123
+ words = builder.words
124
+ map = distribution(words, map)
125
+ end
126
+
127
+ # now that'we re done
128
+ # should have hit each once
129
+ # and should have a reasonable distribution (though who can tell)
130
+ map.should have(refs.size).items
131
+ map.keys.each {|key| refs.include?(key).should be_true }
132
+
133
+ map['faith'].should be_close(25, 20)
134
+ map['hope'].should be_close(50, 20)
135
+ map['charity'].should be_close(75, 20)
136
+ end
137
+
138
+ end
@@ -0,0 +1,102 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+
4
+
5
+ describe MadderLib::Conditional::Recur do
6
+
7
+ it "only occurs once if not told otherwise" do
8
+ builder = madderlib do
9
+ # give it boundaries
10
+ say(:start)
11
+ anytime.say(:any)
12
+ say(:end)
13
+ end
14
+
15
+ builder.phrases.each do |phrase|
16
+ phrase.recurs?.should be_false if phrase.respond_to?(:recurs?)
17
+ end
18
+
19
+ builder.words.should eql(%w{ start any end })
20
+ end
21
+
22
+ it "supports simple recurrence" do
23
+ builder = madderlib do
24
+ # give it boundaries
25
+ say(:start)
26
+ anytime.recurring(2).say(:any)
27
+ say(:end)
28
+ end
29
+
30
+ builder.phrases.each do |phrase|
31
+ phrase.recurs?.should be_true if phrase.respond_to?(:recurs?)
32
+ end
33
+
34
+ builder.words.should eql(%w{ start any any end })
35
+ end
36
+
37
+ it "supports a 0-recurrence, which blocks the phrase" do
38
+ builder = madderlib do
39
+ say(:start)
40
+ anytime.recur(0).say(:never)
41
+ say(:end)
42
+ end
43
+
44
+ builder.words.should eql(%w{ start end })
45
+ end
46
+
47
+ it "supports Ranged recurrence" do
48
+ builder = madderlib do
49
+ # give it boundaries
50
+ say(:start)
51
+ anytime.recurring(Range.new(3, 5)).say(:few)
52
+ anytime.recurring(3, 5).say(:couple)
53
+ say(:end)
54
+ end
55
+
56
+ pound_on do
57
+ words = builder.words
58
+
59
+ words.shift.should eql('start')
60
+ words.pop.should eql('end')
61
+
62
+ count = 0
63
+ words.each {|word| count += 1 if word == 'few' }
64
+ count.should be_close(4, 1.1)
65
+
66
+ count = 0
67
+ words.each {|word| count += 1 if word == 'couple' }
68
+ count.should be_close(4, 1.1)
69
+ end
70
+ end
71
+
72
+ it "supports arbitrary recurrence" do
73
+ spices = [:parsley, :sage, :rosemary, :thyme]
74
+ shoulds = spices.clone
75
+
76
+ builder = madderlib :repeat_exhausting do
77
+ say(:start)
78
+ # if these don't exist, positioning isn't random
79
+ # it's just a visual confirmation from initial testing
80
+ say(:salt)
81
+ say(:pepper)
82
+ say(:flour)
83
+ say(:end)
84
+
85
+ anytime.recur { ! spices.empty? }.say { spices.shift }
86
+ end
87
+
88
+ words = builder.words
89
+ words.shift.should eql('start')
90
+ words.pop.should eql('end')
91
+
92
+ # and all of the others should be present
93
+ shoulds << :salt << :pepper << :flour
94
+ shoulds.each do |spice|
95
+ words.index(spice.to_s).should_not be_nil
96
+ end
97
+
98
+ # the next pass should be exhausted
99
+ builder.words.should eql(%w{ start salt pepper flour end })
100
+ end
101
+
102
+ end