adhearsion-ivr 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 748ecbe4414a38d5ff3b011feac37449447b150d
4
+ data.tar.gz: c15534a4e1395b758aefb698bed447875af832fd
5
+ SHA512:
6
+ metadata.gz: 326c7fd91c2940d5335fd0942b54aaf6e4d00c057f50293de0ee9fd518313408a34363879e709341f2190a1420c2660803f94eebe3ee942cd095e8dc0dd69e6e
7
+ data.tar.gz: ae1d4057a6e2ad860833f31324aa14f14652866ca2876ba33d0d4e5c42d2acb4da584d40f6506ab1bc68511f1e948a3a0d2bb3218d78b1270976288f7207c0bf
@@ -0,0 +1,26 @@
1
+ Gemfile.lock
2
+ *.gem
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ coverage
7
+ InstalledFiles
8
+ lib/bundler/man
9
+ pkg
10
+ rdoc
11
+ spec/reports
12
+ test/tmp
13
+ test/version_tmp
14
+ tmp
15
+
16
+ # YARD artifacts
17
+ .yardoc
18
+ _yardoc
19
+ doc/
20
+
21
+ # Editor files
22
+ .*.sw*
23
+ *~
24
+
25
+ # OS Files
26
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --format documentation
2
+ --colour
3
+ --tty
4
+ --order random
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - jruby
7
+ - rbx-2.1.1
8
+ - ruby-head
9
+ matrix:
10
+ allow_failures:
11
+ - rvm: rbx-2.1.1
12
+ - rvm: ruby-head
13
+ notifications:
14
+ irc: "irc.freenode.org#adhearsion"
@@ -0,0 +1,4 @@
1
+ # [develop](https://github.com/adhearsion/adhearsion-ivr)
2
+
3
+ # [v0.1.0](https://github.com/adhearsion/adhearsion-ivr/compare/2c7ff73f5d6471be23e291c7d6c7b61d0128e09a...0.1.0) - [2014-03-12](https://rubygems.org/gems/adhearsion-ivr/versions/0.1.0)
4
+ * Initial release
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,5 @@
1
+ guard 'rspec', cmd: 'bundle exec rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec/" }
5
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Adhearsion
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,134 @@
1
+ adhearsion-ivr
2
+ ===============
3
+
4
+ IVR building blocks for Adhearsion apps
5
+
6
+ ## Installing
7
+
8
+ Simply add to your Gemfile like any other Adhearsion plugin:
9
+
10
+ ```Ruby
11
+ gem 'adhearsion-ivr'
12
+ ```
13
+
14
+ ## Configuration
15
+
16
+ Adhearsion IVR currently has no configurable options.
17
+
18
+ ## Examples
19
+
20
+ A bare-bones example of creating a prompt. This menu has a single message, "Please enter a number 1 through 3." By default, the caller has 3 attempts to enter any of 1, 2 or 3 (though the actual grammar is left as an exercise to the developer). If 1 is pressed, the caller is sent to the OneController; if 2 is pressed the caller is sent to the TwoController; if 3 is pressed the caller hears a poor impersonation of the Three Stooges and is hung up.
21
+
22
+ If the caller fails to provde input within 3 attempts, he hears a taunting message and is then transferred to a waiting kindergarten teacher.
23
+
24
+ ```Ruby
25
+ class SimplePrompt < Adhearsion::IVRController
26
+ prompts << "Please enter a number 1 through 3"
27
+
28
+ on_complete do |result|
29
+ case result.nlsml.interpretations.first[:instance] # FIXME?
30
+ when 1
31
+ pass OneController
32
+ when 2
33
+ pass TwoController
34
+ when 3
35
+ say "Yuk yuk yuk"
36
+ hangup
37
+ end
38
+ end
39
+
40
+ on_failure do
41
+ say "Sorry you failed kindergarten. Let us transfer you to our trained staff of kindergarten teachers."
42
+ dial 'sip:kindergarten_teachers@elementaryschool.com'
43
+ end
44
+
45
+ def grammar
46
+ RubySpeech::GRXML.draw do
47
+ # ... put a valid GRXML grammar here
48
+ end
49
+ end
50
+ end
51
+ ```
52
+
53
+ An example with escalating prompts:
54
+
55
+ ```Ruby
56
+ class EscalatedPrompt < Adhearsion::IVRController
57
+ prompts << "First attempt: enter a number"
58
+ prompts << "Second attempt: enter a number 1 through 3"
59
+ prompts << "Third attempt: enter a number 1 through 3. That would be the top row of your DTMF keypad. Don't get it wrong again."
60
+ prompts << "Fourth attempt: really? Was I not clear the first 3 times? Last chance, dunce."
61
+
62
+ max_attempts 4
63
+
64
+ on_complete do |result|
65
+ case result.nlsml.interpretations.first[:instance] # FIXME?
66
+ when 1
67
+ pass OneController
68
+ when 2
69
+ pass TwoController
70
+ when 3
71
+ say "Yuk yuk yuk"
72
+ hangup
73
+ end
74
+ end
75
+
76
+ on_failure do
77
+ say "Sorry you failed kindergarten. Let us transfer you to our trained staff of kindergarten teachers."
78
+ dial 'sip:kindergarten_teachers@elementaryschool.com'
79
+ end
80
+
81
+ def grammar
82
+ RubySpeech::GRXML.draw do
83
+ # ... put a valid GRXML grammar here
84
+ end
85
+ end
86
+ end
87
+ ```
88
+
89
+ A slightly more involved example showing integration with I18n:
90
+
91
+ ```Ruby
92
+ class I18nEscalatedPrompts < Adhearsion::IVRController
93
+ # Note that by deferring prompt resolution we can take advantage of per-call variables such as language selection
94
+ prompts << -> { t(:first_attempt) }
95
+ prompts << -> { t(:second_attempt) }
96
+ prompts << -> { t(:third_attempt) }
97
+ prompts << -> { [ t(:fourth_attempt), t(:this_is_your_final_attempt) ] }
98
+ # Future improvement: we could potentially also include the previous input
99
+ # in the re-prompts, but that isn't implemented now
100
+
101
+ max_attempts 4
102
+
103
+ on_complete do |result|
104
+ case result.nlsml.interpretations.first[:instance] # FIXME?
105
+ when 1
106
+ pass OneController
107
+ when 2
108
+ pass TwoController
109
+ when 3
110
+ say "Yuk yuk yuk"
111
+ hangup
112
+ end
113
+ end
114
+
115
+ on_failure do
116
+ say "Sorry you failed kindergarten. Let us transfer you to our trained staff of kindergarten teachers."
117
+ dial 'sip:kindergarten_teachers@elementaryschool.com'
118
+ end
119
+
120
+ def grammar
121
+ RubySpeech::GRXML.draw do
122
+ # ... put a valid GRXML grammar here
123
+ end
124
+ end
125
+ end
126
+ ```
127
+
128
+ ## Credits
129
+
130
+ Copyright (C) 2014 The Adhearsion Foundation
131
+
132
+ adhearsion-ivr is released under the [MIT license](http://opensource.org/licenses/MIT). Please see the [LICENSE](https://github.com/adhearsion/adhearsion-i18n/blob/master/LICENSE) file for details.
133
+
134
+ adhearsion-ivr was created by [Ben Klang](https://twitter.com/bklang) with support from [Mojo Lingo](https://mojolingo.com) and their clients.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new :spec
5
+
6
+ task default: :spec
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "adhearsion-ivr/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "adhearsion-ivr"
7
+ s.version = AdhearsionIVR::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ben Klang", "Ben Langfeld"]
10
+ s.email = "dev@adhearsion.com"
11
+ s.homepage = "http://adhearsion.com"
12
+ s.summary = "IVR building blocks for Adhearsion applications"
13
+ s.description = "This provides a consistent way of implementing Interactive Voice Response prompts, including reprompting and error handling"
14
+
15
+ s.license = 'MIT'
16
+
17
+ s.required_ruby_version = '>= 1.9.3'
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ s.require_paths = ["lib"]
23
+
24
+ s.add_runtime_dependency 'adhearsion', ["~> 2.0"]
25
+ s.add_runtime_dependency 'adhearsion-asr', ["~> 1.0"]
26
+ s.add_runtime_dependency 'state_machine'
27
+
28
+ s.add_development_dependency %q<bundler>, ["~> 1.0"]
29
+ s.add_development_dependency %q<rspec>, ["~> 2.11"]
30
+ s.add_development_dependency %q<rake>, [">= 0"]
31
+ s.add_development_dependency %q<guard-rspec>
32
+ s.add_development_dependency %q<rb-fsevent>, ['~> 0.9']
33
+ end
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ %w{
4
+ version
5
+ plugin
6
+ ivr_controller
7
+ }.each { |r| require "adhearsion-ivr/#{r}" }
8
+
9
+ class AdhearsionIVR
10
+ end
@@ -0,0 +1,118 @@
1
+ # encoding: utf-8
2
+
3
+ require 'state_machine'
4
+ require 'adhearsion-asr'
5
+
6
+ module Adhearsion
7
+ class IVRController < Adhearsion::CallController
8
+ class << self
9
+ # list of prompts to be played to the caller.
10
+ # this should have one prompt for each attempt
11
+ # in case there are not enough prompts, the final prompt will be re-used until
12
+ # the max_attempts are exceeded.
13
+ def prompts
14
+ @prompts ||= []
15
+ end
16
+
17
+ # maximum number of attempts to prompt the caller for input
18
+ def max_attempts(num = nil)
19
+ if num
20
+ @max_attempts = num
21
+ else
22
+ @max_attempts || 3
23
+ end
24
+ end
25
+
26
+ # called when the caller successfully provides input
27
+ def on_complete(&block)
28
+ @completion_callback = block
29
+ end
30
+ attr_reader :completion_callback
31
+
32
+ # Called when the caller errors more than the number of allowed attempts
33
+ def on_failure(&block)
34
+ @failure_callback = block
35
+ end
36
+ attr_reader :failure_callback
37
+ end
38
+
39
+ state_machine initial: :prompting do
40
+ event(:match) { transition prompting: :complete }
41
+ event(:reprompt) { transition input_error: :prompting }
42
+ event(:nomatch) { transition prompting: :input_error }
43
+ event(:noinput) { transition prompting: :input_error }
44
+ event(:failure) { transition prompting: :failure, input_error: :failure }
45
+
46
+ after_transition :prompting => :input_error do |controller|
47
+ controller.increment_errors
48
+ if controller.continue?
49
+ controller.reprompt!
50
+ else
51
+ controller.failure!
52
+ end
53
+ end
54
+
55
+ after_transition any => :prompting do |controller|
56
+ controller.deliver_prompt
57
+ end
58
+
59
+ after_transition :prompting => :complete do |controller|
60
+ controller.completion_callback
61
+ end
62
+
63
+ after_transition any => :failure do |controller|
64
+ controller.failure_callback
65
+ end
66
+ end
67
+
68
+ def run
69
+ @errors = 0
70
+ deliver_prompt
71
+ end
72
+
73
+ def deliver_prompt
74
+ prompt = self.class.prompts[@errors] || self.class.prompts.last
75
+ prompt = instance_exec(&prompt) if prompt.respond_to? :call
76
+ logger.debug "Prompt: #{prompt.inspect}"
77
+
78
+ @result = ask prompt, grammar: grammar, mode: :voice
79
+ logger.debug "Got result #{@result.inspect}"
80
+ case @result.status
81
+ when :match
82
+ match!
83
+ when :stop
84
+ logger.info "Prompt was stopped forcibly. Exiting cleanly..."
85
+ when :hangup
86
+ logger.info "Call was hung up mid-prompt. Exiting controller flow..."
87
+ raise Adhearsion::Call::Hangup
88
+ when :nomatch
89
+ nomatch!
90
+ when :noinput
91
+ noinput!
92
+ else
93
+ raise "Unrecognized result status: #{@result.status}"
94
+ end
95
+ @result
96
+ end
97
+
98
+ def grammar
99
+ raise NotImplementedError, "You must override #grammar and provide a grammar"
100
+ end
101
+
102
+ def increment_errors
103
+ @errors += 1
104
+ end
105
+
106
+ def continue?
107
+ @errors < self.class.max_attempts
108
+ end
109
+
110
+ def completion_callback
111
+ instance_exec @result, &self.class.completion_callback if self.class.completion_callback
112
+ end
113
+
114
+ def failure_callback
115
+ instance_exec &self.class.failure_callback if self.class.failure_callback
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ class AdhearsionIVR::Plugin < Adhearsion::Plugin
4
+ init :ivr do
5
+ logger.info "Adhearsion IVR loaded"
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ class AdhearsionIVR
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,411 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Adhearsion::IVRController do
6
+ describe "when we inherit from it" do
7
+
8
+ let(:call_id) { SecureRandom.uuid }
9
+ let(:call) { Adhearsion::Call.new }
10
+
11
+ let(:controller_class) do
12
+ expected_prompts = self.expected_prompts
13
+ apology_announcement = self.apology_announcement
14
+
15
+ Class.new(Adhearsion::IVRController) do
16
+ expected_prompts.each do |prompt|
17
+ prompts << prompt
18
+ end
19
+
20
+ on_complete do |result|
21
+ say "Let's go to #{result.utterance}"
22
+ end
23
+
24
+ on_failure do
25
+ say apology_announcement
26
+ end
27
+
28
+ def grammar
29
+ :some_grammar
30
+ end
31
+ end
32
+ end
33
+
34
+ subject(:controller) { controller_class.new call }
35
+
36
+ before do
37
+ double call, write_command: true, id: call_id
38
+
39
+ Adhearsion::Plugin.configure_plugins if Adhearsion::Plugin.respond_to?(:configure_plugins)
40
+ Adhearsion::Plugin.init_plugins
41
+ end
42
+
43
+ let(:expected_prompts) { [SecureRandom.uuid, SecureRandom.uuid, SecureRandom.uuid] }
44
+ let(:apology_announcement) { "Sorry, I couldn't understand where you would like to go. I'll put you through to a human." }
45
+
46
+ let(:expected_grammar) { :some_grammar }
47
+
48
+ let(:nlsml) do
49
+ RubySpeech::NLSML.draw do
50
+ interpretation confidence: 1 do
51
+ input 'Paris', mode: :voice
52
+ instance 'Paris'
53
+ end
54
+ end
55
+ end
56
+
57
+ let(:match_result) do
58
+ AdhearsionASR::Result.new.tap do |res|
59
+ res.status = :match
60
+ res.mode = :voice
61
+ res.confidence = 1
62
+ res.utterance = 'Paris'
63
+ res.interpretation = 'Paris'
64
+ res.nlsml = nlsml
65
+ end
66
+ end
67
+
68
+ let(:noinput_result) do
69
+ AdhearsionASR::Result.new.tap do |res|
70
+ res.status = :noinput
71
+ end
72
+ end
73
+
74
+ context "when an utterance is received" do
75
+ before do
76
+ controller.should_receive(:ask).once.with(expected_prompts[0], grammar: expected_grammar, mode: :voice).and_return result
77
+ end
78
+
79
+ context "that is a match" do
80
+ let(:result) { match_result }
81
+
82
+ it "passes the Result object to the on_complete block" do
83
+ controller.should_receive(:say).once.with "Let's go to Paris"
84
+ controller.run
85
+ end
86
+ end
87
+
88
+ context "that is a noinput" do
89
+ let(:result) { noinput_result }
90
+
91
+ context "followed by a match" do
92
+ before do
93
+ controller.should_receive(:ask).once.with(expected_prompts[1], grammar: expected_grammar, mode: :voice).and_return match_result
94
+ end
95
+
96
+ it "re-prompts using the next prompt, and then passes the second Result to the on_complete block" do
97
+ controller.should_receive(:say).once.with "Let's go to Paris"
98
+ controller.run
99
+ end
100
+ end
101
+
102
+ context "when there are not enough prompts available for all retries" do
103
+ let(:expected_prompts) { [SecureRandom.uuid, SecureRandom.uuid] }
104
+
105
+ before do
106
+ controller.should_receive(:ask).once.with(expected_prompts[1], grammar: expected_grammar, mode: :voice).and_return result
107
+ controller.should_receive(:ask).once.with(expected_prompts[1], grammar: expected_grammar, mode: :voice).and_return match_result
108
+ end
109
+
110
+ it "reuses the last prompt" do
111
+ controller.should_receive(:say).once.with "Let's go to Paris"
112
+ controller.run
113
+ end
114
+ end
115
+
116
+ context "until it hits the maximum number of attempts" do
117
+ context "using the default of 3 attempts" do
118
+ before do
119
+ controller.should_receive(:ask).once.with(expected_prompts[1], grammar: expected_grammar, mode: :voice).and_return result
120
+ controller.should_receive(:ask).once.with(expected_prompts[2], grammar: expected_grammar, mode: :voice).and_return result
121
+ end
122
+
123
+ it "invokes the on_failure block" do
124
+ controller.should_receive(:say).once.with apology_announcement
125
+ controller.run
126
+ end
127
+ end
128
+
129
+ context "when that value is different from the default" do
130
+ let(:controller_class) do
131
+ expected_prompts = self.expected_prompts
132
+ apology_announcement = self.apology_announcement
133
+
134
+ Class.new(Adhearsion::IVRController) do
135
+ expected_prompts.each do |prompt|
136
+ prompts << prompt
137
+ end
138
+
139
+ max_attempts 2
140
+
141
+ on_complete do |result|
142
+ say "Let's go to #{result.utterance}"
143
+ end
144
+
145
+ on_failure do
146
+ say apology_announcement
147
+ end
148
+
149
+ def grammar
150
+ :some_grammar
151
+ end
152
+ end
153
+ end
154
+
155
+ before do
156
+ controller.should_receive(:ask).once.with(expected_prompts[1], grammar: expected_grammar, mode: :voice).and_return result
157
+ end
158
+
159
+ it "invokes the on_failure block" do
160
+ controller.should_receive(:say).once.with apology_announcement
161
+ controller.run
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ context "that is a nomatch" do
168
+ let(:result) do
169
+ AdhearsionASR::Result.new.tap do |res|
170
+ res.status = :nomatch
171
+ end
172
+ end
173
+
174
+ context "followed by a match" do
175
+ before do
176
+ controller.should_receive(:ask).once.with(expected_prompts[1], grammar: expected_grammar, mode: :voice).and_return match_result
177
+ end
178
+
179
+ it "re-prompts using the next prompt, and then passes the second Result to the on_complete block" do
180
+ controller.should_receive(:say).once.with "Let's go to Paris"
181
+ controller.run
182
+ end
183
+ end
184
+
185
+ context "when there are not enough prompts available for all retries" do
186
+ let(:expected_prompts) { [SecureRandom.uuid, SecureRandom.uuid] }
187
+
188
+ before do
189
+ controller.should_receive(:ask).once.with(expected_prompts[1], grammar: expected_grammar, mode: :voice).and_return result
190
+ controller.should_receive(:ask).once.with(expected_prompts[1], grammar: expected_grammar, mode: :voice).and_return match_result
191
+ end
192
+
193
+ it "reuses the last prompt" do
194
+ controller.should_receive(:say).once.with "Let's go to Paris"
195
+ controller.run
196
+ end
197
+ end
198
+
199
+ context "until it hits the maximum number of attempts" do
200
+ context "using the default of 3 attempts" do
201
+ before do
202
+ controller.should_receive(:ask).once.with(expected_prompts[1], grammar: expected_grammar, mode: :voice).and_return result
203
+ controller.should_receive(:ask).once.with(expected_prompts[2], grammar: expected_grammar, mode: :voice).and_return result
204
+ end
205
+
206
+ it "invokes the on_failure block" do
207
+ controller.should_receive(:say).once.with apology_announcement
208
+ controller.run
209
+ end
210
+ end
211
+
212
+ context "when that value is different from the default" do
213
+ let(:controller_class) do
214
+ expected_prompts = self.expected_prompts
215
+ apology_announcement = self.apology_announcement
216
+
217
+ Class.new(Adhearsion::IVRController) do
218
+ expected_prompts.each do |prompt|
219
+ prompts << prompt
220
+ end
221
+
222
+ max_attempts 2
223
+
224
+ on_complete do |result|
225
+ say "Let's go to #{result.utterance}"
226
+ end
227
+
228
+ on_failure do
229
+ say apology_announcement
230
+ end
231
+
232
+ def grammar
233
+ :some_grammar
234
+ end
235
+ end
236
+ end
237
+
238
+ before do
239
+ controller.should_receive(:ask).once.with(expected_prompts[1], grammar: expected_grammar, mode: :voice).and_return result
240
+ end
241
+
242
+ it "invokes the on_failure block" do
243
+ controller.should_receive(:say).once.with apology_announcement
244
+ controller.run
245
+ end
246
+ end
247
+ end
248
+ end
249
+
250
+ context "that is a hangup" do
251
+ let(:controller_class) do
252
+ expected_prompts = self.expected_prompts
253
+
254
+ Class.new(Adhearsion::IVRController) do
255
+ expected_prompts.each do |prompt|
256
+ prompts << prompt
257
+ end
258
+
259
+ on_complete do |result|
260
+ raise "Got complete"
261
+ end
262
+
263
+ on_failure do
264
+ raise "Got failure"
265
+ end
266
+
267
+ def grammar
268
+ :some_grammar
269
+ end
270
+ end
271
+ end
272
+
273
+ let(:result) do
274
+ AdhearsionASR::Result.new.tap do |res|
275
+ res.status = :hangup
276
+ end
277
+ end
278
+
279
+ it "raises Adhearsion::Call::Hangup" do
280
+ expect { controller.run }.to raise_error(Adhearsion::Call::Hangup)
281
+ end
282
+ end
283
+
284
+ context "that is a stop" do
285
+ let(:controller_class) do
286
+ expected_prompts = self.expected_prompts
287
+
288
+ Class.new(Adhearsion::IVRController) do
289
+ expected_prompts.each do |prompt|
290
+ prompts << prompt
291
+ end
292
+
293
+ on_complete do |result|
294
+ raise "Got complete"
295
+ end
296
+
297
+ on_failure do
298
+ raise "Got failure"
299
+ end
300
+
301
+ def grammar
302
+ :some_grammar
303
+ end
304
+ end
305
+ end
306
+
307
+ let(:result) do
308
+ AdhearsionASR::Result.new.tap do |res|
309
+ res.status = :stop
310
+ end
311
+ end
312
+
313
+ it "falls through silently" do
314
+ controller.run
315
+ end
316
+ end
317
+ end
318
+
319
+ context "when the prompts are callable" do
320
+ let(:controller_class) do
321
+ Class.new(Adhearsion::IVRController) do
322
+ prompts << -> { thing }
323
+
324
+ on_complete do |result|
325
+ end
326
+
327
+ on_failure do
328
+ end
329
+
330
+ def grammar
331
+ :some_grammar
332
+ end
333
+
334
+ def thing
335
+ @things ||= %w{one two three}
336
+ @things.shift
337
+ end
338
+ end
339
+ end
340
+
341
+ it "should evaluate the prompt repeatedly in the context of the controller instance" do
342
+ controller.should_receive(:ask).once.with('one', grammar: expected_grammar, mode: :voice).and_return noinput_result
343
+ controller.should_receive(:ask).once.with('two', grammar: expected_grammar, mode: :voice).and_return noinput_result
344
+ controller.should_receive(:ask).once.with('three', grammar: expected_grammar, mode: :voice).and_return noinput_result
345
+ controller.run
346
+ end
347
+ end
348
+
349
+ context "when no grammar is provided" do
350
+ let(:controller_class) do
351
+ Class.new(Adhearsion::IVRController) do
352
+ prompts << "Hello"
353
+
354
+ on_complete do |result|
355
+ end
356
+
357
+ on_failure do
358
+ end
359
+ end
360
+ end
361
+
362
+ it "should raise NotImplementedError" do
363
+ expect { controller.run }.to raise_error(NotImplementedError)
364
+ end
365
+ end
366
+
367
+ context "when no complete callback is provided" do
368
+ let(:controller_class) do
369
+ Class.new(Adhearsion::IVRController) do
370
+ prompts << "Hello"
371
+
372
+ def grammar
373
+ :some_grammar
374
+ end
375
+ end
376
+ end
377
+
378
+ it "should simply return the result" do
379
+ controller.should_receive(:ask).once.with('Hello', grammar: expected_grammar, mode: :voice).and_return match_result
380
+ controller.run.should be(match_result)
381
+ end
382
+ end
383
+
384
+ context "when no complete callback is provided" do
385
+ let(:controller_class) do
386
+ Class.new(Adhearsion::IVRController) do
387
+ prompts << "Hello"
388
+
389
+ def grammar
390
+ :some_grammar
391
+ end
392
+ end
393
+ end
394
+
395
+ it "should simply return the last result" do
396
+ controller.should_receive(:ask).once.with('Hello', grammar: expected_grammar, mode: :voice).and_return noinput_result
397
+ controller.should_receive(:ask).once.with('Hello', grammar: expected_grammar, mode: :voice).and_return noinput_result
398
+ controller.should_receive(:ask).once.with('Hello', grammar: expected_grammar, mode: :voice).and_return noinput_result
399
+ controller.run.should be(noinput_result)
400
+ end
401
+ end
402
+
403
+ context "when the call is dead" do
404
+ before { call.terminate }
405
+
406
+ it "executing the controller should raise Adhearsion::Call::Hangup" do
407
+ expect { subject.run }.to raise_error Adhearsion::Call::Hangup
408
+ end
409
+ end
410
+ end
411
+ end
@@ -0,0 +1,19 @@
1
+ require 'adhearsion'
2
+ require 'adhearsion-ivr'
3
+
4
+ RSpec.configure do |config|
5
+ config.color_enabled = true
6
+ config.tty = true
7
+
8
+ config.mock_with :rspec
9
+ config.filter_run :focus => true
10
+ config.run_all_when_everything_filtered = true
11
+
12
+ config.backtrace_clean_patterns = [/rspec/]
13
+
14
+ config.before do
15
+ Punchblock.stub new_request_id: 'foo'
16
+
17
+ Adhearsion::Logging.start Adhearsion::Logging.default_appenders, :trace, Adhearsion.config.platform.logging.formatter
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: adhearsion-ivr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ben Klang
8
+ - Ben Langfeld
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-03-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: adhearsion
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: adhearsion-asr
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: state_machine
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: bundler
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '1.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '1.0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rspec
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '2.11'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '2.11'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rake
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: guard-rspec
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: rb-fsevent
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '0.9'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '0.9'
126
+ description: This provides a consistent way of implementing Interactive Voice Response
127
+ prompts, including reprompting and error handling
128
+ email: dev@adhearsion.com
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - ".gitignore"
134
+ - ".rspec"
135
+ - ".travis.yml"
136
+ - CHANGELOG.md
137
+ - Gemfile
138
+ - Guardfile
139
+ - LICENSE
140
+ - README.md
141
+ - Rakefile
142
+ - adhearsion-ivr.gemspec
143
+ - lib/adhearsion-ivr.rb
144
+ - lib/adhearsion-ivr/ivr_controller.rb
145
+ - lib/adhearsion-ivr/plugin.rb
146
+ - lib/adhearsion-ivr/version.rb
147
+ - spec/adhearsion-ivr/ivr_controller_spec.rb
148
+ - spec/spec_helper.rb
149
+ homepage: http://adhearsion.com
150
+ licenses:
151
+ - MIT
152
+ metadata: {}
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: 1.9.3
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubyforge_project:
169
+ rubygems_version: 2.2.0
170
+ signing_key:
171
+ specification_version: 4
172
+ summary: IVR building blocks for Adhearsion applications
173
+ test_files:
174
+ - spec/adhearsion-ivr/ivr_controller_spec.rb
175
+ - spec/spec_helper.rb