jicksta-adhearsion 0.7.999
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +6 -0
- data/EVENTS +11 -0
- data/LICENSE +456 -0
- data/README.txt +5 -0
- data/Rakefile +120 -0
- data/adhearsion.gemspec +146 -0
- data/app_generators/ahn/USAGE +5 -0
- data/app_generators/ahn/ahn_generator.rb +87 -0
- data/app_generators/ahn/templates/.ahnrc +34 -0
- data/app_generators/ahn/templates/README +8 -0
- data/app_generators/ahn/templates/Rakefile +23 -0
- data/app_generators/ahn/templates/components/ami_remote/ami_remote.rb +15 -0
- data/app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE +7 -0
- data/app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown +47 -0
- data/app_generators/ahn/templates/components/disabled/stomp_gateway/config.yml +12 -0
- data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb +34 -0
- data/app_generators/ahn/templates/components/restful_rpc/README.markdown +11 -0
- data/app_generators/ahn/templates/components/restful_rpc/config.yml +34 -0
- data/app_generators/ahn/templates/components/restful_rpc/example-client.rb +48 -0
- data/app_generators/ahn/templates/components/restful_rpc/restful_rpc.rb +87 -0
- data/app_generators/ahn/templates/components/simon_game/simon_game.rb +56 -0
- data/app_generators/ahn/templates/config/startup.rb +53 -0
- data/app_generators/ahn/templates/dialplan.rb +3 -0
- data/app_generators/ahn/templates/events.rb +32 -0
- data/bin/ahn +28 -0
- data/bin/ahnctl +68 -0
- data/bin/jahn +42 -0
- data/examples/asterisk_manager_interface/standalone.rb +51 -0
- data/lib/adhearsion/cli.rb +223 -0
- data/lib/adhearsion/component_manager/spec_framework.rb +24 -0
- data/lib/adhearsion/component_manager.rb +208 -0
- data/lib/adhearsion/events_support.rb +84 -0
- data/lib/adhearsion/foundation/all.rb +9 -0
- data/lib/adhearsion/foundation/blank_slate.rb +5 -0
- data/lib/adhearsion/foundation/custom_daemonizer.rb +45 -0
- data/lib/adhearsion/foundation/event_socket.rb +203 -0
- data/lib/adhearsion/foundation/future_resource.rb +36 -0
- data/lib/adhearsion/foundation/global.rb +1 -0
- data/lib/adhearsion/foundation/metaprogramming.rb +17 -0
- data/lib/adhearsion/foundation/numeric.rb +13 -0
- data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
- data/lib/adhearsion/foundation/relationship_properties.rb +42 -0
- data/lib/adhearsion/foundation/string.rb +26 -0
- data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
- data/lib/adhearsion/foundation/thread_safety.rb +7 -0
- data/lib/adhearsion/host_definitions.rb +67 -0
- data/lib/adhearsion/initializer/asterisk.rb +81 -0
- data/lib/adhearsion/initializer/configuration.rb +254 -0
- data/lib/adhearsion/initializer/database.rb +49 -0
- data/lib/adhearsion/initializer/drb.rb +31 -0
- data/lib/adhearsion/initializer/freeswitch.rb +22 -0
- data/lib/adhearsion/initializer/rails.rb +40 -0
- data/lib/adhearsion/initializer.rb +373 -0
- data/lib/adhearsion/logging.rb +92 -0
- data/lib/adhearsion/tasks/database.rb +5 -0
- data/lib/adhearsion/tasks/deprecations.rb +59 -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 +16 -0
- data/lib/adhearsion/version.rb +9 -0
- data/lib/adhearsion/voip/asterisk/agi_server.rb +81 -0
- data/lib/adhearsion/voip/asterisk/commands.rb +1284 -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/manager_interface/ami_lexer.rb +1754 -0
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +286 -0
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +78 -0
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +87 -0
- data/lib/adhearsion/voip/asterisk/manager_interface.rb +562 -0
- data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +80 -0
- data/lib/adhearsion/voip/asterisk/super_manager.rb +19 -0
- data/lib/adhearsion/voip/asterisk.rb +4 -0
- data/lib/adhearsion/voip/call.rb +440 -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 +218 -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 +71 -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 +37 -0
- data/lib/theatre/README.markdown +64 -0
- data/lib/theatre/callback_definition_loader.rb +84 -0
- data/lib/theatre/guid.rb +23 -0
- data/lib/theatre/invocation.rb +121 -0
- data/lib/theatre/namespace_manager.rb +153 -0
- data/lib/theatre/version.rb +2 -0
- data/lib/theatre.rb +151 -0
- metadata +177 -0
@@ -0,0 +1,373 @@
|
|
1
|
+
module Adhearsion
|
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
|
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
|
+
|
43
|
+
attr_accessor :component_path, :dialplan_path, :log_path
|
44
|
+
|
45
|
+
def initialize(path)
|
46
|
+
super
|
47
|
+
defaults
|
48
|
+
end
|
49
|
+
|
50
|
+
def defaults
|
51
|
+
@component_path = build_path_for "components"
|
52
|
+
@dialplan_path = dup
|
53
|
+
@log_path = build_path_for "logs"
|
54
|
+
end
|
55
|
+
|
56
|
+
def base_path=(value)
|
57
|
+
replace(value)
|
58
|
+
defaults
|
59
|
+
end
|
60
|
+
|
61
|
+
def using_base_path(temporary_base_path, &block)
|
62
|
+
original_path = dup
|
63
|
+
self.base_path = temporary_base_path
|
64
|
+
block.call
|
65
|
+
ensure
|
66
|
+
self.base_path = original_path
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
def build_path_for(path)
|
71
|
+
File.join(to_s, path)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Initializer
|
76
|
+
|
77
|
+
class << self
|
78
|
+
def get_rules_from(location)
|
79
|
+
location = File.join location, ".ahnrc" if File.directory? location
|
80
|
+
File.exists?(location) ? YAML.load_file(location) : nil
|
81
|
+
end
|
82
|
+
|
83
|
+
def ahn_root=(path)
|
84
|
+
if Object.constants.include?("AHN_ROOT")
|
85
|
+
Object.const_get(:AHN_ROOT).base_path = File.expand_path(path)
|
86
|
+
else
|
87
|
+
Object.const_set(:AHN_ROOT, PathString.new(File.expand_path(path)))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def start(*args, &block)
|
92
|
+
new(*args, &block).start
|
93
|
+
end
|
94
|
+
|
95
|
+
def start_from_init_file(file, ahn_app_path)
|
96
|
+
return if defined?(@@started) && @@started
|
97
|
+
start ahn_app_path, :loaded_init_files => file
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
attr_reader :path, :daemon, :pid_file, :log_file, :ahn_app_log_directory
|
103
|
+
|
104
|
+
# Creation of pid_files
|
105
|
+
#
|
106
|
+
# - You may want to have Adhearsion create a process identification
|
107
|
+
# file when it boots so that a crash monitoring program such as
|
108
|
+
# Monit can reboot if necessary or so the init script can kill it
|
109
|
+
# for system shutdowns.
|
110
|
+
# - To have Adhearsion create a pid file in the default location (i.e.
|
111
|
+
# AHN_INSTALL_DIR/adhearsion.pid), supply :pid_file with 'true'. Otherwise
|
112
|
+
# one is not created UNLESS it is running in daemon mode, in which
|
113
|
+
# case one is created. You can force Adhearsion to not create one
|
114
|
+
# even in daemon mode by supplying "false".
|
115
|
+
def initialize(path=nil, options={})
|
116
|
+
@@started = true
|
117
|
+
@path = path
|
118
|
+
@daemon = options[:daemon]
|
119
|
+
@pid_file = options[:pid_file].nil? ? ENV['PID_FILE'] : options[:pid_file]
|
120
|
+
@loaded_init_files = options[:loaded_init_files]
|
121
|
+
end
|
122
|
+
|
123
|
+
def start
|
124
|
+
self.class.ahn_root = path
|
125
|
+
|
126
|
+
resolve_pid_file_path
|
127
|
+
resolve_log_file_path
|
128
|
+
switch_to_root_directory
|
129
|
+
catch_termination_signal
|
130
|
+
bootstrap_rc
|
131
|
+
daemonize! if should_daemonize?
|
132
|
+
initialize_log_file
|
133
|
+
load_all_init_files
|
134
|
+
init_components_subsystem
|
135
|
+
init_modules
|
136
|
+
init_events_subsystem
|
137
|
+
create_pid_file if pid_file
|
138
|
+
load_components
|
139
|
+
init_events_file
|
140
|
+
|
141
|
+
ahn_log "Adhearsion initialized!"
|
142
|
+
|
143
|
+
trigger_after_initialized_hooks
|
144
|
+
join_important_threads
|
145
|
+
|
146
|
+
self
|
147
|
+
end
|
148
|
+
|
149
|
+
def default_pid_path
|
150
|
+
File.join AHN_ROOT, 'adhearsion.pid'
|
151
|
+
end
|
152
|
+
|
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
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def resolve_log_file_path
|
163
|
+
@ahn_app_log_directory = AHN_ROOT + '/log'
|
164
|
+
@log_file = File.expand_path(ahn_app_log_directory + "/adhearsion.log")
|
165
|
+
end
|
166
|
+
|
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
|
177
|
+
end
|
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
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
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
|
+
|
262
|
+
def init_modules
|
263
|
+
require 'adhearsion/initializer/database.rb'
|
264
|
+
require 'adhearsion/initializer/asterisk.rb'
|
265
|
+
require 'adhearsion/initializer/drb.rb'
|
266
|
+
require 'adhearsion/initializer/rails.rb'
|
267
|
+
# require 'adhearsion/initializer/freeswitch.rb'
|
268
|
+
|
269
|
+
DatabaseInitializer.start if AHN_CONFIG.database_enabled?
|
270
|
+
AsteriskInitializer.start if AHN_CONFIG.asterisk_enabled?
|
271
|
+
DrbInitializer.start if AHN_CONFIG.drb_enabled?
|
272
|
+
RailsInitializer.start if AHN_CONFIG.rails_enabled?
|
273
|
+
# FreeswitchInitializer.start if AHN_CONFIG.freeswitch_enabled?
|
274
|
+
end
|
275
|
+
|
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.'
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
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
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def should_daemonize?
|
297
|
+
@daemon || ENV['DAEMON']
|
298
|
+
end
|
299
|
+
|
300
|
+
def daemonize!
|
301
|
+
ahn_log "Daemonizing now! Creating #{pid_file}."
|
302
|
+
extend Adhearsion::CustomDaemonizer
|
303
|
+
daemonize log_file
|
304
|
+
end
|
305
|
+
|
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
|
316
|
+
end
|
317
|
+
|
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
|
328
|
+
end
|
329
|
+
|
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
|
340
|
+
end
|
341
|
+
|
342
|
+
def load_components
|
343
|
+
if Components.component_manager
|
344
|
+
Components.component_manager.load_components
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
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
|
369
|
+
end
|
370
|
+
|
371
|
+
class InitializationFailedError < Exception; end
|
372
|
+
end
|
373
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'log4r'
|
2
|
+
|
3
|
+
module Adhearsion
|
4
|
+
module Logging
|
5
|
+
|
6
|
+
@@logging_level_lock = Mutex.new
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def silence!
|
11
|
+
self.logging_level = :fatal
|
12
|
+
end
|
13
|
+
|
14
|
+
def unsilence!
|
15
|
+
self.logging_level = :info
|
16
|
+
end
|
17
|
+
|
18
|
+
def logging_level=(new_logging_level)
|
19
|
+
new_logging_level = Log4r.const_get(new_logging_level.to_s.upcase)
|
20
|
+
@@logging_level_lock.synchronize do
|
21
|
+
@@logging_level = new_logging_level
|
22
|
+
Log4r::Logger.each_logger do |logger|
|
23
|
+
logger.level = new_logging_level
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def logging_level
|
29
|
+
@@logging_level_lock.synchronize do
|
30
|
+
return @@logging_level ||= Log4r::INFO
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class AdhearsionLogger < Log4r::Logger
|
36
|
+
|
37
|
+
@@outputters = [Log4r::Outputter.stdout]
|
38
|
+
|
39
|
+
class << self
|
40
|
+
def outputters
|
41
|
+
@@outputters
|
42
|
+
end
|
43
|
+
|
44
|
+
def outputters=(other)
|
45
|
+
@@outputters = other
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize(*args)
|
50
|
+
super
|
51
|
+
redefine_outputters
|
52
|
+
end
|
53
|
+
|
54
|
+
def redefine_outputters
|
55
|
+
self.outputters = @@outputters
|
56
|
+
end
|
57
|
+
|
58
|
+
def method_missing(logger_name, *args, &block)
|
59
|
+
define_logging_method(logger_name, self.class.new(logger_name.to_s))
|
60
|
+
send(logger_name, *args, &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def define_logging_method(name, logger)
|
66
|
+
# Can't use Module#define_method() because blocks in Ruby 1.8.x can't
|
67
|
+
# have their own block arguments.
|
68
|
+
self.class.class_eval(<<-CODE, __FILE__, __LINE__)
|
69
|
+
def #{name}(*args, &block)
|
70
|
+
logger = Log4r::Logger['#{name}']
|
71
|
+
if args.any? || block_given?
|
72
|
+
logger.info(*args, &block)
|
73
|
+
else
|
74
|
+
logger
|
75
|
+
end
|
76
|
+
end
|
77
|
+
CODE
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
DefaultAdhearsionLogger = AdhearsionLogger.new 'ahn'
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def ahn_log(*args)
|
87
|
+
if args.any?
|
88
|
+
Adhearsion::Logging::DefaultAdhearsionLogger.info(*args)
|
89
|
+
else
|
90
|
+
Adhearsion::Logging::DefaultAdhearsionLogger
|
91
|
+
end
|
92
|
+
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
|
@@ -0,0 +1,20 @@
|
|
1
|
+
namespace:create do
|
2
|
+
|
3
|
+
task:war do
|
4
|
+
# Hmm, this will is a tough one
|
5
|
+
end
|
6
|
+
|
7
|
+
task:rails_plugin do
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
task:migration do
|
12
|
+
name = ARGV.shift
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
namespace:delete do
|
17
|
+
task:migration do
|
18
|
+
# Take arg.underscore and remove it
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
namespace:test do
|
2
|
+
desc "Run tests for a component specified by COMPONENT=<component_name>. If no component is specified, tests will be executed for all components"
|
3
|
+
task :component do
|
4
|
+
component = ENV['COMPONENT']
|
5
|
+
components_to_test = component.nil? ? all_component_directories : [full_path_for(component)]
|
6
|
+
components_to_test.each do |component_name|
|
7
|
+
setup_and_execute(component_name)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def setup_and_execute(component_path)
|
14
|
+
task = create_test_task_for(component_path)
|
15
|
+
Rake::Task[task.name].execute
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_test_task_for(component_path)
|
19
|
+
Rake::TestTask.new(task_name_for(component_path)) do |t|
|
20
|
+
t.libs = ["lib", "test"].map{|subdir| File.join(component_path, subdir)}
|
21
|
+
t.test_files = FileList["#{component_path}/test/test_*.rb"]
|
22
|
+
t.verbose = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def task_name_for(component_path)
|
27
|
+
"test_#{component_path.split(/\//).last}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def all_component_directories
|
31
|
+
Dir['components/*']
|
32
|
+
end
|
33
|
+
|
34
|
+
def full_path_for(component)
|
35
|
+
component =~ /^components\// ? component : File.join("components", component)
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
require 'adhearsion'
|
3
|
+
require 'adhearsion/tasks/database'
|
4
|
+
require 'adhearsion/tasks/testing'
|
5
|
+
require 'adhearsion/tasks/generating'
|
6
|
+
require 'adhearsion/tasks/lint'
|
7
|
+
require 'adhearsion/tasks/deprecations'
|
8
|
+
|
9
|
+
namespace :adhearsion do
|
10
|
+
desc "Dump useful information about this application's adhearsion environment"
|
11
|
+
task :about do
|
12
|
+
puts "Adhearsion version: #{Adhearsion::VERSION::STRING}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
task :default => "adhearsion:about"
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'gserver'
|
2
|
+
module Adhearsion
|
3
|
+
module VoIP
|
4
|
+
module Asterisk
|
5
|
+
module AGI
|
6
|
+
class Server
|
7
|
+
|
8
|
+
class RubyServer < GServer
|
9
|
+
|
10
|
+
def initialize(port, host)
|
11
|
+
super(port, host, (1.0/0.0)) # (1.0/0.0) == Infinity
|
12
|
+
end
|
13
|
+
|
14
|
+
def serve(io)
|
15
|
+
call = Adhearsion.receive_call_from(io)
|
16
|
+
Events.trigger_immediately([:asterisk, :before_call], call)
|
17
|
+
ahn_log.agi "Handling call with variables #{call.variables.inspect}"
|
18
|
+
|
19
|
+
return DialPlan::ConfirmationManager.handle(call) if DialPlan::ConfirmationManager.confirmation_call?(call)
|
20
|
+
|
21
|
+
# This is what happens 99.9% of the time.
|
22
|
+
|
23
|
+
DialPlan::Manager.handle call
|
24
|
+
rescue DialPlan::Manager::NoContextError => e
|
25
|
+
ahn_log.agi e.message
|
26
|
+
call.hangup!
|
27
|
+
rescue FailedExtensionCallException => failed_call
|
28
|
+
begin
|
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
|
+
call.hangup!
|
32
|
+
rescue => e
|
33
|
+
ahn_log.agi.error e
|
34
|
+
end
|
35
|
+
rescue HungupExtensionCallException => hungup_call
|
36
|
+
begin
|
37
|
+
ahn_log.agi "Received \"h\" meta-call. Executing /asterisk/call_hangup event callbacks."
|
38
|
+
Events.trigger [:asterisk, :call_hangup], hungup_call.call
|
39
|
+
call.hangup!
|
40
|
+
rescue => e
|
41
|
+
ahn_log.agi.error e
|
42
|
+
end
|
43
|
+
rescue UselessCallException
|
44
|
+
ahn_log.agi "Ignoring meta-AGI request"
|
45
|
+
call.hangup!
|
46
|
+
# TBD: (may have more hooks than what Jay has defined in hooks.rb)
|
47
|
+
rescue => e
|
48
|
+
ahn_log.agi.error e.inspect
|
49
|
+
ahn_log.agi.error e.backtrace.map { |s| " " * 5 + s }.join("\n")
|
50
|
+
ensure
|
51
|
+
Adhearsion.remove_inactive_call call rescue nil
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
DEFAULT_OPTIONS = { :server_class => RubyServer, :port => 4573, :host => "0.0.0.0" } unless defined? DEFAULT_OPTIONS
|
57
|
+
attr_reader :host, :port, :server_class, :server
|
58
|
+
|
59
|
+
def initialize(options = {})
|
60
|
+
options = DEFAULT_OPTIONS.merge options
|
61
|
+
@host, @port, @server_class = options.values_at(:host, :port, :server_class)
|
62
|
+
@server = server_class.new(port, host)
|
63
|
+
end
|
64
|
+
|
65
|
+
def start
|
66
|
+
server.start
|
67
|
+
end
|
68
|
+
|
69
|
+
def shutdown
|
70
|
+
server.stop
|
71
|
+
end
|
72
|
+
|
73
|
+
def join
|
74
|
+
server.join
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|