sevenscale-adhearsion 0.7.1000

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.
Files changed (150) hide show
  1. data/CHANGELOG +3 -0
  2. data/LICENSE +456 -0
  3. data/Manifest.txt +149 -0
  4. data/README.txt +6 -0
  5. data/Rakefile +48 -0
  6. data/ahn_generators/component/USAGE +5 -0
  7. data/ahn_generators/component/component_generator.rb +57 -0
  8. data/ahn_generators/component/templates/configuration.rb +0 -0
  9. data/ahn_generators/component/templates/lib/lib.rb.erb +3 -0
  10. data/ahn_generators/component/templates/test/test.rb.erb +12 -0
  11. data/ahn_generators/component/templates/test/test_helper.rb +14 -0
  12. data/app_generators/ahn/USAGE +5 -0
  13. data/app_generators/ahn/ahn_generator.rb +76 -0
  14. data/app_generators/ahn/templates/.ahnrc +12 -0
  15. data/app_generators/ahn/templates/README +8 -0
  16. data/app_generators/ahn/templates/Rakefile +3 -0
  17. data/app_generators/ahn/templates/components/simon_game/configuration.rb +0 -0
  18. data/app_generators/ahn/templates/components/simon_game/lib/simon_game.rb +61 -0
  19. data/app_generators/ahn/templates/components/simon_game/test/test_helper.rb +14 -0
  20. data/app_generators/ahn/templates/components/simon_game/test/test_simon_game.rb +31 -0
  21. data/app_generators/ahn/templates/config/startup.rb +53 -0
  22. data/app_generators/ahn/templates/dialplan.rb +4 -0
  23. data/bin/ahn +28 -0
  24. data/bin/ahnctl +68 -0
  25. data/bin/jahn +32 -0
  26. data/lib/adhearsion/blank_slate.rb +5 -0
  27. data/lib/adhearsion/cli.rb +106 -0
  28. data/lib/adhearsion/component_manager.rb +277 -0
  29. data/lib/adhearsion/core_extensions/all.rb +9 -0
  30. data/lib/adhearsion/core_extensions/array.rb +0 -0
  31. data/lib/adhearsion/core_extensions/custom_daemonizer.rb +45 -0
  32. data/lib/adhearsion/core_extensions/global.rb +1 -0
  33. data/lib/adhearsion/core_extensions/guid.rb +5 -0
  34. data/lib/adhearsion/core_extensions/hash.rb +0 -0
  35. data/lib/adhearsion/core_extensions/metaprogramming.rb +17 -0
  36. data/lib/adhearsion/core_extensions/numeric.rb +4 -0
  37. data/lib/adhearsion/core_extensions/proc.rb +0 -0
  38. data/lib/adhearsion/core_extensions/pseudo_uuid.rb +11 -0
  39. data/lib/adhearsion/core_extensions/publishable.rb +73 -0
  40. data/lib/adhearsion/core_extensions/relationship_properties.rb +40 -0
  41. data/lib/adhearsion/core_extensions/string.rb +26 -0
  42. data/lib/adhearsion/core_extensions/thread.rb +13 -0
  43. data/lib/adhearsion/core_extensions/thread_safety.rb +7 -0
  44. data/lib/adhearsion/core_extensions/time.rb +0 -0
  45. data/lib/adhearsion/distributed/gateways/dbus_gateway.rb +0 -0
  46. data/lib/adhearsion/distributed/gateways/osa_gateway.rb +0 -0
  47. data/lib/adhearsion/distributed/gateways/rest_gateway.rb +9 -0
  48. data/lib/adhearsion/distributed/gateways/soap_gateway.rb +9 -0
  49. data/lib/adhearsion/distributed/gateways/xmlrpc_gateway.rb +9 -0
  50. data/lib/adhearsion/distributed/peer_finder.rb +0 -0
  51. data/lib/adhearsion/distributed/remote_cli.rb +0 -0
  52. data/lib/adhearsion/hooks.rb +57 -0
  53. data/lib/adhearsion/host_definitions.rb +63 -0
  54. data/lib/adhearsion/initializer/asterisk.rb +59 -0
  55. data/lib/adhearsion/initializer/configuration.rb +202 -0
  56. data/lib/adhearsion/initializer/database.rb +92 -0
  57. data/lib/adhearsion/initializer/drb.rb +25 -0
  58. data/lib/adhearsion/initializer/freeswitch.rb +22 -0
  59. data/lib/adhearsion/initializer/paths.rb +55 -0
  60. data/lib/adhearsion/initializer/rails.rb +40 -0
  61. data/lib/adhearsion/initializer.rb +217 -0
  62. data/lib/adhearsion/logging.rb +92 -0
  63. data/lib/adhearsion/services/scheduler.rb +5 -0
  64. data/lib/adhearsion/tasks/database.rb +5 -0
  65. data/lib/adhearsion/tasks/generating.rb +20 -0
  66. data/lib/adhearsion/tasks/lint.rb +4 -0
  67. data/lib/adhearsion/tasks/testing.rb +37 -0
  68. data/lib/adhearsion/tasks.rb +15 -0
  69. data/lib/adhearsion/version.rb +9 -0
  70. data/lib/adhearsion/voip/asterisk/agi_server.rb +78 -0
  71. data/lib/adhearsion/voip/asterisk/ami/actions.rb +238 -0
  72. data/lib/adhearsion/voip/asterisk/ami/machine.rb +871 -0
  73. data/lib/adhearsion/voip/asterisk/ami/machine.rl +109 -0
  74. data/lib/adhearsion/voip/asterisk/ami/parser.rb +262 -0
  75. data/lib/adhearsion/voip/asterisk/ami.rb +147 -0
  76. data/lib/adhearsion/voip/asterisk/commands.rb +1186 -0
  77. data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +140 -0
  78. data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +101 -0
  79. data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +250 -0
  80. data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +240 -0
  81. data/lib/adhearsion/voip/asterisk/config_manager.rb +71 -0
  82. data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +80 -0
  83. data/lib/adhearsion/voip/asterisk.rb +4 -0
  84. data/lib/adhearsion/voip/call.rb +402 -0
  85. data/lib/adhearsion/voip/call_routing.rb +64 -0
  86. data/lib/adhearsion/voip/commands.rb +9 -0
  87. data/lib/adhearsion/voip/constants.rb +39 -0
  88. data/lib/adhearsion/voip/conveniences.rb +18 -0
  89. data/lib/adhearsion/voip/dial_plan.rb +205 -0
  90. data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +37 -0
  91. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +151 -0
  92. data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +27 -0
  93. data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +124 -0
  94. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +75 -0
  95. data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +16 -0
  96. data/lib/adhearsion/voip/dsl/numerical_string.rb +117 -0
  97. data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +48 -0
  98. data/lib/adhearsion/voip/freeswitch/event_handler.rb +58 -0
  99. data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +129 -0
  100. data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +38 -0
  101. data/lib/adhearsion/voip/freeswitch/oes_server.rb +195 -0
  102. data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +80 -0
  103. data/lib/adhearsion/voip/menu_state_machine/matchers.rb +123 -0
  104. data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +58 -0
  105. data/lib/adhearsion/voip/menu_state_machine/menu_class.rb +149 -0
  106. data/lib/adhearsion.rb +31 -0
  107. data/script/destroy +14 -0
  108. data/script/generate +14 -0
  109. data/spec/fixtures/dialplan.rb +3 -0
  110. data/spec/initializer/test_configuration.rb +267 -0
  111. data/spec/initializer/test_loading.rb +162 -0
  112. data/spec/initializer/test_paths.rb +43 -0
  113. data/spec/silence.rb +10 -0
  114. data/spec/test_ahn_command.rb +149 -0
  115. data/spec/test_code_quality.rb +87 -0
  116. data/spec/test_component_manager.rb +97 -0
  117. data/spec/test_constants.rb +8 -0
  118. data/spec/test_drb.rb +104 -0
  119. data/spec/test_helper.rb +94 -0
  120. data/spec/test_hooks.rb +37 -0
  121. data/spec/test_host_definitions.rb +79 -0
  122. data/spec/test_initialization.rb +105 -0
  123. data/spec/test_logging.rb +80 -0
  124. data/spec/test_relationship_properties.rb +54 -0
  125. data/spec/voip/asterisk/ami_response_definitions.rb +23 -0
  126. data/spec/voip/asterisk/config_file_generators/test_agents.rb +253 -0
  127. data/spec/voip/asterisk/config_file_generators/test_queues.rb +325 -0
  128. data/spec/voip/asterisk/config_file_generators/test_voicemail.rb +306 -0
  129. data/spec/voip/asterisk/menu_command/test_calculated_match.rb +111 -0
  130. data/spec/voip/asterisk/menu_command/test_matchers.rb +98 -0
  131. data/spec/voip/asterisk/mock_ami_server.rb +176 -0
  132. data/spec/voip/asterisk/test_agi_server.rb +453 -0
  133. data/spec/voip/asterisk/test_ami.rb +227 -0
  134. data/spec/voip/asterisk/test_commands.rb +2006 -0
  135. data/spec/voip/asterisk/test_config_manager.rb +129 -0
  136. data/spec/voip/dsl/dispatcher_spec_helper.rb +45 -0
  137. data/spec/voip/dsl/test_dialing_dsl.rb +268 -0
  138. data/spec/voip/dsl/test_dispatcher.rb +82 -0
  139. data/spec/voip/dsl/test_parser.rb +87 -0
  140. data/spec/voip/freeswitch/test_basic_connection_manager.rb +39 -0
  141. data/spec/voip/freeswitch/test_inbound_connection_manager.rb +39 -0
  142. data/spec/voip/freeswitch/test_oes_server.rb +9 -0
  143. data/spec/voip/test_call_routing.rb +127 -0
  144. data/spec/voip/test_dialplan_manager.rb +372 -0
  145. data/spec/voip/test_numerical_string.rb +48 -0
  146. data/spec/voip/test_phone_number.rb +36 -0
  147. data/test/test_ahn_generator.rb +59 -0
  148. data/test/test_component_generator.rb +52 -0
  149. data/test/test_generator_helper.rb +20 -0
  150. metadata +254 -0
@@ -0,0 +1,205 @@
1
+ # Hardcoding require for now since for some reason it's not being loaded
2
+ require 'adhearsion/blank_slate'
3
+ require 'adhearsion/component_manager'
4
+ require 'adhearsion/voip/dsl/dialplan/control_passing_exception'
5
+
6
+ module Adhearsion
7
+ class DialPlan
8
+ attr_accessor :loader, :entry_points
9
+ def initialize(loader = Loader)
10
+ @loader = loader
11
+ @entry_points = @loader.load_dial_plan.contexts
12
+ end
13
+
14
+ ##
15
+ # Lookup and return an entry point by context name
16
+ def lookup(context_name)
17
+ entry_points[context_name]
18
+ end
19
+
20
+ ##
21
+ # Executable environment for a dial plan in the scope of a call
22
+ class ExecutionEnvironment
23
+
24
+ attr_reader :call
25
+ def initialize(call, entry_point=nil)
26
+ @call, @entry_point = call, entry_point
27
+ extend_with_voip_commands!
28
+ extend_with_call_variables!
29
+ extend_with_components_with_call_context!
30
+ end
31
+
32
+ def run
33
+ raise "Cannot run ExecutionEnvironment without an entry point!" unless entry_point
34
+ current_context = entry_point
35
+ answer if AHN_CONFIG.automatically_answer_incoming_calls
36
+ begin
37
+ instance_eval(&current_context)
38
+ rescue Adhearsion::VoIP::DSL::Dialplan::ControlPassingException => exception
39
+ current_context = exception.target
40
+ retry
41
+ end
42
+ end
43
+
44
+ private
45
+ attr_reader :entry_point
46
+
47
+ def extend_with_voip_commands!
48
+ extend(Adhearsion::VoIP::Conveniences)
49
+ extend(Adhearsion::VoIP::Commands.for(call.originating_voip_platform))
50
+ end
51
+
52
+ def extend_with_call_variables!
53
+ call.define_variable_accessors self
54
+ end
55
+
56
+ def extend_with_components_with_call_context!
57
+ ComponentManager.components_with_call_context.keys.each do |component_name|
58
+ eval <<-COMPONENT_BUILDER
59
+ def self.new_#{component_name.underscore}(*args, &block)
60
+ ComponentManager.components_with_call_context['#{component_name}'].instantiate_with_call_context(self, *args, &block)
61
+ end
62
+ COMPONENT_BUILDER
63
+ end
64
+ end
65
+ end
66
+
67
+ class Manager
68
+
69
+ class NoContextError < Exception; end
70
+
71
+ class << self
72
+ def handle(call)
73
+ new.handle(call)
74
+ end
75
+ end
76
+
77
+ attr_accessor :dial_plan, :context
78
+ def initialize
79
+ @dial_plan = DialPlan.new
80
+ end
81
+
82
+ def handle(call)
83
+ if call.failed_call?
84
+ environment = ExecutionEnvironment.new(call)
85
+ call.extract_failed_reason_from(environment)
86
+ raise FailedExtensionCallException.new(environment)
87
+ end
88
+
89
+ if call.hungup_call?
90
+ raise HungupExtensionCallException.new(ExecutionEnvironment.new(call))
91
+ end
92
+
93
+ starting_entry_point = entry_point_for call
94
+ raise NoContextError, "No dialplan entry point for call context '#{call.context}' -- Ignoring call!" unless starting_entry_point
95
+
96
+ @context = ExecutionEnvironment.new(call, starting_entry_point)
97
+ inject_context_names_into_environment(@context)
98
+ @context.run
99
+ end
100
+
101
+ # Find the dialplan by the context name from the call or from the
102
+ # first path entry in the AGI URL
103
+ def entry_point_for(call)
104
+ if entry_point = dial_plan.lookup(call.context.to_sym)
105
+ entry_point
106
+ elsif m = call.request.path.match(%r{/([^/]+)})
107
+ dial_plan.lookup(m[1].to_sym)
108
+ end
109
+ end
110
+
111
+ protected
112
+
113
+ def inject_context_names_into_environment(environment)
114
+ return unless dial_plan.entry_points
115
+ dial_plan.entry_points.each do |name, context|
116
+ environment.meta_def(name) { context }
117
+ end
118
+ end
119
+
120
+ end
121
+
122
+ class Loader
123
+ class << self
124
+ attr_accessor :default_dial_plan_file_name
125
+
126
+ def load(dial_plan_as_string)
127
+ returning new do |loader|
128
+ inject_dial_plan_component_classes_into dial_plan_as_string
129
+ loader.load(dial_plan_as_string)
130
+ end
131
+ end
132
+
133
+ def load_dial_plan(file_name = default_dial_plan_file_name)
134
+ load read_dialplan_file(AHN_ROOT.dial_plan_named(file_name))
135
+ end
136
+
137
+ private
138
+ def inject_dial_plan_component_classes_into(dial_plan_as_string)
139
+ dial_plan_as_string[0, 0] = ComponentManager.components_with_call_context.keys.map do |component|
140
+ "#{component} = ::Adhearsion::ComponentManager.components_with_call_context['#{component}'] unless defined? #{component}\n"
141
+ end.join
142
+ end
143
+
144
+ def read_dialplan_file(filename)
145
+ File.read filename
146
+ end
147
+
148
+ end
149
+
150
+ self.default_dial_plan_file_name ||= 'dialplan.rb'
151
+
152
+ attr_reader :contexts
153
+ def initialize
154
+ @contexts = {}
155
+ end
156
+
157
+ def load(dial_plan_as_string)
158
+ contexts.update(ContextNameCollector.build(dial_plan_as_string))
159
+ end
160
+
161
+ class ContextNameCollector# < ::BlankSlate
162
+
163
+ class << self
164
+
165
+ def build(dial_plan_as_string)
166
+ builder = new
167
+ builder.instance_eval(dial_plan_as_string)
168
+ builder.contexts
169
+ end
170
+
171
+ def const_missing(name)
172
+ super
173
+ rescue ArgumentError
174
+ raise NameError, %(undefined constant "#{name}")
175
+ end
176
+
177
+ end
178
+
179
+ attr_reader :contexts
180
+ def initialize
181
+ @contexts = {}
182
+ end
183
+
184
+ def method_missing(name, *args, &block)
185
+ super if !block_given? || args.any?
186
+ contexts[name] = DialplanContextProc.new(name, &block)
187
+ end
188
+ end
189
+ end
190
+ class DialplanContextProc < Proc
191
+
192
+ attr_reader :name
193
+
194
+ def initialize(name, &block)
195
+ super(&block)
196
+ @name = name
197
+ end
198
+
199
+ def +@
200
+ raise Adhearsion::VoIP::DSL::Dialplan::ControlPassingException.new(self)
201
+ end
202
+
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,37 @@
1
+ module Adhearsion
2
+ module VoIP
3
+ module DSL
4
+ class DialingDSL
5
+ module MonkeyPatches
6
+ module RegexpMonkeyPatch
7
+
8
+ def |(other)
9
+ case other
10
+ when RouteRule
11
+ other.unshift_pattern self
12
+ other
13
+ when Regexp
14
+ RouteRule.new :patterns => [self, other]
15
+ else
16
+ raise ArgumentError, "Unsupported pattern type #{other.inspect}"
17
+ end
18
+ end
19
+
20
+ def >>(other)
21
+ case other
22
+ when ProviderDefinition
23
+ RouteRule.new :patterns => self, :providers => other
24
+ when RouteRule
25
+ returning other do |route|
26
+ route.unshift_pattern self
27
+ end
28
+ else raise ArgumentError, "Unsupported route definition #{other.inspect}"
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,151 @@
1
+ # Adhearsion, open source collaboration framework
2
+ # Copyright (C) 2006,2007,2008 Jay Phillips
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License as published by the Free Software Foundation; either
7
+ # version 2.1 of the License, or (at your option) any later version.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ require 'adhearsion/core_extensions/metaprogramming'
19
+ require 'adhearsion/voip/conveniences'
20
+ require 'adhearsion/voip/constants'
21
+ require 'adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches'
22
+
23
+ module Adhearsion
24
+ module VoIP
25
+ module DSL
26
+ class DialingDSL
27
+
28
+ extend Conveniences
29
+ include Constants
30
+
31
+ Regexp.class_eval do
32
+ include Adhearsion::VoIP::DSL::DialingDSL::MonkeyPatches::RegexpMonkeyPatch
33
+ end
34
+
35
+ def self.inherited(klass)
36
+ klass.class_eval do
37
+ [:@@providers, :@@routes].each do |var|
38
+ class_variable_set(var, [])
39
+ cattr_reader var.to_s.gsub('@@', '')
40
+ end
41
+ end
42
+ end
43
+
44
+ def self.calculate_routes_for(destination)
45
+ destination = destination.to_s
46
+ routes.select { |defined_route| defined_route === destination }.map &:providers
47
+ end
48
+
49
+ class ProviderDefinition < OpenStruct
50
+
51
+ def initialize(name)
52
+ super()
53
+ self.name = name.to_s.to_sym
54
+ end
55
+
56
+ def >>(other)
57
+ RouteRule.new :providers => [self, other]
58
+ end
59
+
60
+ def format_number_for_platform(number, platform=:asterisk)
61
+ case platform
62
+ when :asterisk
63
+ [protocol || "SIP", name || "default", number].join '/'
64
+ else
65
+ raise "Unsupported platform #{platform}!"
66
+ end
67
+ end
68
+
69
+ def format_number_for_platform(number, platform=:asterisk)
70
+ case platform
71
+ when :asterisk
72
+ [protocol || "SIP", name || "default", number].join '/'
73
+ else
74
+ raise "Unsupported platform #{platform}!"
75
+ end
76
+ end
77
+
78
+ def defined_properties_without_name
79
+ returning @table.clone do |copy|
80
+ copy.delete :name
81
+ end
82
+ end
83
+
84
+ end
85
+
86
+ protected
87
+
88
+ def self.provider(name, &block)
89
+ raise ArgumentError, "no block given" unless block_given?
90
+
91
+ options = ProviderDefinition.new name
92
+ yield options
93
+
94
+ providers << options
95
+ meta_def(name) { options }
96
+ end
97
+
98
+ def self.route(route)
99
+ routes << route
100
+ end
101
+
102
+ class RouteRule
103
+
104
+ attr_reader :patterns, :providers
105
+
106
+ def initialize(hash={})
107
+ @patterns = Array hash[:patterns]
108
+ @providers = Array hash[:providers]
109
+ end
110
+
111
+ def merge!(other)
112
+ providers.concat other.providers
113
+ patterns.concat other.patterns
114
+ self
115
+ end
116
+
117
+ def >>(other)
118
+ case other
119
+ when RouteRule: merge! other
120
+ when ProviderDefinition: providers << other
121
+ else raise RouteException, "Unrecognized object in route definition: #{other.inspect}"
122
+ end
123
+ self
124
+ end
125
+
126
+ def |(other)
127
+ case other
128
+ when RouteRule: merge! other
129
+ when Regexp
130
+ patterns << other
131
+ self
132
+ else raise other.inspect
133
+ end
134
+ end
135
+
136
+ def ===(other)
137
+ patterns.each { |pattern| return true if pattern === other }
138
+ false
139
+ end
140
+
141
+ def unshift_pattern(pattern)
142
+ patterns.unshift pattern
143
+ end
144
+
145
+ class RouteException < Exception; end
146
+
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,27 @@
1
+ module Adhearsion
2
+ module VoIP
3
+ module DSL
4
+ module Dialplan
5
+ # A ControlPassingException is used internally to stop execution of one
6
+ # dialplan context and begin execution of another proc instead. It is
7
+ # most notably used by the ~@ unary operator that can be called on a
8
+ # context name within a dialplan to transfer control entirely to that
9
+ # particular context. The serve() method in the servlet_container actually
10
+ # rescues these exceptions specifically and then does +e.target to execute
11
+ # that code.
12
+ class ControlPassingException < Exception
13
+
14
+ attr_reader :target
15
+
16
+ def initialize(target)
17
+ super()
18
+ @target = target
19
+ end
20
+
21
+ end
22
+
23
+ class ContextNotFoundException < Exception; end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,124 @@
1
+ module Adhearsion
2
+ module VoIP
3
+ module DSL
4
+ module Dialplan
5
+
6
+ class ReturnValue < Exception
7
+ attr_reader :obj
8
+ def initialize(obj)
9
+ @obj = obj
10
+ super
11
+ end
12
+ end
13
+
14
+ class Hangup < Exception; end
15
+
16
+ # Instantiated and returned in every dialplan command
17
+ class EventCommand
18
+
19
+ attr_accessor :app, :args, :response_block, :returns, :on_keypress
20
+
21
+ def initialize(app, *args, &block)
22
+ @hash = args.pop if args.last.kind_of?(Hash)
23
+ @app, @args = app, args
24
+
25
+ if @hash
26
+ @returns = @hash[:returns] || :raw
27
+ @on_keypress = @hash[:on_keypress]
28
+ end
29
+
30
+ @response_block = block if block_given?
31
+ end
32
+
33
+ def on_keypress(&block)
34
+ block_given? ? @on_keypress = block : @on_keypress
35
+ end
36
+
37
+ def on_break(&block)
38
+ block_given? ? @on_break = block : @on_break
39
+ end
40
+
41
+ end
42
+
43
+ class NoOpEventCommand < EventCommand
44
+ attr_reader :timeout, :on_keypress
45
+ def initialize(timeout=nil, hash={})
46
+ @timeout = timeout
47
+ @on_keypress = hash[:on_keypress]
48
+ end
49
+ end
50
+
51
+ class ExitingEventCommand < EventCommand; end
52
+
53
+ # Serves as a EventCommand proxy for handling EventCommands. Useful
54
+ # if doing any pre-processing.
55
+ #
56
+ # For example, when sending commands over the event socket to FreeSwitch,
57
+ # every command must start with "api", but, when doing an originate, it
58
+ # must begin with an ampersand. These two pre-processors would be developed
59
+ # as separate CommandDispatchers.
60
+ class CommandDispatcher
61
+
62
+ attr_reader :factory, :context
63
+
64
+ def initialize(factory, context=nil)
65
+ @context = context
66
+ @factory = factory.new context
67
+ end
68
+
69
+ def dispatch!(event_command)
70
+ raise NotImplementedError, "Must subclass #{self.class} and override this!"
71
+ end
72
+
73
+ def method_missing(name, *args, &block)
74
+ *commands = *@factory.send(name, *args, &block)
75
+ commands.map do |command|
76
+ if command.kind_of? Proc
77
+ instance_eval(&command)
78
+ elsif command.kind_of? EventCommand
79
+ dispatched_command = dispatch! command
80
+ if command.response_block
81
+ while (new_cmd = command.response_block.call(dispatched_command)).kind_of? EventCommand
82
+ dispatched_command = dispatch! new_cmd
83
+ end
84
+ end
85
+ dispatched_command
86
+ else
87
+ command
88
+ end
89
+ end.last
90
+ rescue ReturnValue => r
91
+ return r.obj
92
+ end
93
+
94
+ def return!(obj)
95
+ raise DSL::Dialplan::ReturnValue.new(obj)
96
+ end
97
+
98
+ def break!(uuid=@context)
99
+ raise NotImplementedError, "Must subclass #{self.class} and override this!"
100
+ end
101
+
102
+ # Takes a Hash and meta_def()'s a method for each key that returns
103
+ # the key's value in the Hash.
104
+ def def_keys!(hash)
105
+ hash.each_pair do |k,v|
106
+ meta_def(k) { v } rescue nil
107
+ end
108
+ end
109
+
110
+ def clone
111
+ returning super do |nemesis|
112
+ instance_variables.each do |iv|
113
+ value = instance_variable_get(iv)
114
+ nemesis.instance_variable_set iv, value.clone if value
115
+ end
116
+ end
117
+ end
118
+
119
+ end
120
+
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,75 @@
1
+ require 'ostruct'
2
+
3
+ module Adhearsion
4
+ module VoIP
5
+ module DSL
6
+ module Dialplan
7
+ #TODO: This is obsolete, but we still need it for Freeswitch until we port that to the new 0.8.0 APIs
8
+ module DialplanParser
9
+
10
+ # Create a container and then clone that. when the container is created
11
+ # it should have all the contexts meta_def'd into it. for each call coming
12
+ # in, the cloned copy has the variables meta_def'd and set as instance
13
+ # variables
14
+
15
+ #TODO: separate into smaller pieces
16
+ def self.get_contexts
17
+ unless Adhearsion::Paths.manager_for?("dialplans")
18
+ raise "No dialplan files found in .ahnrc!"
19
+ end
20
+
21
+ envelope = ContextsEnvelope.new
22
+
23
+ dialplans = all_dialplans
24
+ warn "No dialplan files were found!" if dialplans.empty?
25
+
26
+ returning({}) do |contexts|
27
+ dialplans.each do |file|
28
+ raise "Dialplan file #{file} does not exist!" unless File.exists? file
29
+ envelope.instance_eval do
30
+ eval File.read(file)
31
+ end
32
+ current_contexts = envelope.parsed_contexts
33
+ current_contexts.each_pair do |name, block|
34
+ if contexts.has_key? name
35
+ warn %'Dialplan context "#{name}" exists in both #{contexts[name].file} and #{file}.' +
36
+ %' Using the "#{name}" context from #{contexts[name].file}.'
37
+ else
38
+ contexts[name] = returning OpenStruct.new do |metadata|
39
+ metadata.file = file
40
+ metadata.name = name
41
+ metadata.block = block
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ class ContextsEnvelope
51
+
52
+ keep = %w"__send__ __id__ define_method instance_eval meta_def meta_eval metaclass"
53
+ (instance_methods - keep).each do |m|
54
+ undef_method m
55
+ end
56
+
57
+ def initialize
58
+ @parsed_contexts = {}
59
+ end
60
+
61
+ attr_reader :parsed_contexts
62
+
63
+ def method_missing(name, *args, &block)
64
+ super unless block_given?
65
+ @parsed_contexts[name] = block
66
+ meta_def(name) { block }
67
+ end
68
+
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,16 @@
1
+ require 'ostruct'
2
+ module Adhearsion
3
+ module VoIP
4
+ module DSL
5
+ module Dialplan
6
+ module ThreadMixin
7
+
8
+ def call
9
+ @thread_call_struct ||= OpenStruct.new
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end