Skip to content
This repository
Browse code

Merge branch 'release/0.5.1'

  • Loading branch information...
commit 7f6097e5e0e64c07062a34305ecbf2526c1e6023 2 parents 798638c + bd9a767
Ben Langfeld authored
5 CHANGELOG.md
Source Rendered
... ... @@ -1,5 +1,10 @@
1 1 # develop
2 2
  3 +# 0.5.1 - 2012-01-09
  4 + * Feature: Chaining child injection using #<< now works
  5 + * Feature: Reading the repeat value for a GRXML Item now returns an Integer or a Range, rather than the plain string
  6 + * Feature: Most simple GRXML grammars now return PotentialMatch when the provided input is valid but incomplete. This does not work for complex grammars including repeats and deep nesting. Fixes for these coming soon.
  7 +
3 8 # 0.5.0 - 2012-01-03
4 9 * Feature: Add a whole bunch more SSML elements:
5 10 ** p & s
1  lib/ruby_speech.rb
@@ -2,6 +2,7 @@
2 2 active_support/dependencies/autoload
3 3 active_support/core_ext/object/blank
4 4 active_support/core_ext/numeric/time
  5 + active_support/core_ext/enumerable
5 6 niceogiri
6 7 }.each { |f| require f }
7 8
1  lib/ruby_speech/generic_element.rb
@@ -164,6 +164,7 @@ def string(other)
164 164 def <<(other)
165 165 other = encode_special_chars other if other.is_a? String
166 166 super other
  167 + self
167 168 end
168 169
169 170 def method_missing(method_name, *args, &block)
1  lib/ruby_speech/grxml.rb
@@ -15,6 +15,7 @@ module GRXML
15 15
16 16 autoload :Match
17 17 autoload :NoMatch
  18 + autoload :PotentialMatch
18 19
19 20 InvalidChildError = Class.new StandardError
20 21
17 lib/ruby_speech/grxml/element.rb
@@ -22,6 +22,23 @@ def self.module
22 22 def regexp_content # :nodoc:
23 23 children.map(&:regexp_content).join
24 24 end
  25 +
  26 + def potential_match?(other)
  27 + false
  28 + end
  29 +
  30 + def max_input_length
  31 + 0
  32 + end
  33 +
  34 + def longest_potential_match(input)
  35 + input.dup.tap do |longest_input|
  36 + begin
  37 + return longest_input if potential_match? longest_input
  38 + longest_input.chop!
  39 + end until longest_input.length.zero?
  40 + end
  41 + end
25 42 end # Element
26 43 end # GRXML
27 44 end # RubySpeech
24 lib/ruby_speech/grxml/grammar.rb
@@ -195,15 +195,19 @@ def normalize_whitespace
195 195 # @interpretation = "1234#"
196 196 # >
197 197 # >> subject.match '111'
198   - # => #<RubySpeech::GRXML::NoMatch:0x00000101371660>
  198 + # => #<RubySpeech::GRXML::PotentialMatch:0x00000101371660>
  199 + #
  200 + # >> subject.match '11111'
  201 + # => #<RubySpeech::GRXML::NoMatch:0x00000101371936>
199 202 #
200 203 # ```
201 204 #
202 205 def match(other)
  206 + other = other.dup
203 207 regex = to_regexp
204   - return NoMatch.new if regex == //
  208 + return check_for_potential_match(other) if regex == //
205 209 match = regex.match other
206   - return NoMatch.new unless match
  210 + return check_for_potential_match(other) unless match
207 211
208 212 Match.new :mode => mode,
209 213 :confidence => dtmf? ? 1 : 0,
@@ -211,6 +215,20 @@ def match(other)
211 215 :interpretation => interpret_utterance(other)
212 216 end
213 217
  218 + def check_for_potential_match(other)
  219 + potential_match?(other) ? PotentialMatch.new : NoMatch.new
  220 + end
  221 +
  222 + def potential_match?(other)
  223 + root_rule.children.each do |token|
  224 + return true if other.length.zero?
  225 + longest_potential_match = token.longest_potential_match other
  226 + return false if longest_potential_match.length.zero?
  227 + other.gsub! /^#{Regexp.escape longest_potential_match}/, ''
  228 + end
  229 + other.length.zero?
  230 + end
  231 +
214 232 ##
215 233 # Converts the grammar into a regular expression for matching
216 234 #
43 lib/ruby_speech/grxml/item.rb
@@ -26,6 +26,7 @@ module GRXML
26 26 # xml:lang declares declaration declares the language of the grammar section for the item element just as xml:lang in the <grammar> element declares for the entire document
27 27 #
28 28 class Item < Element
  29 + Inf = 1.0 / 0.0
29 30
30 31 register :item
31 32
@@ -61,7 +62,14 @@ def weight=(w)
61 62 # @return [String]
62 63 #
63 64 def repeat
64   - read_attr :repeat
  65 + repeat = read_attr :repeat
  66 + return nil unless repeat
  67 + if repeat.include?('-')
  68 + min, max = repeat.split('-').map &:to_i
  69 + (min || 0)..(max || Inf)
  70 + else
  71 + repeat.to_i
  72 + end
65 73 end
66 74
67 75 ##
@@ -72,7 +80,7 @@ def repeat
72 80 # @param [String] r
73 81 #
74 82 def repeat=(r)
75   - r = "#{r.min}-#{r.max}" if r.is_a?(Range)
  83 + r = "#{r.min}-#{r.max unless r.max == Inf}" if r.is_a?(Range)
76 84 r = r.to_s
77 85 error = ArgumentError.new "A Item's repeat must be 0 or a positive integer"
78 86
@@ -122,13 +130,34 @@ def eql?(o)
122 130 end
123 131
124 132 def regexp_content # :nodoc:
125   - return super unless repeat
  133 + case repeat
  134 + when Range
  135 + "#{super}{#{repeat.min},#{repeat.max unless repeat.max == Inf}}"
  136 + when Integer
  137 + "#{super}{#{repeat}}"
  138 + else
  139 + super
  140 + end
  141 + end
126 142
127   - if repeat.include?('-')
128   - min, max = repeat.split '-'
129   - "#{super}{#{min},#{max}}"
  143 + def potential_match?(other)
  144 + tokens = children
  145 + return false if other.length > max_input_length
  146 + other.chars.each_with_index do |digit, index|
  147 + index -= tokens.size until index < tokens.size if repeat
  148 + return false unless tokens[index].potential_match?(digit)
  149 + end
  150 + true
  151 + end
  152 +
  153 + def max_input_length # :nodoc:
  154 + case repeat
  155 + when Range
  156 + children.size * repeat.max
  157 + when Integer
  158 + children.size * repeat
130 159 else
131   - "#{super}{#{repeat}}"
  160 + children.size
132 161 end
133 162 end
134 163 end # Item
4 lib/ruby_speech/grxml/one_of.rb
@@ -26,6 +26,10 @@ def <<(arg)
26 26 def regexp_content # :nodoc:
27 27 "(#{children.map(&:regexp_content).join '|'})"
28 28 end
  29 +
  30 + def potential_match?(input)
  31 + children.any? { |c| c.potential_match? input }
  32 + end
29 33 end # OneOf
30 34 end # GRXML
31 35 end # RubySpeech
10 lib/ruby_speech/grxml/potential_match.rb
... ... @@ -0,0 +1,10 @@
  1 +module RubySpeech
  2 + module GRXML
  3 + class PotentialMatch
  4 + def eql?(o)
  5 + o.is_a? self.class
  6 + end
  7 + alias :== :eql?
  8 + end
  9 + end
  10 +end
4 lib/ruby_speech/grxml/token.rb
@@ -26,6 +26,10 @@ def normalize_whitespace
26 26 def regexp_content # :nodoc:
27 27 Regexp.escape content
28 28 end
  29 +
  30 + def potential_match?(other)
  31 + other == content
  32 + end
29 33 end # Token
30 34 end # GRXML
31 35 end # RubySpeech
2  lib/ruby_speech/version.rb
... ... @@ -1,3 +1,3 @@
1 1 module RubySpeech
2   - VERSION = "0.5.0"
  2 + VERSION = "0.5.1"
3 3 end
234 spec/ruby_speech/grxml/grammar_spec.rb
@@ -360,11 +360,13 @@ def single_rule_grammar(content = [])
360 360 end
361 361
362 362 it "should match '6'" do
  363 + input = '6'
363 364 expected_match = GRXML::Match.new :mode => :dtmf,
364 365 :confidence => 1,
365 366 :utterance => '6',
366 367 :interpretation => 'dtmf-6'
367   - subject.match('6').should == expected_match
  368 + subject.match(input).should == expected_match
  369 + input.should == '6'
368 370 end
369 371
370 372 %w{1 2 3 4 5 7 8 9 10 66 26 61}.each do |input|
@@ -391,7 +393,13 @@ def single_rule_grammar(content = [])
391 393 subject.match('56').should == expected_match
392 394 end
393 395
394   - %w{* *7 #6 6* 1 2 3 4 5 6 7 8 9 10 65 57 46 26 61}.each do |input|
  396 + it "should potentially match '5'" do
  397 + input = '5'
  398 + subject.match(input).should == GRXML::PotentialMatch.new
  399 + input.should == '5'
  400 + end
  401 +
  402 + %w{* *7 #6 6* 1 2 3 4 6 7 8 9 10 65 57 46 26 61}.each do |input|
395 403 it "should not match '#{input}'" do
396 404 subject.match(input).should == GRXML::NoMatch.new
397 405 end
@@ -415,7 +423,11 @@ def single_rule_grammar(content = [])
415 423 subject.match('*6').should == expected_match
416 424 end
417 425
418   - %w{* *7 #6 6* 1 2 3 4 5 6 7 8 9 10 66 26 61}.each do |input|
  426 + it "should potentially match '*'" do
  427 + subject.match('*').should == GRXML::PotentialMatch.new
  428 + end
  429 +
  430 + %w{*7 #6 6* 1 2 3 4 5 6 7 8 9 10 66 26 61}.each do |input|
419 431 it "should not match '#{input}'" do
420 432 subject.match(input).should == GRXML::NoMatch.new
421 433 end
@@ -439,6 +451,10 @@ def single_rule_grammar(content = [])
439 451 subject.match('#6').should == expected_match
440 452 end
441 453
  454 + it "should potentially match '#'" do
  455 + subject.match('#').should == GRXML::PotentialMatch.new
  456 + end
  457 +
442 458 %w{* *6 #7 6* 1 2 3 4 5 6 7 8 9 10 66 26 61}.each do |input|
443 459 it "should not match '#{input}'" do
444 460 subject.match(input).should == GRXML::NoMatch.new
@@ -468,7 +484,142 @@ def single_rule_grammar(content = [])
468 484 subject.match('*6').should == expected_match
469 485 end
470 486
471   - %w{* *7 #6 6* 1 2 3 4 5 6 7 8 9 10 66 26 61}.each do |input|
  487 + it "should potentially match '*'" do
  488 + subject.match('*').should == GRXML::PotentialMatch.new
  489 + end
  490 +
  491 + %w{*7 #6 6* 1 2 3 4 5 6 7 8 9 10 66 26 61}.each do |input|
  492 + it "should not match '#{input}'" do
  493 + subject.match(input).should == GRXML::NoMatch.new
  494 + end
  495 + end
  496 + end
  497 +
  498 + context "with a grammar that takes a single digit alternative" do
  499 + subject do
  500 + GRXML.draw :mode => :dtmf, :root => 'digits' do
  501 + rule :id => 'digits' do
  502 + one_of do
  503 + item { '6' }
  504 + item { '7' }
  505 + end
  506 + end
  507 + end
  508 + end
  509 +
  510 + it "should match '6'" do
  511 + expected_match = GRXML::Match.new :mode => :dtmf,
  512 + :confidence => 1,
  513 + :utterance => '6',
  514 + :interpretation => 'dtmf-6'
  515 + subject.match('6').should == expected_match
  516 + end
  517 +
  518 + it "should match '7'" do
  519 + expected_match = GRXML::Match.new :mode => :dtmf,
  520 + :confidence => 1,
  521 + :utterance => '7',
  522 + :interpretation => 'dtmf-7'
  523 + subject.match('7').should == expected_match
  524 + end
  525 +
  526 + %w{* # 1 2 3 4 5 8 9 10 66 26 61}.each do |input|
  527 + it "should not match '#{input}'" do
  528 + subject.match(input).should == GRXML::NoMatch.new
  529 + end
  530 + end
  531 + end
  532 +
  533 + context "with a grammar that takes a double digit alternative" do
  534 + subject do
  535 + GRXML.draw :mode => :dtmf, :root => 'digits' do
  536 + rule :id => 'digits' do
  537 + one_of do
  538 + item do
  539 + token { '6' }
  540 + token { '5' }
  541 + end
  542 + item do
  543 + token { '7' }
  544 + token { '2' }
  545 + end
  546 + end
  547 + end
  548 + end
  549 + end
  550 +
  551 + it "should match '65'" do
  552 + expected_match = GRXML::Match.new :mode => :dtmf,
  553 + :confidence => 1,
  554 + :utterance => '65',
  555 + :interpretation => 'dtmf-6 dtmf-5'
  556 + subject.match('65').should == expected_match
  557 + end
  558 +
  559 + it "should match '72'" do
  560 + expected_match = GRXML::Match.new :mode => :dtmf,
  561 + :confidence => 1,
  562 + :utterance => '72',
  563 + :interpretation => 'dtmf-7 dtmf-2'
  564 + subject.match('72').should == expected_match
  565 + end
  566 +
  567 + %w{6 7}.each do |input|
  568 + it "should potentially match '#{input}'" do
  569 + subject.match(input).should == GRXML::PotentialMatch.new
  570 + end
  571 + end
  572 +
  573 + %w{* # 1 2 3 4 5 8 9 10 66 26 61 75}.each do |input|
  574 + it "should not match '#{input}'" do
  575 + subject.match(input).should == GRXML::NoMatch.new
  576 + end
  577 + end
  578 + end
  579 +
  580 + context "with a grammar that takes a triple digit alternative" do
  581 + subject do
  582 + GRXML.draw :mode => :dtmf, :root => 'digits' do
  583 + rule :id => 'digits' do
  584 + one_of do
  585 + item do
  586 + token { '6' }
  587 + token { '5' }
  588 + token { '2' }
  589 + end
  590 + item do
  591 + token { '7' }
  592 + token { '2' }
  593 + token { '8' }
  594 + end
  595 + end
  596 + end
  597 + end
  598 + end
  599 +
  600 + it "should match '652'" do
  601 + expected_match = GRXML::Match.new :mode => :dtmf,
  602 + :confidence => 1,
  603 + :utterance => '652',
  604 + :interpretation => 'dtmf-6 dtmf-5 dtmf-2'
  605 + subject.match('652').should == expected_match
  606 + end
  607 +
  608 + it "should match '728'" do
  609 + expected_match = GRXML::Match.new :mode => :dtmf,
  610 + :confidence => 1,
  611 + :utterance => '728',
  612 + :interpretation => 'dtmf-7 dtmf-2 dtmf-8'
  613 + subject.match('728').should == expected_match
  614 + end
  615 +
  616 + %w{6 65 7 72}.each do |input|
  617 + it "should potentially match '#{input}'" do
  618 + subject.match(input).should == GRXML::PotentialMatch.new
  619 + end
  620 + end
  621 +
  622 + %w{* # 1 2 3 4 5 8 9 10 66 26 61 75 729 654}.each do |input|
472 623 it "should not match '#{input}'" do
473 624 subject.match(input).should == GRXML::NoMatch.new
474 625 end
@@ -504,7 +655,55 @@ def single_rule_grammar(content = [])
504 655 subject.match('*7').should == expected_match
505 656 end
506 657
507   - %w{* *8 #6 6* 1 2 3 4 5 6 7 8 9 10 66 26 61}.each do |input|
  658 + it "should potentially match '*'" do
  659 + subject.match('*').should == GRXML::PotentialMatch.new
  660 + end
  661 +
  662 + %w{*8 #6 6* 1 2 3 4 5 6 7 8 9 10 66 26 61}.each do |input|
  663 + it "should not match '#{input}'" do
  664 + subject.match(input).should == GRXML::NoMatch.new
  665 + end
  666 + end
  667 + end
  668 +
  669 + context "with a grammar that takes two specific digits with the first being an alternative" do
  670 + subject do
  671 + GRXML.draw :mode => :dtmf, :root => 'digits' do
  672 + rule :id => 'digits' do
  673 + one_of do
  674 + item { '6' }
  675 + item { '7' }
  676 + end
  677 + string '*'
  678 + end
  679 + end
  680 + end
  681 +
  682 + it "should match '6*'" do
  683 + expected_match = GRXML::Match.new :mode => :dtmf,
  684 + :confidence => 1,
  685 + :utterance => '6*',
  686 + :interpretation => 'dtmf-6 dtmf-star'
  687 + subject.match('6*').should == expected_match
  688 + end
  689 +
  690 + it "should match '7*'" do
  691 + expected_match = GRXML::Match.new :mode => :dtmf,
  692 + :confidence => 1,
  693 + :utterance => '7*',
  694 + :interpretation => 'dtmf-7 dtmf-star'
  695 + subject.match('7*').should == expected_match
  696 + end
  697 +
  698 + it "should potentially match '6'" do
  699 + subject.match('6').should == GRXML::PotentialMatch.new
  700 + end
  701 +
  702 + it "should potentially match '7'" do
  703 + subject.match('7').should == GRXML::PotentialMatch.new
  704 + end
  705 +
  706 + %w{8* 6# *6 *7 1 2 3 4 5 8 9 10 66 26 61}.each do |input|
508 707 it "should not match '#{input}'" do
509 708 subject.match(input).should == GRXML::NoMatch.new
510 709 end
@@ -531,7 +730,13 @@ def single_rule_grammar(content = [])
531 730 subject.match('166').should == expected_match
532 731 end
533 732
534   - %w{1 16 1666 16666 17}.each do |input|
  733 + %w{1 16}.each do |input|
  734 + it "should potentially match '#{input}'" do
  735 + subject.match(input).should == GRXML::PotentialMatch.new
  736 + end
  737 + end
  738 +
  739 + %w{1666 16666 17}.each do |input|
535 740 it "should not match '#{input}'" do
536 741 subject.match(input).should == GRXML::NoMatch.new
537 742 end
@@ -598,7 +803,13 @@ def single_rule_grammar(content = [])
598 803 end
599 804 end
600 805
601   - %w{1 16 17}.each do |input|
  806 + %w{1 16}.each do |input|
  807 + it "should potentially match '#{input}'" do
  808 + subject.match(input).should == GRXML::PotentialMatch.new
  809 + end
  810 + end
  811 +
  812 + %w{7 17}.each do |input|
602 813 it "should not match '#{input}'" do
603 814 subject.match(input).should == GRXML::NoMatch.new
604 815 end
@@ -645,7 +856,14 @@ def single_rule_grammar(content = [])
645 856 end
646 857 end
647 858
648   - %w{111}.each do |input|
  859 + %w{* 1 12 123 1234}.each do |input|
  860 + it "should potentially match '#{input}'" do
  861 + pending
  862 + subject.match(input).should == GRXML::PotentialMatch.new
  863 + end
  864 + end
  865 +
  866 + %w{11111 #1111 *7}.each do |input|
649 867 it "should not match '#{input}'" do
650 868 subject.match(input).should == GRXML::NoMatch.new
651 869 end
440 spec/ruby_speech/grxml/item_spec.rb
@@ -8,7 +8,7 @@ module GRXML
8 8 its(:name) { should == 'item' }
9 9
10 10 its(:weight) { should == 1.1 }
11   - its(:repeat) { should == '1' }
  11 + its(:repeat) { should == 1 }
12 12
13 13 it 'registers itself' do
14 14 Element.class_from_registration(:item).should == Item
@@ -22,7 +22,7 @@ module GRXML
22 22 it { should be_instance_of Item }
23 23
24 24 its(:weight) { should == 1.1 }
25   - its(:repeat) { should == '1' }
  25 + its(:repeat) { should == 1 }
26 26 its(:content) { should == 'one' }
27 27 end
28 28
@@ -69,10 +69,19 @@ module GRXML
69 69 # Validate various values for repeat -- http://www.w3.org/TR/speech-grammar/#S2.5
70 70 describe "#repeat" do
71 71 context "exact" do
72   - it "valid values (0 or a positive integer)" do
73   - lambda { subject.repeat = 0 }.should_not raise_error
74   - lambda { subject.repeat = 5 }.should_not raise_error
75   - lambda { subject.repeat = '1' }.should_not raise_error
  72 + context "0" do
  73 + before { subject.repeat = 0 }
  74 + its(:repeat) { should == 0 }
  75 + end
  76 +
  77 + context "5" do
  78 + before { subject.repeat = 5 }
  79 + its(:repeat) { should == 5 }
  80 + end
  81 +
  82 + context "'1'" do
  83 + before { subject.repeat = '1' }
  84 + its(:repeat) { should == 1 }
76 85 end
77 86
78 87 it "invalid values" do
@@ -82,10 +91,21 @@ module GRXML
82 91 end
83 92
84 93 context "ranges" do
85   - it "valid ranges from m to n" do
86   - lambda { subject.repeat = '1-5' }.should_not raise_error
87   - lambda { subject.repeat = '0-5' }.should_not raise_error
88   - lambda { subject.repeat = 0..5 }.should_not raise_error
  94 + context "valid ranges from m to n" do
  95 + context "'1-5'" do
  96 + before { subject.repeat = '1-5' }
  97 + its(:repeat) { should == (1..5) }
  98 + end
  99 +
  100 + context "'0-5'" do
  101 + before { subject.repeat = '0-5' }
  102 + its(:repeat) { should == (0..5) }
  103 + end
  104 +
  105 + context "0..5" do
  106 + before { subject.repeat = 0..5 }
  107 + its(:repeat) { should == (0..5) }
  108 + end
89 109 end
90 110
91 111 it "illegal ranges from m to n" do
@@ -97,9 +117,18 @@ module GRXML
97 117 lambda { subject.repeat = 1..-2 }.should raise_error(ArgumentError, "A Item's repeat must be 0 or a positive integer")
98 118 end
99 119
100   - it "valid ranges of m or more" do
101   - lambda { subject.repeat = '3-' }.should_not raise_error
102   - lambda { subject.repeat = '0-' }.should_not raise_error
  120 + context "valid ranges of m or more" do
  121 + context "'3-'" do
  122 + before { subject.repeat = '3-' }
  123 + its(:repeat) { should == (3..Item::Inf) }
  124 + its(:repeat) { should include 10000 }
  125 + end
  126 +
  127 + context "'0-'" do
  128 + before { subject.repeat = '0-' }
  129 + its(:repeat) { should == (0..Item::Inf) }
  130 + its(:repeat) { should include 10000 }
  131 + end
103 132 end
104 133
105 134 it "illegal ranges for m or more" do
@@ -158,6 +187,391 @@ module GRXML
158 187 lambda { subject << Token.new }.should_not raise_error
159 188 end
160 189 end
  190 +
  191 + describe "#potential_match?" do
  192 + subject { Item.new }
  193 +
  194 + before do
  195 + tokens.each { |token| subject << token }
  196 + subject.repeat = repeat if repeat
  197 + end
  198 +
  199 + context "with a single token of '6'" do
  200 + let(:tokens) { [Token.new << '6'] }
  201 +
  202 + context "with no repeat" do
  203 + let(:repeat) { nil }
  204 +
  205 + it "should be true for '6'" do
  206 + subject.potential_match?('6').should be true
  207 + end
  208 +
  209 + %w{5 55 65 66}.each do |input|
  210 + it "should be false for '#{input}'" do
  211 + subject.potential_match?(input).should be false
  212 + end
  213 + end
  214 + end
  215 +
  216 + context "with an absolute repeat of 3" do
  217 + let(:repeat) { 3 }
  218 +
  219 + %w{6 66 666}.each do |input|
  220 + it "should be true for '#{input}'" do
  221 + subject.potential_match?(input).should be true
  222 + end
  223 + end
  224 +
  225 + %w{5 55 65 6666}.each do |input|
  226 + it "should be false for '#{input}'" do
  227 + subject.potential_match?(input).should be false
  228 + end
  229 + end
  230 + end
  231 +
  232 + context "with a range repeat of 0..2" do
  233 + let(:repeat) { 0..2 }
  234 +
  235 + it "should be true for ''" do
  236 + subject.potential_match?('').should be true
  237 + end
  238 +
  239 + %w{6 66}.each do |input|
  240 + it "should be true for '#{input}'" do
  241 + subject.potential_match?(input).should be true
  242 + end
  243 + end
  244 +
  245 + %w{5 55 65 666}.each do |input|
  246 + it "should be false for '#{input}'" do
  247 + subject.potential_match?(input).should be false
  248 + end
  249 + end
  250 + end
  251 +
  252 + context "with a minimum repeat of 2" do
  253 + let(:repeat) { 2..Item::Inf }
  254 +
  255 + %w{6 66 666 6666 66666}.each do |input|
  256 + it "should be true for '#{input}'" do
  257 + subject.potential_match?(input).should be true
  258 + end
  259 + end
  260 +
  261 + %w{5 55 65}.each do |input|
  262 + it "should be false for '#{input}'" do
  263 + subject.potential_match?(input).should be false
  264 + end
  265 + end
  266 + end
  267 + end
  268 +
  269 + context "with a collection of two tokens of '6' and '7'" do
  270 + let(:tokens) { [Token.new << '6', Token.new << '7'] }
  271 +
  272 + context "with no repeat" do
  273 + let(:repeat) { nil }
  274 +
  275 + %w{6 67}.each do |input|
  276 + it "should be true for '#{input}'" do
  277 + subject.potential_match?(input).should be true
  278 + end
  279 + end
  280 +
  281 + %w{5 55 65 66 676}.each do |input|
  282 + it "should be false for '#{input}'" do
  283 + subject.potential_match?(input).should be false
  284 + end
  285 + end
  286 + end
  287 +
  288 + context "with an absolute repeat of 3" do
  289 + let(:repeat) { 3 }
  290 +
  291 + %w{6 67 676 6767 67676 676767}.each do |input|
  292 + it "should be true for '#{input}'" do
  293 + subject.potential_match?(input).should be true
  294 + end
  295 + end
  296 +
  297 + %w{5 57 66 677 5767 67677 676766 6767676}.each do |input|
  298 + it "should be false for '#{input}'" do
  299 + subject.potential_match?(input).should be false
  300 + end
  301 + end
  302 + end
  303 +
  304 + context "with a range repeat of 0..2" do
  305 + let(:repeat) { 0..2 }
  306 +
  307 + it "should be true for ''" do
  308 + subject.potential_match?('').should be true
  309 + end
  310 +
  311 + %w{6 67 676 6767}.each do |input|
  312 + it "should be true for '#{input}'" do
  313 + subject.potential_match?(input).should be true
  314 + end
  315 + end
  316 +
  317 + %w{5 57 66 677 5767 67676 67677 676766 6767676}.each do |input|
  318 + it "should be false for '#{input}'" do
  319 + subject.potential_match?(input).should be false
  320 + end
  321 + end
  322 + end
  323 +
  324 + context "with a minimum repeat of 2" do
  325 + let(:repeat) { 2..Item::Inf }
  326 +
  327 + %w{6 67 676 6767 67676 676767 67676767}.each do |input|
  328 + it "should be true for '#{input}'" do
  329 + subject.potential_match?(input).should be true
  330 + end
  331 + end
  332 +
  333 + %w{5 57 66 677 5767 67677 676766}.each do |input|
  334 + it "should be false for '#{input}'" do
  335 + subject.potential_match?(input).should be false
  336 + end
  337 + end
  338 + end
  339 + end
  340 +
  341 + context "with a nested item" do
  342 + let(:repeat) { nil }
  343 + let(:tokens) { [Item.new << (Token.new << '6') << (Token.new << '6')] }
  344 +
  345 + before do
  346 + tokens.each { |token| token.repeat = nested_repeat if nested_repeat }
  347 + end
  348 +
  349 + context "with no repeat" do
  350 + before { pending }
  351 + let(:nested_repeat) { nil }
  352 +
  353 + %w{6 66}.each do |input|
  354 + it "should be true for '#{input}'" do
  355 + subject.potential_match?(input).should be true
  356 + end
  357 + end
  358 +
  359 + %w{5 55 65 666}.each do |input|
  360 + it "should be false for '#{input}'" do
  361 + subject.potential_match?(input).should be false
  362 + end
  363 + end
  364 + end
  365 +
  366 + context "with an absolute repeat of 3" do
  367 + before { pending }
  368 + let(:nested_repeat) { 3 }
  369 +
  370 + %w{6 66 666}.each do |input|
  371 + it "should be true for '#{input}'" do
  372 + subject.potential_match?(input).should be true
  373 + end
  374 + end
  375 +
  376 + %w{5 55 6666}.each do |input|
  377 + it "should be false for '#{input}'" do
  378 + subject.potential_match?(input).should be false
  379 + end
  380 + end
  381 + end
  382 +
  383 + context "with a range repeat of 0..2" do
  384 + before { pending }
  385 + let(:nested_repeat) { 0..2 }
  386 +
  387 + it "should be true for ''" do
  388 + subject.potential_match?('').should be true
  389 + end
  390 +
  391 + %w{6 67 676 6767}.each do |input|
  392 + it "should be true for '#{input}'" do
  393 + subject.potential_match?(input).should be true
  394 + end
  395 + end
  396 +
  397 + %w{5 57 66 677 5767 67676 67677 676766 6767676}.each do |input|
  398 + it "should be false for '#{input}'" do
  399 + subject.potential_match?(input).should be false
  400 + end
  401 + end
  402 + end
  403 +
  404 + context "with a minimum repeat of 2" do
  405 + before { pending }
  406 + let(:nested_repeat) { 2..Item::Inf }
  407 +
  408 + %w{6 67 676 6767 67676 676767 67676767}.each do |input|
  409 + it "should be true for '#{input}'" do
  410 + subject.potential_match?(input).should be true
  411 + end
  412 + end
  413 +
  414 + %w{5 57 66 677 5767 67677 676766}.each do |input|
  415 + it "should be false for '#{input}'" do
  416 + subject.potential_match?(input).should be false
  417 + end
  418 + end
  419 + end
  420 + end
  421 + end
  422 +
  423 + describe "#longest_potential_match" do
  424 + subject { Item.new }
  425 +
  426 + before do
  427 + tokens.each { |token| subject << token }
  428 + subject.repeat = repeat if repeat
  429 + end
  430 +
  431 + context "with a single token of '6'" do
  432 + let(:tokens) { [Token.new << '6'] }
  433 +
  434 + context "with no repeat" do
  435 + let(:repeat) { nil }
  436 +
  437 + %w{6 65 6776}.each do |input|
  438 + it "should be '6' for '#{input}'" do
  439 + subject.longest_potential_match(input).should == '6'
  440 + end
  441 + end
  442 +
  443 + %w{5 7 55 56}.each do |input|
  444 + it "should be '' for '#{input}'" do
  445 + subject.longest_potential_match(input).should == ''
  446 + end
  447 + end
  448 + end
  449 +
  450 + context "with an absolute repeat of 3" do
  451 + let(:repeat) { 3 }
  452 +
  453 + {
  454 + '6' => '6',
  455 + '66' => '66',
  456 + '666' => '666',
  457 + '6666' => '666',
  458 + '66666' => '666'
  459 + }.each do |input, match|
  460 + it "should be '#{match}' for '#{input}'" do
  461 + subject.longest_potential_match(input).should == match
  462 + end
  463 + end
  464 + end
  465 +
  466 + context "with a range repeat of 0..2" do
  467 + let(:repeat) { 0..2 }
  468 +
  469 + {
  470 + '6' => '6',
  471 + '66' => '66',
  472 + '666' => '66',
  473 + '6666' => '66'
  474 + }.each do |input, match|
  475 + it "should be '#{match}' for '#{input}'" do
  476 + subject.longest_potential_match(input).should == match
  477 + end
  478 + end
  479 + end
  480 +
  481 + context "with a minimum repeat of 2" do
  482 + let(:repeat) { 2..Item::Inf }
  483 +
  484 + {
  485 + '6' => '6',
  486 + '66' => '66',
  487 + '666' => '666',
  488 + '6666' => '6666',
  489 + '6'*100 => '6'*100
  490 + }.each do |input, match|
  491 + it "should be '#{match}' for '#{input}'" do
  492 + subject.longest_potential_match(input).should == match
  493 + end
  494 + end
  495 + end
  496 + end
  497 +
  498 + context "with a collection of two tokens of '6' and '7'" do
  499 + let(:tokens) { [Token.new << '6', Token.new << '7'] }
  500 +
  501 + context "with no repeat" do
  502 + let(:repeat) { nil }
  503 +
  504 + {
  505 + '6' => '6',
  506 + '66' => '6',
  507 + '67' => '67',
  508 + '676' => '67',
  509 + '6767' => '67'
  510 + }.each do |input, match|
  511 + it "should be '#{match}' for '#{input}'" do
  512 + subject.longest_potential_match(input).should == match
  513 + end
  514 + end
  515 + end
  516 +
  517 + context "with an absolute repeat of 3" do
  518 + let(:repeat) { 3 }
  519 +
  520 + {
  521 + '6' => '6',
  522 + '66' => '6',
  523 + '67' => '67',
  524 + '676' => '676',
  525 + '6767' => '6767',
  526 + '67676' => '67676',
  527 + '676767' => '676767',
  528 + '6767676' => '676767'
  529 + }.each do |input, match|
  530 + it "should be '#{match}' for '#{input}'" do
  531 + subject.longest_potential_match(input).should == match
  532 + end
  533 + end
  534 + end
  535 +
  536 + context "with a range repeat of 0..2" do
  537 + let(:repeat) { 0..2 }
  538 +
  539 + {
  540 + '6' => '6',
  541 + '66' => '6',
  542 + '67' => '67',
  543 + '676' => '676',
  544 + '6767' => '6767',
  545 + '67676' => '6767',
  546 + '676767' => '6767'
  547 + }.each do |input, match|
  548 + it "should be '#{match}' for '#{input}'" do
  549 + subject.longest_potential_match(input).should == match
  550 + end
  551 + end
  552 + end
  553 +
  554 + context "with a minimum repeat of 2" do
  555 + let(:repeat) { 2..Item::Inf }
  556 +
  557 + {
  558 + '6' => '6',
  559 + '66' => '6',
  560 + '67' => '67',
  561 + '676' => '676',
  562 + '6767' => '6767',
  563 + '67676' => '67676',
  564 + '676767' => '676767',
  565 + '6767676' => '6767676',
  566 + '67'*100 => '67'*100
  567 + }.each do |input, match|
  568 + it "should be '#{match}' for '#{input}'" do
  569 + subject.longest_potential_match(input).should == match
  570 + end
  571 + end
  572 + end
  573 + end
  574 + end
161 575 end # Item
162 576 end # GRXML
163 577 end # RubySpeech
237 spec/ruby_speech/grxml/one_of_spec.rb
@@ -45,6 +45,243 @@ module GRXML
45 45 end
46 46 end
47 47
  48 + describe "#potential_match?" do
  49 + before do
  50 + items.each { |item| subject << item }
  51 + end
  52 +
  53 + context "with a single item of '6'" do
  54 + let(:items) { [Item.new << (Token.new << '6')] }
  55 +
  56 + it "should be true for '6'" do
  57 + subject.potential_match?('6').should be true
  58 + end
  59 +
  60 + %w{5 7}.each do |input|
  61 + it "should be false for '#{input}'" do
  62 + subject.potential_match?(input).should be false
  63 + end
  64 + end
  65 + end
  66 +
  67 + context "with options of '6' or '7'" do
  68 + let(:items) { [Item.new << (Token.new << '6'), Item.new << (Token.new << '7')] }
  69 +
  70 + %w{6 7}.each do |input|
  71 + it "should be true for '#{input}'" do
  72 + subject.potential_match?(input).should be true
  73 + end
  74 + end
  75 +
  76 + %w{5 8 67 76}.each do |input|
  77 + it "should be false for '#{input}'" do
  78 + subject.potential_match?(input).should be false
  79 + end
  80 + end
  81 + end
  82 +
  83 + context "with options of '67' or '25'" do
  84 + let(:items) { [Item.new << (Token.new << '6') << (Token.new << '7'), Item.new << (Token.new << '2') << (Token.new << '5')] }
  85 +
  86 + %w{6 2}.each do |input|
  87 + it "should be true for '#{input}'" do
  88 + subject.potential_match?(input).should be true
  89 + end
  90 + end
  91 +
  92 + %w{3 7 5 65 27 76 52}.each do |input|
  93 + it "should be false for '#{input}'" do
  94 + subject.potential_match?(input).should be false
  95 + end
  96 + end
  97 + end
  98 +
  99 + context "with options of '678' or '251'" do
  100 + let(:items) { [Item.new << (Token.new << '6') << (Token.new << '7') << (Token.new << '8'), Item.new << (Token.new << '2') << (Token.new << '5') << (Token.new << '1')] }
  101 +
  102 + %w{6 67 2 25}.each do |input|
  103 + it "should be true for '#{input}'" do
  104 + subject.potential_match?(input).should be true
  105 + end
  106 + end
  107 +
  108 + %w{3 7 5 65 27 76 52}.each do |input|
  109 + it "should be false for '#{input}'" do
  110 + subject.potential_match?(input).should be false
  111 + end
  112 + end
  113 + end
  114 +
  115 + context "with options of '6' or ('7' repeated twice)" do
  116 + let(:items) { [Item.new << (Token.new << '6'), Item.new << (Item.new(:repeat => 2) << (Token.new << '7'))] }
  117 +
  118 + %w{6 7 77}.each do |input|
  119 + it "should be true for '#{input}'" do
  120 + pending
  121 + subject.potential_match?(input).should be true
  122 + end
  123 + end
  124 +
  125 + %w{5 67 76 66}.each do |input|
  126 + it "should be false for '#{input}'" do
  127 + subject.potential_match?(input).should be false
  128 + end
  129 + end
  130 + end
  131 + end
  132 +
  133 + describe "#longest_potential_match" do
  134 + before do
  135 + items.each { |item| subject << item }
  136 + end
  137 +
  138 + context "with a single item of '6'" do
  139 + let(:items) { [Item.new << (Token.new << '6')] }
  140 +
  141 + %w{6 65 6776}.each do |input|
  142 + it "should be '6' for '#{input}'" do
  143 + subject.longest_potential_match(input).should == '6'
  144 + end
  145 + end
  146 +
  147 + %w{5 7 55 56}.each do |input|
  148 + it "should be '' for '#{input}'" do
  149 + subject.longest_potential_match(input).should == ''
  150 + end
  151 + end
  152 + end
  153 +
  154 + context "with options of '6' or '7'" do
  155 + let(:items) { [Item.new << (Token.new << '6'), Item.new << (Token.new << '7')] }
  156 +
  157 + %w{6 65 6776}.each do |input|
  158 + it "should be '6' for '#{input}'" do
  159 + subject.longest_potential_match(input).should == '6'
  160 + end
  161 +