ruby_speech 1.1.0 → 2.0.1

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.
@@ -14,6 +14,7 @@ module RubySpeech
14
14
  end
15
15
 
16
16
  autoload :Match
17
+ autoload :Matcher
17
18
  autoload :NoMatch
18
19
  autoload :PotentialMatch
19
20
 
@@ -24,23 +24,6 @@ module RubySpeech
24
24
  def regexp_content # :nodoc:
25
25
  children.map(&:regexp_content).join
26
26
  end
27
-
28
- def potential_match?(other)
29
- false
30
- end
31
-
32
- def max_input_length
33
- 0
34
- end
35
-
36
- def longest_potential_match(input)
37
- input.dup.tap do |longest_input|
38
- begin
39
- return longest_input if potential_match? longest_input
40
- longest_input.chop!
41
- end until longest_input.length.zero?
42
- end
43
- end
44
27
  end # Element
45
28
  end # GRXML
46
29
  end # RubySpeech
@@ -149,99 +149,6 @@ module RubySpeech
149
149
  end
150
150
  end
151
151
 
152
- ##
153
- # Checks the grammar for a match against an input string
154
- #
155
- # @param [String] other the input string to check for a match with the grammar
156
- #
157
- # @return [NoMatch, Match] depending on the result of a match attempt. If a match can be found, it will be returned with appropriate mode/confidence/utterance and interpretation attributes
158
- #
159
- # @example A grammar that takes a 4 digit pin terminated by hash, or the *9 escape sequence
160
- # ```ruby
161
- # grammar = RubySpeech::GRXML.draw :mode => :dtmf, :root => 'pin' do
162
- # rule :id => 'digit' do
163
- # one_of do
164
- # ('0'..'9').map { |d| item { d } }
165
- # end
166
- # end
167
- #
168
- # rule :id => 'pin', :scope => 'public' do
169
- # one_of do
170
- # item do
171
- # item :repeat => '4' do
172
- # ruleref :uri => '#digit'
173
- # end
174
- # "#"
175
- # end
176
- # item do
177
- # "\* 9"
178
- # end
179
- # end
180
- # end
181
- # end
182
- #
183
- # >> subject.match '*9'
184
- # => #<RubySpeech::GRXML::Match:0x00000100ae5d98
185
- # @mode = :dtmf,
186
- # @confidence = 1,
187
- # @utterance = "*9",
188
- # @interpretation = "*9"
189
- # >
190
- # >> subject.match '1234#'
191
- # => #<RubySpeech::GRXML::Match:0x00000100b7e020
192
- # @mode = :dtmf,
193
- # @confidence = 1,
194
- # @utterance = "1234#",
195
- # @interpretation = "1234#"
196
- # >
197
- # >> subject.match '111'
198
- # => #<RubySpeech::GRXML::PotentialMatch:0x00000101371660>
199
- #
200
- # >> subject.match '11111'
201
- # => #<RubySpeech::GRXML::NoMatch:0x00000101371936>
202
- #
203
- # ```
204
- #
205
- def match(other)
206
- other = other.dup
207
- regex = to_regexp
208
- return check_for_potential_match(other) if regex == //
209
- match = regex.match other
210
- return check_for_potential_match(other) unless match
211
-
212
- Match.new :mode => mode,
213
- :confidence => dtmf? ? 1 : 0,
214
- :utterance => other,
215
- :interpretation => interpret_utterance(other)
216
- end
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
-
232
- ##
233
- # Converts the grammar into a regular expression for matching
234
- #
235
- # @return [Regexp] a regular expression which is equivalent to the grammar
236
- #
237
- def to_regexp
238
- /^#{regexp_content.join}$/
239
- end
240
-
241
- def regexp_content
242
- root_rule.children.map &:regexp_content
243
- end
244
-
245
152
  def dtmf?
246
153
  mode == :dtmf
247
154
  end
@@ -270,16 +177,6 @@ module RubySpeech
270
177
  !root || root_rule
271
178
  end
272
179
 
273
- def interpret_utterance(utterance)
274
- conversion = Hash.new { |hash, key| hash[key] = key }
275
- conversion['*'] = 'star'
276
- conversion['#'] = 'pound'
277
-
278
- utterance.chars.inject [] do |array, digit|
279
- array << "dtmf-#{conversion[digit]}"
280
- end.join ' '
281
- end
282
-
283
180
  def split_tokens(element)
284
181
  element.to_s.split(/(\".*\")/).reject(&:empty?).map do |string|
285
182
  match = string.match /^\"(.*)\"$/
@@ -139,27 +139,6 @@ module RubySpeech
139
139
  super
140
140
  end
141
141
  end
142
-
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
159
- else
160
- children.size
161
- end
162
- end
163
142
  end # Item
164
143
  end # GRXML
165
144
  end # RubySpeech
@@ -0,0 +1,129 @@
1
+ require 'ruby_speech/ruby_speech'
2
+
3
+ if RUBY_PLATFORM =~ /java/
4
+ require 'jruby'
5
+ com.benlangfeld.ruby_speech.RubySpeechService.new.basicLoad(JRuby.runtime)
6
+ end
7
+
8
+ module RubySpeech
9
+ module GRXML
10
+ class Matcher
11
+
12
+ BLANK_REGEX = //.freeze
13
+
14
+ attr_reader :grammar, :regex
15
+
16
+ def initialize(grammar)
17
+ @grammar = grammar
18
+ prepare_grammar
19
+ @regex = /^#{regexp_content.join}$/
20
+ end
21
+
22
+ ##
23
+ # Checks the grammar for a match against an input string
24
+ #
25
+ # @param [String] other the input string to check for a match with the grammar
26
+ #
27
+ # @return [NoMatch, Match] depending on the result of a match attempt. If a match can be found, it will be returned with appropriate mode/confidence/utterance and interpretation attributes
28
+ #
29
+ # @example A grammar that takes a 4 digit pin terminated by hash, or the *9 escape sequence
30
+ # ```ruby
31
+ # grammar = RubySpeech::GRXML.draw :mode => :dtmf, :root => 'pin' do
32
+ # rule :id => 'digit' do
33
+ # one_of do
34
+ # ('0'..'9').map { |d| item { d } }
35
+ # end
36
+ # end
37
+ #
38
+ # rule :id => 'pin', :scope => 'public' do
39
+ # one_of do
40
+ # item do
41
+ # item :repeat => '4' do
42
+ # ruleref :uri => '#digit'
43
+ # end
44
+ # "#"
45
+ # end
46
+ # item do
47
+ # "\* 9"
48
+ # end
49
+ # end
50
+ # end
51
+ # end
52
+ #
53
+ # matcher = RubySpeech::GRXML::Matcher.new grammar
54
+ #
55
+ # >> matcher.match '*9'
56
+ # => #<RubySpeech::GRXML::Match:0x00000100ae5d98
57
+ # @mode = :dtmf,
58
+ # @confidence = 1,
59
+ # @utterance = "*9",
60
+ # @interpretation = "*9"
61
+ # >
62
+ # >> matcher.match '1234#'
63
+ # => #<RubySpeech::GRXML::Match:0x00000100b7e020
64
+ # @mode = :dtmf,
65
+ # @confidence = 1,
66
+ # @utterance = "1234#",
67
+ # @interpretation = "1234#"
68
+ # >
69
+ # >> matcher.match '5678#'
70
+ # => #<RubySpeech::GRXML::Match:0x00000101218688
71
+ # @mode = :dtmf,
72
+ # @confidence = 1,
73
+ # @utterance = "5678#",
74
+ # @interpretation = "5678#"
75
+ # >
76
+ # >> matcher.match '1111#'
77
+ # => #<RubySpeech::GRXML::Match:0x000001012f69d8
78
+ # @mode = :dtmf,
79
+ # @confidence = 1,
80
+ # @utterance = "1111#",
81
+ # @interpretation = "1111#"
82
+ # >
83
+ # >> matcher.match '111'
84
+ # => #<RubySpeech::GRXML::NoMatch:0x00000101371660>
85
+ # ```
86
+ #
87
+ def match(buffer)
88
+ buffer = buffer.dup
89
+
90
+ return check_potential_match(buffer) if regex == BLANK_REGEX
91
+
92
+ check_full_match(buffer) || check_potential_match(buffer) || NoMatch.new
93
+ end
94
+
95
+ private
96
+
97
+ def prepare_grammar
98
+ grammar.inline!
99
+ grammar.tokenize!
100
+ grammar.normalize_whitespace
101
+ end
102
+
103
+ def check_full_match(buffer)
104
+ match = regex.match buffer
105
+
106
+ return unless match
107
+
108
+ Match.new :mode => grammar.mode,
109
+ :confidence => grammar.dtmf? ? 1 : 0,
110
+ :utterance => buffer,
111
+ :interpretation => interpret_utterance(buffer)
112
+ end
113
+
114
+ def regexp_content
115
+ grammar.root_rule.children.map &:regexp_content
116
+ end
117
+
118
+ def interpret_utterance(utterance)
119
+ conversion = Hash.new { |hash, key| hash[key] = key }
120
+ conversion['*'] = 'star'
121
+ conversion['#'] = 'pound'
122
+
123
+ utterance.chars.inject [] do |array, digit|
124
+ array << "dtmf-#{conversion[digit]}"
125
+ end.join ' '
126
+ end
127
+ end
128
+ end
129
+ end
@@ -26,10 +26,6 @@ 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
33
29
  end # OneOf
34
30
  end # GRXML
35
31
  end # RubySpeech
@@ -26,10 +26,6 @@ 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
33
29
  end # Token
34
30
  end # GRXML
35
31
  end # RubySpeech
@@ -2,8 +2,7 @@ module RubySpeech
2
2
  module NLSML
3
3
  extend ActiveSupport::Autoload
4
4
 
5
- NLSML_NAMESPACE = 'http://www.w3c.org/2000/11/nlsml'
6
- XFORMS_NAMESPACE = 'http://www.w3.org/2000/xforms'
5
+ NLSML_NAMESPACE = 'http://www.ietf.org/xml/ns/mrcpv2'
7
6
 
8
7
  eager_autoload do
9
8
  autoload :Builder
@@ -4,7 +4,7 @@ module RubySpeech
4
4
  attr_reader :document
5
5
 
6
6
  def initialize(options = {}, &block)
7
- options = {'xmlns' => NLSML_NAMESPACE, 'xmlns:xf' => XFORMS_NAMESPACE}.merge(options)
7
+ options = {'xmlns' => NLSML_NAMESPACE}.merge(options)
8
8
  @document = Nokogiri::XML::Builder.new do |builder|
9
9
  builder.result options do |r|
10
10
  apply_block r, &block
@@ -14,19 +14,11 @@ module RubySpeech
14
14
 
15
15
  def interpretation(*args, &block)
16
16
  if args.last.respond_to?(:has_key?) && args.last.has_key?(:confidence)
17
- args.last[:confidence] = (args.last[:confidence] * 100).to_i
17
+ args.last[:confidence] = args.last[:confidence].to_f
18
18
  end
19
19
  @result.send :interpretation, *args, &block
20
20
  end
21
21
 
22
- def model(*args, &block)
23
- xf_namespaced_element :model, *args, &block
24
- end
25
-
26
- def instance(*args, &block)
27
- xf_namespaced_element :instance, *args, &block
28
- end
29
-
30
22
  def method_missing(method_name, *args, &block)
31
23
  @result.send method_name, *args, &block
32
24
  end
@@ -37,11 +29,6 @@ module RubySpeech
37
29
  @result = result
38
30
  instance_eval &block
39
31
  end
40
-
41
- def xf_namespaced_element(element_name, *args, &block)
42
- namespace = @result.send :[], 'xf'
43
- namespace.send element_name, &block
44
- end
45
32
  end
46
33
  end
47
34
  end
@@ -4,8 +4,11 @@ module RubySpeech
4
4
  module NLSML
5
5
  class Document < SimpleDelegator
6
6
  def initialize(xml)
7
+ unless xml.root.namespace
8
+ xml.root.default_namespace = NLSML_NAMESPACE
9
+ xml = Nokogiri::XML.parse xml.to_xml, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS
10
+ end
7
11
  super
8
- @xml = xml
9
12
  end
10
13
 
11
14
  def grammar
@@ -41,19 +44,19 @@ module RubySpeech
41
44
  end
42
45
 
43
46
  def nomatch_elements
44
- result.xpath 'ns:interpretation/ns:input/ns:nomatch|interpretation/input/nomatch', 'ns' => NLSML_NAMESPACE
47
+ result.xpath 'ns:interpretation/ns:input/ns:nomatch', 'ns' => NLSML_NAMESPACE
45
48
  end
46
49
 
47
50
  def noinput_elements
48
- result.xpath 'ns:interpretation/ns:input/ns:noinput|interpretation/input/noinput', 'ns' => NLSML_NAMESPACE
51
+ result.xpath 'ns:interpretation/ns:input/ns:noinput', 'ns' => NLSML_NAMESPACE
49
52
  end
50
53
 
51
54
  def input_elements
52
- result.xpath 'ns:interpretation/ns:input|interpretation/input', 'ns' => NLSML_NAMESPACE
55
+ result.xpath 'ns:interpretation/ns:input', 'ns' => NLSML_NAMESPACE
53
56
  end
54
57
 
55
58
  def input_hash_for_interpretation(interpretation)
56
- input_element = interpretation.at_xpath '(ns:input|input)', 'ns' => NLSML_NAMESPACE
59
+ input_element = interpretation.at_xpath 'ns:input', 'ns' => NLSML_NAMESPACE
57
60
  { content: input_element.content }.tap do |h|
58
61
  h[:mode] = input_element['mode'].to_sym if input_element['mode']
59
62
  end
@@ -73,10 +76,11 @@ module RubySpeech
73
76
  end
74
77
 
75
78
  def instance_elements(interpretation)
76
- interpretation.xpath '(xf:instance|ns:instance|instance)', 'xf' => XFORMS_NAMESPACE, 'ns' => NLSML_NAMESPACE
79
+ interpretation.xpath 'ns:instance', 'ns' => NLSML_NAMESPACE
77
80
  end
78
81
 
79
82
  def element_children_key_value(element)
83
+ return element.children.first.content if element.children.first.is_a?(Nokogiri::XML::Text)
80
84
  element.children.inject({}) do |acc, child|
81
85
  acc[child.node_name.to_sym] = case child.children.count
82
86
  when 0
@@ -96,7 +100,7 @@ module RubySpeech
96
100
 
97
101
  def interpretation_hash_for_interpretation(interpretation)
98
102
  {
99
- confidence: interpretation['confidence'].to_f/100,
103
+ confidence: interpretation['confidence'].to_f,
100
104
  input: input_hash_for_interpretation(interpretation),
101
105
  instance: instance_hash_for_interpretation(interpretation),
102
106
  instances: instances_collection_for_interpretation(interpretation)
@@ -108,8 +112,8 @@ module RubySpeech
108
112
  end
109
113
 
110
114
  def interpretation_nodes
111
- nodes = result.xpath '(ns:interpretation|interpretation)', 'ns' => NLSML_NAMESPACE
112
- nodes.sort_by { |int| -int[:confidence].to_i }
115
+ nodes = result.xpath 'ns:interpretation', 'ns' => NLSML_NAMESPACE
116
+ nodes.sort_by { |int| -int[:confidence].to_f }
113
117
  end
114
118
  end
115
119
  end