ruby_speech 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
 
@@ -164,6 +164,7 @@ module RubySpeech
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)
@@ -15,6 +15,7 @@ module RubySpeech
15
15
 
16
16
  autoload :Match
17
17
  autoload :NoMatch
18
+ autoload :PotentialMatch
18
19
 
19
20
  InvalidChildError = Class.new StandardError
20
21
 
@@ -22,6 +22,23 @@ module RubySpeech
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
@@ -195,15 +195,19 @@ module RubySpeech
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 @@ module RubySpeech
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
  #
@@ -26,6 +26,7 @@ module RubySpeech
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 @@ module RubySpeech
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 @@ module RubySpeech
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 @@ module RubySpeech
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
@@ -26,6 +26,10 @@ module RubySpeech
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
@@ -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
@@ -26,6 +26,10 @@ module RubySpeech
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
@@ -1,3 +1,3 @@
1
1
  module RubySpeech
2
- VERSION = "0.5.0"
2
+ VERSION = "0.5.1"
3
3
  end
@@ -360,11 +360,13 @@ module RubySpeech
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 @@ module RubySpeech
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 @@ module RubySpeech
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 @@ module RubySpeech
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 @@ module RubySpeech
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 @@ module RubySpeech
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 @@ module RubySpeech
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 @@ module RubySpeech
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 @@ module RubySpeech
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