adhearsion 2.0.0.beta1 → 2.0.0.rc1
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.
- 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
|