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,33 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+
4
+
5
+ describe MadderLib::Error do
6
+ it "takes a simple message" do
7
+ e = nil
8
+ message = 'message'
9
+
10
+ lambda { raise MadderLib::Error, message }.should raise_error {|error| e = error }
11
+ e.message.should eql(message)
12
+ e.cause.should be_nil
13
+ end
14
+
15
+ it "takes a cause, sans message" do
16
+ e = nil
17
+ cause = Exception.new('cause')
18
+
19
+ lambda { raise MadderLib::Error.new(cause) }.should raise_error {|error| e = error }
20
+ e.message.should eql('cause')
21
+ e.cause.should equal(cause)
22
+ end
23
+
24
+ it "takes both a cause and message" do
25
+ e = nil
26
+ message = 'message'
27
+ cause = Exception.new('cause')
28
+
29
+ lambda { raise MadderLib::Error.new(message, cause) }.should raise_error {|error| e = error }
30
+ e.message.should eql(message)
31
+ e.cause.should equal(cause)
32
+ end
33
+ end
@@ -0,0 +1,151 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+
4
+
5
+ describe MadderLib::KernelMethods, "simple examples" do
6
+
7
+ it "README.doc" do
8
+ puts madderlib {
9
+ say 'hello,'
10
+ say('welcome to').or.say("you're viewing")
11
+ say('the README.doc')
12
+ }.sentence
13
+ end
14
+
15
+
16
+
17
+ it "Snake 'n' Bacon" do
18
+ builder = madderlib do
19
+ say "i'm"
20
+
21
+ # build method #1
22
+ # empty un-said phrase
23
+ # then batch inject all options
24
+ # includes 'nothing' -- nil or ''
25
+ and_then
26
+ ['sometimes', 'always', nil].each {|word| alternately.say(word) }
27
+
28
+ # build method #2
29
+ # englicized
30
+ # alternates, some with weights, or nothing
31
+ say('quite').or(2).say('rather').or(2).say('so').or.nothing
32
+
33
+ # build method #3
34
+ # single start, then batch complete
35
+ # say one thing, add likely / weight if needed
36
+ # alternates, with weights if needed
37
+ say 'salty'
38
+ ['smoky', 'peppery'].each {|word| alternately(2).say(word) }
39
+ end
40
+ builder.validate
41
+
42
+ 5.times { puts builder.sentence }
43
+
44
+ # just to clarify what we've done
45
+ builder.should have(4).phrases
46
+ builder.phrases[1].should have(3).instructions
47
+ builder.phrases[2].should have(4).instructions
48
+ builder.phrases[3].should have(3).instructions
49
+
50
+ nothing_time = 0
51
+ nothing_degree = 0
52
+ pound_on do
53
+ words = builder.words
54
+
55
+ # guaranteed
56
+ words.include?("i'm").should be_true
57
+
58
+ match = words.find do |item|
59
+ %{ sometimes always }.include? item
60
+ end
61
+ nothing_time += 1 unless match
62
+
63
+ match = words.find do |item|
64
+ %{ quite rather so }.include? item
65
+ end
66
+ nothing_degree += 1 unless match
67
+
68
+ # something or other
69
+ match = words.find do |item|
70
+ %{ salty smoky peppery }.include? item
71
+ end
72
+ match.should_not be_nil
73
+ end
74
+
75
+ nothing_time.should be_close(33, 20)
76
+ nothing_degree.should be_close(25, 20)
77
+ end
78
+
79
+
80
+
81
+ it "The Conet Project" do
82
+ builder = madderlib do
83
+ meta[:audio] = [
84
+ 'http://www.archive.org/download/ird059/tcp_d1_06_the_lincolnshire_poacher_mi5_irdial.mp3',
85
+ 'http://www.archive.org/download/ird059/tcp_d3_02_iran_iraq_jamming_efficacy_testting_irdial.mp3',
86
+ ]
87
+
88
+ digits = lambda do |len|
89
+ s = rand(10 ** len).to_s
90
+ s = ('0' * (len - s.size)) + s
91
+ s
92
+ end
93
+
94
+ say 'Lincolnshire Poacher'
95
+ say { digits.call(5) }.repeat(10)
96
+
97
+ say('~').repeat(6)
98
+
99
+ 200.times do
100
+ say { s = digits.call(5); [s, s] }
101
+ end
102
+
103
+ say('~').repeat(6)
104
+
105
+ say 'Lincolnshire Poacher'
106
+ end
107
+ builder.validate
108
+
109
+ 5.times { puts builder.sentence }
110
+ end
111
+
112
+
113
+
114
+ it "time-based user greeting" do
115
+ user = Struct.new(:name)
116
+
117
+ builder = madderlib do
118
+ setup {|context| context[:hour] ||= Time.new.hour }
119
+ a(:morning).says('top of the morning,').if {|c| Range.new(8, 12).include?(c[:hour]) }
120
+ say('good afternoon,').if {|c| Range.new(12, 17).include?(c[:hour]) }
121
+ say("g'night").if {|c| Range.new(19, 24).include?(c[:hour]) }
122
+ say {|c| c[:user].name + '.' }
123
+ end
124
+ builder.validate
125
+
126
+ puts builder.sentence {|c| c[:user] = user.new('joe')}
127
+
128
+ puts builder.sentence {|c|
129
+ c[:user] = user.new('fred')
130
+ c[:hour] = 13
131
+ }
132
+
133
+ extended = builder.clone.extend { say('have a nice day!').if(:morning) }
134
+ puts extended.sentence {|c|
135
+ c[:user] = user.new('charlie')
136
+ c[:hour] = 8
137
+ }
138
+ end
139
+
140
+
141
+
142
+ it "combinatorial ranges" do
143
+ # useless, but at leasts tests composite operations
144
+ builder = madderlib do
145
+ say 'hello', 'and'
146
+ say('never').if { false }.or.say('likely').likely(2).or.say('repeat').repeat(3).or(99).nothing
147
+ end
148
+ builder.validate
149
+ end
150
+
151
+ end
@@ -0,0 +1,47 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+
4
+
5
+ describe Range do
6
+ it "still doesn't handle floats" do
7
+ range = Range.new(0, 1.9)
8
+ range.max.should eql(1)
9
+ end
10
+
11
+ it "still doesn't inverted orders" do
12
+ range = Range.new(6, 4)
13
+ range.min.should be_nil
14
+ range.max.should be_nil
15
+ end
16
+
17
+ it "provides a random value, with optional precision" do
18
+ # trial by fire
19
+ [Range.new(1, 3), Range.new(-1, 5)].each do |range|
20
+ span = [range.min, range.max]
21
+ # high precision, to increase remainder likelihood
22
+ [0, 5].each do |precision|
23
+ pound_on do
24
+ # exclusive
25
+ value = range.rand(precision)
26
+ (value.to_f == value.to_i.to_f).should equal(precision == 0)
27
+ value.should satisfy {|v| (v >= span.min) && (v < span.max) }
28
+
29
+ # inclusive
30
+ value = range.rand_inclusive(precision)
31
+ (value.to_f == value.to_i.to_f).should equal(precision == 0)
32
+ value.should satisfy {|v| (v >= span.min) && (v < (span.max + 1)) }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ describe Array do
40
+ it "has to_byte_s, which requires all integers" do
41
+ (lambda { [32, 32, '32'].to_byte_s }).should raise_error
42
+ end
43
+
44
+ it "has to_byte_s" do
45
+ [32, 32, 32].to_byte_s.should eql(' ' * 3)
46
+ end
47
+ end
@@ -0,0 +1,101 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+
4
+
5
+ describe MadderLib::Grammar do
6
+ before(:each) do
7
+ @class = MadderLib::Grammar
8
+
9
+ # close any pre-existing grammar
10
+ madderlib_grammar.close
11
+ end
12
+
13
+
14
+
15
+ it "get_instance returns a singleton" do
16
+ @class.get_instance.should equal(@class.get_instance)
17
+ end
18
+
19
+ it "retains a list of unique Builders, and a map of id'd Builders" do
20
+ grammar = @class.get_instance
21
+
22
+ grammar.should have(0).builders
23
+ grammar.builder_map.should have(0).builders
24
+
25
+ # un-id'd
26
+ builder = MadderLib::Builder.new
27
+ grammar << builder
28
+
29
+ grammar.should have(1).builders
30
+ grammar.builders.last.should equal(builder)
31
+ grammar.builder_map.should have(0).builders
32
+
33
+ # duplicate
34
+ grammar << builder
35
+
36
+ grammar.should have(1).builders
37
+ grammar.builder_map.should have(0).builders
38
+
39
+ # id'd
40
+ builder = MadderLib::Builder.new(:named)
41
+ grammar << builder
42
+
43
+ grammar.should have(2).builders
44
+ grammar.builders.last.should equal(builder)
45
+ grammar.builder_map.should have(1).builders
46
+ grammar.builder_map[builder.id].should equal(builder)
47
+ end
48
+
49
+
50
+
51
+ it "can be closed, even using the Kernel context" do
52
+ madderlib :one do
53
+ say :one
54
+ end
55
+ madderlib :two do
56
+ say :two
57
+ end
58
+
59
+ held = madderlib_grammar
60
+ held.close
61
+
62
+ held.builders.should have(2).builders
63
+ [:one, :two].each {|key| held.builder_map[key].should_not be_nil }
64
+
65
+ # more builders
66
+ madderlib :three do
67
+ say :three
68
+ end
69
+
70
+ open = madderlib_grammar
71
+ open.builders.should have(1).builder
72
+ [:three].each {|key| open.builder_map[key].should_not be_nil }
73
+
74
+ # does not impact the one we locked down
75
+ held.builders.should have(2).builders
76
+ [:one, :two].each {|key| held.builder_map[key].should_not be_nil }
77
+
78
+ # sorry, you're frozen
79
+ lambda { held.add :anything }.should raise_error TypeError
80
+ end
81
+
82
+ it "has many ways to add a Builder" do
83
+ grammar = @class.new
84
+
85
+ builder = grammar.add MadderLib::Builder.new :explicit
86
+ builder.should equal(grammar.builders.last)
87
+
88
+ builder.id.should equal(:explicit)
89
+ grammar.add(:implicit) { say('implicit') }
90
+ builder = grammar.builders.last
91
+ builder.id.should equal(:implicit)
92
+ builder.to_s.should eql('implicit')
93
+
94
+ builder = grammar.add { say('no-id') }
95
+ builder.should equal(grammar.builders.last)
96
+
97
+ builder.id.should be_nil
98
+ builder.to_s.should eql('no-id')
99
+ end
100
+
101
+ end
@@ -0,0 +1,133 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+
4
+
5
+ describe MadderLib::Instruction do
6
+
7
+ it "'say' ignores nils" do
8
+ phrase = nil
9
+ context = MadderLib::Context.new
10
+
11
+ # nil, nothing else
12
+ instruction = MadderLib::Instruction.new(phrase, nil)
13
+ instruction.speak(context).should eql([])
14
+
15
+ # some counter-balancing value
16
+ instruction = MadderLib::Instruction.new(phrase, nil, :value)
17
+ instruction.speak(context).should eql(%w{ value })
18
+
19
+ # late-evaluated nil, array test as well
20
+ instruction = MadderLib::Instruction.new(phrase, lambda { nil }, :proc)
21
+ instruction.speak(context).should eql(%w{ proc })
22
+ end
23
+
24
+ it "'say' ignores blanks" do
25
+ phrase = nil
26
+ context = MadderLib::Context.new
27
+
28
+ # blank, nothing else
29
+ instruction = MadderLib::Instruction.new(phrase, "")
30
+ instruction.speak(context).should eql([])
31
+
32
+ # some counter-balancing value
33
+ instruction = MadderLib::Instruction.new(phrase, :value, '')
34
+ instruction.speak(context).should eql(%w{ value })
35
+
36
+ # late-evaluated blank
37
+ instruction = MadderLib::Instruction.new(phrase, :proc, lambda { '' })
38
+ instruction.speak(context).should eql(%w{ proc })
39
+ end
40
+
41
+ it "flattens Arrays" do
42
+ phrase = nil
43
+ context = MadderLib::Context.new
44
+
45
+ instruction = MadderLib::Instruction.new(phrase, :a, [:b, :c])
46
+ instruction.speak(context).should eql(%w{ a b c })
47
+
48
+ instruction = MadderLib::Instruction.new(phrase, [:a, :b, :c])
49
+ instruction.speak(context).should eql(%w{ a b c })
50
+
51
+ instruction = MadderLib::Instruction.new(phrase, :a, [:b, '', [:c, nil, :d]])
52
+ instruction.speak(context).should eql(%w{ a b c d })
53
+
54
+ instruction = MadderLib::Instruction.new(phrase, :a, [1, nil, [lambda { :proc }, lambda { ''}, :d]])
55
+ instruction.speak(context).should eql(%w{ a 1 proc d })
56
+ end
57
+
58
+ it "handles sub-Builders" do
59
+ phrase = nil
60
+ context = MadderLib::Context.new
61
+
62
+ # a nice variety of challenges
63
+ string = madderlib { say 'string' }
64
+ one = madderlib { say 1 }
65
+ array = madderlib { say [:a, :b] }
66
+ symbol = madderlib { say :symbol }
67
+ proc = madderlib { say { :proc } }
68
+
69
+ # simple
70
+ instruction = MadderLib::Instruction.new(phrase, string, one, array, symbol, proc)
71
+ instruction.speak(context).should eql(%w{ string 1 a b symbol proc })
72
+
73
+ # crazy
74
+ # procs returning Builders, arrays of Builders, etc
75
+ instruction = MadderLib::Instruction.new(phrase, [ string, [ one , lambda { [ array, symbol, proc ] } ]])
76
+ instruction.speak(context).should eql(%w{ string 1 a b symbol proc })
77
+ end
78
+
79
+ it "handles Procs" do
80
+ # this is a pretty serious coverage case
81
+ # includes Proc-of-a-Proc, etc
82
+ words = [ 'one', :two, 3, lambda { :four }, madderlib { say 5 }, [ :six, lambda { 7 } ] ]
83
+ builder = madderlib do
84
+ say { words.shift }.repeat { ! words.empty? }
85
+ end
86
+ builder.words.should eql(%w{ one two 3 four 5 six 7 })
87
+ end
88
+
89
+
90
+
91
+ it "can convert Phrase results into words" do
92
+ #
93
+ # this test may be redundant to the above
94
+ # but it's not as thorough
95
+ #
96
+
97
+ context = MadderLib::Context.new
98
+
99
+ # simple values, and a Proc
100
+ words = ['one', :two, nil, lambda { 3 }].collect do |value|
101
+ MadderLib::Instruction.wordify(value, context)
102
+ end
103
+
104
+ words.should eql(['one', 'two', nil, '3'])
105
+
106
+ # arrays are stringified but retained
107
+ # flattening within array
108
+ # nil considerations are retained
109
+ words = [:a, [:b, [:c, nil]], lambda { [:d, :e] }].collect do |value|
110
+ MadderLib::Instruction.wordify(value, context)
111
+ end
112
+
113
+ words.should eql(['a', ['b', 'c', nil], ['d', 'e']])
114
+
115
+ builder = madderlib do
116
+ say 'one'
117
+ say :two
118
+ say { 3 }
119
+ # both ignored
120
+ say nil
121
+ say { nil }
122
+ # back to real data
123
+ say 'd', :e
124
+ say { ['f', :g] }
125
+ end
126
+ builder.words.should eql(%w{ one two 3 d e f g })
127
+
128
+ words = MadderLib::Instruction.wordify(builder, context)
129
+ words.should have(7).words
130
+ words.should eql(%w{ one two 3 d e f g })
131
+ end
132
+
133
+ end