eric-adhearsion 0.7.999 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. data/CHANGELOG +8 -2
  2. data/EVENTS +11 -0
  3. data/Rakefile +96 -24
  4. data/adhearsion.gemspec +148 -0
  5. data/app_generators/ahn/ahn_generator.rb +24 -9
  6. data/app_generators/ahn/templates/.ahnrc +25 -3
  7. data/app_generators/ahn/templates/Rakefile +22 -2
  8. data/app_generators/ahn/templates/components/ami_remote/ami_remote.rb +15 -0
  9. data/app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE +7 -0
  10. data/app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown +47 -0
  11. data/app_generators/ahn/templates/components/disabled/stomp_gateway/config.yml +12 -0
  12. data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb +34 -0
  13. data/app_generators/ahn/templates/components/simon_game/{lib/simon_game.rb → simon_game.rb} +14 -19
  14. data/app_generators/ahn/templates/config/startup.rb +3 -6
  15. data/app_generators/ahn/templates/dialplan.rb +2 -3
  16. data/app_generators/ahn/templates/events.rb +32 -0
  17. data/bin/jahn +10 -0
  18. data/examples/asterisk_manager_interface/standalone.rb +51 -0
  19. data/lib/adhearsion.rb +17 -11
  20. data/lib/adhearsion/cli.rb +141 -24
  21. data/lib/adhearsion/component_manager.rb +169 -238
  22. data/lib/adhearsion/component_manager/component_tester.rb +55 -0
  23. data/lib/adhearsion/component_manager/spec_framework.rb +24 -0
  24. data/lib/adhearsion/events_support.rb +84 -0
  25. data/lib/adhearsion/{core_extensions → foundation}/all.rb +0 -0
  26. data/lib/adhearsion/{blank_slate.rb → foundation/blank_slate.rb} +0 -0
  27. data/lib/adhearsion/{core_extensions → foundation}/custom_daemonizer.rb +0 -0
  28. data/lib/adhearsion/foundation/event_socket.rb +203 -0
  29. data/lib/adhearsion/foundation/future_resource.rb +36 -0
  30. data/lib/adhearsion/{core_extensions → foundation}/global.rb +0 -0
  31. data/lib/adhearsion/{core_extensions → foundation}/metaprogramming.rb +0 -0
  32. data/lib/adhearsion/foundation/numeric.rb +13 -0
  33. data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
  34. data/lib/adhearsion/{core_extensions → foundation}/relationship_properties.rb +2 -0
  35. data/lib/adhearsion/foundation/string.rb +26 -0
  36. data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
  37. data/lib/adhearsion/{core_extensions → foundation}/thread_safety.rb +0 -0
  38. data/lib/adhearsion/host_definitions.rb +5 -1
  39. data/lib/adhearsion/initializer.rb +229 -73
  40. data/lib/adhearsion/initializer/asterisk.rb +33 -11
  41. data/lib/adhearsion/initializer/configuration.rb +58 -6
  42. data/lib/adhearsion/initializer/database.rb +3 -46
  43. data/lib/adhearsion/initializer/drb.rb +9 -3
  44. data/lib/adhearsion/initializer/freeswitch.rb +3 -3
  45. data/lib/adhearsion/initializer/rails.rb +1 -1
  46. data/lib/adhearsion/tasks.rb +2 -1
  47. data/lib/adhearsion/tasks/deprecations.rb +59 -0
  48. data/lib/adhearsion/version.rb +3 -3
  49. data/lib/adhearsion/voip/asterisk.rb +2 -2
  50. data/lib/adhearsion/voip/asterisk/agi_server.rb +9 -6
  51. data/lib/adhearsion/voip/asterisk/commands.rb +106 -4
  52. data/lib/adhearsion/voip/asterisk/manager_interface.rb +562 -0
  53. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +1754 -0
  54. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +286 -0
  55. data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +78 -0
  56. data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +87 -0
  57. data/lib/adhearsion/voip/asterisk/super_manager.rb +19 -0
  58. data/lib/adhearsion/voip/call.rb +51 -2
  59. data/lib/adhearsion/voip/dial_plan.rb +74 -61
  60. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +1 -1
  61. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +2 -6
  62. data/lib/adhearsion/voip/dsl/numerical_string.rb +2 -2
  63. data/lib/adhearsion/voip/freeswitch/oes_server.rb +2 -2
  64. data/lib/theatre.rb +151 -0
  65. data/lib/theatre/README.markdown +64 -0
  66. data/lib/theatre/callback_definition_loader.rb +84 -0
  67. data/lib/theatre/guid.rb +23 -0
  68. data/lib/theatre/invocation.rb +121 -0
  69. data/lib/theatre/namespace_manager.rb +153 -0
  70. data/lib/theatre/version.rb +2 -0
  71. metadata +63 -138
  72. data/Manifest.txt +0 -149
  73. data/README.txt +0 -6
  74. data/ahn_generators/component/USAGE +0 -5
  75. data/ahn_generators/component/component_generator.rb +0 -57
  76. data/ahn_generators/component/templates/configuration.rb +0 -0
  77. data/ahn_generators/component/templates/lib/lib.rb.erb +0 -3
  78. data/ahn_generators/component/templates/test/test.rb.erb +0 -12
  79. data/ahn_generators/component/templates/test/test_helper.rb +0 -14
  80. data/app_generators/ahn/templates/components/simon_game/configuration.rb +0 -0
  81. data/app_generators/ahn/templates/components/simon_game/test/test_helper.rb +0 -14
  82. data/app_generators/ahn/templates/components/simon_game/test/test_simon_game.rb +0 -31
  83. data/lib/adhearsion/core_extensions/array.rb +0 -0
  84. data/lib/adhearsion/core_extensions/guid.rb +0 -5
  85. data/lib/adhearsion/core_extensions/hash.rb +0 -0
  86. data/lib/adhearsion/core_extensions/numeric.rb +0 -4
  87. data/lib/adhearsion/core_extensions/proc.rb +0 -0
  88. data/lib/adhearsion/core_extensions/pseudo_uuid.rb +0 -11
  89. data/lib/adhearsion/core_extensions/publishable.rb +0 -73
  90. data/lib/adhearsion/core_extensions/string.rb +0 -26
  91. data/lib/adhearsion/core_extensions/thread.rb +0 -13
  92. data/lib/adhearsion/core_extensions/time.rb +0 -0
  93. data/lib/adhearsion/distributed/gateways/dbus_gateway.rb +0 -0
  94. data/lib/adhearsion/distributed/gateways/osa_gateway.rb +0 -0
  95. data/lib/adhearsion/distributed/gateways/rest_gateway.rb +0 -9
  96. data/lib/adhearsion/distributed/gateways/soap_gateway.rb +0 -9
  97. data/lib/adhearsion/distributed/gateways/xmlrpc_gateway.rb +0 -9
  98. data/lib/adhearsion/distributed/peer_finder.rb +0 -0
  99. data/lib/adhearsion/distributed/remote_cli.rb +0 -0
  100. data/lib/adhearsion/hooks.rb +0 -57
  101. data/lib/adhearsion/initializer/paths.rb +0 -55
  102. data/lib/adhearsion/services/scheduler.rb +0 -5
  103. data/lib/adhearsion/voip/asterisk/ami.rb +0 -147
  104. data/lib/adhearsion/voip/asterisk/ami/actions.rb +0 -238
  105. data/lib/adhearsion/voip/asterisk/ami/machine.rb +0 -871
  106. data/lib/adhearsion/voip/asterisk/ami/machine.rl +0 -109
  107. data/lib/adhearsion/voip/asterisk/ami/parser.rb +0 -262
  108. data/script/destroy +0 -14
  109. data/script/generate +0 -14
  110. data/spec/fixtures/dialplan.rb +0 -3
  111. data/spec/initializer/test_configuration.rb +0 -267
  112. data/spec/initializer/test_loading.rb +0 -162
  113. data/spec/initializer/test_paths.rb +0 -43
  114. data/spec/silence.rb +0 -10
  115. data/spec/test_ahn_command.rb +0 -149
  116. data/spec/test_code_quality.rb +0 -87
  117. data/spec/test_component_manager.rb +0 -97
  118. data/spec/test_constants.rb +0 -8
  119. data/spec/test_drb.rb +0 -104
  120. data/spec/test_helper.rb +0 -94
  121. data/spec/test_hooks.rb +0 -37
  122. data/spec/test_host_definitions.rb +0 -79
  123. data/spec/test_initialization.rb +0 -105
  124. data/spec/test_logging.rb +0 -80
  125. data/spec/test_relationship_properties.rb +0 -54
  126. data/spec/voip/asterisk/ami_response_definitions.rb +0 -23
  127. data/spec/voip/asterisk/config_file_generators/test_agents.rb +0 -253
  128. data/spec/voip/asterisk/config_file_generators/test_queues.rb +0 -325
  129. data/spec/voip/asterisk/config_file_generators/test_voicemail.rb +0 -306
  130. data/spec/voip/asterisk/menu_command/test_calculated_match.rb +0 -111
  131. data/spec/voip/asterisk/menu_command/test_matchers.rb +0 -98
  132. data/spec/voip/asterisk/mock_ami_server.rb +0 -176
  133. data/spec/voip/asterisk/test_agi_server.rb +0 -451
  134. data/spec/voip/asterisk/test_ami.rb +0 -227
  135. data/spec/voip/asterisk/test_commands.rb +0 -2006
  136. data/spec/voip/asterisk/test_config_manager.rb +0 -129
  137. data/spec/voip/dsl/dispatcher_spec_helper.rb +0 -45
  138. data/spec/voip/dsl/test_dialing_dsl.rb +0 -268
  139. data/spec/voip/dsl/test_dispatcher.rb +0 -82
  140. data/spec/voip/dsl/test_parser.rb +0 -87
  141. data/spec/voip/freeswitch/test_basic_connection_manager.rb +0 -39
  142. data/spec/voip/freeswitch/test_inbound_connection_manager.rb +0 -39
  143. data/spec/voip/freeswitch/test_oes_server.rb +0 -9
  144. data/spec/voip/test_call_routing.rb +0 -127
  145. data/spec/voip/test_dialplan_manager.rb +0 -372
  146. data/spec/voip/test_numerical_string.rb +0 -48
  147. data/spec/voip/test_phone_number.rb +0 -36
  148. data/test/test_ahn_generator.rb +0 -59
  149. data/test/test_component_generator.rb +0 -52
  150. data/test/test_generator_helper.rb +0 -20
@@ -0,0 +1,19 @@
1
+ module Adhearsion
2
+ module VoIP
3
+ module Asterisk
4
+ module Manager
5
+
6
+ ##
7
+ # Higher level abstraction of the Asterisk Manager Interface.
8
+ #
9
+ class SuperManager
10
+
11
+ def initialize
12
+ raise NotImplementedError
13
+ end
14
+
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -61,6 +61,18 @@ module Adhearsion
61
61
  end
62
62
  end
63
63
 
64
+ def with_tag(tag)
65
+ atomically do
66
+ calls.inject(Array.new) do |calls_with_tag,(key,call)|
67
+ call.tagged_with?(tag) ? calls_with_tag << call : calls_with_tag
68
+ end
69
+ end
70
+ end
71
+
72
+ def to_a
73
+ calls.values
74
+ end
75
+
64
76
  private
65
77
  attr_reader :semaphore, :calls
66
78
 
@@ -80,7 +92,6 @@ module Adhearsion
80
92
  end
81
93
  end
82
94
 
83
-
84
95
  class FailedExtensionCallException < MetaAgiCallException; end
85
96
 
86
97
  class HungupExtensionCallException < MetaAgiCallException; end
@@ -142,6 +153,33 @@ module Adhearsion
142
153
  check_if_valid_call
143
154
  define_variable_accessors
144
155
  set_originating_voip_platform!
156
+ @tag_mutex = Mutex.new
157
+ @tags = []
158
+ end
159
+
160
+ def tags
161
+ @tag_mutex.synchronize do
162
+ return @tags.clone
163
+ end
164
+ end
165
+
166
+ def tag(symbol)
167
+ raise ArgumentError, "tag must be a Symbol" unless symbol.is_a? Symbol
168
+ @tag_mutex.synchronize do
169
+ @tags << symbol
170
+ end
171
+ end
172
+
173
+ def remove_tag(symbol)
174
+ @tag_mutex.synchronize do
175
+ @tags.reject! { |tag| tag == symbol }
176
+ end
177
+ end
178
+
179
+ def tagged_with?(symbol)
180
+ @tag_mutex.synchronize do
181
+ @tags.include? symbol
182
+ end
145
183
  end
146
184
 
147
185
  def deliver_message(message)
@@ -237,6 +275,7 @@ module Adhearsion
237
275
  replace_yes_no_answers_with_booleans
238
276
  coerce_request_into_uri_object
239
277
  decompose_uri_query_into_hash
278
+ override_variables_with_query_params
240
279
  remove_dashes_from_context_name
241
280
  coerce_type_of_number_into_symbol
242
281
  }
@@ -315,7 +354,7 @@ module Adhearsion
315
354
  returning variables do
316
355
  if variables[:request].query
317
356
  variables[:query] = variables[:request].query.split('&').inject({}) do |query_string_parameters, key_value_pair|
318
- parameter_name, parameter_value = *key_value_pair.match(/(.+)=(.+)/).captures
357
+ parameter_name, parameter_value = *key_value_pair.match(/(.+)=(.*)/).captures
319
358
  query_string_parameters[parameter_name] = parameter_value
320
359
  query_string_parameters
321
360
  end
@@ -325,6 +364,16 @@ module Adhearsion
325
364
  end
326
365
  end
327
366
 
367
+ def override_variables_with_query_params(variables)
368
+ returning variables do
369
+ if variables[:query]
370
+ variables[:query].each do |key, value|
371
+ variables[key.to_sym] = value
372
+ end
373
+ end
374
+ end
375
+ end
376
+
328
377
  def remove_dashes_from_context_name(variables)
329
378
  returning variables do
330
379
  variables[:context].gsub!('-', '_')
@@ -1,6 +1,4 @@
1
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
2
  require 'adhearsion/voip/dsl/dialplan/control_passing_exception'
5
3
 
6
4
  module Adhearsion
@@ -8,25 +6,40 @@ module Adhearsion
8
6
  attr_accessor :loader, :entry_points
9
7
  def initialize(loader = Loader)
10
8
  @loader = loader
11
- @entry_points = @loader.load_dial_plan.contexts
9
+ @entry_points = @loader.load_dialplans.contexts
12
10
  end
13
11
 
14
12
  ##
15
13
  # Lookup and return an entry point by context name
14
+ #
16
15
  def lookup(context_name)
17
16
  entry_points[context_name]
18
17
  end
19
18
 
20
19
  ##
21
- # Executable environment for a dial plan in the scope of a call
20
+ # Executable environment for a dial plan in the scope of a call. This class has all the dialplan methods mixed into it.
21
+ #
22
22
  class ExecutionEnvironment
23
23
 
24
+ class << self
25
+ def create(*args)
26
+ returning(new(*args)) { |instance| instance.stage! }
27
+ end
28
+ end
29
+
24
30
  attr_reader :call
25
- def initialize(call, entry_point=nil)
31
+ def initialize(call, entry_point)
26
32
  @call, @entry_point = call, entry_point
33
+ end
34
+
35
+ ##
36
+ # Adds the methods to this ExecutionEnvironment which make it useful. e.g. dialplan-related methods, call variables,
37
+ # and component methods.
38
+ #
39
+ def stage!
27
40
  extend_with_voip_commands!
28
41
  extend_with_call_variables!
29
- extend_with_components_with_call_context!
42
+ extend_with_dialplan_component_methods!
30
43
  end
31
44
 
32
45
  def run
@@ -41,27 +54,22 @@ module Adhearsion
41
54
  end
42
55
  end
43
56
 
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
57
+ protected
58
+
59
+ attr_reader :entry_point
60
+ def extend_with_voip_commands!
61
+ extend Adhearsion::VoIP::Conveniences
62
+ extend Adhearsion::VoIP::Commands.for(call.originating_voip_platform)
63
+ end
64
+
65
+ def extend_with_call_variables!
66
+ call.define_variable_accessors self
67
+ end
68
+
69
+ def extend_with_dialplan_component_methods!
70
+ Components.component_manager.extend_object_with(self, :dialplan) if Components.component_manager
71
+ end
72
+
65
73
  end
66
74
 
67
75
  class Manager
@@ -81,20 +89,19 @@ module Adhearsion
81
89
 
82
90
  def handle(call)
83
91
  if call.failed_call?
84
- environment = ExecutionEnvironment.new(call)
85
- call.extract_failed_reason_from(environment)
92
+ environment = ExecutionEnvironment.create(call, nil)
93
+ call.extract_failed_reason_from environment
86
94
  raise FailedExtensionCallException.new(environment)
87
95
  end
88
96
 
89
97
  if call.hungup_call?
90
- raise HungupExtensionCallException.new(ExecutionEnvironment.new(call))
98
+ raise HungupExtensionCallException.new(ExecutionEnvironment.new(call, nil))
91
99
  end
92
100
 
93
101
  starting_entry_point = entry_point_for call
94
102
  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)
103
+ @context = ExecutionEnvironment.create(call, starting_entry_point)
104
+ inject_context_names_into_environment @context
98
105
  @context.run
99
106
  end
100
107
 
@@ -103,7 +110,7 @@ module Adhearsion
103
110
  def entry_point_for(call)
104
111
  if entry_point = dial_plan.lookup(call.context.to_sym)
105
112
  entry_point
106
- elsif m = call.request.path.match(%r{/([^/]+)})
113
+ elsif call.respond_to?(:request) && m = call.request.path.match(%r{/([^/]+)})
107
114
  dial_plan.lookup(m[1].to_sym)
108
115
  end
109
116
  end
@@ -124,50 +131,55 @@ module Adhearsion
124
131
  attr_accessor :default_dial_plan_file_name
125
132
 
126
133
  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)
134
+ string_io = StringIO.new dial_plan_as_string
135
+ def string_io.path
136
+ "(eval)"
130
137
  end
138
+ load_dialplans string_io
131
139
  end
132
140
 
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
141
+ def load_dialplans(*files)
142
+ files = Adhearsion::AHN_CONFIG.files_from_setting("paths", "dialplan") if files.empty?
143
+ files = Array files
144
+ files.map! do |file|
145
+ case file
146
+ when File, StringIO
147
+ file
148
+ when String
149
+ File.new file
150
+ else
151
+ raise ArgumentError, "Unrecognized type of file #{file.inspect}"
152
+ end
142
153
  end
143
-
144
- def read_dialplan_file(filename)
145
- File.read filename
154
+ returning new do |loader|
155
+ files.each do |file|
156
+ loader.load file
157
+ end
146
158
  end
147
-
159
+ end
160
+
148
161
  end
149
162
 
150
163
  self.default_dial_plan_file_name ||= 'dialplan.rb'
151
164
 
152
- attr_reader :contexts
153
165
  def initialize
154
- @contexts = {}
166
+ @context_collector = ContextNameCollector.new
155
167
  end
156
168
 
157
- def load(dial_plan_as_string)
158
- contexts.update(ContextNameCollector.build(dial_plan_as_string))
169
+ def contexts
170
+ @context_collector.contexts
171
+ end
172
+
173
+ def load(dialplan_file)
174
+ dialplan_code = dialplan_file.read
175
+ @context_collector.instance_eval(dialplan_code, dialplan_file.path)
176
+ nil
159
177
  end
160
178
 
161
179
  class ContextNameCollector# < ::BlankSlate
162
180
 
163
181
  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
-
182
+
171
183
  def const_missing(name)
172
184
  super
173
185
  rescue ArgumentError
@@ -185,6 +197,7 @@ module Adhearsion
185
197
  super if !block_given? || args.any?
186
198
  contexts[name] = DialplanContextProc.new(name, &block)
187
199
  end
200
+
188
201
  end
189
202
  end
190
203
  class DialplanContextProc < Proc
@@ -15,7 +15,7 @@
15
15
  # License along with this library; if not, write to the Free Software
16
16
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
 
18
- require 'adhearsion/core_extensions/metaprogramming'
18
+ require 'adhearsion/foundation/metaprogramming'
19
19
  require 'adhearsion/voip/conveniences'
20
20
  require 'adhearsion/voip/constants'
21
21
  require 'adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches'
@@ -14,14 +14,10 @@ module Adhearsion
14
14
 
15
15
  #TODO: separate into smaller pieces
16
16
  def self.get_contexts
17
- unless Adhearsion::Paths.manager_for?("dialplans")
18
- raise "No dialplan files found in .ahnrc!"
19
- end
20
-
21
17
  envelope = ContextsEnvelope.new
22
18
 
23
- dialplans = all_dialplans
24
- warn "No dialplan files were found!" if dialplans.empty?
19
+ dialplans = AHN_CONFIG.files_from_setting "paths", "dialplan"
20
+ ahn_log.dialplan.warn "No dialplan files were found!" if dialplans.empty?
25
21
 
26
22
  returning({}) do |contexts|
27
23
  dialplans.each do |file|
@@ -52,10 +52,10 @@ module Adhearsion
52
52
 
53
53
  # Checks against a pattern identifying US local numbers (i.e numbers
54
54
  # without an area code seven digits long)
55
- def local_number?() to_s =~ Adhearsion::VoIP::Constants::US_LOCAL_NUMBER end
55
+ def us_local_number?() to_s =~ Adhearsion::VoIP::Constants::US_LOCAL_NUMBER end
56
56
 
57
57
  # Checks against a pattern identifying US domestic numbers.
58
- def national_number?() to_s =~ Adhearsion::VoIP::Constants::US_NATIONAL_NUMBER end
58
+ def us_national_number?() to_s =~ Adhearsion::VoIP::Constants::US_NATIONAL_NUMBER end
59
59
 
60
60
  # Checks against a pattern identifying an ISN number. See http://freenum.org
61
61
  # for more info.
@@ -52,7 +52,7 @@ module Adhearsion
52
52
  # If the target context does not exist, warn and don't handle the call
53
53
  unless first_context
54
54
  log "No context '#{first_context_name}' found in " +
55
- "#{all_dialplans.to_sentence(:connector => "or")}. Ignoring request!"
55
+ "#{AHN_CONFIG.files_from_setting("paths", "").to_sentence(:connector => "or")}. Ignoring request!"
56
56
  return
57
57
  end
58
58
 
@@ -91,7 +91,7 @@ module Adhearsion
91
91
  # time.
92
92
  def should_reload_contexts?
93
93
  !@abstract_contexts || !@abstract_dispatcher ||
94
- all_dialplans.map { |x| File.mtime(x) }.max < Time.now
94
+ AHN_CONFIG.files_from_setting("paths", "dialplan").map { |x| File.mtime(x) }.max < Time.now
95
95
  end
96
96
 
97
97
  def rubyize_keys_for(hash)
@@ -0,0 +1,151 @@
1
+ require 'thread'
2
+ require 'rubygems'
3
+
4
+ $: << File.expand_path(File.dirname(__FILE__))
5
+
6
+ require 'theatre/version'
7
+ require 'theatre/namespace_manager'
8
+ require 'theatre/invocation'
9
+ require 'theatre/callback_definition_loader'
10
+
11
+ module Theatre
12
+
13
+ class Theatre
14
+
15
+ attr_reader :namespace_manager
16
+
17
+ ##
18
+ # Creates a new stopped Theatre. You must call start!() after you instantiate this for it to begin processing events.
19
+ #
20
+ # @param [Fixnum] thread_count Number of Threads to spawn when started.
21
+ #
22
+ def initialize(thread_count=6)
23
+ @thread_count = thread_count
24
+ @started = false
25
+ @namespace_manager = ActorNamespaceManager.new
26
+ @thread_group = ThreadGroup.new
27
+ @master_queue = Queue.new
28
+ @loader_mixins = []
29
+ end
30
+
31
+ ##
32
+ # Send a message to this Theatre for asynchronous processing.
33
+ #
34
+ # @param [String] namespace The namespace to which the payload should be sent
35
+ # @param [Object] payload The actual content to be sent to the callback. Optional.
36
+ # @return [Array<Theatre::Invocation>] An Array of Invocation objects
37
+ # @raise Theatre::NamespaceNotFound Raised when told to enqueue an unrecognized namespace
38
+ #
39
+ def trigger(namespace, payload=:argument_undefined)
40
+ @namespace_manager.callbacks_for_namespaces(namespace).map do |callback|
41
+ invocation = if payload.equal?(:argument_undefined)
42
+ Invocation.new(namespace, callback)
43
+ else
44
+ Invocation.new(namespace, callback, payload)
45
+ end
46
+ invocation.queued
47
+ @master_queue << invocation
48
+ invocation
49
+ end
50
+ end
51
+
52
+ ##
53
+ # Send a message to this Theatre for synchronous processing. The execution of this will not go through this Theatre's
54
+ # Thread pool. If an error occurred in any of callbacks, the Exception object will be placed in the returned Array
55
+ # instead for you to act upon.
56
+ #
57
+ # @param [String] namespace The namespace to which the payload should be sent
58
+ # @param [Object] payload The actual content to be sent to the callback. Optional.
59
+ # @return [Array] An Array containing each callback's return value (or Exception raised, if any) when given the payload
60
+ # @raise Theatre::NamespaceNotFound Raised when told to enqueue an unrecognized namespace
61
+ #
62
+ def trigger_immediately(namespace, payload=:argument_undefined)
63
+ @namespace_manager.callbacks_for_namespaces(namespace).map do |callback|
64
+ begin
65
+ invocation = if payload.equal?(:argument_undefined)
66
+ callback.call
67
+ else
68
+ callback.call payload
69
+ end
70
+ rescue => captured_error_to_be_returned
71
+ captured_error_to_be_returned
72
+ end
73
+ end
74
+ end
75
+
76
+ def load_events_code(code, *args)
77
+ loader = CallbackDefinitionLoader.new(self, *args)
78
+ loader.load_events_code code
79
+ end
80
+
81
+ def load_events_file(file, *args)
82
+ loader = CallbackDefinitionLoader.new(self, *args)
83
+ loader.load_events_file file
84
+ end
85
+
86
+ def register_namespace_name(*args)
87
+ @namespace_manager.register_namespace_name(*args)
88
+ end
89
+
90
+ def register_callback_at_namespace(*args)
91
+ @namespace_manager.register_callback_at_namespace(*args)
92
+ end
93
+
94
+ def register_loader_mixin(mod)
95
+ @loader_mixins << mod
96
+ end
97
+
98
+ def join
99
+ @thread_group.list.each do |thread|
100
+ begin
101
+ thread.join
102
+ rescue
103
+ # Ignore any exceptions
104
+ end
105
+ end
106
+ end
107
+
108
+ ##
109
+ # Starts this Theatre.
110
+ #
111
+ # When this method is called, the Threads are spawned and begin pulling messages off this Theatre's master queue.
112
+ #
113
+ def start!
114
+ return false if @thread_group.list.any? # Already started
115
+ @started_time = Time.now
116
+ @thread_count.times do
117
+ @thread_group.add Thread.new(&method(:thread_loop))
118
+ end
119
+ end
120
+
121
+ ##
122
+ # Notifies all Threads for this Theatre to stop by sending them special messages. Any messages which were queued and
123
+ # untriggered when this method is received will still be processed. Note: you may start this Theatre again later once it
124
+ # has been stopped.
125
+ #
126
+ def graceful_stop!
127
+ @thread_count.times { @master_queue << :THEATRE_SHUTDOWN! }
128
+ @started_time = nil
129
+ end
130
+
131
+ protected
132
+
133
+ # This will use the Adhearsion logger eventually.
134
+ def warn(exception)
135
+ # STDERR.puts exception.message, *exception.backtrace
136
+ end
137
+
138
+ def thread_loop
139
+ loop do
140
+ begin
141
+ next_invocation = @master_queue.pop
142
+ return :stopped if next_invocation.equal? :THEATRE_SHUTDOWN!
143
+ next_invocation.start
144
+ rescue => error
145
+ warn error
146
+ end
147
+ end
148
+ end
149
+
150
+ end
151
+ end