adhearsion 2.0.0.beta1 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +2 -4
- data/CHANGELOG.md +34 -4
- data/README.markdown +2 -1
- data/Rakefile +22 -1
- data/adhearsion.gemspec +1 -0
- data/bin/ahn +0 -2
- data/features/cli_daemon.feature +2 -0
- data/features/cli_restart.feature +19 -0
- data/features/cli_start.feature +4 -6
- data/features/cli_stop.feature +3 -0
- data/features/step_definitions/app_generator_steps.rb +2 -0
- data/features/step_definitions/cli_steps.rb +2 -0
- data/features/support/aruba_helper.rb +2 -0
- data/features/support/env.rb +8 -46
- data/features/support/utils.rb +2 -0
- data/lib/adhearsion.rb +4 -6
- data/lib/adhearsion/call.rb +71 -17
- data/lib/adhearsion/call_controller.rb +25 -14
- data/lib/adhearsion/call_controller/dial.rb +34 -15
- data/lib/adhearsion/call_controller/input.rb +186 -144
- data/lib/adhearsion/call_controller/output.rb +10 -6
- data/lib/adhearsion/call_controller/record.rb +11 -13
- data/lib/adhearsion/call_controller/utility.rb +2 -0
- data/lib/adhearsion/calls.rb +4 -2
- data/lib/adhearsion/cli.rb +4 -0
- data/lib/adhearsion/cli_commands.rb +8 -2
- data/lib/adhearsion/configuration.rb +7 -3
- data/lib/adhearsion/console.rb +17 -17
- data/lib/adhearsion/events.rb +10 -4
- data/lib/adhearsion/foundation.rb +9 -0
- data/lib/adhearsion/foundation/custom_daemonizer.rb +3 -1
- data/lib/adhearsion/foundation/exception_handler.rb +2 -0
- data/lib/adhearsion/foundation/libc.rb +2 -0
- data/lib/adhearsion/foundation/object.rb +3 -0
- data/lib/adhearsion/foundation/thread_safety.rb +5 -11
- data/lib/adhearsion/generators.rb +2 -0
- data/lib/adhearsion/generators/app/app_generator.rb +2 -0
- data/lib/adhearsion/generators/app/templates/README.md +9 -0
- data/lib/adhearsion/generators/app/templates/config/adhearsion.rb +38 -16
- data/lib/adhearsion/generators/app/templates/config/environment.rb +2 -0
- data/lib/adhearsion/generators/app/templates/lib/simon_game.rb +5 -3
- data/lib/adhearsion/generators/controller/controller_generator.rb +2 -0
- data/lib/adhearsion/generators/controller/templates/lib/controller.rb +2 -0
- data/lib/adhearsion/generators/controller/templates/spec/controller_spec.rb +2 -0
- data/lib/adhearsion/generators/generator.rb +3 -1
- data/lib/adhearsion/generators/plugin/plugin_generator.rb +2 -0
- data/lib/adhearsion/initializer.rb +31 -17
- data/lib/adhearsion/linux_proc_name.rb +2 -0
- data/lib/adhearsion/logging.rb +5 -3
- data/lib/adhearsion/menu_dsl.rb +2 -0
- data/lib/adhearsion/menu_dsl/calculated_match.rb +2 -0
- data/lib/adhearsion/menu_dsl/calculated_match_collection.rb +2 -0
- data/lib/adhearsion/menu_dsl/fixnum_match_calculator.rb +2 -0
- data/lib/adhearsion/menu_dsl/match_calculator.rb +2 -0
- data/lib/adhearsion/menu_dsl/menu.rb +58 -4
- data/lib/adhearsion/menu_dsl/menu_builder.rb +14 -1
- data/lib/adhearsion/menu_dsl/range_match_calculator.rb +4 -1
- data/lib/adhearsion/menu_dsl/string_match_calculator.rb +2 -0
- data/lib/adhearsion/outbound_call.rb +2 -0
- data/lib/adhearsion/plugin.rb +9 -7
- data/lib/adhearsion/plugin/collection.rb +3 -1
- data/lib/adhearsion/plugin/initializer.rb +3 -1
- data/lib/adhearsion/process.rb +8 -2
- data/lib/adhearsion/punchblock_plugin.rb +3 -1
- data/lib/adhearsion/punchblock_plugin/initializer.rb +34 -11
- data/lib/adhearsion/router.rb +4 -2
- data/lib/adhearsion/router/route.rb +2 -0
- data/lib/adhearsion/script_ahn_loader.rb +2 -0
- data/lib/adhearsion/tasks.rb +2 -0
- data/lib/adhearsion/tasks/configuration.rb +2 -0
- data/lib/adhearsion/tasks/debugging.rb +8 -0
- data/lib/adhearsion/tasks/environment.rb +2 -0
- data/lib/adhearsion/tasks/plugins.rb +2 -0
- data/lib/adhearsion/tasks/testing.rb +2 -0
- data/lib/adhearsion/version.rb +3 -1
- data/pre-commit +2 -0
- data/spec/adhearsion/call_controller/dial_spec.rb +114 -25
- data/spec/adhearsion/call_controller/input_spec.rb +192 -169
- data/spec/adhearsion/call_controller/output_spec.rb +26 -12
- data/spec/adhearsion/call_controller/record_spec.rb +29 -77
- data/spec/adhearsion/call_controller/utility_spec.rb +69 -0
- data/spec/adhearsion/call_controller_spec.rb +90 -15
- data/spec/adhearsion/call_spec.rb +92 -24
- data/spec/adhearsion/calls_spec.rb +9 -7
- data/spec/adhearsion/configuration_spec.rb +58 -56
- data/spec/adhearsion/console_spec.rb +4 -2
- data/spec/adhearsion/events_spec.rb +9 -7
- data/spec/adhearsion/generators_spec.rb +3 -1
- data/spec/adhearsion/initializer_spec.rb +16 -14
- data/spec/adhearsion/logging_spec.rb +11 -9
- data/spec/adhearsion/menu_dsl/calculated_match_collection_spec.rb +6 -4
- data/spec/adhearsion/menu_dsl/calculated_match_spec.rb +6 -4
- data/spec/adhearsion/menu_dsl/fixnum_match_calculator_spec.rb +3 -1
- data/spec/adhearsion/menu_dsl/match_calculator_spec.rb +2 -0
- data/spec/adhearsion/menu_dsl/menu_builder_spec.rb +42 -11
- data/spec/adhearsion/menu_dsl/menu_spec.rb +197 -36
- data/spec/adhearsion/menu_dsl/range_match_calculator_spec.rb +4 -2
- data/spec/adhearsion/menu_dsl/string_match_calculator_spec.rb +5 -3
- data/spec/adhearsion/outbound_call_spec.rb +7 -5
- data/spec/adhearsion/plugin_spec.rb +19 -15
- data/spec/adhearsion/process_spec.rb +12 -7
- data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +35 -15
- data/spec/adhearsion/punchblock_plugin_spec.rb +4 -1
- data/spec/adhearsion/router/route_spec.rb +8 -6
- data/spec/adhearsion/router_spec.rb +12 -10
- data/spec/adhearsion_spec.rb +13 -2
- data/spec/capture_warnings.rb +33 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support/call_controller_test_helpers.rb +2 -4
- data/spec/support/initializer_stubs.rb +8 -5
- data/spec/support/logging_helpers.rb +2 -0
- data/spec/support/punchblock_mocks.rb +2 -0
- metadata +84 -71
- data/EVENTS +0 -11
- data/lib/adhearsion/call_controller/menu.rb +0 -124
- data/lib/adhearsion/foundation/all.rb +0 -8
- data/spec/adhearsion/call_controller/menu_spec.rb +0 -120
- data/spec/adhearsion/menu_dsl_spec.rb +0 -12
data/EVENTS
DELETED
@@ -1,124 +0,0 @@
|
|
1
|
-
module Adhearsion
|
2
|
-
class CallController
|
3
|
-
module Menu
|
4
|
-
|
5
|
-
# Creates and manages a multiple choice menu driven by DTMF, handling playback of prompts,
|
6
|
-
# invalid input, retries and timeouts, and final failures.
|
7
|
-
#
|
8
|
-
# @example A complete example of the method is as follows:
|
9
|
-
# menu "Welcome, ", "/opt/sounds/menu-prompt.mp3", :tries => 2, :timeout => 10 do
|
10
|
-
# match 1, OperatorController
|
11
|
-
#
|
12
|
-
# match 10..19 do
|
13
|
-
# pass DirectController
|
14
|
-
# end
|
15
|
-
#
|
16
|
-
# match 5, 6, 9 do |exten|
|
17
|
-
# play "The #{exten} extension is currently not active"
|
18
|
-
# end
|
19
|
-
#
|
20
|
-
# match '7', OfficeController
|
21
|
-
#
|
22
|
-
# invalid { play "Please choose a valid extension" }
|
23
|
-
# timeout { play "Input timed out, try again." }
|
24
|
-
# failure { pass OperatorController }
|
25
|
-
# end
|
26
|
-
#
|
27
|
-
# The first arguments to #menu will be a list of sounds to play, as accepted by #play, including strings for TTS, Date and Time objects, and file paths.
|
28
|
-
# :tries and :timeout options respectively specify the number of tries before going into failure, and the timeout in seconds allowed on each digit input.
|
29
|
-
# The most important part is the following block, which specifies how the menu will be constructed and handled.
|
30
|
-
#
|
31
|
-
# #match handles connecting an input pattern to a payload.
|
32
|
-
# The pattern can be one or more of: an integer, a Range, a string, an Array of the possible single types.
|
33
|
-
# Input is matched against patterns, and the first exact match has it's payload executed.
|
34
|
-
# Matched input is passed in to the associated block, or to the controller through #options.
|
35
|
-
#
|
36
|
-
# Allowed payloads are the name of a controller class, in which case it is executed through its #run method, or a block.
|
37
|
-
#
|
38
|
-
# #invalid has its associated block executed when the input does not possibly match any pattern.
|
39
|
-
# #timeout's block is run when time expires before or between input digits.
|
40
|
-
# #failure runs its block when the maximum number of tries is reached without an input match.
|
41
|
-
#
|
42
|
-
# Execution of the current context resumes after #menu finishes. If you wish to jump to an entirely different controller, use #pass.
|
43
|
-
# Menu will return :failed if failure was reached, or :done if a match was executed.
|
44
|
-
#
|
45
|
-
# @param [Object] A list of outputs to play, as accepted by #play
|
46
|
-
# @param [Hash] options Options to use for the menu
|
47
|
-
# @option options [Integer] :tries Number of tries allowed before failure
|
48
|
-
# @option options [Integer] :timeout Timeout in seconds before the first and between each input digit
|
49
|
-
#
|
50
|
-
# @return [Symbol] :failure on failure, :done if a match is reached and executed. Will only return if control is not passed.
|
51
|
-
#
|
52
|
-
# @raise [ArgumentError] Raised if no block is passed in
|
53
|
-
#
|
54
|
-
# @see play
|
55
|
-
# @see pass
|
56
|
-
#
|
57
|
-
def menu(*args, &block)
|
58
|
-
raise ArgumentError, "You must provide a block to the #menu method." unless block_given?
|
59
|
-
|
60
|
-
options = args.last.kind_of?(Hash) ? args.pop : {}
|
61
|
-
sound_files = args.flatten
|
62
|
-
|
63
|
-
menu_instance = MenuDSL::Menu.new options, &block
|
64
|
-
result_of_menu = nil
|
65
|
-
|
66
|
-
until MenuDSL::Menu::MenuResultDone === result_of_menu
|
67
|
-
if menu_instance.should_continue?
|
68
|
-
result_of_menu = menu_instance.continue
|
69
|
-
else
|
70
|
-
logger.debug "Menu failed to get valid input. Executing failure hook."
|
71
|
-
menu_instance.execute_failure_hook
|
72
|
-
return :failed
|
73
|
-
end
|
74
|
-
|
75
|
-
case result_of_menu
|
76
|
-
when MenuDSL::Menu::MenuResultInvalid
|
77
|
-
logger.debug "Menu get invalid input. Executing invalid hook and restarting."
|
78
|
-
menu_instance.execute_invalid_hook
|
79
|
-
menu_instance.restart!
|
80
|
-
result_of_menu = nil
|
81
|
-
when MenuDSL::Menu::MenuGetAnotherDigit
|
82
|
-
next_digit = play_sound_files_for_menu menu_instance, sound_files
|
83
|
-
if next_digit
|
84
|
-
menu_instance << next_digit
|
85
|
-
else
|
86
|
-
case result_of_menu
|
87
|
-
when MenuDSL::Menu::MenuGetAnotherDigitOrFinish
|
88
|
-
jump_to result_of_menu.match_object, :extension => result_of_menu.new_extension
|
89
|
-
return true
|
90
|
-
when MenuDSL::Menu::MenuGetAnotherDigitOrTimeout
|
91
|
-
logger.debug "Menu timed out. Executing timeout hook and restarting."
|
92
|
-
menu_instance.execute_timeout_hook
|
93
|
-
menu_instance.restart!
|
94
|
-
result_of_menu = nil
|
95
|
-
end
|
96
|
-
end
|
97
|
-
when MenuDSL::Menu::MenuResultFound
|
98
|
-
logger.debug "Menu got a valid input (#{result_of_menu.new_extension}). Executing the match."
|
99
|
-
jump_to result_of_menu.match_object, :extension => result_of_menu.new_extension
|
100
|
-
return true
|
101
|
-
end # case
|
102
|
-
end # while
|
103
|
-
return :done
|
104
|
-
end
|
105
|
-
|
106
|
-
def play_sound_files_for_menu(menu_instance, sound_files) # :nodoc:
|
107
|
-
digit = nil
|
108
|
-
if sound_files.any? && menu_instance.digit_buffer_empty?
|
109
|
-
digit = interruptible_play *sound_files
|
110
|
-
end
|
111
|
-
digit || wait_for_digit(menu_instance.timeout)
|
112
|
-
end
|
113
|
-
|
114
|
-
def jump_to(match_object, overrides = nil) # :nodoc:
|
115
|
-
if match_object.block
|
116
|
-
instance_exec overrides[:extension], &match_object.block
|
117
|
-
else
|
118
|
-
invoke match_object.match_payload, overrides
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
end # module
|
123
|
-
end
|
124
|
-
end
|
@@ -1,120 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Adhearsion
|
4
|
-
class CallController
|
5
|
-
describe Menu do
|
6
|
-
include CallControllerTestHelpers
|
7
|
-
|
8
|
-
describe "#play_sound_files_for_menu" do
|
9
|
-
let(:options) { Hash.new }
|
10
|
-
let(:menu_instance) { Adhearsion::MenuDSL::Menu.new(options) {} }
|
11
|
-
let(:sound_file) { "press a button" }
|
12
|
-
let(:sound_files) { [sound_file] }
|
13
|
-
|
14
|
-
it "should play the sound files for the menu" do
|
15
|
-
subject.should_receive(:interruptible_play).with(sound_file).and_return("1")
|
16
|
-
subject.play_sound_files_for_menu(menu_instance, sound_files)
|
17
|
-
end
|
18
|
-
|
19
|
-
it "should wait for digit if nothing is pressed during playback" do
|
20
|
-
subject.should_receive(:interruptible_play).with(sound_file).and_return(nil)
|
21
|
-
subject.should_receive(:wait_for_digit).with(menu_instance.timeout).and_return("1")
|
22
|
-
subject.play_sound_files_for_menu(menu_instance, sound_files)
|
23
|
-
end
|
24
|
-
end#play_sound_files_for_menu
|
25
|
-
|
26
|
-
describe "#jump_to" do
|
27
|
-
let(:match_object) { flexmock(Class.new) }
|
28
|
-
let(:overrides) { Hash.new(:extension => "1") }
|
29
|
-
let(:block) { Proc.new() {} }
|
30
|
-
|
31
|
-
it "calls instance_exec if the match object has a block" do
|
32
|
-
match_object.should_receive(:block).and_return(block)
|
33
|
-
subject.should_receive(:instance_exec).with(overrides[:extension], block)
|
34
|
-
subject.jump_to(match_object, overrides)
|
35
|
-
end
|
36
|
-
|
37
|
-
it "calls invoke if the match object does not have a block" do
|
38
|
-
match_object.should_receive(:block).and_return(false)
|
39
|
-
match_object.should_receive(:match_payload).and_return(:payload)
|
40
|
-
subject.should_receive(:invoke).with(:payload, overrides)
|
41
|
-
subject.jump_to(match_object, overrides)
|
42
|
-
end
|
43
|
-
end#jump_to
|
44
|
-
|
45
|
-
describe "#menu" do
|
46
|
-
let(:sound_files) { ["press", "button"] }
|
47
|
-
context "menu state flow" do
|
48
|
-
let(:menu_instance) do
|
49
|
-
menu_instance = flexmock(MenuDSL::Menu.new({}) {})
|
50
|
-
end
|
51
|
-
let(:result_done) { MenuDSL::Menu::MenuResultDone.new }
|
52
|
-
let(:result_invalid) { MenuDSL::Menu::MenuResultInvalid.new }
|
53
|
-
let(:result_get_another_or_timeout) { MenuDSL::Menu::MenuGetAnotherDigitOrTimeout.new }
|
54
|
-
let(:result_get_another_or_finish) { MenuDSL::Menu::MenuGetAnotherDigitOrFinish.new(:match_object, :new_extension) }
|
55
|
-
let(:result_found) { MenuDSL::Menu::MenuResultFound.new(:match_object, :new_extension) }
|
56
|
-
|
57
|
-
before(:each) do
|
58
|
-
flexmock(MenuDSL::Menu).should_receive(:new).and_return(menu_instance)
|
59
|
-
end
|
60
|
-
|
61
|
-
it "exits the function if MenuResultDone" do
|
62
|
-
menu_instance.should_receive(:should_continue?).and_return(true)
|
63
|
-
menu_instance.should_receive(:continue).and_return(result_done)
|
64
|
-
result = subject.menu(sound_files) {}
|
65
|
-
result.should == :done
|
66
|
-
end
|
67
|
-
|
68
|
-
it "executes failure hook and returns :failure if menu fails" do
|
69
|
-
menu_instance.should_receive(:should_continue?).and_return(false)
|
70
|
-
menu_instance.should_receive(:execute_failure_hook)
|
71
|
-
result = subject.menu(sound_files) {}
|
72
|
-
result.should == :failed
|
73
|
-
end
|
74
|
-
|
75
|
-
it "executes invalid hook if input is invalid" do
|
76
|
-
menu_instance.should_receive(:should_continue?).twice.and_return(true)
|
77
|
-
menu_instance.should_receive(:continue).and_return(result_invalid, result_done)
|
78
|
-
menu_instance.should_receive(:execute_invalid_hook)
|
79
|
-
menu_instance.should_receive(:restart!)
|
80
|
-
subject.menu(sound_files) {}
|
81
|
-
end
|
82
|
-
|
83
|
-
it "plays audio, then executes timeout hook if input times out" do
|
84
|
-
menu_instance.should_receive(:should_continue?).twice.and_return(true)
|
85
|
-
menu_instance.should_receive(:continue).and_return(result_get_another_or_timeout, result_done)
|
86
|
-
subject.should_receive(:play_sound_files_for_menu).with(menu_instance, sound_files).and_return(nil)
|
87
|
-
menu_instance.should_receive(:execute_timeout_hook)
|
88
|
-
menu_instance.should_receive(:restart!)
|
89
|
-
subject.menu(sound_files) {}
|
90
|
-
end
|
91
|
-
|
92
|
-
it "plays audio, then adds digit to digit buffer if input is received" do
|
93
|
-
menu_instance.should_receive(:should_continue?).twice.and_return(true)
|
94
|
-
menu_instance.should_receive(:continue).and_return(result_get_another_or_timeout, result_done)
|
95
|
-
subject.should_receive(:play_sound_files_for_menu).with(menu_instance, sound_files).and_return("1")
|
96
|
-
menu_instance.should_receive(:<<).with("1")
|
97
|
-
subject.menu(sound_files) {}
|
98
|
-
end
|
99
|
-
|
100
|
-
it "plays audio, then jumps to payload when input is finished" do
|
101
|
-
menu_instance.should_receive(:should_continue?).and_return(true)
|
102
|
-
menu_instance.should_receive(:continue).and_return(result_get_another_or_finish)
|
103
|
-
subject.should_receive(:play_sound_files_for_menu).with(menu_instance, sound_files).and_return(nil)
|
104
|
-
subject.should_receive(:jump_to).with(:match_object, :extension => :new_extension)
|
105
|
-
subject.menu(sound_files) {}
|
106
|
-
end
|
107
|
-
|
108
|
-
it "jumps to payload when result is found" do
|
109
|
-
menu_instance.should_receive(:should_continue?).and_return(true)
|
110
|
-
menu_instance.should_receive(:continue).and_return(result_found)
|
111
|
-
subject.should_receive(:jump_to).with(:match_object, :extension => :new_extension)
|
112
|
-
subject.menu(sound_files) {}
|
113
|
-
end
|
114
|
-
end#context
|
115
|
-
|
116
|
-
end#describe
|
117
|
-
|
118
|
-
end#shared
|
119
|
-
end
|
120
|
-
end
|