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
data/spec/error_spec.rb
ADDED
@@ -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
|