adhearsion 2.2.1 → 2.3.0
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.
- checksums.yaml +7 -0
- data/.travis.yml +1 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +1 -1
- data/README.markdown +2 -2
- data/adhearsion.gemspec +1 -1
- data/features/app_generator.feature +1 -1
- data/lib/adhearsion/call.rb +1 -1
- data/lib/adhearsion/call_controller.rb +2 -1
- data/lib/adhearsion/call_controller/input.rb +2 -2
- data/lib/adhearsion/call_controller/menu_dsl/menu.rb +3 -3
- data/lib/adhearsion/call_controller/output.rb +21 -10
- data/lib/adhearsion/call_controller/record.rb +6 -4
- data/lib/adhearsion/foundation/custom_daemonizer.rb +15 -8
- data/lib/adhearsion/generators/app/templates/Gemfile.erb +1 -1
- data/lib/adhearsion/initializer.rb +14 -7
- data/lib/adhearsion/plugin.rb +4 -4
- data/lib/adhearsion/process.rb +1 -1
- data/lib/adhearsion/punchblock_plugin.rb +3 -2
- data/lib/adhearsion/punchblock_plugin/initializer.rb +19 -21
- data/lib/adhearsion/version.rb +1 -1
- data/spec/adhearsion/call_controller/input_spec.rb +24 -4
- data/spec/adhearsion/call_controller/menu_dsl/menu_spec.rb +34 -0
- data/spec/adhearsion/call_controller/output_spec.rb +65 -0
- data/spec/adhearsion/call_controller/record_spec.rb +18 -0
- data/spec/adhearsion/call_controller_spec.rb +12 -10
- data/spec/adhearsion/call_spec.rb +3 -7
- data/spec/adhearsion/initializer_spec.rb +6 -4
- data/spec/adhearsion/plugin_spec.rb +2 -2
- data/spec/adhearsion/process_spec.rb +1 -1
- data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +9 -7
- data/spec/support/initializer_stubs.rb +1 -1
- metadata +39 -107
data/lib/adhearsion/version.rb
CHANGED
@@ -9,27 +9,47 @@ module Adhearsion
|
|
9
9
|
include CallControllerTestHelpers
|
10
10
|
|
11
11
|
describe "#play_sound_files_for_menu" do
|
12
|
+
let(:renderer_options) { { renderer: nil } }
|
12
13
|
let(:options) { Hash.new }
|
13
14
|
let(:menu_instance) { MenuDSL::Menu.new(options) }
|
14
15
|
let(:sound_file) { "press a button" }
|
15
16
|
let(:sound_files) { [sound_file] }
|
16
17
|
|
17
18
|
it "should play the sound files for the menu" do
|
18
|
-
subject.should_receive(:interruptible_play).with(sound_file).and_return("1")
|
19
|
+
subject.should_receive(:interruptible_play).with(sound_file, renderer_options).and_return("1")
|
19
20
|
subject.play_sound_files_for_menu(menu_instance, sound_files).should be == '1'
|
20
21
|
end
|
21
22
|
|
22
23
|
it "should wait for digit if nothing is pressed during playback" do
|
23
|
-
subject.should_receive(:interruptible_play).with(sound_file).and_return(nil)
|
24
|
+
subject.should_receive(:interruptible_play).with(sound_file, renderer_options).and_return(nil)
|
24
25
|
subject.should_receive(:wait_for_digit).with(menu_instance.timeout).and_return("1")
|
25
26
|
subject.play_sound_files_for_menu(menu_instance, sound_files).should be == '1'
|
26
27
|
end
|
27
28
|
|
29
|
+
context "with a renderer specified" do
|
30
|
+
let(:options) { { :renderer => :native } }
|
31
|
+
let(:renderer_options) { { renderer: :native } }
|
32
|
+
it "should play the sound files for the menu" do
|
33
|
+
subject.should_receive(:interruptible_play).with(sound_file, renderer_options).and_return("1")
|
34
|
+
subject.play_sound_files_for_menu(menu_instance, sound_files).should be == '1'
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
28
39
|
context "when the menu is not interruptible" do
|
29
40
|
let(:options) { { :interruptible => false } }
|
30
|
-
|
31
41
|
it "should play the sound files and wait for digit" do
|
32
|
-
subject.should_receive(:play).with(sound_file).and_return true
|
42
|
+
subject.should_receive(:play).with(sound_file, renderer_options).and_return true
|
43
|
+
subject.should_receive(:wait_for_digit).with(menu_instance.timeout).and_return("1")
|
44
|
+
subject.play_sound_files_for_menu(menu_instance, sound_files).should be == '1'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "with a renderer specified and not interruptible" do
|
49
|
+
let(:options) { { :renderer => :native, :interruptible => false } }
|
50
|
+
let(:renderer_options) { { renderer: :native } }
|
51
|
+
it "should pass the renderer option to #play" do
|
52
|
+
subject.should_receive(:play).with(sound_file, renderer_options).and_return true
|
33
53
|
subject.should_receive(:wait_for_digit).with(menu_instance.timeout).and_return("1")
|
34
54
|
subject.play_sound_files_for_menu(menu_instance, sound_files).should be == '1'
|
35
55
|
end
|
@@ -117,6 +117,22 @@ module Adhearsion
|
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
120
|
+
context 'when renderer is not specified' do
|
121
|
+
it 'should have a nil renderer' do
|
122
|
+
subject.renderer.should be nil
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context 'when renderer is specified' do
|
127
|
+
let(:options) {
|
128
|
+
{:renderer => :native}
|
129
|
+
}
|
130
|
+
|
131
|
+
it 'should have the specified renderer' do
|
132
|
+
subject.renderer.should == :native
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
120
136
|
context 'when matchers are specified' do
|
121
137
|
subject do
|
122
138
|
Menu.new do
|
@@ -347,6 +363,24 @@ module Adhearsion
|
|
347
363
|
menu_instance.result.should be == '242'
|
348
364
|
end
|
349
365
|
end
|
366
|
+
|
367
|
+
context "when a digit limit and validator is defined" do
|
368
|
+
let(:menu_instance) do
|
369
|
+
Menu.new options.merge(:limit => 3) do
|
370
|
+
validator { |buffer| buffer == "242" }
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
it "applies the validator before checking the digit limit" do
|
375
|
+
menu_instance << 2
|
376
|
+
menu_instance << 4
|
377
|
+
menu_instance << 2
|
378
|
+
menu_instance.continue.should be_a Menu::MenuValidatorTerminated
|
379
|
+
menu_instance.continue.should be_a Menu::MenuResultDone
|
380
|
+
menu_instance.status.should be == :validator_terminated
|
381
|
+
menu_instance.result.should be == '242'
|
382
|
+
end
|
383
|
+
end
|
350
384
|
end
|
351
385
|
|
352
386
|
end#continue
|
@@ -69,6 +69,14 @@ module Adhearsion
|
|
69
69
|
subject.play_audio(audio_file, :fallback => fallback).should be true
|
70
70
|
end
|
71
71
|
end
|
72
|
+
|
73
|
+
context "with a media engine" do
|
74
|
+
let(:media_engine) { :native }
|
75
|
+
it "should use the specified media engine in the component" do
|
76
|
+
expect_ssml_output ssml, renderer: media_engine
|
77
|
+
subject.play_audio(audio_file, renderer: media_engine).should be true
|
78
|
+
end
|
79
|
+
end
|
72
80
|
end
|
73
81
|
|
74
82
|
describe "#play_audio!" do
|
@@ -100,6 +108,14 @@ module Adhearsion
|
|
100
108
|
subject.play_audio!(audio_file, :fallback => fallback).should be_a Punchblock::Component::Output
|
101
109
|
end
|
102
110
|
end
|
111
|
+
|
112
|
+
context "with a media engine" do
|
113
|
+
let(:media_engine) { :native }
|
114
|
+
it "should use the specified media engine in the SSML" do
|
115
|
+
expect_async_ssml_output ssml, renderer: media_engine
|
116
|
+
subject.play_audio!(audio_file, renderer: media_engine).should be_a Punchblock::Component::Output
|
117
|
+
end
|
118
|
+
end
|
103
119
|
end
|
104
120
|
|
105
121
|
describe "#play_numeric" do
|
@@ -319,6 +335,9 @@ module Adhearsion
|
|
319
335
|
end
|
320
336
|
|
321
337
|
describe '#play' do
|
338
|
+
let(:extra_options) do
|
339
|
+
{ renderer: :native }
|
340
|
+
end
|
322
341
|
describe "with a single string" do
|
323
342
|
let(:audio_file) { "/foo/bar.wav" }
|
324
343
|
let :ssml do
|
@@ -330,6 +349,11 @@ module Adhearsion
|
|
330
349
|
expect_ssml_output ssml
|
331
350
|
subject.play(audio_file).should be true
|
332
351
|
end
|
352
|
+
|
353
|
+
it 'plays the audio file with the specified extra options if present' do
|
354
|
+
expect_ssml_output ssml, extra_options
|
355
|
+
subject.play(audio_file, extra_options).should be true
|
356
|
+
end
|
333
357
|
end
|
334
358
|
|
335
359
|
describe "with multiple arguments" do
|
@@ -349,6 +373,12 @@ module Adhearsion
|
|
349
373
|
expect_ssml_output ssml
|
350
374
|
subject.play(*args).should be true
|
351
375
|
end
|
376
|
+
|
377
|
+
it 'plays all arguments in one document with the extra options if present' do
|
378
|
+
expect_ssml_output ssml, extra_options
|
379
|
+
args << extra_options
|
380
|
+
subject.play(*args).should be true
|
381
|
+
end
|
352
382
|
end
|
353
383
|
|
354
384
|
describe "with a collection of arguments" do
|
@@ -463,6 +493,9 @@ module Adhearsion
|
|
463
493
|
end
|
464
494
|
|
465
495
|
describe '#play!' do
|
496
|
+
let(:extra_options) do
|
497
|
+
{ renderer: :native }
|
498
|
+
end
|
466
499
|
describe "with a single string" do
|
467
500
|
let(:audio_file) { "/foo/bar.wav" }
|
468
501
|
let :ssml do
|
@@ -474,6 +507,11 @@ module Adhearsion
|
|
474
507
|
expect_async_ssml_output ssml
|
475
508
|
subject.play!(audio_file).should be_a Punchblock::Component::Output
|
476
509
|
end
|
510
|
+
|
511
|
+
it 'plays the audio file with the specified extra options if present' do
|
512
|
+
expect_async_ssml_output ssml, extra_options
|
513
|
+
subject.play!(audio_file, extra_options)
|
514
|
+
end
|
477
515
|
end
|
478
516
|
|
479
517
|
describe "with multiple arguments" do
|
@@ -493,6 +531,12 @@ module Adhearsion
|
|
493
531
|
expect_async_ssml_output ssml
|
494
532
|
subject.play!(*args).should be_a Punchblock::Component::Output
|
495
533
|
end
|
534
|
+
|
535
|
+
it 'plays all arguments in one document with the extra options if present' do
|
536
|
+
expect_async_ssml_output ssml, extra_options
|
537
|
+
args << extra_options
|
538
|
+
subject.play!(*args)
|
539
|
+
end
|
496
540
|
end
|
497
541
|
|
498
542
|
describe "with a number" do
|
@@ -591,6 +635,7 @@ module Adhearsion
|
|
591
635
|
let(:output1) { "one two" }
|
592
636
|
let(:output2) { "three four" }
|
593
637
|
let(:non_existing) { "http://adhearsion.com/nonexistingfile.mp3" }
|
638
|
+
let(:extra_options) { {renderer: :native } }
|
594
639
|
|
595
640
|
it "plays two outputs in succession" do
|
596
641
|
subject.should_receive(:stream_file).twice
|
@@ -604,6 +649,13 @@ module Adhearsion
|
|
604
649
|
digit.should be == 2
|
605
650
|
end
|
606
651
|
|
652
|
+
it "passes options on to #stream_file" do
|
653
|
+
subject.should_receive(:stream_file).once.with(output1, '0123456789#*', extra_options)
|
654
|
+
subject.should_receive(:stream_file).once.with(output2, '0123456789#*', extra_options)
|
655
|
+
digit = subject.interruptible_play output1, output2, extra_options
|
656
|
+
digit.should be_nil
|
657
|
+
end
|
658
|
+
|
607
659
|
it 'raises an exception when output is unsuccessful' do
|
608
660
|
subject.should_receive(:stream_file).once.and_raise Output::PlaybackError, "Output failed"
|
609
661
|
expect { subject.interruptible_play non_existing }.to raise_error(Output::PlaybackError)
|
@@ -669,6 +721,19 @@ module Adhearsion
|
|
669
721
|
expect_component_execution output_component
|
670
722
|
subject.stream_file(prompt, allowed_digits).should be == '5'
|
671
723
|
end
|
724
|
+
|
725
|
+
context "with output options passed in" do
|
726
|
+
let(:extra_options) { {renderer: :native } }
|
727
|
+
it "plays the correct output with options" do
|
728
|
+
def controller.write_and_await_response(input_component)
|
729
|
+
# it is actually a no-op here
|
730
|
+
end
|
731
|
+
|
732
|
+
expect_component_complete_event
|
733
|
+
expect_component_execution Punchblock::Component::Output.new({:ssml => ssml.to_s}.merge(extra_options))
|
734
|
+
subject.stream_file prompt, allowed_digits, extra_options
|
735
|
+
end
|
736
|
+
end
|
672
737
|
end
|
673
738
|
|
674
739
|
describe "#say" do
|
@@ -117,6 +117,24 @@ module Adhearsion
|
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
120
|
+
context "with :interruptible => '123'" do
|
121
|
+
let(:interruptible) { '123' }
|
122
|
+
|
123
|
+
let :stopper_grammar do
|
124
|
+
RubySpeech::GRXML.draw :mode => 'dtmf', :root => 'inputdigits' do
|
125
|
+
rule id: 'inputdigits', scope: 'public' do
|
126
|
+
one_of do
|
127
|
+
item { '1' }
|
128
|
+
item { '2' }
|
129
|
+
item { '3' }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
its(:stopper_component) { should == input_component }
|
136
|
+
end
|
137
|
+
|
120
138
|
describe "setting completion handlers" do
|
121
139
|
let(:complete_event) { Punchblock::Event::Complete.new }
|
122
140
|
|
@@ -94,7 +94,7 @@ module Adhearsion
|
|
94
94
|
class InvokeController < CallController
|
95
95
|
def run
|
96
96
|
before
|
97
|
-
invoke second_controller, :foo => 'bar'
|
97
|
+
metadata[:invoke_result] = invoke second_controller, :foo => 'bar'
|
98
98
|
after
|
99
99
|
end
|
100
100
|
|
@@ -114,7 +114,7 @@ module Adhearsion
|
|
114
114
|
before do
|
115
115
|
flexmock subject, :execute_component_and_await_completion => nil
|
116
116
|
flexmock call.wrapped_object, :write_and_await_response => nil
|
117
|
-
flexmock call, :register_controller
|
117
|
+
flexmock call, :register_controller => nil
|
118
118
|
flexmock(Events).should_receive(:trigger).with(:exception, Exception).never
|
119
119
|
end
|
120
120
|
|
@@ -126,6 +126,12 @@ module Adhearsion
|
|
126
126
|
subject.execute!
|
127
127
|
end
|
128
128
|
|
129
|
+
it "should return the outer controller's run method return value" do
|
130
|
+
flexmock(SecondController).new_instances.should_receive(:run).once.and_return(:run_result)
|
131
|
+
subject.execute!
|
132
|
+
subject.metadata[:invoke_result].should be == :run_result
|
133
|
+
end
|
134
|
+
|
129
135
|
it "should invoke the new controller with metadata" do
|
130
136
|
flexmock(SecondController).new_instances.should_receive(:md_check).once.with :foo => 'bar'
|
131
137
|
subject.execute!
|
@@ -166,7 +172,7 @@ module Adhearsion
|
|
166
172
|
|
167
173
|
before do
|
168
174
|
flexmock call.wrapped_object, :write_and_await_response => nil
|
169
|
-
flexmock call, :register_controller
|
175
|
+
flexmock call, :register_controller => nil
|
170
176
|
flexmock subject, :execute_component_and_await_completion => nil
|
171
177
|
flexmock(SecondController).new_instances.should_receive(:md_check).once.with :foo => 'bar'
|
172
178
|
flexmock(Events).should_receive(:trigger).with(:exception, Exception).never
|
@@ -348,15 +354,11 @@ module Adhearsion
|
|
348
354
|
|
349
355
|
describe "with an error response" do
|
350
356
|
let(:response) do
|
351
|
-
Punchblock::Event::Complete.new
|
352
|
-
complete << error
|
353
|
-
end
|
357
|
+
Punchblock::Event::Complete.new :reason => error
|
354
358
|
end
|
355
359
|
|
356
|
-
let(:error) do
|
357
|
-
Punchblock::Event::Complete::Error.new
|
358
|
-
e << details
|
359
|
-
end
|
360
|
+
let(:error) do
|
361
|
+
Punchblock::Event::Complete::Error.new :details => details
|
360
362
|
end
|
361
363
|
|
362
364
|
let(:details) { "Oh noes, it's all borked" }
|
@@ -30,7 +30,7 @@ module Adhearsion
|
|
30
30
|
end
|
31
31
|
|
32
32
|
after do
|
33
|
-
Adhearsion.active_calls.clear
|
33
|
+
Adhearsion.active_calls.clear
|
34
34
|
end
|
35
35
|
|
36
36
|
it { should respond_to :<< }
|
@@ -236,9 +236,7 @@ module Adhearsion
|
|
236
236
|
|
237
237
|
describe "for end events" do
|
238
238
|
let :event do
|
239
|
-
Punchblock::Event::End.new
|
240
|
-
flexmock e, :reason => :hangup
|
241
|
-
end
|
239
|
+
Punchblock::Event::End.new :reason => :hangup
|
242
240
|
end
|
243
241
|
|
244
242
|
it "should trigger any on_end callbacks set" do
|
@@ -317,9 +315,7 @@ module Adhearsion
|
|
317
315
|
describe "#<<" do
|
318
316
|
describe "with a Punchblock End" do
|
319
317
|
let :end_event do
|
320
|
-
Punchblock::Event::End.new
|
321
|
-
flexmock e, :reason => :hangup
|
322
|
-
end
|
318
|
+
Punchblock::Event::End.new :reason => :hangup
|
323
319
|
end
|
324
320
|
|
325
321
|
it "should mark the call as ended" do
|
@@ -43,9 +43,9 @@ describe Adhearsion::Initializer do
|
|
43
43
|
|
44
44
|
it "should create a pid file in the app's path when given 'true' as the pid_file hash key argument" do
|
45
45
|
stub_behavior_for_initializer_with_no_path_changing_behavior do
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
flexmock(File).should_receive(:open).with(File.join(path, 'adhearsion.pid'), 'w', Proc).at_least.once
|
47
|
+
ahn = Adhearsion::Initializer.start :pid_file => true
|
48
|
+
ahn.pid_file[0, path.length].should be == path
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -58,6 +58,7 @@ describe Adhearsion::Initializer do
|
|
58
58
|
|
59
59
|
it "should create a pid file in the app's path by default when daemonizing" do
|
60
60
|
stub_behavior_for_initializer_with_no_path_changing_behavior do
|
61
|
+
flexmock(Adhearsion::CustomDaemonizer).should_receive(:daemonize).and_yield
|
61
62
|
flexmock(File).should_receive(:open).once.with(File.join(path, 'adhearsion.pid'), 'w', Proc)
|
62
63
|
ahn = Adhearsion::Initializer.start :mode => :daemon
|
63
64
|
ahn.pid_file[0, path.size].should be == path
|
@@ -66,7 +67,8 @@ describe Adhearsion::Initializer do
|
|
66
67
|
|
67
68
|
it "should NOT create a pid file in the app's path when daemonizing and :pid_file is given as false" do
|
68
69
|
stub_behavior_for_initializer_with_no_path_changing_behavior do
|
69
|
-
|
70
|
+
flexmock(Adhearsion::CustomDaemonizer).should_receive(:daemonize).and_yield
|
71
|
+
ahn = Adhearsion::Initializer.start :mode => :daemon, :pid_file => false
|
70
72
|
ahn.pid_file.should be nil
|
71
73
|
end
|
72
74
|
end
|
@@ -146,7 +146,7 @@ describe Adhearsion::Plugin do
|
|
146
146
|
it "should add a initializer when Plugin defines it" do
|
147
147
|
FooBar = Class.new Adhearsion::Plugin do
|
148
148
|
init :foo_bar do
|
149
|
-
|
149
|
+
log "foo bar"
|
150
150
|
end
|
151
151
|
def self.log
|
152
152
|
end
|
@@ -245,7 +245,7 @@ describe Adhearsion::Plugin do
|
|
245
245
|
it "should add a runner when Plugin defines it" do
|
246
246
|
FooBar = Class.new Adhearsion::Plugin do
|
247
247
|
run :foo_bar do
|
248
|
-
|
248
|
+
log "foo bar"
|
249
249
|
end
|
250
250
|
def self.log
|
251
251
|
end
|
@@ -50,7 +50,7 @@ module Adhearsion
|
|
50
50
|
it "should hang up active calls" do
|
51
51
|
3.times do
|
52
52
|
fake_call = flexmock Call.new, :id => random_call_id
|
53
|
-
flexmock(fake_call).should_receive(:hangup
|
53
|
+
flexmock(fake_call).should_receive(:hangup).once
|
54
54
|
Adhearsion.active_calls << fake_call
|
55
55
|
end
|
56
56
|
|
@@ -54,7 +54,7 @@ module Adhearsion
|
|
54
54
|
end
|
55
55
|
|
56
56
|
let(:call_id) { rand }
|
57
|
-
let(:offer) { Punchblock::Event::Offer.new
|
57
|
+
let(:offer) { Punchblock::Event::Offer.new :target_call_id => call_id }
|
58
58
|
let(:mock_call) { flexmock Call.new, :id => call_id }
|
59
59
|
|
60
60
|
describe "starts the client with the default values" do
|
@@ -224,14 +224,16 @@ module Adhearsion
|
|
224
224
|
end
|
225
225
|
end
|
226
226
|
|
227
|
-
|
228
|
-
|
227
|
+
[ :running, :stopping ].each do |state|
|
228
|
+
context "when when Adhearsion::Process is in :#{state}" do
|
229
|
+
let(:process_state) { state }
|
229
230
|
|
230
|
-
|
231
|
-
|
232
|
-
|
231
|
+
it "should dispatch via the router" do
|
232
|
+
Adhearsion.router do
|
233
|
+
route 'foobar', Class.new
|
234
|
+
end
|
235
|
+
flexmock(Adhearsion.router).should_receive(:handle).once.with mock_call
|
233
236
|
end
|
234
|
-
flexmock(Adhearsion.router).should_receive(:handle).once.with mock_call
|
235
237
|
end
|
236
238
|
end
|
237
239
|
|