eric-adhearsion 0.7.999
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -0
- data/LICENSE +456 -0
- data/Manifest.txt +149 -0
- data/README.txt +6 -0
- data/Rakefile +48 -0
- data/ahn_generators/component/USAGE +5 -0
- data/ahn_generators/component/component_generator.rb +57 -0
- data/ahn_generators/component/templates/configuration.rb +0 -0
- data/ahn_generators/component/templates/lib/lib.rb.erb +3 -0
- data/ahn_generators/component/templates/test/test.rb.erb +12 -0
- data/ahn_generators/component/templates/test/test_helper.rb +14 -0
- data/app_generators/ahn/USAGE +5 -0
- data/app_generators/ahn/ahn_generator.rb +76 -0
- data/app_generators/ahn/templates/.ahnrc +12 -0
- data/app_generators/ahn/templates/README +8 -0
- data/app_generators/ahn/templates/Rakefile +3 -0
- data/app_generators/ahn/templates/components/simon_game/configuration.rb +0 -0
- data/app_generators/ahn/templates/components/simon_game/lib/simon_game.rb +61 -0
- data/app_generators/ahn/templates/components/simon_game/test/test_helper.rb +14 -0
- data/app_generators/ahn/templates/components/simon_game/test/test_simon_game.rb +31 -0
- data/app_generators/ahn/templates/config/startup.rb +53 -0
- data/app_generators/ahn/templates/dialplan.rb +4 -0
- data/bin/ahn +28 -0
- data/bin/ahnctl +68 -0
- data/bin/jahn +32 -0
- data/lib/adhearsion/blank_slate.rb +5 -0
- data/lib/adhearsion/cli.rb +106 -0
- data/lib/adhearsion/component_manager.rb +277 -0
- data/lib/adhearsion/core_extensions/all.rb +9 -0
- data/lib/adhearsion/core_extensions/array.rb +0 -0
- data/lib/adhearsion/core_extensions/custom_daemonizer.rb +45 -0
- data/lib/adhearsion/core_extensions/global.rb +1 -0
- data/lib/adhearsion/core_extensions/guid.rb +5 -0
- data/lib/adhearsion/core_extensions/hash.rb +0 -0
- data/lib/adhearsion/core_extensions/metaprogramming.rb +17 -0
- data/lib/adhearsion/core_extensions/numeric.rb +4 -0
- data/lib/adhearsion/core_extensions/proc.rb +0 -0
- data/lib/adhearsion/core_extensions/pseudo_uuid.rb +11 -0
- data/lib/adhearsion/core_extensions/publishable.rb +73 -0
- data/lib/adhearsion/core_extensions/relationship_properties.rb +40 -0
- data/lib/adhearsion/core_extensions/string.rb +26 -0
- data/lib/adhearsion/core_extensions/thread.rb +13 -0
- data/lib/adhearsion/core_extensions/thread_safety.rb +7 -0
- data/lib/adhearsion/core_extensions/time.rb +0 -0
- data/lib/adhearsion/distributed/gateways/dbus_gateway.rb +0 -0
- data/lib/adhearsion/distributed/gateways/osa_gateway.rb +0 -0
- data/lib/adhearsion/distributed/gateways/rest_gateway.rb +9 -0
- data/lib/adhearsion/distributed/gateways/soap_gateway.rb +9 -0
- data/lib/adhearsion/distributed/gateways/xmlrpc_gateway.rb +9 -0
- data/lib/adhearsion/distributed/peer_finder.rb +0 -0
- data/lib/adhearsion/distributed/remote_cli.rb +0 -0
- data/lib/adhearsion/hooks.rb +57 -0
- data/lib/adhearsion/host_definitions.rb +63 -0
- data/lib/adhearsion/initializer/asterisk.rb +59 -0
- data/lib/adhearsion/initializer/configuration.rb +202 -0
- data/lib/adhearsion/initializer/database.rb +92 -0
- data/lib/adhearsion/initializer/drb.rb +25 -0
- data/lib/adhearsion/initializer/freeswitch.rb +22 -0
- data/lib/adhearsion/initializer/paths.rb +55 -0
- data/lib/adhearsion/initializer/rails.rb +40 -0
- data/lib/adhearsion/initializer.rb +217 -0
- data/lib/adhearsion/logging.rb +92 -0
- data/lib/adhearsion/services/scheduler.rb +5 -0
- data/lib/adhearsion/tasks/database.rb +5 -0
- data/lib/adhearsion/tasks/generating.rb +20 -0
- data/lib/adhearsion/tasks/lint.rb +4 -0
- data/lib/adhearsion/tasks/testing.rb +37 -0
- data/lib/adhearsion/tasks.rb +15 -0
- data/lib/adhearsion/version.rb +9 -0
- data/lib/adhearsion/voip/asterisk/agi_server.rb +78 -0
- data/lib/adhearsion/voip/asterisk/ami/actions.rb +238 -0
- data/lib/adhearsion/voip/asterisk/ami/machine.rb +871 -0
- data/lib/adhearsion/voip/asterisk/ami/machine.rl +109 -0
- data/lib/adhearsion/voip/asterisk/ami/parser.rb +262 -0
- data/lib/adhearsion/voip/asterisk/ami.rb +147 -0
- data/lib/adhearsion/voip/asterisk/commands.rb +1182 -0
- data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +140 -0
- data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +101 -0
- data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +250 -0
- data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +240 -0
- data/lib/adhearsion/voip/asterisk/config_manager.rb +71 -0
- data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +80 -0
- data/lib/adhearsion/voip/asterisk.rb +4 -0
- data/lib/adhearsion/voip/call.rb +391 -0
- data/lib/adhearsion/voip/call_routing.rb +64 -0
- data/lib/adhearsion/voip/commands.rb +9 -0
- data/lib/adhearsion/voip/constants.rb +39 -0
- data/lib/adhearsion/voip/conveniences.rb +18 -0
- data/lib/adhearsion/voip/dial_plan.rb +205 -0
- data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +37 -0
- data/lib/adhearsion/voip/dsl/dialing_dsl.rb +151 -0
- data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +27 -0
- data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +124 -0
- data/lib/adhearsion/voip/dsl/dialplan/parser.rb +75 -0
- data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +16 -0
- data/lib/adhearsion/voip/dsl/numerical_string.rb +117 -0
- data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +48 -0
- data/lib/adhearsion/voip/freeswitch/event_handler.rb +58 -0
- data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +129 -0
- data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +38 -0
- data/lib/adhearsion/voip/freeswitch/oes_server.rb +195 -0
- data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +80 -0
- data/lib/adhearsion/voip/menu_state_machine/matchers.rb +123 -0
- data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +58 -0
- data/lib/adhearsion/voip/menu_state_machine/menu_class.rb +149 -0
- data/lib/adhearsion.rb +31 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/fixtures/dialplan.rb +3 -0
- data/spec/initializer/test_configuration.rb +267 -0
- data/spec/initializer/test_loading.rb +162 -0
- data/spec/initializer/test_paths.rb +43 -0
- data/spec/silence.rb +10 -0
- data/spec/test_ahn_command.rb +149 -0
- data/spec/test_code_quality.rb +87 -0
- data/spec/test_component_manager.rb +97 -0
- data/spec/test_constants.rb +8 -0
- data/spec/test_drb.rb +104 -0
- data/spec/test_helper.rb +94 -0
- data/spec/test_hooks.rb +37 -0
- data/spec/test_host_definitions.rb +79 -0
- data/spec/test_initialization.rb +105 -0
- data/spec/test_logging.rb +80 -0
- data/spec/test_relationship_properties.rb +54 -0
- data/spec/voip/asterisk/ami_response_definitions.rb +23 -0
- data/spec/voip/asterisk/config_file_generators/test_agents.rb +253 -0
- data/spec/voip/asterisk/config_file_generators/test_queues.rb +325 -0
- data/spec/voip/asterisk/config_file_generators/test_voicemail.rb +306 -0
- data/spec/voip/asterisk/menu_command/test_calculated_match.rb +111 -0
- data/spec/voip/asterisk/menu_command/test_matchers.rb +98 -0
- data/spec/voip/asterisk/mock_ami_server.rb +176 -0
- data/spec/voip/asterisk/test_agi_server.rb +451 -0
- data/spec/voip/asterisk/test_ami.rb +227 -0
- data/spec/voip/asterisk/test_commands.rb +2006 -0
- data/spec/voip/asterisk/test_config_manager.rb +129 -0
- data/spec/voip/dsl/dispatcher_spec_helper.rb +45 -0
- data/spec/voip/dsl/test_dialing_dsl.rb +268 -0
- data/spec/voip/dsl/test_dispatcher.rb +82 -0
- data/spec/voip/dsl/test_parser.rb +87 -0
- data/spec/voip/freeswitch/test_basic_connection_manager.rb +39 -0
- data/spec/voip/freeswitch/test_inbound_connection_manager.rb +39 -0
- data/spec/voip/freeswitch/test_oes_server.rb +9 -0
- data/spec/voip/test_call_routing.rb +127 -0
- data/spec/voip/test_dialplan_manager.rb +372 -0
- data/spec/voip/test_numerical_string.rb +48 -0
- data/spec/voip/test_phone_number.rb +36 -0
- data/test/test_ahn_generator.rb +59 -0
- data/test/test_component_generator.rb +52 -0
- data/test/test_generator_helper.rb +20 -0
- metadata +254 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'calculated_match')
|
2
|
+
|
3
|
+
module Adhearsion
|
4
|
+
module VoIP
|
5
|
+
class MatchCalculator
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def build_with_pattern(pattern, match_payload, &block)
|
10
|
+
class_for_pattern_type(pattern.class.name).new(pattern, match_payload, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def inherited(klass)
|
14
|
+
subclasses << klass
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def class_for_pattern_type(pattern_type)
|
20
|
+
sought_class_name = "Adhearsion::VoIP::#{pattern_type.camelize}MatchCalculator"
|
21
|
+
subclasses.find { |klass| klass.name == sought_class_name }
|
22
|
+
end
|
23
|
+
|
24
|
+
def subclasses
|
25
|
+
@@subclasses ||= []
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :pattern, :match_payload
|
31
|
+
def initialize(pattern, match_payload)
|
32
|
+
@pattern, @match_payload = pattern, match_payload
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def new_calculated_match(options)
|
38
|
+
CalculatedMatch.new({:pattern => pattern, :match_payload => match_payload}.merge(options))
|
39
|
+
end
|
40
|
+
|
41
|
+
def coerce_to_numeric(victim)
|
42
|
+
victim.kind_of?(Numeric) ? victim : (victim.to_s =~ /^\d+$/ ? victim.to_s.to_i : nil )
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class RangeMatchCalculator < MatchCalculator
|
47
|
+
|
48
|
+
def initialize(pattern, match_payload)
|
49
|
+
raise unless pattern.first.kind_of?(Numeric) && pattern.last.kind_of?(Numeric)
|
50
|
+
super
|
51
|
+
end
|
52
|
+
|
53
|
+
def match(query)
|
54
|
+
numerical_query = coerce_to_numeric query
|
55
|
+
if numerical_query
|
56
|
+
exact_match = pattern.include?(numerical_query) ? query : nil
|
57
|
+
potential_matches = numbers_in_range_like numerical_query
|
58
|
+
potential_matches.reject! { |m| m.to_s == exact_match.to_s } if exact_match
|
59
|
+
|
60
|
+
new_calculated_match :query => query, :exact_matches => exact_match,
|
61
|
+
:potential_matches => potential_matches
|
62
|
+
else
|
63
|
+
CalculatedMatch.failed_match!(pattern, query, match_payload)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# Returns all numbers in the range (@pattern) that +begin with+ the number given
|
70
|
+
# as the first argument.
|
71
|
+
#
|
72
|
+
# NOTE! If you're having trouble reading what this method is actually doing, it's
|
73
|
+
# effectively a much more efficient version of this:
|
74
|
+
#
|
75
|
+
# pattern.to_a.select { |x| x.to_s.starts_with? num.to_s }.flatten
|
76
|
+
#
|
77
|
+
# Huge thanks to Dave Troy (http://davetroy.blogspot.com) for this awesomely
|
78
|
+
# efficient code!
|
79
|
+
def numbers_in_range_like(num)
|
80
|
+
return (pattern === 0 ? [0] : nil) if num == 0
|
81
|
+
raise ArgumentError unless num.kind_of?(Numeric)
|
82
|
+
returning Array.new do |matches|
|
83
|
+
first, last = pattern.first, pattern.last
|
84
|
+
power = 0
|
85
|
+
while num < last
|
86
|
+
ones_count = 10**power - 1
|
87
|
+
matches.concat((([num, first].max)..[(num+ones_count), last].min).to_a)
|
88
|
+
num *= 10
|
89
|
+
power += 1
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class FixnumMatchCalculator < MatchCalculator
|
96
|
+
def match(query)
|
97
|
+
numeric_query = coerce_to_numeric query
|
98
|
+
exact_match, potential_match = nil
|
99
|
+
if pattern == numeric_query
|
100
|
+
exact_match = pattern
|
101
|
+
elsif pattern.to_s.starts_with? query.to_s
|
102
|
+
potential_match = pattern
|
103
|
+
end
|
104
|
+
new_calculated_match :query => query, :exact_matches => exact_match, :potential_matches => potential_match
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class StringMatchCalculator < MatchCalculator
|
109
|
+
def match(query)
|
110
|
+
args = { :query => query, :exact_matches => nil,
|
111
|
+
:potential_matches => nil }
|
112
|
+
|
113
|
+
if pattern == query.to_s
|
114
|
+
args[:exact_matches] = [pattern]
|
115
|
+
elsif pattern.starts_with? query.to_s
|
116
|
+
args[:potential_matches] = [pattern]
|
117
|
+
end
|
118
|
+
|
119
|
+
new_calculated_match args
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'matchers.rb')
|
2
|
+
|
3
|
+
module Adhearsion
|
4
|
+
module VoIP
|
5
|
+
class MenuBuilder
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@patterns = []
|
9
|
+
@menu_callbacks = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(match_payload, *patterns, &block)
|
13
|
+
name_string = match_payload.to_s
|
14
|
+
if patterns.any?
|
15
|
+
patterns.each do |pattern|
|
16
|
+
@patterns << MatchCalculator.build_with_pattern(pattern, match_payload)
|
17
|
+
end
|
18
|
+
else
|
19
|
+
raise ArgumentError, "You cannot call this method without patterns!"
|
20
|
+
end
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def weighted_match_calculators
|
25
|
+
@patterns
|
26
|
+
end
|
27
|
+
|
28
|
+
def execute_hook_for(symbol, input)
|
29
|
+
callback = @menu_callbacks[symbol]
|
30
|
+
callback.call input if callback
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_invalid(&block)
|
34
|
+
raise LocalJumpError, "Must supply a block!" unless block_given?
|
35
|
+
@menu_callbacks[:invalid] = block
|
36
|
+
end
|
37
|
+
|
38
|
+
def on_premature_timeout(&block)
|
39
|
+
raise LocalJumpError, "Must supply a block!" unless block_given?
|
40
|
+
@menu_callbacks[:premature_timeout] = block
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_failure(&block)
|
44
|
+
raise LocalJumpError, "Must supply a block!" unless block_given?
|
45
|
+
@menu_callbacks[:failure] = block
|
46
|
+
end
|
47
|
+
|
48
|
+
def calculate_matches_for(result)
|
49
|
+
returning CalculatedMatchCollection.new do |collection|
|
50
|
+
weighted_match_calculators.each do |pattern|
|
51
|
+
collection << pattern.match(result)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'adhearsion/voip/menu_state_machine/menu_builder'
|
2
|
+
module Adhearsion
|
3
|
+
module VoIP
|
4
|
+
class Menu
|
5
|
+
|
6
|
+
DEFAULT_MAX_NUMBER_OF_TRIES = 1
|
7
|
+
DEFAULT_TIMEOUT = 5 # seconds
|
8
|
+
|
9
|
+
relationships :menu_builder => MenuBuilder
|
10
|
+
|
11
|
+
attr_reader :builder, :timeout, :tries_count, :max_number_of_tries
|
12
|
+
def initialize(options={}, &block)
|
13
|
+
@tries_count = 0 # Counts the number of tries the menu's been executed
|
14
|
+
|
15
|
+
@timeout = options[:timeout] || DEFAULT_TIMEOUT
|
16
|
+
@max_number_of_tries = options[:tries] || DEFAULT_MAX_NUMBER_OF_TRIES
|
17
|
+
|
18
|
+
@builder = menu_builder.new
|
19
|
+
yield @builder
|
20
|
+
|
21
|
+
initialize_digit_buffer
|
22
|
+
end
|
23
|
+
|
24
|
+
def <<(other)
|
25
|
+
digit_buffer << other
|
26
|
+
end
|
27
|
+
|
28
|
+
def digit_buffer
|
29
|
+
@digit_buffer
|
30
|
+
end
|
31
|
+
|
32
|
+
def digit_buffer_string
|
33
|
+
digit_buffer.to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
def digit_buffer_empty?
|
37
|
+
digit_buffer.empty?
|
38
|
+
end
|
39
|
+
|
40
|
+
def continue
|
41
|
+
raise MenuGetAnotherDigitOrTimeout if digit_buffer_empty?
|
42
|
+
|
43
|
+
calculated_matches = builder.calculate_matches_for digit_buffer_string
|
44
|
+
|
45
|
+
if calculated_matches.exact_match_count >= 1
|
46
|
+
first_exact_match = calculated_matches.exact_matches.first
|
47
|
+
if calculated_matches.potential_match_count.zero?
|
48
|
+
# Match found with no extenuating ambiguities! Go with the first exact match
|
49
|
+
menu_result_found! first_exact_match.match_payload, digit_buffer_string
|
50
|
+
else
|
51
|
+
get_another_digit_or_finish!(first_exact_match.match_payload, first_exact_match.query)
|
52
|
+
end
|
53
|
+
elsif calculated_matches.potential_match_count >= 1
|
54
|
+
get_another_digit_or_timeout!
|
55
|
+
else
|
56
|
+
invalid!
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def should_continue?
|
61
|
+
tries_count < max_number_of_tries
|
62
|
+
end
|
63
|
+
|
64
|
+
def restart!
|
65
|
+
@tries_count += 1
|
66
|
+
digit_buffer.clear!
|
67
|
+
end
|
68
|
+
|
69
|
+
def execute_invalid_hook
|
70
|
+
builder.execute_hook_for(:invalid, digit_buffer_string)
|
71
|
+
end
|
72
|
+
|
73
|
+
def execute_timeout_hook
|
74
|
+
builder.execute_hook_for(:premature_timeout, digit_buffer_string)
|
75
|
+
end
|
76
|
+
|
77
|
+
def execute_failure_hook
|
78
|
+
builder.execute_hook_for(:failure, digit_buffer_string)
|
79
|
+
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
|
83
|
+
# If you're using a more complex class in subclasses, you may want to override this method in addition to the
|
84
|
+
# digit_buffer, digit_buffer_empty, and digit_buffer_string methods
|
85
|
+
def initialize_digit_buffer
|
86
|
+
@digit_buffer = ClearableStringBuffer.new
|
87
|
+
end
|
88
|
+
|
89
|
+
def invalid!
|
90
|
+
raise MenuResultInvalid
|
91
|
+
end
|
92
|
+
|
93
|
+
def menu_result_found!(match_payload, new_extension)
|
94
|
+
raise MenuResultFound.new(match_payload, new_extension)
|
95
|
+
end
|
96
|
+
|
97
|
+
def get_another_digit_or_finish!(match_payload, new_extension)
|
98
|
+
raise MenuGetAnotherDigitOrFinish.new(match_payload, new_extension)
|
99
|
+
end
|
100
|
+
|
101
|
+
def get_another_digit_or_timeout!
|
102
|
+
raise MenuGetAnotherDigitOrTimeout
|
103
|
+
end
|
104
|
+
|
105
|
+
# The superclass from which all message-like exceptions decend. It should never
|
106
|
+
# be instantiated directly.
|
107
|
+
class MenuResult < Exception; end
|
108
|
+
|
109
|
+
# Raised when the user's input matches
|
110
|
+
class MenuResultFound < MenuResult
|
111
|
+
attr_reader :match_payload, :new_extension
|
112
|
+
def initialize(match_payload, new_extension)
|
113
|
+
super()
|
114
|
+
@match_payload = match_payload
|
115
|
+
@new_extension = new_extension
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
module MenuGetAnotherDigit; end
|
120
|
+
|
121
|
+
class MenuGetAnotherDigitOrFinish < MenuResultFound
|
122
|
+
include MenuGetAnotherDigit
|
123
|
+
end
|
124
|
+
|
125
|
+
class MenuGetAnotherDigitOrTimeout < MenuResult
|
126
|
+
include MenuGetAnotherDigit
|
127
|
+
end
|
128
|
+
|
129
|
+
class MenuResultFound < MenuResult; end
|
130
|
+
|
131
|
+
# Raised when the user's input matches no patterns
|
132
|
+
class MenuResultInvalid < MenuResult; end
|
133
|
+
|
134
|
+
# For our default purposes, we need the digit_buffer to behave much like a normal String except that it should
|
135
|
+
# handle its own resetting (clearing).
|
136
|
+
class ClearableStringBuffer < String
|
137
|
+
def clear!
|
138
|
+
replace ""
|
139
|
+
end
|
140
|
+
|
141
|
+
def <<(other)
|
142
|
+
super other.to_s
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
data/lib/adhearsion.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Check the Ruby version
|
2
|
+
STDERR.puts "WARNING: You are running Adhearsion in an unsupported
|
3
|
+
version of Ruby (Ruby #{RUBY_VERSION} #{RUBY_RELEASE_DATE})!
|
4
|
+
Please upgrade to at least Ruby v1.8.5." if RUBY_VERSION < "1.8.5"
|
5
|
+
|
6
|
+
module Adhearsion
|
7
|
+
# Sets up the Gem require path.
|
8
|
+
AHN_INSTALL_DIR = File.expand_path(File.dirname(__FILE__) + "/..")
|
9
|
+
CONFIG = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
$: << File.expand_path(File.dirname(__FILE__))
|
13
|
+
|
14
|
+
require 'rubygems'
|
15
|
+
require 'adhearsion/version'
|
16
|
+
require 'adhearsion/voip/call'
|
17
|
+
require 'adhearsion/voip/dial_plan'
|
18
|
+
require 'adhearsion/voip/asterisk/special_dial_plan_managers'
|
19
|
+
require 'adhearsion/core_extensions/all'
|
20
|
+
require 'adhearsion/blank_slate'
|
21
|
+
require 'adhearsion/hooks'
|
22
|
+
require 'adhearsion/logging'
|
23
|
+
require 'adhearsion/initializer/configuration'
|
24
|
+
require 'adhearsion/initializer'
|
25
|
+
require 'adhearsion/initializer/paths'
|
26
|
+
require 'adhearsion/voip/dsl/numerical_string'
|
27
|
+
require 'adhearsion/voip/dsl/dialplan/parser'
|
28
|
+
require 'adhearsion/voip/commands'
|
29
|
+
require 'adhearsion/voip/asterisk/commands'
|
30
|
+
require 'adhearsion/voip/dsl/dialing_dsl'
|
31
|
+
require 'adhearsion/voip/call_routing'
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.join(File.dirname(__FILE__), '..')
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, "test_unit"]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.join(File.dirname(__FILE__), '..')
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, "test_unit"]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
@@ -0,0 +1,267 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../test_helper"
|
2
|
+
|
3
|
+
context "Configuration defaults" do
|
4
|
+
include ConfigurationTestHelper
|
5
|
+
attr_reader :config
|
6
|
+
|
7
|
+
setup do
|
8
|
+
@config = default_config
|
9
|
+
end
|
10
|
+
|
11
|
+
test "incoming calls are answered by default" do
|
12
|
+
assert config.automatically_answer_incoming_calls
|
13
|
+
end
|
14
|
+
|
15
|
+
test "calls are ended when hung up" do
|
16
|
+
assert config.end_call_on_hangup
|
17
|
+
end
|
18
|
+
|
19
|
+
test "if an error occurs, a call is hungup" do
|
20
|
+
assert config.end_call_on_error
|
21
|
+
end
|
22
|
+
|
23
|
+
test "asterisk is enabled by default" do
|
24
|
+
assert config.asterisk_enabled?
|
25
|
+
end
|
26
|
+
|
27
|
+
test "default asterisk configuration is available" do
|
28
|
+
assert_kind_of Adhearsion::Configuration::AsteriskConfiguration, config.asterisk
|
29
|
+
end
|
30
|
+
|
31
|
+
test "database access is NOT enabled by default" do
|
32
|
+
assert !config.database_enabled?
|
33
|
+
end
|
34
|
+
|
35
|
+
test "freeswith is NOT enabled by default" do
|
36
|
+
assert !config.freeswitch_enabled?
|
37
|
+
end
|
38
|
+
|
39
|
+
test "AMI is NOT enabled by default" do
|
40
|
+
assert !config.asterisk.ami_enabled?
|
41
|
+
end
|
42
|
+
|
43
|
+
test "Drb is NOT enabled by default" do
|
44
|
+
assert !config.drb_enabled?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "Asterisk AGI configuration defaults" do
|
49
|
+
attr_reader :config
|
50
|
+
|
51
|
+
setup do
|
52
|
+
@config = Adhearsion::Configuration::AsteriskConfiguration.new
|
53
|
+
end
|
54
|
+
|
55
|
+
test "asterisk configuration sets default listening port" do
|
56
|
+
config.listening_port.should.equal Adhearsion::Configuration::AsteriskConfiguration.default_listening_port
|
57
|
+
end
|
58
|
+
|
59
|
+
test "asterisk configuration sets default listening host" do
|
60
|
+
config.listening_host.should.equal Adhearsion::Configuration::AsteriskConfiguration.default_listening_host
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'Logging configuration' do
|
65
|
+
|
66
|
+
attr_reader :config
|
67
|
+
before :each do
|
68
|
+
@config = Adhearsion::Configuration.new
|
69
|
+
end
|
70
|
+
|
71
|
+
after :each do
|
72
|
+
Adhearsion::Logging.logging_level = :fatal
|
73
|
+
end
|
74
|
+
|
75
|
+
test 'the logging level should translate from symbols into Log4r constants' do
|
76
|
+
Adhearsion::Logging.logging_level.should.not.equal Log4r::WARN
|
77
|
+
config.logging :level => :warn
|
78
|
+
Adhearsion::Logging.logging_level.should.equal Log4r::WARN
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
context "AMI configuration defaults" do
|
84
|
+
attr_reader :config
|
85
|
+
|
86
|
+
setup do
|
87
|
+
@config = Adhearsion::Configuration::AsteriskConfiguration::AMIConfiguration.new
|
88
|
+
end
|
89
|
+
|
90
|
+
test "ami configuration sets default port" do
|
91
|
+
config.port.should.equal Adhearsion::Configuration::AsteriskConfiguration::AMIConfiguration.default_port
|
92
|
+
end
|
93
|
+
|
94
|
+
test "ami allows you to configure a username and a password, both of which default to nil" do
|
95
|
+
config.username.should.be.nil
|
96
|
+
config.password.should.be.nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "Rails configuration defaults" do
|
101
|
+
test "should require the path to the Rails app in the constructor" do
|
102
|
+
config = Adhearsion::Configuration::RailsConfiguration.new :path => "path here doesn't matter right now", :env => :development
|
103
|
+
config.rails_root.should.not.be.nil
|
104
|
+
end
|
105
|
+
|
106
|
+
test "should expand_path() the first constructor parameter" do
|
107
|
+
rails_root = "gui"
|
108
|
+
flexmock(File).should_receive(:expand_path).once.with(rails_root)
|
109
|
+
config = Adhearsion::Configuration::RailsConfiguration.new :path => rails_root, :env => :development
|
110
|
+
end
|
111
|
+
|
112
|
+
test "should ensure that the environment provided is one supported by Rails" do
|
113
|
+
the_following_code {
|
114
|
+
Adhearsion::Configuration::RailsConfiguration.new :path => "doesnt matter", :env => :this_is_not_a_rails_env
|
115
|
+
}.should.raise ArgumentError
|
116
|
+
|
117
|
+
the_following_code {
|
118
|
+
Adhearsion::Configuration::RailsConfiguration.new :path => "doesnt matter", :env => :development
|
119
|
+
Adhearsion::Configuration::RailsConfiguration.new :path => "doesnt matter", :env => :production
|
120
|
+
Adhearsion::Configuration::RailsConfiguration.new :path => "doesnt matter", :env => :test
|
121
|
+
}.should.not.raise ArgumentError
|
122
|
+
end
|
123
|
+
|
124
|
+
test "should convert the environment into a Symbol" do
|
125
|
+
Adhearsion::Configuration::RailsConfiguration::SUPPORTED_RAILS_ENVIRONMENTS.should.not.be.empty
|
126
|
+
Adhearsion::Configuration::RailsConfiguration::SUPPORTED_RAILS_ENVIRONMENTS.each do |env|
|
127
|
+
config = Adhearsion::Configuration::RailsConfiguration.new :path => "doesnt matter", :env => env.to_s
|
128
|
+
config.environment.should.equal env
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "Database configuration defaults" do
|
134
|
+
|
135
|
+
setup do
|
136
|
+
Adhearsion.send(:remove_const, :AHN_CONFIG) if Adhearsion.const_defined?(:AHN_CONFIG)
|
137
|
+
Adhearsion::Configuration.configure {}
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should store the constructor's argument in connection_options()" do
|
141
|
+
sample_options = { :adapter => "sqlite3", :dbfile => "foo.sqlite3" }
|
142
|
+
config = Adhearsion::Configuration::DatabaseConfiguration.new(sample_options)
|
143
|
+
config.connection_options.should.equal sample_options
|
144
|
+
end
|
145
|
+
it "should remove the :orm key from the connection options" do
|
146
|
+
sample_options = { :orm => :active_record, :adapter => "mysql", :host => "::1",
|
147
|
+
:user => "a", :pass => "b", :database => "ahn" }
|
148
|
+
config = Adhearsion::Configuration::DatabaseConfiguration.new(sample_options.clone)
|
149
|
+
config.orm.should.equal sample_options.delete(:orm)
|
150
|
+
config.connection_options.should.equal sample_options
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context "Freeswitch configuration defaults" do
|
155
|
+
attr_reader :config
|
156
|
+
|
157
|
+
setup do
|
158
|
+
@config = Adhearsion::Configuration::FreeswitchConfiguration.new
|
159
|
+
end
|
160
|
+
|
161
|
+
test "freeswitch configuration sets default listening port" do
|
162
|
+
config.listening_port.should.equal Adhearsion::Configuration::FreeswitchConfiguration.default_listening_port
|
163
|
+
end
|
164
|
+
|
165
|
+
test "freeswitch configuration sets default listening host" do
|
166
|
+
config.listening_host.should.equal Adhearsion::Configuration::FreeswitchConfiguration.default_listening_host
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context "Configuration scenarios" do
|
171
|
+
include ConfigurationTestHelper
|
172
|
+
|
173
|
+
test "enabling AMI using all its defaults" do
|
174
|
+
config = enable_ami
|
175
|
+
|
176
|
+
assert config.asterisk.ami_enabled?
|
177
|
+
config.asterisk.ami.should.be.kind_of Adhearsion::Configuration::AsteriskConfiguration::AMIConfiguration
|
178
|
+
end
|
179
|
+
|
180
|
+
test "enabling AMI with custom configuration overrides the defaults" do
|
181
|
+
overridden_port = 911
|
182
|
+
config = enable_ami :port => overridden_port
|
183
|
+
config.asterisk.ami.port.should.equal overridden_port
|
184
|
+
end
|
185
|
+
|
186
|
+
test "enabling Drb without any configuration" do
|
187
|
+
config = enable_drb
|
188
|
+
assert config.drb
|
189
|
+
config.drb.should.be.kind_of Adhearsion::Configuration::DrbConfiguration
|
190
|
+
end
|
191
|
+
|
192
|
+
test "enabling Drb with a port specified sets the port" do
|
193
|
+
target_port = 911
|
194
|
+
config = enable_drb :port => target_port
|
195
|
+
config.drb.port.should.be.equal target_port
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
def enable_ami(*overrides)
|
200
|
+
default_config do |config|
|
201
|
+
config.asterisk.enable_ami(*overrides)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def enable_drb(*overrides)
|
206
|
+
default_config do |config|
|
207
|
+
config.enable_drb *overrides
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
context "AHN_CONFIG" do
|
213
|
+
setup do
|
214
|
+
Adhearsion.send(:remove_const, :AHN_CONFIG) if Adhearsion.const_defined?(:AHN_CONFIG)
|
215
|
+
end
|
216
|
+
|
217
|
+
test "Running configure sets configuration to Adhearsion::AHN_CONFIG" do
|
218
|
+
assert !Adhearsion.const_defined?(:AHN_CONFIG)
|
219
|
+
Adhearsion::Configuration.configure do |config|
|
220
|
+
# Nothing needs to happen here
|
221
|
+
end
|
222
|
+
|
223
|
+
assert Adhearsion.const_defined?(:AHN_CONFIG)
|
224
|
+
Adhearsion::AHN_CONFIG.should.be.kind_of(Adhearsion::Configuration)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
context "DRb configuration" do
|
229
|
+
test "should not add any ACL rules when :raw_acl is passed in" do
|
230
|
+
config = Adhearsion::Configuration::DrbConfiguration.new :raw_acl => :this_is_an_acl
|
231
|
+
config.acl.should.equal :this_is_an_acl
|
232
|
+
end
|
233
|
+
|
234
|
+
test "should, by default, allow only localhost connections" do
|
235
|
+
config = Adhearsion::Configuration::DrbConfiguration.new
|
236
|
+
config.acl.should == %w[allow 127.0.0.1]
|
237
|
+
end
|
238
|
+
|
239
|
+
test "should add ACL 'deny' rules before 'allow' rules" do
|
240
|
+
config = Adhearsion::Configuration::DrbConfiguration.new :allow => %w[1.1.1.1 2.2.2.2],
|
241
|
+
:deny => %w[9.9.9.9]
|
242
|
+
config.acl.should == %w[deny 9.9.9.9 allow 1.1.1.1 allow 2.2.2.2]
|
243
|
+
end
|
244
|
+
|
245
|
+
test "should allow both an Array and a String to be passed as an allow/deny ACL rule" do
|
246
|
+
config = Adhearsion::Configuration::DrbConfiguration.new :allow => "1.1.1.1", :deny => "9.9.9.9"
|
247
|
+
config.acl.should == %w[deny 9.9.9.9 allow 1.1.1.1]
|
248
|
+
end
|
249
|
+
|
250
|
+
test "should have a default host and port" do
|
251
|
+
config = Adhearsion::Configuration::DrbConfiguration.new
|
252
|
+
config.host.should.not.equal nil
|
253
|
+
config.port.should.not.equal nil
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
BEGIN {
|
259
|
+
module ConfigurationTestHelper
|
260
|
+
def default_config(&block)
|
261
|
+
Adhearsion::Configuration.new do |config|
|
262
|
+
config.enable_asterisk
|
263
|
+
yield config if block_given?
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
}
|