sevenscale-adhearsion 0.7.1003 → 0.8.0

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 (149) hide show
  1. data/CHANGELOG +8 -2
  2. data/EVENTS +11 -0
  3. data/Rakefile +92 -26
  4. data/adhearsion.gemspec +131 -23
  5. data/app_generators/ahn/ahn_generator.rb +21 -7
  6. data/app_generators/ahn/templates/.ahnrc +10 -4
  7. data/app_generators/ahn/templates/Rakefile +7 -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 -6
  17. data/bin/jahn +10 -0
  18. data/examples/asterisk_manager_interface/standalone.rb +51 -0
  19. data/lib/adhearsion/cli.rb +140 -23
  20. data/lib/adhearsion/component_manager/component_tester.rb +55 -0
  21. data/lib/adhearsion/component_manager/spec_framework.rb +24 -0
  22. data/lib/adhearsion/component_manager.rb +169 -238
  23. data/lib/adhearsion/events_support.rb +59 -237
  24. data/lib/adhearsion/{core_extensions → foundation}/all.rb +0 -0
  25. data/lib/adhearsion/{blank_slate.rb → foundation/blank_slate.rb} +0 -0
  26. data/lib/adhearsion/{core_extensions → foundation}/custom_daemonizer.rb +0 -0
  27. data/lib/adhearsion/foundation/event_socket.rb +203 -0
  28. data/lib/adhearsion/foundation/future_resource.rb +36 -0
  29. data/lib/adhearsion/{core_extensions → foundation}/global.rb +0 -0
  30. data/lib/adhearsion/{core_extensions → foundation}/metaprogramming.rb +0 -0
  31. data/lib/adhearsion/foundation/numeric.rb +13 -0
  32. data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
  33. data/lib/adhearsion/{core_extensions → foundation}/relationship_properties.rb +2 -0
  34. data/lib/adhearsion/foundation/string.rb +26 -0
  35. data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
  36. data/lib/adhearsion/{core_extensions → foundation}/thread_safety.rb +0 -0
  37. data/lib/adhearsion/host_definitions.rb +5 -1
  38. data/lib/adhearsion/initializer/asterisk.rb +33 -11
  39. data/lib/adhearsion/initializer/configuration.rb +58 -6
  40. data/lib/adhearsion/initializer/database.rb +3 -46
  41. data/lib/adhearsion/initializer/drb.rb +9 -3
  42. data/lib/adhearsion/initializer/freeswitch.rb +3 -3
  43. data/lib/adhearsion/initializer/rails.rb +1 -1
  44. data/lib/adhearsion/initializer.rb +213 -87
  45. data/lib/adhearsion/tasks/deprecations.rb +59 -0
  46. data/lib/adhearsion/tasks.rb +2 -1
  47. data/lib/adhearsion/version.rb +3 -3
  48. data/lib/adhearsion/voip/asterisk/agi_server.rb +6 -6
  49. data/lib/adhearsion/voip/asterisk/commands.rb +100 -2
  50. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +1754 -0
  51. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +286 -0
  52. data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +78 -0
  53. data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +87 -0
  54. data/lib/adhearsion/voip/asterisk/manager_interface.rb +562 -0
  55. data/lib/adhearsion/voip/asterisk/super_manager.rb +19 -0
  56. data/lib/adhearsion/voip/asterisk.rb +1 -8
  57. data/lib/adhearsion/voip/call.rb +5 -1
  58. data/lib/adhearsion/voip/dial_plan.rb +74 -61
  59. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +1 -1
  60. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +2 -6
  61. data/lib/adhearsion/voip/dsl/numerical_string.rb +2 -2
  62. data/lib/adhearsion/voip/freeswitch/oes_server.rb +2 -2
  63. data/lib/adhearsion.rb +16 -11
  64. data/lib/theatre/README.markdown +64 -0
  65. data/lib/theatre/callback_definition_loader.rb +84 -0
  66. data/lib/theatre/guid.rb +23 -0
  67. data/lib/theatre/invocation.rb +121 -0
  68. data/lib/theatre/namespace_manager.rb +153 -0
  69. data/lib/theatre/version.rb +2 -0
  70. data/lib/theatre.rb +151 -0
  71. metadata +60 -147
  72. data/Manifest.txt +0 -151
  73. data/README.txt +0 -5
  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/voip/asterisk/ami/actions.rb +0 -238
  103. data/lib/adhearsion/voip/asterisk/ami/machine.rb +0 -871
  104. data/lib/adhearsion/voip/asterisk/ami/machine.rl +0 -109
  105. data/lib/adhearsion/voip/asterisk/ami/parser.rb +0 -262
  106. data/lib/adhearsion/voip/asterisk/ami.rb +0 -147
  107. data/spec/fixtures/dialplan.rb +0 -3
  108. data/spec/initializer/test_configuration.rb +0 -267
  109. data/spec/initializer/test_loading.rb +0 -162
  110. data/spec/initializer/test_paths.rb +0 -43
  111. data/spec/sample.rb +0 -9
  112. data/spec/silence.rb +0 -10
  113. data/spec/test_ahn_command.rb +0 -149
  114. data/spec/test_code_quality.rb +0 -87
  115. data/spec/test_component_manager.rb +0 -97
  116. data/spec/test_constants.rb +0 -8
  117. data/spec/test_drb.rb +0 -104
  118. data/spec/test_events.rb +0 -136
  119. data/spec/test_helper.rb +0 -106
  120. data/spec/test_hooks.rb +0 -15
  121. data/spec/test_host_definitions.rb +0 -79
  122. data/spec/test_initialization.rb +0 -124
  123. data/spec/test_logging.rb +0 -80
  124. data/spec/test_relationship_properties.rb +0 -54
  125. data/spec/voip/asterisk/ami_response_definitions.rb +0 -23
  126. data/spec/voip/asterisk/config_file_generators/test_agents.rb +0 -253
  127. data/spec/voip/asterisk/config_file_generators/test_queues.rb +0 -325
  128. data/spec/voip/asterisk/config_file_generators/test_voicemail.rb +0 -306
  129. data/spec/voip/asterisk/menu_command/test_calculated_match.rb +0 -111
  130. data/spec/voip/asterisk/menu_command/test_matchers.rb +0 -98
  131. data/spec/voip/asterisk/mock_ami_server.rb +0 -176
  132. data/spec/voip/asterisk/test_agi_server.rb +0 -453
  133. data/spec/voip/asterisk/test_ami.rb +0 -227
  134. data/spec/voip/asterisk/test_commands.rb +0 -2006
  135. data/spec/voip/asterisk/test_config_manager.rb +0 -129
  136. data/spec/voip/dsl/dispatcher_spec_helper.rb +0 -45
  137. data/spec/voip/dsl/test_dialing_dsl.rb +0 -268
  138. data/spec/voip/dsl/test_dispatcher.rb +0 -82
  139. data/spec/voip/dsl/test_parser.rb +0 -87
  140. data/spec/voip/freeswitch/test_basic_connection_manager.rb +0 -39
  141. data/spec/voip/freeswitch/test_inbound_connection_manager.rb +0 -39
  142. data/spec/voip/freeswitch/test_oes_server.rb +0 -9
  143. data/spec/voip/test_call_routing.rb +0 -127
  144. data/spec/voip/test_dialplan_manager.rb +0 -442
  145. data/spec/voip/test_numerical_string.rb +0 -48
  146. data/spec/voip/test_phone_number.rb +0 -36
  147. data/test/test_ahn_generator.rb +0 -59
  148. data/test/test_component_generator.rb +0 -52
  149. data/test/test_generator_helper.rb +0 -20
@@ -1,6 +1,45 @@
1
1
  module Adhearsion
2
2
 
3
+ class << self
4
+
5
+ ##
6
+ # Shuts down the framework.
7
+ #
8
+ def self.shutdown!
9
+ ahn_log "Shutting down gracefully at #{Time.now}."
10
+ Events.stop!
11
+ exit
12
+ end
13
+
14
+ end
3
15
  class PathString < String
16
+
17
+ class << self
18
+
19
+ ##
20
+ # Will return a PathString for the application root folder to which the specified arbitrarily nested subfolder belongs.
21
+ # It works by traversing parent directories looking for the .ahnrc file. If no .ahnrc is found, nil is returned.
22
+ #
23
+ # @param [String] folder The path to the directory which should be a
24
+ # @return [nil] if the subdirectory does not belong to a parent Adhearsion app directory
25
+ # @return [PathString] if a directory is found
26
+ #
27
+ def from_application_subdirectory(folder)
28
+ folder = File.expand_path folder
29
+ ahn_rc = nil
30
+
31
+ until ahn_rc || folder == "/"
32
+ possible_ahn_rc = File.join(folder, ".ahnrc")
33
+ if File.exists?(possible_ahn_rc)
34
+ ahn_rc = possible_ahn_rc
35
+ else
36
+ folder = File.expand_path(folder + "/..")
37
+ end
38
+ end
39
+ ahn_rc ? new(folder) : nil
40
+ end
41
+ end
42
+
4
43
  attr_accessor :component_path, :dialplan_path, :log_path
5
44
 
6
45
  def initialize(path)
@@ -27,10 +66,6 @@ module Adhearsion
27
66
  self.base_path = original_path
28
67
  end
29
68
 
30
- def dial_plan_named(name)
31
- File.join(dialplan_path, name)
32
- end
33
-
34
69
  private
35
70
  def build_path_for(path)
36
71
  File.join(to_s, path)
@@ -66,8 +101,6 @@ module Adhearsion
66
101
 
67
102
  attr_reader :path, :daemon, :pid_file, :log_file, :ahn_app_log_directory
68
103
 
69
- DEFAULT_FRAMEWORK_EVENT_CALLBACK_NAMES = [:after_initialized, :shutdown]
70
-
71
104
  # Creation of pid_files
72
105
  #
73
106
  # - You may want to have Adhearsion create a process identification
@@ -89,48 +122,41 @@ module Adhearsion
89
122
 
90
123
  def start
91
124
  self.class.ahn_root = path
125
+
92
126
  resolve_pid_file_path
93
127
  resolve_log_file_path
94
128
  switch_to_root_directory
95
129
  catch_termination_signal
96
130
  bootstrap_rc
97
- load_all_init_files
98
- init_modules
99
131
  daemonize! if should_daemonize?
100
132
  initialize_log_file
133
+ load_all_init_files
134
+ init_components_subsystem
135
+ init_modules
136
+ init_events_subsystem
101
137
  create_pid_file if pid_file
102
138
  load_components
103
- init_events
139
+ init_events_file
140
+
104
141
  ahn_log "Adhearsion initialized!"
105
142
 
106
143
  trigger_after_initialized_hooks
107
- join_framework_threads
144
+ join_important_threads
108
145
 
109
146
  self
110
147
  end
111
148
 
112
- def init_events
113
- if Paths.manager_for? "events"
114
- framework = Events.register_namespace_path(:framework)
115
- DEFAULT_FRAMEWORK_EVENT_CALLBACK_NAMES.each do |framework_event_callback_name|
116
- framework.register_callback_name framework_event_callback_name
117
- end
118
- Events.load_definitions_from_files *all_events
119
- else
120
- ahn_log.events.warn 'No "events" section in .ahnrc. Skipping its initialization.'
121
- end
149
+ def default_pid_path
150
+ File.join AHN_ROOT, 'adhearsion.pid'
122
151
  end
123
152
 
124
- def initialize_log_file
125
- Dir.mkdir(ahn_app_log_directory) unless File.directory? ahn_app_log_directory
126
- file_logger = Log4r::FileOutputter.new("Main Adhearsion log file", :filename => log_file, :trunc => false)
127
-
128
- if should_daemonize?
129
- Logging::AdhearsionLogger.outputters = file_logger
130
- else
131
- Logging::AdhearsionLogger.outputters << file_logger
153
+ def resolve_pid_file_path
154
+ @pid_file = if pid_file.equal?(true) then default_pid_path
155
+ elsif pid_file then pid_file
156
+ elsif pid_file.equal?(false) then nil
157
+ # FIXME @pid_file = @daemon? Assignment or equality? I'm assuming equality.
158
+ else @pid_file = @daemon ? default_pid_path : nil
132
159
  end
133
- Logging::DefaultAdhearsionLogger.redefine_outputters
134
160
  end
135
161
 
136
162
  def resolve_log_file_path
@@ -138,18 +164,101 @@ module Adhearsion
138
164
  @log_file = File.expand_path(ahn_app_log_directory + "/adhearsion.log")
139
165
  end
140
166
 
141
- def create_pid_file(file = pid_file)
142
- if file
143
- File.open pid_file, 'w' do |file|
144
- file.puts Process.pid
167
+ def switch_to_root_directory
168
+ Dir.chdir AHN_ROOT
169
+ end
170
+
171
+ def catch_termination_signal
172
+ %w'INT TERM'.each do |process_signal|
173
+ trap process_signal do
174
+ ahn_log "Shutting down gracefully at #{Time.now}."
175
+ Events.trigger :shutdown
176
+ exit
145
177
  end
146
-
147
- Hooks::TearDown.create_hook do
148
- File.delete(pid_file) if File.exists?(pid_file)
178
+ end
179
+ end
180
+
181
+ ##
182
+ # This step in the initialization process loads the .ahnrc in the given app folder. With the information in .ahnrc, we
183
+ # can continue the initialization knowing where certain files are specifically.
184
+ #
185
+ def bootstrap_rc
186
+ rules = self.class.get_rules_from AHN_ROOT
187
+
188
+ AHN_CONFIG.ahnrc = rules
189
+
190
+ # DEPRECATION: Check if the old paths format is being used. If so, abort and notify.
191
+ if rules.has_key?("paths") && rules["paths"].kind_of?(Hash)
192
+ paths = rules["paths"].each_pair do |key,value|
193
+ if value.kind_of?(Hash)
194
+ if value.has_key?("directory") || value.has_key?("pattern")
195
+ puts
196
+ puts *caller
197
+ puts
198
+
199
+ abort <<-WARNING
200
+ Deprecation Warning
201
+ -------------------
202
+ The (hidden) .ahnrc file in this app is of an older format and needs to be fixed.
203
+
204
+ There is a rake task to automatically fix it or you can do it manually. Note: it's
205
+ best if you do it manually so you can retain the YAML comments in your .ahnrc file.
206
+
207
+ The rake task is called "deprecations:fix_ahnrc_path_format".
208
+
209
+ To do it manually, find all entries in the "paths" section of your ".ahnrc" file
210
+ which look like the following:
211
+
212
+ paths:
213
+ key_name_could_be_anything:
214
+ directory: some_folder
215
+ pattern: *.rb
216
+
217
+ Note: the "models" section had this syntax before:
218
+
219
+ models:
220
+ directory: models
221
+ pattern: "*.rb"
222
+
223
+ The NEW syntax is as follows (using models as an example):
224
+
225
+ models: models/*.rb
226
+
227
+ This new format is much cleaner.
228
+
229
+ Adhearsion will abort until you fix this. Sorry for the incovenience.
230
+ WARNING
231
+ end
232
+ end
233
+ end
234
+ end
235
+
236
+ gems = rules['gems']
237
+ if gems.kind_of?(Hash) && gems.any? && respond_to?(:gem)
238
+ gems.each_pair do |gem_name,properties_hash|
239
+ if properties_hash && properties_hash["version"]
240
+ gem gem_name, properties_hash["version"]
241
+ else
242
+ gem gem_name
243
+ end
244
+ if properties_hash
245
+ case properties_hash["require"]
246
+ when Array
247
+ properties_hash["require"].each { |lib| require lib }
248
+ when String
249
+ require properties_hash["require"]
250
+ end
251
+ end
149
252
  end
150
253
  end
151
254
  end
152
255
 
256
+ def load_all_init_files
257
+ init_files_from_rc = AHN_CONFIG.files_from_setting("paths", "init").map { |file| File.expand_path(file) }
258
+ already_loaded_init_files = Array(@loaded_init_files).map { |file| File.expand_path(file) }
259
+ (init_files_from_rc - already_loaded_init_files).each { |init| load init }
260
+ end
261
+
153
262
  def init_modules
154
263
  require 'adhearsion/initializer/database.rb'
155
264
  require 'adhearsion/initializer/asterisk.rb'
@@ -164,28 +273,23 @@ module Adhearsion
164
273
  # FreeswitchInitializer.start if AHN_CONFIG.freeswitch_enabled?
165
274
  end
166
275
 
167
- def resolve_pid_file_path
168
- @pid_file = if pid_file.equal?(true) then default_pid_path
169
- elsif pid_file then pid_file
170
- elsif pid_file.equal?(false) then nil
171
- # FIXME @pid_file = @daemon? Assignment or equality? I'm assuming equality.
172
- else @pid_file = @daemon ? default_pid_path : nil
276
+ def init_events_subsystem
277
+ application_events_files = AHN_CONFIG.files_from_setting("paths", "events")
278
+ if application_events_files.any?
279
+ Events.register_callback(:shutdown) do
280
+ ahn_log.events "Performing a graceful stop of events subsystem"
281
+ Events.framework_theatre.graceful_stop!
282
+ end
283
+ Events.framework_theatre.start!
284
+ else
285
+ ahn_log.events.warn 'No entries in the "events" section of .ahnrc. Skipping its initialization.'
173
286
  end
174
287
  end
175
288
 
176
- def switch_to_root_directory
177
- Dir.chdir AHN_ROOT
178
- end
179
-
180
- def catch_termination_signal
181
- Hooks::TearDown.catch_termination_signals
182
- end
183
-
184
- def load_all_init_files
185
- if Paths.manager_for? "init"
186
- init_files_from_rc = all_inits.map { |file| File.expand_path(file) }
187
- already_loaded_init_files = Array(@loaded_init_files).map { |file| File.expand_path(file) }
188
- (init_files_from_rc - already_loaded_init_files).each { |init| load init }
289
+ def init_events_file
290
+ application_events_files = AHN_CONFIG.files_from_setting("paths", "events")
291
+ application_events_files.each do |file|
292
+ Events.framework_theatre.load_events_file file
189
293
  end
190
294
  end
191
295
 
@@ -199,47 +303,69 @@ module Adhearsion
199
303
  daemonize log_file
200
304
  end
201
305
 
202
- def load_components
203
- ComponentManager.load
204
- ComponentManager.start
306
+ def initialize_log_file
307
+ Dir.mkdir(ahn_app_log_directory) unless File.directory? ahn_app_log_directory
308
+ file_logger = Log4r::FileOutputter.new("Main Adhearsion log file", :filename => log_file, :trunc => false)
309
+
310
+ if should_daemonize?
311
+ Logging::AdhearsionLogger.outputters = file_logger
312
+ else
313
+ Logging::AdhearsionLogger.outputters << file_logger
314
+ end
315
+ Logging::DefaultAdhearsionLogger.redefine_outputters
205
316
  end
206
317
 
207
- def trigger_after_initialized_hooks
208
- Hooks::AfterInitialized.trigger_hooks
318
+ def create_pid_file(file = pid_file)
319
+ if file
320
+ File.open pid_file, 'w' do |file|
321
+ file.puts Process.pid
322
+ end
323
+
324
+ Events.register_callback :shutdown do
325
+ File.delete(pid_file) if File.exists?(pid_file)
326
+ end
327
+ end
209
328
  end
210
329
 
211
- def join_framework_threads
212
- Hooks::ThreadsJoinedAfterInitialized.trigger_hooks
330
+ def init_components_subsystem
331
+ @components_directory = File.expand_path "components"
332
+ if File.directory? @components_directory
333
+ Components.component_manager = Components::ComponentManager.new @components_directory
334
+ Kernel.send(:const_set, :COMPONENTS, Components.component_manager.lazy_config_loader)
335
+ Components.component_manager.globalize_global_scope!
336
+ Components.component_manager.extend_object_with(Theatre::CallbackDefinitionLoader, :events)
337
+ else
338
+ ahn_log.warn "No components directory found. Not initializing any components."
339
+ end
213
340
  end
214
341
 
215
- def bootstrap_rc
216
- rules = self.class.get_rules_from AHN_ROOT
217
-
218
- paths = rules['paths']
219
- paths.each_pair do |path_name, pattern_or_ruleset|
220
- if pattern_or_ruleset.kind_of? Hash
221
- directory, pattern = pattern_or_ruleset['directory'] || '.', pattern_or_ruleset['pattern'] || '*'
222
- Paths.manager_for path_name, :pattern => File.join(directory, pattern)
223
- else
224
- directory, pattern = '.', pattern_or_ruleset
225
- Paths.manager_for path_name, :pattern => File.join(directory,pattern)
226
- end
227
- end
228
-
229
- gems = rules['gems']
230
- if gems.kind_of?(Hash) && gems.any? && respond_to?(:gem)
231
- gems.each_pair do |gem_name,properties_hash|
232
- if properties_hash && properties_hash["version"]
233
- gem gem_name, properties_hash["version"]
234
- else
235
- gem gem_name
236
- end
237
- end
342
+ def load_components
343
+ if Components.component_manager
344
+ Components.component_manager.load_components
238
345
  end
239
346
  end
240
347
 
241
- def default_pid_path
242
- File.join AHN_ROOT, 'adhearsion.pid'
348
+ def trigger_after_initialized_hooks
349
+ Events.trigger_immediately :after_initialized
350
+ end
351
+
352
+ ##
353
+ # This method will block Thread.main() until calling join() has returned for all Threads in IMPORTANT_THREADS.
354
+ # Note: IMPORTANT_THREADS won't always contain Thread instances. It simply requires the objects respond to join().
355
+ #
356
+ def join_important_threads
357
+ # Note: we're using this ugly accumulator to ensure that all threads have ended since IMPORTANT_THREADS will almost
358
+ # certainly change sizes after this method is called.
359
+ index = 0
360
+ until index == IMPORTANT_THREADS.size
361
+ begin
362
+ IMPORTANT_THREADS[index].join
363
+ rescue => e
364
+ ahn_log.error "Error after join()ing Thread #{thread.inspect}. #{e.message}"
365
+ ensure
366
+ index = index + 1
367
+ end
368
+ end
243
369
  end
244
370
 
245
371
  class InitializationFailedError < Exception; end
@@ -0,0 +1,59 @@
1
+ namespace :deprecations do
2
+ desc <<-DESC
3
+ Older versions of Adhearsion had an .ahnrc "paths" section similar to the following...
4
+
5
+ paths:
6
+ models:
7
+ directory: models
8
+ pattern: *.rb
9
+
10
+ This has been deprecated. The new format is this:
11
+
12
+ paths:
13
+ models: {models,gui/app/models}/*.rb
14
+
15
+ This Rake task will fix your .ahnrc if you have
16
+ DESC
17
+ task :fix_ahnrc_path_format do
18
+ puts "\nThis will remove all comments from your .ahnrc file. A backup will be created as .ahnrc.backup."
19
+ puts "If you wish to do this manually to preserve your comments, simply overwrite .ahnrc with .ahnrc.backup"
20
+ puts "and apply the change manually."
21
+ puts
22
+
23
+ require 'fileutils'
24
+ require 'yaml'
25
+
26
+ ahnrc_file = File.expand_path(".ahnrc")
27
+
28
+ FileUtils.cp ahnrc_file, ahnrc_file + ".backup"
29
+ ahnrc_contents = YAML.load_file ahnrc_file
30
+
31
+ abort '.ahnrc does not have a "paths" section!' unless ahnrc_contents.has_key? "paths"
32
+
33
+ paths = ahnrc_contents["paths"]
34
+ paths.clone.each_pair do |key,value|
35
+ if value.kind_of?(Hash)
36
+ if value.has_key?("directory") || value.has_key?("pattern")
37
+ directory, pattern = value.values_at "directory", "pattern"
38
+ new_path = "#{directory}/#{pattern}"
39
+
40
+ puts "!!! CHANGING KEY #{key.inspect}!"
41
+ puts "!!! NEW: #{new_path.inspect}"
42
+ puts "!!! OLD:\n#{{key => value}.to_yaml.sub("---", "")}\n\n"
43
+
44
+ paths[key] = new_path
45
+ end
46
+ end
47
+ end
48
+
49
+ ahnrc_contents["paths"] = paths
50
+ new_yaml = ahnrc_contents.to_yaml.gsub("--- \n", "")
51
+
52
+ puts "New .ahnrc file:\n" + ("#" * 25) + "\n"
53
+ puts new_yaml
54
+ puts '#' * 25
55
+
56
+ File.open(ahnrc_file, "w") { |file| file.puts new_yaml }
57
+ puts "Wrote to .ahnrc. Done!"
58
+ end
59
+ end
@@ -4,6 +4,7 @@ require 'adhearsion/tasks/database'
4
4
  require 'adhearsion/tasks/testing'
5
5
  require 'adhearsion/tasks/generating'
6
6
  require 'adhearsion/tasks/lint'
7
+ require 'adhearsion/tasks/deprecations'
7
8
 
8
9
  namespace :adhearsion do
9
10
  desc "Dump useful information about this application's adhearsion environment"
@@ -12,4 +13,4 @@ namespace :adhearsion do
12
13
  end
13
14
  end
14
15
 
15
- task :default => "adhearsion:about"
16
+ task :default => "adhearsion:about"
@@ -1,8 +1,8 @@
1
1
  module Adhearsion #:nodoc:
2
2
  module VERSION #:nodoc:
3
- MAJOR = 0 unless defined? MAJOR
4
- MINOR = 7 unless defined? MINOR
5
- TINY = 999 unless defined? TINY
3
+ MAJOR = 0 unless defined? MAJOR
4
+ MINOR = 8 unless defined? MINOR
5
+ TINY = 0 unless defined? TINY
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.') unless defined? STRING
8
8
  end
@@ -12,8 +12,8 @@ module Adhearsion
12
12
  end
13
13
 
14
14
  def serve(io)
15
- Hooks::BeforeCall.trigger_hooks
16
- call = Adhearsion.receive_call_from(io)
15
+ call = Adhearsion.receive_call_from(io)
16
+ Events.trigger_immediately([:asterisk, :before_call], call)
17
17
  ahn_log.agi "Handling call with variables #{call.variables.inspect}"
18
18
 
19
19
  return DialPlan::ConfirmationManager.handle(call) if DialPlan::ConfirmationManager.confirmation_call?(call)
@@ -26,16 +26,16 @@ module Adhearsion
26
26
  call.hangup!
27
27
  rescue FailedExtensionCallException => failed_call
28
28
  begin
29
- ahn_log.agi "Received \"failed\" meta-call with :failed_reason => #{failed_call.call.failed_reason.inspect}. Executing OnFailedCall hooks."
30
- Hooks::OnFailedCall.trigger_hooks(failed_call.call)
29
+ ahn_log.agi "Received \"failed\" meta-call with :failed_reason => #{failed_call.call.failed_reason.inspect}. Executing Executing /asterisk/failed_call event callbacks."
30
+ Events.trigger [:asterisk, :failed_call], failed_call.call
31
31
  call.hangup!
32
32
  rescue => e
33
33
  ahn_log.agi.error e
34
34
  end
35
35
  rescue HungupExtensionCallException => hungup_call
36
36
  begin
37
- ahn_log.agi "Received \"h\" meta-call. Executing OnHungupCall hooks."
38
- Hooks::OnHungupCall.trigger_hooks(hungup_call.call)
37
+ ahn_log.agi "Received \"h\" meta-call. Executing /asterisk/call_hangup event callbacks."
38
+ Events.trigger [:asterisk, :call_hangup], hungup_call.call
39
39
  call.hangup!
40
40
  rescue => e
41
41
  ahn_log.agi.error e
@@ -7,6 +7,22 @@ module Adhearsion
7
7
  module Commands
8
8
 
9
9
  RESPONSE_PREFIX = "200 result=" unless defined? RESPONSE_PREFIX
10
+
11
+ # These are the status messages that asterisk will issue after a dial command is executed.
12
+ # More information here: http://www.voip-info.org/wiki/index.php?page=Asterisk+variable+DIALSTATUS
13
+ # Here is a current list of dial status messages which are not all necessarily supported by adhearsion:
14
+ #
15
+ # ANSWER: Call is answered. A successful dial. The caller reached the callee.
16
+ # BUSY: Busy signal. The dial command reached its number but the number is busy.
17
+ # NOANSWER: No answer. The dial command reached its number, the number rang for too long, then the dial timed out.
18
+ # CANCEL: Call is cancelled. The dial command reached its number but the caller hung up before the callee picked up.
19
+ # CONGESTION: Congestion. This status is usually a sign that the dialled number is not recognised.
20
+ # CHANUNAVAIL: Channel unavailable. On SIP, peer may not be registered.
21
+ # DONTCALL: Privacy mode, callee rejected the call
22
+ # TORTURE: Privacy mode, callee chose to send caller to torture menu
23
+ # INVALIDARGS: Error parsing Dial command arguments (added for Asterisk 1.4.1, SVN r53135-53136)
24
+ #
25
+ #
10
26
  DIAL_STATUSES = Hash.new(:unknown).merge(:answer => :answered,
11
27
  :congestion => :congested,
12
28
  :busy => :busy,
@@ -36,17 +52,47 @@ module Adhearsion
36
52
  end
37
53
  end
38
54
 
55
+ # This method is the underlying method executed by nearly all the command methods in this module.
56
+ # It is used to send the plaintext commands in the proper AGI format over TCP/IP back to an Asterisk server via the
57
+ # FAGI protocol.
58
+ # It is not recommended that you call this method directly unless you plan to write a new command method
59
+ # in which case use this method you to communicate directly with an Asterisk server via the FAGI protocol.
60
+ # For more information about FAGI visit: http://www.voip-info.org/wiki/view/Asterisk+FastAGI
39
61
  def raw_response(message = nil)
40
62
  ahn_log.agi.debug ">>> #{message}"
41
63
  write message if message
42
64
  read
43
65
  end
44
66
 
67
+ # The answer command must be called first before any other commands can be issued.
68
+ # In typical adhearsion applications the answer command is called by default as soon
69
+ # as a call is transfered to a valid context in dialplan.rb.
70
+ # If you do not want your adhearsion application to automatically issue an answer command,
71
+ # then you must edit your startup.rb file and configure this setting.
72
+ # Keep in mind that you should not need to issue another answer command after
73
+ # an answer command has already been issued either explicitly by your code or implicitly
74
+ # by the standard adhearsion configuration.
45
75
  def answer
46
76
  raw_response "ANSWER"
47
77
  true
48
78
  end
49
79
 
80
+ # This asterisk dialplan command allows you to instruct Asterisk to start applications
81
+ # which are typically run from extensions.conf. For a complete list of these commands
82
+ # please visit: http://www.voip-info.org/wiki/view/Asterisk+-+documentation+of+application+commands
83
+ #
84
+ # The most common commands are already made available through the FAGI interface provided
85
+ # by this code base. For commands that do not fall into this category, then exec is what you
86
+ # should use.
87
+ #
88
+ # For example, if there are specific asterisk modules you have loaded that will not
89
+ # available through the standard commands provided through FAGI - then you can used EXEC.
90
+ #
91
+ # Example:
92
+ # execute 'SIPAddHeader', '"Call-Info: answer-after=0"
93
+ #
94
+ # Using execute in this way will add a header to an existing SIP call.
95
+ #
50
96
  def execute(application, *arguments)
51
97
  result = raw_response("EXEC #{application} #{arguments * '|'}")
52
98
  return false if error?(result)
@@ -54,6 +100,10 @@ module Adhearsion
54
100
  end
55
101
 
56
102
  # Hangs up the current channel.
103
+ # After this command is issued, your application will stop executing.
104
+ # This should be used in the same way you would call the ruby exit() method to exit an application.
105
+ # If it is necessary to do some additional cleanup tasks before returning control back to asterisk, then
106
+ # make sure you have setup a begin...ensure block in the context of your adhearsion application dialplan.
57
107
  def hangup
58
108
  raw_response 'HANGUP'
59
109
  end
@@ -116,10 +166,12 @@ module Adhearsion
116
166
  block.call(next_message)
117
167
  end
118
168
 
169
+ # This command shouled be used to advance to the next message in the Asterisk Comedian Voicemail application
119
170
  def next_message
120
171
  @call.inbox.pop
121
172
  end
122
173
 
174
+ # This command should be used to check if a message is waiting on the Asterisk Comedian Voicemail application.
123
175
  def messages_waiting?
124
176
  not @call.inbox.empty?
125
177
  end
@@ -482,6 +534,10 @@ module Adhearsion
482
534
  execute "MeetMe", conference_id, command_flags, options[:pin]
483
535
  end
484
536
 
537
+ # Issue this command to access a channel variable that exists in the asterisk dialplan (i.e. extensions.conf)
538
+ # A complete description is available here: http://www.voip-info.org/wiki/view/get+variable
539
+ # Use get_variable to pass information from other modules or high level configurations from the asterisk dialplan
540
+ # to the adhearsion dialplan.
485
541
  def get_variable(variable_name)
486
542
  result = raw_response("GET VARIABLE #{variable_name}")
487
543
  case result
@@ -492,6 +548,12 @@ module Adhearsion
492
548
  end
493
549
  end
494
550
 
551
+ # Use set_variable to pass information back to the asterisk dial plan.
552
+ # A complete decription is available here: http://www.voip-info.org/wiki/view/set+variable
553
+ # Keep in mind that the variables are not global variables. These variables only exist for the channel
554
+ # related to the call that is being serviced by the particular instance of your adhearsion application.
555
+ # You will not be able to pass information back to the asterisk dialplan for other instances of your adhearsion
556
+ # application to share. Once the channel is "hungup" then the variables are cleared and their information is gone.
495
557
  def set_variable(variable_name, value)
496
558
  raw_response("SET VARIABLE %s %p" % [variable_name.to_s, value.to_s]) == "200 result=1"
497
559
  end
@@ -580,6 +642,42 @@ module Adhearsion
580
642
  voicemail_main
581
643
  end
582
644
 
645
+ # Use this command to dial an extension i.e. "phone number" in asterisk
646
+ # This command maps to the Asterisk DIAL command in the asterisk dialplan: http://www.voip-info.org/wiki-Asterisk+cmd+Dial
647
+ #
648
+ # The first parameter, number, must be a string that represents the extension or "number" that asterisk should dial.
649
+ # Be careful to not just specify a number like 5001, 9095551001
650
+ # You must specify a properly formatted string as Asterisk would expect to use in order to understand
651
+ # whether the call should be dialed using SIP, IAX, or some other means.
652
+ # Examples:
653
+ #
654
+ # Make a call to the PSTN using my SIP provider for VoIP termination:
655
+ # dial("SIP/19095551001@my.sip.voip.terminator.us")
656
+ #
657
+ # Make 3 Simulataneous calls to the SIP extensions separated by & symbols, try for 15 seconds and use the callerid
658
+ # for this call specified by the variable my_callerid
659
+ # dial "SIP/jay-desk-650&SIP/jay-desk-601&SIP/jay-desk-601-2", :for => 15.seconds, :caller_id => my_callerid
660
+ #
661
+ # Make a call using the IAX provider to the PSTN
662
+ # dial("IAX2/my.id@voipjet/19095551234", :name=>"John Doe", :caller_id=>"9095551234")
663
+ #
664
+ # Options Parameter:
665
+ # :caller_id - the caller id number to be used when the call is placed. It is advised you properly adhere to the
666
+ # policy of VoIP termination providers with respect to caller id values.
667
+ #
668
+ # :name - this is the name which should be passed with the caller ID information
669
+ # if :name=>"John Doe" and :caller_id => "444-333-1000" then the compelete CID and name would be "John Doe" <4443331000>
670
+ # support for caller id information varies from country to country and from one VoIP termination provider to another.
671
+ #
672
+ # :for - this option can be thought of best as a timeout. i.e. timeout after :for if no one answers the call
673
+ # For example, dial("SIP/jay-desk-650&SIP/jay-desk-601&SIP/jay-desk-601-2", :for => 15.seconds, :caller_id => callerid)
674
+ # this call will timeout after 15 seconds if 1 of the 3 extensions being dialed do not pick prior to the 15 second time limit
675
+ #
676
+ # :options - This is a string of options like "Tr" which are supported by the asterisk DIAL application.
677
+ # for a complete list of these options and their usage please visit: http://www.voip-info.org/wiki-Asterisk+cmd+Dial
678
+ #
679
+ # :confirm - ?
680
+ #
583
681
  def dial(number, options={})
584
682
  *recognized_options = :caller_id, :name, :for, :options, :confirm
585
683
 
@@ -645,13 +743,13 @@ module Adhearsion
645
743
  end
646
744
  nil
647
745
  end
648
-
746
+
649
747
  def set_caller_id_number(caller_id)
650
748
  return unless caller_id
651
749
  raise ArgumentError, "Caller ID must be numerical" if caller_id.to_s !~ /^\d+$/
652
750
  raw_response %(SET CALLERID %p) % caller_id
653
751
  end
654
-
752
+
655
753
  def set_caller_id_name(caller_id_name)
656
754
  return unless caller_id_name
657
755
  variable "CALLERID(name)" => caller_id_name