adhearsion 2.2.1 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|