adhearsion 2.0.0.rc4 → 2.0.0.rc5
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.md +4 -0
- data/adhearsion.gemspec +1 -1
- data/bin/ahn +0 -3
- data/lib/adhearsion.rb +7 -4
- data/lib/adhearsion/call.rb +1 -1
- data/lib/adhearsion/call_controller.rb +1 -0
- data/lib/adhearsion/call_controller/menu_dsl.rb +19 -0
- data/lib/adhearsion/call_controller/menu_dsl/calculated_match.rb +43 -0
- data/lib/adhearsion/call_controller/menu_dsl/calculated_match_collection.rb +45 -0
- data/lib/adhearsion/call_controller/menu_dsl/fixnum_match_calculator.rb +22 -0
- data/lib/adhearsion/call_controller/menu_dsl/match_calculator.rb +40 -0
- data/lib/adhearsion/call_controller/menu_dsl/menu.rb +203 -0
- data/lib/adhearsion/call_controller/menu_dsl/menu_builder.rb +84 -0
- data/lib/adhearsion/call_controller/menu_dsl/range_match_calculator.rb +60 -0
- data/lib/adhearsion/call_controller/menu_dsl/string_match_calculator.rb +25 -0
- data/lib/adhearsion/cli.rb +0 -1
- data/lib/adhearsion/router/route.rb +2 -2
- data/lib/adhearsion/version.rb +1 -1
- data/spec/adhearsion/call_controller/input_spec.rb +1 -1
- data/spec/adhearsion/call_controller/menu_dsl/calculated_match_collection_spec.rb +60 -0
- data/spec/adhearsion/call_controller/menu_dsl/calculated_match_spec.rb +61 -0
- data/spec/adhearsion/call_controller/menu_dsl/fixnum_match_calculator_spec.rb +37 -0
- data/spec/adhearsion/call_controller/menu_dsl/match_calculator_spec.rb +17 -0
- data/spec/adhearsion/call_controller/menu_dsl/menu_builder_spec.rb +151 -0
- data/spec/adhearsion/call_controller/menu_dsl/menu_spec.rb +373 -0
- data/spec/adhearsion/call_controller/menu_dsl/range_match_calculator_spec.rb +32 -0
- data/spec/adhearsion/call_controller/menu_dsl/string_match_calculator_spec.rb +40 -0
- metadata +91 -91
- data/lib/adhearsion/menu_dsl.rb +0 -17
- data/lib/adhearsion/menu_dsl/calculated_match.rb +0 -41
- data/lib/adhearsion/menu_dsl/calculated_match_collection.rb +0 -43
- data/lib/adhearsion/menu_dsl/fixnum_match_calculator.rb +0 -20
- data/lib/adhearsion/menu_dsl/match_calculator.rb +0 -38
- data/lib/adhearsion/menu_dsl/menu.rb +0 -201
- data/lib/adhearsion/menu_dsl/menu_builder.rb +0 -82
- data/lib/adhearsion/menu_dsl/range_match_calculator.rb +0 -58
- data/lib/adhearsion/menu_dsl/string_match_calculator.rb +0 -23
- data/spec/adhearsion/menu_dsl/calculated_match_collection_spec.rb +0 -58
- data/spec/adhearsion/menu_dsl/calculated_match_spec.rb +0 -59
- data/spec/adhearsion/menu_dsl/fixnum_match_calculator_spec.rb +0 -35
- data/spec/adhearsion/menu_dsl/match_calculator_spec.rb +0 -15
- data/spec/adhearsion/menu_dsl/menu_builder_spec.rb +0 -149
- data/spec/adhearsion/menu_dsl/menu_spec.rb +0 -371
- data/spec/adhearsion/menu_dsl/range_match_calculator_spec.rb +0 -30
- data/spec/adhearsion/menu_dsl/string_match_calculator_spec.rb +0 -38
@@ -1,58 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Adhearsion
|
4
|
-
module MenuDSL
|
5
|
-
|
6
|
-
class RangeMatchCalculator < MatchCalculator
|
7
|
-
|
8
|
-
def initialize(pattern, match_payload)
|
9
|
-
raise unless pattern.first.kind_of?(Numeric) && pattern.last.kind_of?(Numeric)
|
10
|
-
super
|
11
|
-
end
|
12
|
-
|
13
|
-
def match(query)
|
14
|
-
numerical_query = coerce_to_numeric query
|
15
|
-
if numerical_query
|
16
|
-
exact_match = pattern.include?(numerical_query) ? query : nil
|
17
|
-
potential_matches = numbers_in_range_like numerical_query
|
18
|
-
potential_matches.reject! { |m| m.to_s == exact_match.to_s } if exact_match
|
19
|
-
|
20
|
-
new_calculated_match :query => query, :exact_matches => exact_match,
|
21
|
-
:potential_matches => potential_matches
|
22
|
-
else
|
23
|
-
CalculatedMatch.failed_match! pattern, query, match_payload
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
# Returns all numbers in the range (@pattern) that +begin with+ the number given
|
30
|
-
# as the first arguement.
|
31
|
-
#
|
32
|
-
# NOTE: If you're having trouble reading what this method is actually doing. It's
|
33
|
-
# effectively a much more efficient version of this:
|
34
|
-
#
|
35
|
-
# pattern.to_a.select { |x| x.to_s.starts_with? num.to_s }.flatten
|
36
|
-
#
|
37
|
-
# Huge thanks to Dave Troy (http://davetroy.blogspot.com) for this awesomely
|
38
|
-
# efficient code!
|
39
|
-
def numbers_in_range_like(num)
|
40
|
-
return (pattern === 0 ? [0] : nil) if num == 0
|
41
|
-
raise ArgumentError unless num.kind_of?(Numeric)
|
42
|
-
Array.new.tap do |matches|
|
43
|
-
first, last = pattern.first, pattern.last
|
44
|
-
power = 0
|
45
|
-
while num < last
|
46
|
-
ones_count = 10**power - 1
|
47
|
-
range = ([num, first].max..[num + ones_count, last].min).to_a
|
48
|
-
matches.concat range
|
49
|
-
num *= 10
|
50
|
-
power += 1
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
end # class RangeMatchCalculator
|
56
|
-
|
57
|
-
end
|
58
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Adhearsion
|
4
|
-
module MenuDSL
|
5
|
-
|
6
|
-
class StringMatchCalculator < MatchCalculator
|
7
|
-
|
8
|
-
def match(query)
|
9
|
-
args = { :query => query, :exact_matches => nil, :potential_matches => nil }
|
10
|
-
|
11
|
-
if pattern == query.to_s
|
12
|
-
args[:exact_matches] = [pattern]
|
13
|
-
elsif pattern.starts_with? query.to_s
|
14
|
-
args[:potential_matches] = [pattern]
|
15
|
-
end
|
16
|
-
|
17
|
-
new_calculated_match args
|
18
|
-
end
|
19
|
-
|
20
|
-
end # class StringMatchCalculator
|
21
|
-
|
22
|
-
end
|
23
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
module Adhearsion
|
6
|
-
module MenuDSL
|
7
|
-
describe CalculatedMatchCollection do
|
8
|
-
def mock_with_potential_matches(potential_matches)
|
9
|
-
CalculatedMatch.new :potential_matches => potential_matches
|
10
|
-
end
|
11
|
-
|
12
|
-
def mock_with_exact_matches(exact_matches)
|
13
|
-
CalculatedMatch.new :exact_matches => exact_matches
|
14
|
-
end
|
15
|
-
|
16
|
-
def mock_with_potential_and_exact_matches(potential_matches, exact_matches)
|
17
|
-
CalculatedMatch.new :potential_matches => potential_matches,
|
18
|
-
:exact_matches => exact_matches
|
19
|
-
end
|
20
|
-
|
21
|
-
it "the <<() method should collect the potential matches into the actual_potential_matches Array" do
|
22
|
-
mock_matches_array_1 = [:foo, :bar, :qaz],
|
23
|
-
mock_matches_array_2 = [10, 20, 30]
|
24
|
-
mock_matches_1 = mock_with_potential_matches mock_matches_array_1
|
25
|
-
mock_matches_2 = mock_with_potential_matches mock_matches_array_2
|
26
|
-
|
27
|
-
subject << mock_matches_1
|
28
|
-
subject.actual_potential_matches.should be == mock_matches_array_1
|
29
|
-
|
30
|
-
subject << mock_matches_2
|
31
|
-
subject.actual_potential_matches.should be == mock_matches_array_1 + mock_matches_array_2
|
32
|
-
end
|
33
|
-
|
34
|
-
it "the <<() method should collect the exact matches into the actual_exact_matches Array" do
|
35
|
-
mock_matches_array_1 = [:blam, :blargh],
|
36
|
-
mock_matches_array_2 = [5,4,3,2,1]
|
37
|
-
mock_matches_1 = mock_with_exact_matches mock_matches_array_1
|
38
|
-
mock_matches_2 = mock_with_exact_matches mock_matches_array_2
|
39
|
-
|
40
|
-
subject << mock_matches_1
|
41
|
-
subject.actual_exact_matches.should be == mock_matches_array_1
|
42
|
-
|
43
|
-
subject << mock_matches_2
|
44
|
-
subject.actual_exact_matches.should be == mock_matches_array_1 + mock_matches_array_2
|
45
|
-
end
|
46
|
-
|
47
|
-
it "if any exact matches exist, the exact_match?() method should return true" do
|
48
|
-
subject << mock_with_exact_matches([1,2,3])
|
49
|
-
subject.exact_match?.should be true
|
50
|
-
end
|
51
|
-
|
52
|
-
it "if any potential matches exist, the potential_match?() method should return true" do
|
53
|
-
subject << mock_with_potential_matches([1,2,3])
|
54
|
-
subject.potential_match?.should be true
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
module Adhearsion
|
6
|
-
module MenuDSL
|
7
|
-
describe CalculatedMatch do
|
8
|
-
it "should make accessible the context name" do
|
9
|
-
CalculatedMatch.new(:match_payload => :foobar).match_payload.should be :foobar
|
10
|
-
end
|
11
|
-
|
12
|
-
it "should make accessible the original pattern" do
|
13
|
-
CalculatedMatch.new(:pattern => :something).pattern.should be :something
|
14
|
-
end
|
15
|
-
|
16
|
-
it "should make accessible the matched query" do
|
17
|
-
CalculatedMatch.new(:query => 123).query.should be 123
|
18
|
-
end
|
19
|
-
|
20
|
-
it "#type_of_match should return :exact, :potential, or nil" do
|
21
|
-
CalculatedMatch.new(:potential_matches => [1]).type_of_match.should be :potential
|
22
|
-
CalculatedMatch.new(:exact_matches => [3,3]).type_of_match.should be :exact
|
23
|
-
CalculatedMatch.new(:exact_matches => [8,3], :potential_matches => [0,9]).type_of_match.should be :exact
|
24
|
-
end
|
25
|
-
|
26
|
-
it "#exact_match? should return true if the match was exact" do
|
27
|
-
CalculatedMatch.new(:exact_matches => [0,3,5]).exact_match?.should be true
|
28
|
-
end
|
29
|
-
|
30
|
-
it "#potential_match? should return true if the match was exact" do
|
31
|
-
CalculatedMatch.new(:potential_matches => [88,99,77]).potential_match?.should be true
|
32
|
-
end
|
33
|
-
|
34
|
-
it "#failed_match? should return false if the match was exact" do
|
35
|
-
CalculatedMatch.new(:potential_matches => [88,99,77]).failed_match?.should be false
|
36
|
-
end
|
37
|
-
|
38
|
-
it "#exact_matches should return an array of exact matches" do
|
39
|
-
CalculatedMatch.new(:exact_matches => [0,3,5]).exact_matches.should be == [0,3,5]
|
40
|
-
end
|
41
|
-
|
42
|
-
it "#potential_matches should return an array of potential matches" do
|
43
|
-
CalculatedMatch.new(:potential_matches => [88,99,77]).potential_matches.should be == [88,99,77]
|
44
|
-
end
|
45
|
-
|
46
|
-
it "::failed_match! should return a match that *really* failed" do
|
47
|
-
failure = CalculatedMatch.failed_match! 10..20, 30, :match_payload_does_not_matter
|
48
|
-
failure.exact_match?.should_not be true
|
49
|
-
failure.potential_match?.should_not be true
|
50
|
-
failure.failed_match?.should be true
|
51
|
-
failure.type_of_match.should be nil
|
52
|
-
|
53
|
-
failure.match_payload.should be :match_payload_does_not_matter
|
54
|
-
failure.pattern.should be == (10..20)
|
55
|
-
failure.query.should be == 30
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
module Adhearsion
|
6
|
-
module MenuDSL
|
7
|
-
describe FixnumMatchCalculator do
|
8
|
-
let(:match_payload) { :main }
|
9
|
-
|
10
|
-
it "a potential match scenario" do
|
11
|
-
calculator = FixnumMatchCalculator.new(444, match_payload)
|
12
|
-
match = calculator.match 4
|
13
|
-
match.potential_match?.should be true
|
14
|
-
match.exact_match?.should_not be true
|
15
|
-
match.potential_matches.should be == [444]
|
16
|
-
end
|
17
|
-
|
18
|
-
it "a multi-digit exact match scenario" do
|
19
|
-
calculator = FixnumMatchCalculator.new(5555, match_payload)
|
20
|
-
calculator.match(5555).exact_match?.should be true
|
21
|
-
end
|
22
|
-
|
23
|
-
it "a single-digit exact match scenario" do
|
24
|
-
calculator = FixnumMatchCalculator.new(1, match_payload)
|
25
|
-
calculator.match(1).exact_match?.should be true
|
26
|
-
end
|
27
|
-
|
28
|
-
it "the context name given to the calculator should be passed on the CalculatedMatch" do
|
29
|
-
match_payload = :icanhascheezburger
|
30
|
-
calculator = FixnumMatchCalculator.new(1337, match_payload)
|
31
|
-
calculator.match(1337).match_payload.should be match_payload
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
module Adhearsion
|
6
|
-
module MenuDSL
|
7
|
-
describe MatchCalculator do
|
8
|
-
describe ".build_with_pattern" do
|
9
|
-
it "should return an appropriate subclass instance based on the pattern's class" do
|
10
|
-
MatchCalculator.build_with_pattern(1..2, :main).should be_an_instance_of RangeMatchCalculator
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,149 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
module Adhearsion
|
6
|
-
module MenuDSL
|
7
|
-
|
8
|
-
describe MenuBuilder do
|
9
|
-
subject{ MenuDSL::MenuBuilder.new }
|
10
|
-
|
11
|
-
describe "#build" do
|
12
|
-
it "sets the context and instance_eval's the block" do
|
13
|
-
flexmock(subject).should_receive(:foo).with(:bar)
|
14
|
-
subject.build do
|
15
|
-
foo :bar
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end#build
|
19
|
-
|
20
|
-
describe "#match" do
|
21
|
-
let(:match_block) { Proc.new() {} }
|
22
|
-
|
23
|
-
it "raises an exception if called without a CallController and no block" do
|
24
|
-
expect { subject.match 1 }.to raise_error(ArgumentError)
|
25
|
-
end
|
26
|
-
|
27
|
-
it "raises an exception if given both a payload and a block" do
|
28
|
-
expect { subject.match(1, Object) {} }.to raise_error(ArgumentError)
|
29
|
-
end
|
30
|
-
|
31
|
-
it "raises an exception if given no patterns" do
|
32
|
-
expect { subject.match() {} }.to raise_error(ArgumentError, "You cannot call this method without patterns.")
|
33
|
-
end
|
34
|
-
|
35
|
-
it "creates a pattern based on a payload" do
|
36
|
-
flexmock(MenuDSL::MatchCalculator).should_receive(:build_with_pattern).with("1", Object)
|
37
|
-
subject.match "1", Object
|
38
|
-
end
|
39
|
-
|
40
|
-
it "creates a pattern based on a block" do
|
41
|
-
flexmock(MenuDSL::MatchCalculator).should_receive(:build_with_pattern).with("1", nil, match_block)
|
42
|
-
subject.match("1", &match_block)
|
43
|
-
end
|
44
|
-
|
45
|
-
it "creates multiple patterns if multiple arguments are passed in" do
|
46
|
-
flexmock(MenuDSL::MatchCalculator).should_receive(:build_with_pattern).with(1, Object)
|
47
|
-
flexmock(MenuDSL::MatchCalculator).should_receive(:build_with_pattern).with(2, Object)
|
48
|
-
subject.match(1, 2, Object)
|
49
|
-
end
|
50
|
-
end#match
|
51
|
-
|
52
|
-
describe "#has_matchers?" do
|
53
|
-
context "with no matchers specified" do
|
54
|
-
its(:has_matchers?) { should be false }
|
55
|
-
end
|
56
|
-
|
57
|
-
context "with at least one matcher specified" do
|
58
|
-
before do
|
59
|
-
subject.match(1) {}
|
60
|
-
end
|
61
|
-
|
62
|
-
its(:has_matchers?) { should be true }
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
describe "#weighted_match_calculators" do
|
67
|
-
let(:expected_pattern) { MenuDSL::MatchCalculator.build_with_pattern("1", Object) }
|
68
|
-
|
69
|
-
it "returns the generated patterns" do
|
70
|
-
flexmock(MenuDSL::MatchCalculator).should_receive(:build_with_pattern).with("1", Object).and_return(expected_pattern)
|
71
|
-
subject.match("1", Object)
|
72
|
-
subject.weighted_match_calculators.should be == [expected_pattern]
|
73
|
-
end
|
74
|
-
end#weighted_match_calculators
|
75
|
-
|
76
|
-
describe "#invalid" do
|
77
|
-
let(:callback) { Proc.new() {} }
|
78
|
-
|
79
|
-
it "raises an error if not passed a block" do
|
80
|
-
expect { subject.invalid }.to raise_error(LocalJumpError)
|
81
|
-
end
|
82
|
-
|
83
|
-
it "sets the invalid callback" do
|
84
|
-
subject.invalid(&callback)
|
85
|
-
subject.menu_callbacks[:invalid].should be == callback
|
86
|
-
end
|
87
|
-
end#invalid
|
88
|
-
|
89
|
-
describe "#timeout" do
|
90
|
-
let(:callback) { Proc.new() {} }
|
91
|
-
|
92
|
-
it "raises an error if not passed a block" do
|
93
|
-
expect { subject.timeout }.to raise_error(LocalJumpError)
|
94
|
-
end
|
95
|
-
|
96
|
-
it "sets the timeout callback" do
|
97
|
-
subject.timeout(&callback)
|
98
|
-
subject.menu_callbacks[:timeout].should be == callback
|
99
|
-
end
|
100
|
-
end#timeout
|
101
|
-
|
102
|
-
describe "#failure" do
|
103
|
-
let(:callback) { Proc.new() {} }
|
104
|
-
|
105
|
-
it "raises an error if not passed a block" do
|
106
|
-
expect { subject.failure }.to raise_error(LocalJumpError)
|
107
|
-
end
|
108
|
-
|
109
|
-
it "sets the failure callback" do
|
110
|
-
subject.failure(&callback)
|
111
|
-
subject.menu_callbacks[:failure].should be == callback
|
112
|
-
end
|
113
|
-
end#failure
|
114
|
-
|
115
|
-
describe "#validator" do
|
116
|
-
let(:callback) { Proc.new() {} }
|
117
|
-
|
118
|
-
it "raises an error if not passed a block" do
|
119
|
-
expect { subject.validator }.to raise_error(LocalJumpError)
|
120
|
-
end
|
121
|
-
|
122
|
-
it "sets the invalid callback" do
|
123
|
-
subject.validator(&callback)
|
124
|
-
subject.menu_callbacks[:validator].should be == callback
|
125
|
-
end
|
126
|
-
end#invalid
|
127
|
-
|
128
|
-
describe "#execute_hook_for" do
|
129
|
-
it "executes the correct hook" do
|
130
|
-
bar = nil
|
131
|
-
subject.invalid do |baz|
|
132
|
-
bar = baz
|
133
|
-
end
|
134
|
-
subject.execute_hook_for(:invalid, "1")
|
135
|
-
bar.should be == "1"
|
136
|
-
end
|
137
|
-
end#execute_hook_for
|
138
|
-
|
139
|
-
describe "#calculate_matches_for" do
|
140
|
-
it "returns a calculated match collection" do
|
141
|
-
subject.match("1", Object)
|
142
|
-
subject.calculate_matches_for("1").should be_a CalculatedMatchCollection
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
end# describe MenuBuilder
|
147
|
-
|
148
|
-
end# module MenuDSL
|
149
|
-
end
|
@@ -1,371 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
module Adhearsion
|
6
|
-
module MenuDSL
|
7
|
-
describe Menu do
|
8
|
-
|
9
|
-
let(:options) { Hash.new }
|
10
|
-
subject { Menu.new(options) }
|
11
|
-
|
12
|
-
describe "#initialize" do
|
13
|
-
its(:tries_count) { should be == 0 }
|
14
|
-
|
15
|
-
context 'when no timeout is set' do
|
16
|
-
it "should have the default timeout" do
|
17
|
-
subject.timeout.should be == 5
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
context 'when a timeout is set' do
|
22
|
-
let(:options) {
|
23
|
-
{:timeout => 20}
|
24
|
-
}
|
25
|
-
|
26
|
-
it 'should have the passed timeout' do
|
27
|
-
subject.timeout.should be == 20
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
context 'when no max number of tries is set' do
|
32
|
-
it "should have the default max number of tries" do
|
33
|
-
subject.max_number_of_tries.should be == 1
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
context 'when a max number of tries is set' do
|
38
|
-
let(:options) {
|
39
|
-
{:tries => 3}
|
40
|
-
}
|
41
|
-
|
42
|
-
it 'should have the passed max number of tries' do
|
43
|
-
subject.max_number_of_tries.should be == 3
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
context 'when no terminator is set' do
|
48
|
-
it "should have no terminator" do
|
49
|
-
subject.terminator.should be == ''
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'should not validate successfully' do
|
53
|
-
lambda { subject.validate }.should raise_error(Menu::InvalidStructureError)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
context 'when a terminator is set' do
|
58
|
-
let(:options) {
|
59
|
-
{:terminator => 3}
|
60
|
-
}
|
61
|
-
|
62
|
-
it 'should have the passed terminator' do
|
63
|
-
subject.terminator.should be == '3'
|
64
|
-
end
|
65
|
-
|
66
|
-
it 'should validate(:basic) successfully' do
|
67
|
-
subject.validate(:basic).should be true
|
68
|
-
end
|
69
|
-
|
70
|
-
it 'should not validate successfully' do
|
71
|
-
lambda { subject.validate }.should raise_error(Menu::InvalidStructureError)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
context 'when no limit is set' do
|
76
|
-
it "should have no limit" do
|
77
|
-
subject.limit.should be nil
|
78
|
-
end
|
79
|
-
|
80
|
-
it 'should not validate successfully' do
|
81
|
-
lambda { subject.validate }.should raise_error(Menu::InvalidStructureError)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
context 'when a limit is set' do
|
86
|
-
let(:options) {
|
87
|
-
{:limit => 3}
|
88
|
-
}
|
89
|
-
|
90
|
-
it 'should have the passed limit' do
|
91
|
-
subject.limit.should be == 3
|
92
|
-
end
|
93
|
-
|
94
|
-
it 'should validate(:basic) successfully' do
|
95
|
-
subject.validate(:basic).should be true
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'should not validate successfully' do
|
99
|
-
lambda { subject.validate }.should raise_error(Menu::InvalidStructureError)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
context 'when no interruptibility is set' do
|
104
|
-
it "should be interruptible" do
|
105
|
-
subject.interruptible.should be true
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
context 'when interruptible is set false' do
|
110
|
-
let(:options) {
|
111
|
-
{:interruptible => false}
|
112
|
-
}
|
113
|
-
|
114
|
-
it 'should be interruptible' do
|
115
|
-
subject.interruptible.should be false
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
context 'when matchers are specified' do
|
120
|
-
subject do
|
121
|
-
Menu.new do
|
122
|
-
match(1) { }
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
it 'should validate successfully' do
|
127
|
-
subject.validate.should be true
|
128
|
-
end
|
129
|
-
|
130
|
-
it 'should not validate(:basic) successfully' do
|
131
|
-
lambda { subject.validate :basic }.should raise_error(Menu::InvalidStructureError)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
context 'menu builder setup' do
|
136
|
-
its(:builder) { should be_a MenuBuilder }
|
137
|
-
|
138
|
-
it "should evaluate the block on the builder object" do
|
139
|
-
mock_menu_builder = flexmock(MenuBuilder.new)
|
140
|
-
flexmock(MenuBuilder).should_receive(:new).and_return(mock_menu_builder)
|
141
|
-
mock_menu_builder.should_receive(:match).once.with(1)
|
142
|
-
Menu.new { match 1 }
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
end # describe #initialize
|
147
|
-
|
148
|
-
describe "#digit_buffer" do
|
149
|
-
its(:digit_buffer) { should be_a Menu::ClearableStringBuffer }
|
150
|
-
its(:digit_buffer) { should be == "" }
|
151
|
-
end
|
152
|
-
|
153
|
-
describe "#<<" do
|
154
|
-
it "should add a digit to the buffer" do
|
155
|
-
subject << 'a'
|
156
|
-
subject.digit_buffer.should be == 'a'
|
157
|
-
subject.result.should be == 'a'
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
describe "#digit_buffer_empty?" do
|
162
|
-
it "returns true if buffer is empty" do
|
163
|
-
subject.digit_buffer_empty?.should be == true
|
164
|
-
end
|
165
|
-
|
166
|
-
it "returns false if buffer is not empty" do
|
167
|
-
subject << 1
|
168
|
-
subject.digit_buffer_empty?.should be == false
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
describe "#digit_buffer_string" do
|
173
|
-
it "returns the digit buffer as a string" do
|
174
|
-
subject << 1
|
175
|
-
subject.digit_buffer_string.should be == "1"
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
describe "#should_continue?" do
|
180
|
-
it "returns true if the number of tries is less than the maximum" do
|
181
|
-
subject.max_number_of_tries.should be == 1
|
182
|
-
subject.tries_count.should be == 0
|
183
|
-
subject.should_continue?.should be == true
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
describe "#restart!" do
|
188
|
-
it "increments tries and clears the digit buffer" do
|
189
|
-
subject << 1
|
190
|
-
subject.restart!
|
191
|
-
subject.tries_count.should be == 1
|
192
|
-
subject.digit_buffer_empty?.should be == true
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
describe "#execute_invalid_hook" do
|
197
|
-
it "calls the builder's execute_hook_for with :invalid" do
|
198
|
-
mock_menu_builder = flexmock(MenuBuilder.new)
|
199
|
-
flexmock(MenuBuilder).should_receive(:new).and_return(mock_menu_builder)
|
200
|
-
mock_menu_builder.should_receive(:execute_hook_for).with(:invalid, "")
|
201
|
-
menu_instance = Menu.new
|
202
|
-
menu_instance.execute_invalid_hook
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
describe "#execute_timeout_hook" do
|
207
|
-
it "calls the builder's execute_hook_for with :timeout" do
|
208
|
-
mock_menu_builder = flexmock(MenuBuilder.new)
|
209
|
-
flexmock(MenuBuilder).should_receive(:new).and_return(mock_menu_builder)
|
210
|
-
mock_menu_builder.should_receive(:execute_hook_for).with(:timeout, "")
|
211
|
-
menu_instance = Menu.new
|
212
|
-
menu_instance.execute_timeout_hook
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
describe "#execute_failure_hook" do
|
217
|
-
it "calls the builder's execute_hook_for with :failure" do
|
218
|
-
mock_menu_builder = flexmock(MenuBuilder.new)
|
219
|
-
flexmock(MenuBuilder).should_receive(:new).and_return(mock_menu_builder)
|
220
|
-
mock_menu_builder.should_receive(:execute_hook_for).with(:failure, "")
|
221
|
-
menu_instance = Menu.new
|
222
|
-
menu_instance.execute_failure_hook
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
describe "#execute_validator_hook" do
|
227
|
-
it "calls the builder's execute_hook_for with :validator" do
|
228
|
-
mock_menu_builder = flexmock(MenuBuilder.new)
|
229
|
-
flexmock(MenuBuilder).should_receive(:new).and_return(mock_menu_builder)
|
230
|
-
mock_menu_builder.should_receive(:execute_hook_for).with(:validator, "")
|
231
|
-
menu_instance = Menu.new
|
232
|
-
menu_instance.execute_validator_hook
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
describe "#continue" do
|
237
|
-
class MockControllerA; end
|
238
|
-
class MockControllerB; end
|
239
|
-
class MockControllerC; end
|
240
|
-
let(:options) { {} }
|
241
|
-
let(:menu_instance) {
|
242
|
-
Menu.new options do
|
243
|
-
match 1, MockControllerA
|
244
|
-
match 21, MockControllerA
|
245
|
-
match 23, MockControllerA
|
246
|
-
match 3, MockControllerB
|
247
|
-
match 3..5, MockControllerC
|
248
|
-
match 33, MockControllerA
|
249
|
-
match 6, MockControllerC
|
250
|
-
match 6..8, MockControllerA
|
251
|
-
end
|
252
|
-
}
|
253
|
-
|
254
|
-
it "returns a MenuGetAnotherDigitOrTimeout if the digit buffer is empty" do
|
255
|
-
subject.continue.should be_a Menu::MenuGetAnotherDigitOrTimeout
|
256
|
-
menu_instance.status.should be nil
|
257
|
-
end
|
258
|
-
|
259
|
-
it "asks for another digit if it has potential matches" do
|
260
|
-
menu_instance << 2
|
261
|
-
menu_instance.continue.should be_a Menu::MenuGetAnotherDigitOrTimeout
|
262
|
-
menu_instance.status.should be == :potential
|
263
|
-
end
|
264
|
-
|
265
|
-
it "returns a MenuResultInvalid if there are no matches" do
|
266
|
-
menu_instance << 9
|
267
|
-
menu_instance.continue.should be_a Menu::MenuResultInvalid
|
268
|
-
menu_instance.status.should be == :invalid
|
269
|
-
end
|
270
|
-
|
271
|
-
it "returns the first exact match when it has exact and potentials" do
|
272
|
-
menu_instance << 3
|
273
|
-
menu_result = menu_instance.continue
|
274
|
-
menu_result.should be_a Menu::MenuGetAnotherDigitOrFinish
|
275
|
-
menu_result.match_object.should be == MockControllerB
|
276
|
-
menu_result.new_extension.should be == "3"
|
277
|
-
menu_instance.status.should be == :multi_matched
|
278
|
-
end
|
279
|
-
|
280
|
-
it "returns a MenuResultFound if it has exact matches" do
|
281
|
-
menu_instance << 6
|
282
|
-
menu_result = menu_instance.continue
|
283
|
-
menu_result.should be_a Menu::MenuResultFound
|
284
|
-
menu_instance.status.should be == :matched
|
285
|
-
end
|
286
|
-
|
287
|
-
it "returns the first exact match when it has only exact matches" do
|
288
|
-
menu_instance << 6
|
289
|
-
menu_result = menu_instance.continue
|
290
|
-
menu_result.should be_a Menu::MenuResultFound
|
291
|
-
menu_result.match_object.match_payload.should be == MockControllerC
|
292
|
-
menu_result.match_object.pattern.to_s.should be == "6"
|
293
|
-
end
|
294
|
-
|
295
|
-
context "with no matchers" do
|
296
|
-
let(:menu_instance) { Menu.new options }
|
297
|
-
|
298
|
-
context "when a terminator digit is set" do
|
299
|
-
let(:options) { { :terminator => '#' } }
|
300
|
-
|
301
|
-
it "buffers until the terminator is issued then returns a MenuTerminated and sets the status to :terminated, removing the terminator from the buffer" do
|
302
|
-
menu_instance << 2
|
303
|
-
menu_instance << 4
|
304
|
-
menu_instance.continue.should be_a Menu::MenuGetAnotherDigitOrTimeout
|
305
|
-
menu_instance.status.should be == :potential
|
306
|
-
menu_instance << '#'
|
307
|
-
menu_instance.continue.should be_a Menu::MenuTerminated
|
308
|
-
menu_instance.continue.should be_a Menu::MenuResultDone
|
309
|
-
menu_instance.status.should be == :terminated
|
310
|
-
menu_instance.result.should be == '24'
|
311
|
-
end
|
312
|
-
end
|
313
|
-
|
314
|
-
context "when a digit limit is set" do
|
315
|
-
let(:options) { { :limit => 3 } }
|
316
|
-
|
317
|
-
it "buffers until the limit is reached, then returns MenuLimitReached and sets the status to :limited" do
|
318
|
-
menu_instance << 2
|
319
|
-
menu_instance << 4
|
320
|
-
menu_instance.continue.should be_a Menu::MenuGetAnotherDigitOrTimeout
|
321
|
-
menu_instance.status.should be == :potential
|
322
|
-
menu_instance << 2
|
323
|
-
menu_instance.continue.should be_a Menu::MenuLimitReached
|
324
|
-
menu_instance.continue.should be_a Menu::MenuResultDone
|
325
|
-
menu_instance.status.should be == :limited
|
326
|
-
menu_instance.result.should be == '242'
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
330
|
-
context "when a validator is defined" do
|
331
|
-
let(:menu_instance) do
|
332
|
-
Menu.new options do
|
333
|
-
validator { |buffer| buffer == "242" }
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
|
-
it "buffers until the validator returns true, then returns MenuValidatorTerminated and sets the status to :validator_terminated" do
|
338
|
-
menu_instance << 2
|
339
|
-
menu_instance << 4
|
340
|
-
menu_instance.continue.should be_a Menu::MenuGetAnotherDigitOrTimeout
|
341
|
-
menu_instance.status.should be == :potential
|
342
|
-
menu_instance << 2
|
343
|
-
menu_instance.continue.should be_a Menu::MenuValidatorTerminated
|
344
|
-
menu_instance.continue.should be_a Menu::MenuResultDone
|
345
|
-
menu_instance.status.should be == :validator_terminated
|
346
|
-
menu_instance.result.should be == '242'
|
347
|
-
end
|
348
|
-
end
|
349
|
-
end
|
350
|
-
|
351
|
-
end#continue
|
352
|
-
|
353
|
-
describe Menu::ClearableStringBuffer do
|
354
|
-
subject { Menu::ClearableStringBuffer.new }
|
355
|
-
|
356
|
-
it "adds a string to itself" do
|
357
|
-
subject << 'b'
|
358
|
-
subject << 'c'
|
359
|
-
subject.should be == 'bc'
|
360
|
-
end
|
361
|
-
|
362
|
-
it "clears itself" do
|
363
|
-
subject << 'a'
|
364
|
-
subject.clear!
|
365
|
-
subject.should be == ""
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
|
-
end # describe Menu
|
370
|
-
end
|
371
|
-
end
|