ruby_speech 2.2.2 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f773e0cb875c8a40f62c2fbf8763293fc1666ff7
4
- data.tar.gz: 7c54e1b650e3e9542740608f108a7455c7625e9c
3
+ metadata.gz: 891276a2d84f160536a3eb7bd4841b96a466a462
4
+ data.tar.gz: b97014a5a89012d01a7a7429aa4a0a0055af58e4
5
5
  SHA512:
6
- metadata.gz: 31b7e25e0214bf2738601217c0a417c308c8057a5a109f39eaa80632dbfc9fc7423122ac9dc7e7c2de665b4a6be4fe563338749f0d4093fb648246dad27656b2
7
- data.tar.gz: 5140d70c2c151201ffe5fe75e14653292944111470f86caa483d4fee774fc0f92d21ddea656f62f722e95482deba0da8e6fb16e5652b3bcb4cc76041bf956392
6
+ metadata.gz: 54672c541a1203e51fa9058f7fff47f469854ddb551c90b0c786dd8c370bf2f9177360abe61d2bb357cf6d266b5b84e4e069851c499c7311cce9dc035acf119e
7
+ data.tar.gz: 9493a2b75a32fc7d2217708362848f131a69136fdade9f95fd9dff6d9b84c947567f56beb55e4535186a59fa18c66620e7933e44ca44f84d56814754044463fe
@@ -1,5 +1,9 @@
1
1
  # [develop](https://github.com/benlangfeld/ruby_speech)
2
2
 
3
+ # [2.3.0](https://github.com/benlangfeld/ruby_speech/compare/v2.2.2...v2.3.0) - [2013-09-30](https://rubygems.org/gems/ruby_speech/versions/2.3.0)
4
+ * Feature: Allow generation of a boolean, date, digits, currency, number, phone or time grammar including from URIs
5
+ * Bugfix: Ensure that rule refs can be reused when inlining grammars
6
+
3
7
  # [2.2.2](https://github.com/benlangfeld/ruby_speech/compare/v2.2.1...v2.2.2) - [2013-09-03](https://rubygems.org/gems/ruby_speech/versions/2.2.2)
4
8
  * Bugfix: Fix an exception message to include object type
5
9
 
data/README.md CHANGED
@@ -135,6 +135,56 @@ which becomes
135
135
  </grammar>
136
136
  ```
137
137
 
138
+ #### Built-in grammars
139
+
140
+ There are some grammars pre-defined which are available from the `RubySpeech::GRXML::Builtins` module like so:
141
+
142
+ ```ruby
143
+ require 'ruby_speech'
144
+
145
+ RubySpeech::GRXML::Builtins.currency
146
+ ```
147
+
148
+ which yields
149
+
150
+ ```xml
151
+ <grammar xmlns="http://www.w3.org/2001/06/grammar" version="1.0" xml:lang="en-US" mode="dtmf" root="currency">
152
+ <rule id="currency" scope="public">
153
+ <item repeat="0-">
154
+ <ruleref uri="#digit"/>
155
+ </item>
156
+ <item>*</item>
157
+ <item repeat="2">
158
+ <ruleref uri="#digit"/>
159
+ </item>
160
+ </rule>
161
+ <rule id="digit">
162
+ <one-of>
163
+ <item>0</item>
164
+ <item>1</item>
165
+ <item>2</item>
166
+ <item>3</item>
167
+ <item>4</item>
168
+ <item>5</item>
169
+ <item>6</item>
170
+ <item>7</item>
171
+ <item>8</item>
172
+ <item>9</item>
173
+ </one-of>
174
+ </rule>
175
+ </grammar>
176
+ ```
177
+
178
+ These grammars come from the VoiceXML specification, and can be used as indicated there (including parameterisation). They can be used just like any you would manually create, and there's nothing special about them except that they are already defined for you. A full list of available grammars can be found in [the API documentation](http://rubydoc.info/gems/ruby_speech/RubySpeech/GRXML/Builtins).
179
+
180
+ These grammars are also available via URI like so:
181
+
182
+ ```ruby
183
+ require 'ruby_speech'
184
+
185
+ RubySpeech::GRXML.from_uri('builtin:dtmf/boolean?y=3;n=4')
186
+ ```
187
+
138
188
  #### Grammar matching
139
189
 
140
190
  It is possible to match some arbitrary input against a GRXML grammar, like so:
@@ -50,7 +50,7 @@ static int is_match_end(pcre *compiled_regex, const char *input)
50
50
  search_input[input_size] = *search++;
51
51
  result = pcre_exec(compiled_regex, NULL, search_input, input_size + 1, 0, 0,
52
52
  ovector, sizeof(ovector) / sizeof(ovector[0]));
53
- if (result > 0) return 0;
53
+ if (result > -1) return 0;
54
54
  }
55
55
  return 1;
56
56
  }
@@ -82,7 +82,7 @@ static VALUE method_find_match(VALUE self, VALUE buffer)
82
82
  }
83
83
  return rb_funcall(self, rb_intern("match_for_buffer"), 1, buffer);
84
84
  }
85
- if (result == PCRE_ERROR_PARTIAL) {
85
+ if (result == PCRE_ERROR_PARTIAL || (int)strlen(input) == 0) {
86
86
  VALUE PotentialMatch = rb_const_get(GRXML, rb_intern("PotentialMatch"));
87
87
  return rb_class_new_instance(0, NULL, PotentialMatch);
88
88
  }
@@ -8,6 +8,7 @@ module RubySpeech
8
8
  GRXML_NAMESPACE = 'http://www.w3.org/2001/06/grammar'
9
9
 
10
10
  %w{
11
+ builtins
11
12
  grammar
12
13
  rule
13
14
  item
@@ -29,5 +30,28 @@ module RubySpeech
29
30
  def self.import(other)
30
31
  Element.import other
31
32
  end
33
+
34
+ URI_REGEX = /builtin:(?<class>.*)\/(?<type>\w*)(\?)?(?<query>(\w*=\w*;?)*)?/.freeze
35
+
36
+ #
37
+ # Fetch a builtin grammar by URI
38
+ #
39
+ # @param [String] uri The builtin grammar URI of the form "builtin:dtmf/type?param=value"
40
+ #
41
+ # @return [RubySpeech::GRXML::Grammar] a grammar from the builtin set
42
+ #
43
+ def self.from_uri(uri)
44
+ match = uri.match(URI_REGEX)
45
+ raise ArgumentError, "Only builtin grammars are supported" unless match
46
+ raise ArgumentError, "Only DTMF builtins are supported" unless match[:class] == 'dtmf'
47
+ type = match[:type]
48
+ query = {}
49
+ match[:query].split(';').each do |s|
50
+ key, value = s.split('=')
51
+ query[key] = value
52
+ end
53
+ raise ArgumentError, "#{type} is an invalid builtin grammar" unless Builtins.respond_to?(type)
54
+ Builtins.send type, query
55
+ end
32
56
  end # GRXML
33
57
  end # RubySpeech
@@ -0,0 +1,174 @@
1
+ module RubySpeech::GRXML::Builtins
2
+ #
3
+ # Create a grammar for interpreting a boolean response, where 1 is true and two is false.
4
+ #
5
+ # @param [Hash] options Options to parameterize the grammar
6
+ # @option options [#to_s] :y The positive/truthy/affirmative digit
7
+ # @option options [#to_s] :n The negative/falsy digit
8
+ #
9
+ # @return [RubySpeech::GRXML::Grammar] a grammar for interpreting a boolean response.
10
+ #
11
+ # @raise [ArgumentError] if the :y and :n options are the same
12
+ #
13
+ def self.boolean(options = {})
14
+ truthy_digit = (options[:y] || options['y'] || '1').to_s
15
+ falsy_digit = (options[:n] || options['n'] || '2').to_s
16
+
17
+ raise ArgumentError, "Yes and no values cannot be the same" if truthy_digit == falsy_digit
18
+
19
+ RubySpeech::GRXML.draw mode: :dtmf, root: 'boolean' do
20
+ rule id: 'boolean', scope: 'public' do
21
+ one_of do
22
+ item do
23
+ tag { 'true' }
24
+ truthy_digit
25
+ end
26
+ item do
27
+ tag { 'false' }
28
+ falsy_digit
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ #
36
+ # Create a grammar for interpreting a date.
37
+ #
38
+ # @return [RubySpeech::GRXML::Grammar] a grammar for interpreting a date in the format yyyymmdd
39
+ #
40
+ def self.date(options = nil)
41
+ RubySpeech::GRXML.draw mode: :dtmf, root: 'date' do
42
+ rule id: 'date', scope: 'public' do
43
+ item repeat: '8' do
44
+ one_of do
45
+ 0.upto(9) { |d| item { d.to_s } }
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ #
53
+ # Create a grammar for interpreting a string of digits.
54
+ #
55
+ # @param [Hash] options Options to parameterize the grammar
56
+ # @option options [#to_i] :minlength Minimum length for the string of digits.
57
+ # @option options [#to_i] :maxlength Maximum length for the string of digits.
58
+ # @option options [#to_i] :length Absolute length for the string of digits.
59
+ #
60
+ # @return [RubySpeech::GRXML::Grammar] a grammar for interpreting a boolean response.
61
+ #
62
+ # @raise [ArgumentError] if any of the length attributes logically conflict
63
+ #
64
+ def self.digits(options = {})
65
+ raise ArgumentError, "Cannot specify both absolute length and a length range" if options[:length] && (options[:minlength] || options[:maxlength])
66
+
67
+ minlength = options[:minlength] || options['minlength'] || 0
68
+ maxlength = options[:maxlength] || options['maxlength']
69
+ length = options[:length] || options['length']
70
+
71
+ repeat = length ? length : "#{minlength}-#{maxlength}"
72
+
73
+ RubySpeech::GRXML.draw mode: :dtmf, root: 'digits' do
74
+ rule id: 'digits', scope: 'public' do
75
+ item repeat: repeat do
76
+ one_of do
77
+ 0.upto(9) { |d| item { d.to_s } }
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ #
85
+ # Create a grammar for interpreting a monetary value. Uses '*' as the decimal point.
86
+ # Matches any number of digits, optionally followed by a '*' and up to two more digits.
87
+ #
88
+ # @return [RubySpeech::GRXML::Grammar] a grammar for interpreting a monetary value.
89
+ #
90
+ def self.currency(options = nil)
91
+ RubySpeech::GRXML.draw mode: :dtmf, root: 'currency' do
92
+ rule id: 'currency', scope: 'public' do
93
+ item repeat: '0-' do
94
+ ruleref uri: '#digit'
95
+ end
96
+ item repeat: '0-1' do
97
+ item { '*' }
98
+ item repeat: '0-2' do
99
+ ruleref uri: '#digit'
100
+ end
101
+ end
102
+ end
103
+
104
+ rule id: 'digit' do
105
+ one_of do
106
+ 0.upto(9) { |d| item { d.to_s } }
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ #
113
+ # Create a grammar for interpreting a numeric value. Uses '*' as the decimal point.
114
+ # Matches any number of digits, optionally followed by a '*' and any number more digits.
115
+ #
116
+ # @return [RubySpeech::GRXML::Grammar] a grammar for interpreting a numeric value.
117
+ #
118
+ def self.number(options = nil)
119
+ RubySpeech::GRXML.draw mode: :dtmf, root: 'number' do
120
+ rule id: 'number', scope: 'public' do
121
+ item repeat: '0-' do
122
+ ruleref uri: '#digit'
123
+ end
124
+ item repeat: '0-1' do
125
+ item { '*' }
126
+ item repeat: '0-' do
127
+ ruleref uri: '#digit'
128
+ end
129
+ end
130
+ end
131
+
132
+ rule id: 'digit' do
133
+ one_of do
134
+ 0.upto(9) { |d| item { d.to_s } }
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ #
141
+ # Create a grammar for interpreting a phone number. Uses '*' to represent 'x' for a number with an extension.
142
+ #
143
+ # @return [RubySpeech::GRXML::Grammar] a grammar for interpreting a phone number.
144
+ #
145
+ def self.phone(options = nil)
146
+ RubySpeech::GRXML.draw mode: :dtmf, root: 'number' do
147
+ rule id: 'number', scope: 'public' do
148
+ item repeat: '1-' do
149
+ one_of do
150
+ 0.upto(9) { |d| item { d.to_s } }
151
+ item { '*' }
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ #
159
+ # Create a grammar for interpreting a time.
160
+ #
161
+ # @return [RubySpeech::GRXML::Grammar] a grammar for interpreting a time.
162
+ #
163
+ def self.time(options = nil)
164
+ RubySpeech::GRXML.draw mode: :dtmf, root: 'time' do
165
+ rule id: 'time', scope: 'public' do
166
+ item repeat: '1-4' do
167
+ one_of do
168
+ 0.upto(9) { |d| item { d.to_s } }
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
@@ -118,7 +118,7 @@ module RubySpeech
118
118
  def inline!
119
119
  xpath("//ns:ruleref", :ns => GRXML_NAMESPACE).each do |ref|
120
120
  rule = rule_with_id ref[:uri].sub(/^#/, '')
121
- ref.swap rule.children
121
+ ref.swap rule.dup.children
122
122
  end
123
123
 
124
124
  query = "./ns:rule[@id!='#{root}']"
@@ -1,3 +1,3 @@
1
1
  module RubySpeech
2
- VERSION = "2.2.2"
2
+ VERSION = "2.3.0"
3
3
  end
@@ -10,6 +10,8 @@ Gem::Specification.new do |s|
10
10
  s.homepage = "https://github.com/benlangfeld/ruby_speech"
11
11
  s.summary = %q{A Ruby library for TTS & ASR document preparation}
12
12
  s.description = %q{Prepare SSML and GRXML documents with ease}
13
+
14
+ s.license = 'MIT'
13
15
 
14
16
  s.rubyforge_project = "ruby_speech"
15
17
 
@@ -0,0 +1,165 @@
1
+ require 'spec_helper'
2
+
3
+ describe RubySpeech::GRXML::Builtins do
4
+ describe "boolean" do
5
+ subject(:grammar) { described_class.boolean }
6
+
7
+ it { should max_match('1').and_interpret_as('true') }
8
+ it { should max_match('2').and_interpret_as('false') }
9
+
10
+ it { should not_match('0') }
11
+ it { should not_match('3') }
12
+ it { should not_match('10') }
13
+
14
+ context "with the true/false digits parameterized" do
15
+ subject { described_class.boolean y: 3, n: 7 }
16
+
17
+ it { should max_match('3').and_interpret_as('true') }
18
+ it { should max_match('7').and_interpret_as('false') }
19
+
20
+ it { should not_match('1') }
21
+ it { should not_match('2') }
22
+ it { should not_match('4') }
23
+
24
+ context "both the same" do
25
+ it "should raise ArgumentError" do
26
+ expect { described_class.boolean y: '1', n: 1 }.to raise_error(ArgumentError, /same/)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "date" do
33
+ subject(:grammar) { described_class.date }
34
+
35
+ it { should max_match('20130929').and_interpret_as('dtmf-2 dtmf-0 dtmf-1 dtmf-3 dtmf-0 dtmf-9 dtmf-2 dtmf-9') }
36
+
37
+ it { should potentially_match('130929') }
38
+ it { should potentially_match('0929') }
39
+ it { should potentially_match('29') }
40
+ it { should potentially_match('1') }
41
+ end
42
+
43
+ describe "digits" do
44
+ subject(:grammar) { described_class.digits }
45
+
46
+ it { should match('1').and_interpret_as('dtmf-1') }
47
+ it { should match('123').and_interpret_as('dtmf-1 dtmf-2 dtmf-3') }
48
+ it { should match('1').and_interpret_as('dtmf-1') }
49
+
50
+ context "with a minimum length" do
51
+ subject { described_class.digits minlength: 3 }
52
+
53
+ it { should match('123').and_interpret_as('dtmf-1 dtmf-2 dtmf-3') }
54
+ it { should match('1234567890').and_interpret_as('dtmf-1 dtmf-2 dtmf-3 dtmf-4 dtmf-5 dtmf-6 dtmf-7 dtmf-8 dtmf-9 dtmf-0') }
55
+
56
+ it { should potentially_match('1') }
57
+ it { should potentially_match('11') }
58
+ it { should potentially_match('4') }
59
+ end
60
+
61
+ context "with a maximum length" do
62
+ subject { described_class.digits maxlength: 3 }
63
+
64
+ it { should match('1').and_interpret_as('dtmf-1') }
65
+ it { should match('12').and_interpret_as('dtmf-1 dtmf-2') }
66
+ it { should match('123').and_interpret_as('dtmf-1 dtmf-2 dtmf-3') }
67
+
68
+ it { should not_match('1111') }
69
+ it { should not_match('1111111') }
70
+ end
71
+
72
+ context "with an absolute length" do
73
+ subject { described_class.digits length: 3 }
74
+
75
+ it { should max_match('123').and_interpret_as('dtmf-1 dtmf-2 dtmf-3') }
76
+ it { should max_match('111').and_interpret_as('dtmf-1 dtmf-1 dtmf-1') }
77
+
78
+ it { should potentially_match('1') }
79
+ it { should potentially_match('12') }
80
+
81
+ it { should not_match('1234') }
82
+ it { should not_match('12345') }
83
+ end
84
+
85
+ context "when the min and max lengths are swapped" do
86
+ it "should raise ArgumentError" do
87
+ expect { described_class.digits minlength: 5, maxlength: 2 }.to raise_error(ArgumentError, /repeat/)
88
+ end
89
+ end
90
+
91
+ context "when the length and minlength are specified" do
92
+ it "should raise ArgumentError" do
93
+ expect { described_class.digits minlength: 5, length: 5 }.to raise_error(ArgumentError, /absolute length/)
94
+ end
95
+ end
96
+
97
+ context "when the length and maxlength are specified" do
98
+ it "should raise ArgumentError" do
99
+ expect { described_class.digits maxlength: 5, length: 5 }.to raise_error(ArgumentError, /absolute length/)
100
+ end
101
+ end
102
+ end
103
+
104
+ describe "currency" do
105
+ subject(:grammar) { described_class.currency }
106
+
107
+ it { should max_match('1*01').and_interpret_as('dtmf-1 dtmf-star dtmf-0 dtmf-1') }
108
+ it { should max_match('01*00').and_interpret_as('dtmf-0 dtmf-1 dtmf-star dtmf-0 dtmf-0') }
109
+ it { should max_match('100000000000*00').and_interpret_as('dtmf-1 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-star dtmf-0 dtmf-0') }
110
+ it { should max_match('0*08').and_interpret_as('dtmf-0 dtmf-star dtmf-0 dtmf-8') }
111
+ it { should max_match('*59').and_interpret_as('dtmf-star dtmf-5 dtmf-9') }
112
+
113
+ it { should match('0').and_interpret_as('dtmf-0') }
114
+ it { should match('0*0').and_interpret_as('dtmf-0 dtmf-star dtmf-0') }
115
+ it { should match('10*5').and_interpret_as('dtmf-1 dtmf-0 dtmf-star dtmf-5') }
116
+ it { should match('123').and_interpret_as('dtmf-1 dtmf-2 dtmf-3') }
117
+ it { should match('123*').and_interpret_as('dtmf-1 dtmf-2 dtmf-3 dtmf-star') }
118
+
119
+ it { should not_match('#') }
120
+ end
121
+
122
+ describe "number" do
123
+ subject(:grammar) { described_class.number }
124
+
125
+ it { should match('0').and_interpret_as('dtmf-0') }
126
+ it { should match('123').and_interpret_as('dtmf-1 dtmf-2 dtmf-3') }
127
+ it { should match('1*01').and_interpret_as('dtmf-1 dtmf-star dtmf-0 dtmf-1') }
128
+ it { should match('01*00').and_interpret_as('dtmf-0 dtmf-1 dtmf-star dtmf-0 dtmf-0') }
129
+ it { should match('100000000000*00').and_interpret_as('dtmf-1 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-0 dtmf-star dtmf-0 dtmf-0') }
130
+ it { should match('0*08').and_interpret_as('dtmf-0 dtmf-star dtmf-0 dtmf-8') }
131
+ it { should match('*59').and_interpret_as('dtmf-star dtmf-5 dtmf-9') }
132
+ it { should match('0*0').and_interpret_as('dtmf-0 dtmf-star dtmf-0') }
133
+ it { should match('10*5').and_interpret_as('dtmf-1 dtmf-0 dtmf-star dtmf-5') }
134
+ it { should match('123*').and_interpret_as('dtmf-1 dtmf-2 dtmf-3 dtmf-star') }
135
+ it { should match('123*2342').and_interpret_as('dtmf-1 dtmf-2 dtmf-3 dtmf-star dtmf-2 dtmf-3 dtmf-4 dtmf-2') }
136
+
137
+ it { should not_match('#') }
138
+ end
139
+
140
+ describe "phone" do
141
+ subject(:grammar) { described_class.phone }
142
+
143
+ it { should match('0').and_interpret_as('dtmf-0') }
144
+ it { should match('0123').and_interpret_as('dtmf-0 dtmf-1 dtmf-2 dtmf-3') }
145
+ it { should match('0123*456').and_interpret_as('dtmf-0 dtmf-1 dtmf-2 dtmf-3 dtmf-star dtmf-4 dtmf-5 dtmf-6') }
146
+ it { should match('0123*456*789').and_interpret_as('dtmf-0 dtmf-1 dtmf-2 dtmf-3 dtmf-star dtmf-4 dtmf-5 dtmf-6 dtmf-star dtmf-7 dtmf-8 dtmf-9') }
147
+
148
+ it { should potentially_match('') }
149
+
150
+ it { should not_match('#') }
151
+ end
152
+
153
+ describe "time" do
154
+ subject(:grammar) { described_class.time }
155
+
156
+ it { should max_match('1235').and_interpret_as('dtmf-1 dtmf-2 dtmf-3 dtmf-5') }
157
+
158
+ it { should match('12').and_interpret_as('dtmf-1 dtmf-2') }
159
+ it { should match('4').and_interpret_as('dtmf-4') }
160
+ it { should match('123').and_interpret_as('dtmf-1 dtmf-2 dtmf-3') }
161
+
162
+ it { should not_match('*') }
163
+ it { should not_match('#') }
164
+ end
165
+ end
@@ -185,6 +185,8 @@ module RubySpeech
185
185
  item :repeat => '4' do
186
186
  ruleref :uri => '#digits'
187
187
  end
188
+ item { '*' }
189
+ item { ruleref uri: '#digits' }
188
190
  "#"
189
191
  end
190
192
  item do
@@ -205,6 +207,12 @@ module RubySpeech
205
207
  0.upto(9) { |d| item { d.to_s } }
206
208
  end
207
209
  end
210
+ item { '*' }
211
+ item do
212
+ one_of do
213
+ 0.upto(9) { |d| item { d.to_s } }
214
+ end
215
+ end
208
216
  "#"
209
217
  end
210
218
  item do
@@ -27,6 +27,10 @@ module RubySpeech
27
27
  input.should == '6'
28
28
  end
29
29
 
30
+ it "should potentially match an empty buffer" do
31
+ subject.match('').should == GRXML::PotentialMatch.new
32
+ end
33
+
30
34
  %w{1 2 3 4 5 7 8 9 10 66 26 61}.each do |input|
31
35
  it "should not match '#{input}'" do
32
36
  subject.match(input).should == GRXML::NoMatch.new
@@ -2,6 +2,36 @@ require 'spec_helper'
2
2
 
3
3
  module RubySpeech
4
4
  describe GRXML do
5
+ describe ".from_uri" do
6
+ context "with a builtin URI" do
7
+ it "should fetch a simple builtin grammar by type" do
8
+ subject.from_uri("builtin:dtmf/phone").should == GRXML::Builtins.phone
9
+ end
10
+
11
+ it "should fetch a parameterized builtin grammar" do
12
+ subject.from_uri("builtin:dtmf/boolean?y=3;n=4").should == GRXML::Builtins.boolean(y: 3, n: 4)
13
+ end
14
+
15
+ context "for speech" do
16
+ it "should raise ArgumentError" do
17
+ expect { subject.from_uri("builtin:speech/phone") }.to raise_error(ArgumentError, /Only DTMF/)
18
+ end
19
+ end
20
+
21
+ context "that doesn't exist" do
22
+ it "should raise ArgumentError" do
23
+ expect { subject.from_uri("builtin:dtmf/foobar") }.to raise_error(ArgumentError, /invalid/)
24
+ end
25
+ end
26
+ end
27
+
28
+ context "with an http URI" do
29
+ it "should raise ArgumentError" do
30
+ expect { subject.from_uri("http://foo.com/grammar.grxml") }.to raise_error(ArgumentError, /builtin/)
31
+ end
32
+ end
33
+ end
34
+
5
35
  describe "#draw" do
6
36
  let(:doc) { Nokogiri::XML::Document.new }
7
37
 
@@ -18,4 +18,5 @@ puts "Finished loading schema."
18
18
  RSpec.configure do |config|
19
19
  config.filter_run :focus => true
20
20
  config.run_all_when_everything_filtered = true
21
+ config.treat_symbols_as_metadata_keys_with_true_values = true
21
22
  end
@@ -0,0 +1,79 @@
1
+ require 'rspec/expectations'
2
+
3
+ RSpec::Matchers.define :not_match do |input|
4
+ match do |grammar|
5
+ @result = RubySpeech::GRXML::Matcher.new(grammar).match(input)
6
+ @result.is_a?(RubySpeech::GRXML::NoMatch)
7
+ end
8
+
9
+ failure_message_for_should do |grammar|
10
+ "expected #{grammar} to not match #{input}, but received a #{@result.class}"
11
+ end
12
+ end
13
+
14
+ RSpec::Matchers.define :potentially_match do |input|
15
+ match do |grammar|
16
+ @result = RubySpeech::GRXML::Matcher.new(grammar).match(input)
17
+ @result.is_a?(RubySpeech::GRXML::PotentialMatch)
18
+ end
19
+
20
+ failure_message_for_should do |grammar|
21
+ "expected #{grammar} to potentially match #{input}, but received a #{@result.class}"
22
+ end
23
+ end
24
+
25
+ RSpec::Matchers.define :match do |input|
26
+ match do |grammar|
27
+ @result = RubySpeech::GRXML::Matcher.new(grammar).match(input)
28
+ @result.is_a?(RubySpeech::GRXML::Match) && (@interpretation ? @result.interpretation == @interpretation : true)
29
+ end
30
+
31
+ chain :and_interpret_as do |interpretation|
32
+ @interpretation = interpretation
33
+ end
34
+
35
+ description do
36
+ %{#{default_description} and interpret as "#{@interpretation}"}
37
+ end
38
+
39
+ failure_message_for_should do |grammar|
40
+ messages = []
41
+ unless @result.is_a?(RubySpeech::GRXML::Match)
42
+ messages << "expected #{grammar} to match, got a #{@result.class}"
43
+ end
44
+
45
+ if @result.respond_to?(:interpretation) && @result.interpretation != @interpretation
46
+ messages << %{expected interpretation to be "#{@interpretation}" but received "#{@result.interpretation}"}
47
+ end
48
+
49
+ messages.join(' ')
50
+ end
51
+ end
52
+
53
+ RSpec::Matchers.define :max_match do |input|
54
+ match do |grammar|
55
+ @result = RubySpeech::GRXML::Matcher.new(grammar).match(input)
56
+ @result.is_a?(RubySpeech::GRXML::MaxMatch) && (@interpretation ? @result.interpretation == @interpretation : true)
57
+ end
58
+
59
+ chain :and_interpret_as do |interpretation|
60
+ @interpretation = interpretation
61
+ end
62
+
63
+ description do
64
+ %{#{default_description} and interpret as "#{@interpretation}"}
65
+ end
66
+
67
+ failure_message_for_should do |grammar|
68
+ messages = []
69
+ unless @result.is_a?(RubySpeech::GRXML::MaxMatch)
70
+ messages << "expected #{grammar} to max-match, got a #{@result.class}"
71
+ end
72
+
73
+ if @result.respond_to?(:interpretation) && @result.interpretation != @interpretation
74
+ messages << %{expected interpretation to be "#{@interpretation}" but received "#{@result.interpretation}"}
75
+ end
76
+
77
+ messages.join(' ')
78
+ end
79
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_speech
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.2
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Langfeld
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-09-03 00:00:00.000000000 Z
11
+ date: 2013-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -227,6 +227,7 @@ files:
227
227
  - lib/ruby_speech.rb
228
228
  - lib/ruby_speech/generic_element.rb
229
229
  - lib/ruby_speech/grxml.rb
230
+ - lib/ruby_speech/grxml/builtins.rb
230
231
  - lib/ruby_speech/grxml/element.rb
231
232
  - lib/ruby_speech/grxml/grammar.rb
232
233
  - lib/ruby_speech/grxml/item.rb
@@ -261,6 +262,7 @@ files:
261
262
  - lib/ruby_speech/version.rb
262
263
  - lib/ruby_speech/xml/language.rb
263
264
  - ruby_speech.gemspec
265
+ - spec/ruby_speech/grxml/builtins_spec.rb
264
266
  - spec/ruby_speech/grxml/grammar_spec.rb
265
267
  - spec/ruby_speech/grxml/item_spec.rb
266
268
  - spec/ruby_speech/grxml/match_spec.rb
@@ -291,10 +293,12 @@ files:
291
293
  - spec/ruby_speech/ssml_spec.rb
292
294
  - spec/ruby_speech_spec.rb
293
295
  - spec/spec_helper.rb
296
+ - spec/support/grammar_matchers.rb
294
297
  - spec/support/match_examples.rb
295
298
  - spec/support/matchers.rb
296
299
  homepage: https://github.com/benlangfeld/ruby_speech
297
- licenses: []
300
+ licenses:
301
+ - MIT
298
302
  metadata: {}
299
303
  post_install_message:
300
304
  rdoc_options: []
@@ -317,6 +321,7 @@ signing_key:
317
321
  specification_version: 4
318
322
  summary: A Ruby library for TTS & ASR document preparation
319
323
  test_files:
324
+ - spec/ruby_speech/grxml/builtins_spec.rb
320
325
  - spec/ruby_speech/grxml/grammar_spec.rb
321
326
  - spec/ruby_speech/grxml/item_spec.rb
322
327
  - spec/ruby_speech/grxml/match_spec.rb
@@ -347,6 +352,7 @@ test_files:
347
352
  - spec/ruby_speech/ssml_spec.rb
348
353
  - spec/ruby_speech_spec.rb
349
354
  - spec/spec_helper.rb
355
+ - spec/support/grammar_matchers.rb
350
356
  - spec/support/match_examples.rb
351
357
  - spec/support/matchers.rb
352
358
  has_rdoc: