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,277 @@
1
+ module Adhearsion
2
+
3
+ module Components
4
+ class Manager
5
+ attr_reader :active_components, :host_information, :started_components, :components_with_call_context
6
+ def initialize
7
+ @active_components = {}
8
+ @started_components = []
9
+ @components_with_call_context = {}
10
+ end
11
+
12
+ def [](component_name)
13
+ active_components[component_name].component_class.instance
14
+ end
15
+
16
+ def component(component_name)
17
+ active_components[component_name]
18
+ end
19
+
20
+ def has_component?(component_name)
21
+ active_components.has_key?(component_name)
22
+ end
23
+
24
+ def component_gems
25
+ @repository ||= RubygemsRepository.new
26
+ @repository.adhearsion_gems
27
+ end
28
+
29
+ def load
30
+ return unless File.exist?(AHN_ROOT.component_path)
31
+ component_directories = Dir.glob(File.join(AHN_ROOT.component_path, "*"))
32
+ component_directories.each do |component_directory|
33
+ component_name = File.basename(component_directory).to_sym
34
+ @active_components[component_name] = Component.new(self, component_name, component_directory)
35
+ end
36
+ end
37
+
38
+ def start
39
+ @active_components.keys.each do |name|
40
+ @active_components[name].start
41
+ end
42
+ end
43
+
44
+ def stop
45
+ @started_components.reverse.each do |name|
46
+ @active_components[name].stop
47
+ end
48
+ end
49
+
50
+ class RubygemsRepository
51
+ def initialize
52
+ require 'rubygems'
53
+ Gem.manage_gems
54
+ end
55
+
56
+ def adhearsion_gems
57
+ gems = {}
58
+ Gem.source_index.each {|name, spec| gems[spec.name] = spec.full_gem_path if spec.requirements.include?("adhearsion")}
59
+ gems
60
+ end
61
+ end
62
+ end
63
+
64
+ ClassToGetCallContext = Struct.new(:component_class, :instance_variable)
65
+ class ClassToGetCallContext
66
+ def instantiate_with_call_context(call_context, *args, &block)
67
+ component = component_class.allocate
68
+ component.instance_variable_set(("@"+instance_variable.to_s).to_sym, call_context)
69
+ component.send(:initialize, *args, &block)
70
+ component
71
+ end
72
+ end
73
+
74
+
75
+ #component behavior is shared across components
76
+ module Behavior
77
+ def self.included(component_class)
78
+ component_class.extend(ClassMethods)
79
+ end
80
+
81
+ def component_name
82
+ Component.name
83
+ end
84
+
85
+ def component_description
86
+ Configuration.description || Component.name
87
+ end
88
+
89
+ module ClassMethods
90
+ def add_call_context(params = {:as => :call_context})
91
+ attr_reader params[:as]
92
+ ComponentManager.components_with_call_context[name] = ClassToGetCallContext.new(self, params[:as])
93
+ end
94
+ end
95
+ end
96
+
97
+ class Component
98
+ class << self
99
+ def prepare_component_class(component_module, name)
100
+ component_class_name = name.to_s.camelize
101
+ component_module.module_eval(<<-EVAL, __FILE__, __LINE__)
102
+ class #{component_class_name}
103
+ def self.name
104
+ '#{component_class_name}'
105
+ end
106
+ include Adhearsion::Components::Behavior
107
+ end
108
+ EVAL
109
+ end
110
+ end
111
+
112
+ attr_reader :manager, :name, :path, :component_module, :component_class
113
+ def initialize(manager, name, path)
114
+ @manager = manager
115
+ @name = name
116
+ @path = path
117
+ unless File.exist?(main_file_name)
118
+ gem_path = @manager.component_gems[@name.to_s]
119
+ raise "The component '#{@name}' does not have the main file: #{main_file_name}" unless gem_path
120
+ @path = gem_path
121
+ end
122
+ @started = false
123
+ end
124
+
125
+ def start
126
+ return if @started
127
+ manager.started_components << @name
128
+ @started = true
129
+ @component_module = ComponentModule.new(self) do |component_module|
130
+ Component.prepare_component_class(component_module, @name)
131
+ component_module.load_configuration_file
132
+ end
133
+ @component_module.require(File.join("lib", @name.to_s))
134
+ end
135
+
136
+ def configuration
137
+ @component_module.const_get(:Configuration)
138
+ end
139
+
140
+ def stop
141
+ #@component_class.unload if @component_class && @component_class.respond_to?(:unload)
142
+ end
143
+
144
+ def configuration_file
145
+ File.join(path, configuration_file_name)
146
+ end
147
+
148
+ private
149
+
150
+ def main_file_name
151
+ File.join(@path, "lib", @name.to_s+".rb")
152
+ end
153
+
154
+ def configuration_file_name
155
+ "configuration.rb"
156
+ end
157
+ end
158
+
159
+ class ComponentModule < Module
160
+ # The file with which the Script was instantiated.
161
+ attr_reader :main_file
162
+
163
+ # The directory in which main_file is located, and relative to which
164
+ # #load searches for files before falling back to Kernel#load.
165
+ attr_reader :dir
166
+
167
+ # A hash that maps <tt>filename=>true</tt> for each file that has been
168
+ # required locally by the script. This has the same semantics as <tt>$"</tt>,
169
+ # alias <tt>$LOADED_FEATURES</tt>, except that it is local to this script.
170
+ attr_reader :loaded_features
171
+
172
+ class << self
173
+ alias load new
174
+ end
175
+
176
+ # Creates new Script, and loads _main_file_ in the scope of the Script. If a
177
+ # block is given, the script is passed to it before loading from the file, and
178
+ # constants can be defined as inputs to the script.
179
+ attr_reader :component
180
+ def initialize(component) # :yields: self
181
+ extend ComponentModuleMethods
182
+ @component = component
183
+ @loaded_features = {}
184
+
185
+ const_set :Component, component
186
+ const_set :Configuration, OpenStruct.new(:description => nil)
187
+
188
+ yield self if block_given?
189
+ end
190
+
191
+ def load_configuration_file
192
+ load_in_module(component.configuration_file)
193
+ end
194
+
195
+ # Loads _file_ into this Script. Searches relative to the local dir, that is,
196
+ # the dir of the file given in the original call to
197
+ # <tt>Script.load(file)</tt>, loads the file, if found, into this Script's
198
+ # scope, and returns true. If the file is not found, falls back to
199
+ # <tt>Kernel.load</tt>, which searches on <tt>$LOAD_PATH</tt>, loads the file,
200
+ # if found, into global scope, and returns true. Otherwise, raises
201
+ # <tt>LoadError</tt>.
202
+ #
203
+ # The _wrap_ argument is passed to <tt>Kernel.load</tt> in the fallback case,
204
+ # when the file is not found locally.
205
+ #
206
+ # Typically called from within the main file to load additional sub files, or
207
+ # from those sub files.
208
+
209
+ def load(file, wrap = false)
210
+ load_in_module(File.join(component.path, file))
211
+ true
212
+ rescue MissingFile
213
+ super
214
+ end
215
+
216
+ # Analogous to <tt>Kernel#require</tt>. First tries the local dir, then falls
217
+ # back to <tt>Kernel#require</tt>. Will load a given _feature_ only once.
218
+ #
219
+ # Note that extensions (*.so, *.dll) can be required in the global scope, as
220
+ # usual, but not in the local scope. (This is not much of a limitation in
221
+ # practice--you wouldn't want to load an extension more than once.) This
222
+ # implementation falls back to <tt>Kernel#require</tt> when the argument is an
223
+ # extension or is not found locally.
224
+
225
+ def require(feature)
226
+ unless @loaded_features[feature]
227
+ @loaded_features[feature] = true
228
+ file = feature
229
+ file += ".rb" unless /\.rb$/ =~ file
230
+ load_in_module(File.join(component.path, file))
231
+ end
232
+ rescue MissingFile
233
+ @loaded_features[feature] = false
234
+ super
235
+ end
236
+
237
+ # Raised by #load_in_module, caught by #load and #require.
238
+ class MissingFile < LoadError; end
239
+
240
+ # Loads _file_ in this module's context. Note that <tt>\_\_FILE\_\_</tt> and
241
+ # <tt>\_\_LINE\_\_</tt> work correctly in _file_.
242
+ # Called by #load and #require; not normally called directly.
243
+
244
+ def load_in_module(file)
245
+ module_eval(File.read(file), File.expand_path(file))
246
+ rescue Errno::ENOENT => e
247
+ if /#{file}$/ =~ e.message
248
+ raise MissingFile, e.message
249
+ else
250
+ raise
251
+ end
252
+ end
253
+
254
+ def to_s
255
+ "#<#{self.class}:#{File.basename(component.path)}>"
256
+ end
257
+
258
+ module ComponentModuleMethods
259
+ # This is so that <tt>def meth...</tt> behaves like in Ruby's top-level
260
+ # context. The implementation simply calls
261
+ # <tt>Module#module_function(name)</tt>.
262
+ def method_added(name) # :nodoc:
263
+ module_function(name)
264
+ end
265
+
266
+ def start_component_after(*others)
267
+ others.each do |component_name|
268
+ component.manager.active_components[component_name].start
269
+ end
270
+ end
271
+
272
+ end
273
+ end
274
+ end
275
+
276
+ ComponentManager = Components::Manager.new unless defined? ComponentManager
277
+ end
@@ -0,0 +1,9 @@
1
+ require 'English'
2
+ require 'tmpdir'
3
+ require 'tempfile'
4
+ require 'active_support'
5
+
6
+ # Require all other files here.
7
+ Dir.glob File.join(File.dirname(__FILE__), "*rb") do |file|
8
+ require file
9
+ end
File without changes
@@ -0,0 +1,45 @@
1
+ # This is largely based on the Daemonize library by Travis Whitton and
2
+ # Judson Lester. http://grub.ath.cx/daemonize. I cleaned it up a bit to
3
+ # meet Adhearsion's quality standards.
4
+ module Adhearsion
5
+ module CustomDaemonizer
6
+
7
+ # Try to fork if at all possible retrying every 5 sec if the
8
+ # maximum process limit for the system has been reached
9
+ def safefork
10
+ begin
11
+ pid = fork
12
+ return pid if pid
13
+ rescue Errno::EWOULDBLOCK
14
+ sleep 5
15
+ retry
16
+ end
17
+ end
18
+
19
+ # This method causes the current running process to become a daemon
20
+ def daemonize(log_file='/dev/null')
21
+ oldmode = 0
22
+ srand # Split rand streams between spawning and daemonized process
23
+ safefork and exit # Fork and exit from the parent
24
+
25
+ # Detach from the controlling terminal
26
+ unless sess_id = Process.setsid
27
+ raise 'Cannot detach from controlled terminal'
28
+ end
29
+
30
+ # Prevent the possibility of acquiring a controlling terminal
31
+ if oldmode.zero?
32
+ trap 'SIGHUP', 'IGNORE'
33
+ exit if pid = safefork
34
+ end
35
+
36
+ Dir.chdir "/" # Release old working directory
37
+ File.umask 0000 # Ensure sensible umask
38
+
39
+ STDIN.reopen "/dev/null"
40
+ STDOUT.reopen '/dev/null', "a"
41
+ STDERR.reopen log_file
42
+ return oldmode ? sess_id : 0
43
+ end
44
+ end
45
+ end
@@ -0,0 +1 @@
1
+ Infinity = 1.0/0.0
@@ -0,0 +1,5 @@
1
+ module Kernel
2
+ def new_guid(args)
3
+
4
+ end
5
+ end
File without changes
@@ -0,0 +1,17 @@
1
+ class Object
2
+ def metaclass
3
+ class << self
4
+ self
5
+ end
6
+ end
7
+
8
+ def meta_eval(&block)
9
+ metaclass.instance_eval &block
10
+ end
11
+
12
+ def meta_def(name, &block)
13
+ meta_eval do
14
+ define_method name, &block
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,4 @@
1
+ class Numeric
2
+ def digit() self end
3
+ def digits() self end
4
+ end
File without changes
@@ -0,0 +1,11 @@
1
+ module PseudoUuidGenerator
2
+ def uuid
3
+ returning Array.new(32) { String.random_char } do |string|
4
+ [8, 13, 18, 23].each do |index|
5
+ string.insert index, '-'
6
+ end
7
+ end.join
8
+ end
9
+ end
10
+
11
+ include PseudoUuidGenerator
@@ -0,0 +1,73 @@
1
+ module Adhearsion
2
+ class DrbDoor
3
+
4
+ include Singleton
5
+
6
+ def add(interface, name, meth)
7
+ @interfaces ||= {}
8
+ @interfaces[interface] ||= returning(Object.new) { |obj| obj.metaclass.send(:attr_accessor, :__methods) }
9
+ obj = @interfaces[interface]
10
+ obj.__methods ||= {}
11
+ obj.__methods[name] = meth
12
+ obj.instance_eval(<<-STR, __FILE__, __LINE__)
13
+ def #{name}(*args, &block)
14
+ begin
15
+ __methods["#{name}"].call(*args, &block)
16
+ rescue => exception
17
+ raise RuntimeError, exception.message, exception.backtrace
18
+ end
19
+ end
20
+ STR
21
+ end
22
+
23
+ def method_missing(name, *args, &block)
24
+ return Module.const_get(name) if name.to_s =~ /^[A-Z]/
25
+ super unless @interfaces && @interfaces.has_key?(name.to_s)
26
+ @interfaces[name.to_s]
27
+ end
28
+ end
29
+
30
+ module Publishable
31
+ def self.included(base)
32
+ base.send(:alias_method_chain, :initialize, :publishable)
33
+ base.extend(ClassMethods)
34
+ end
35
+
36
+ def initialize_with_publishable(*args, &block)
37
+ initialize_without_publishable(*args, &block)
38
+ self.class.published_instance_methods.each do |(sym, interface)|
39
+ DrbDoor.instance.add(interface, sym.to_s, self.method(sym))
40
+ end
41
+ end
42
+
43
+ module ClassMethods
44
+ attr_reader :interface
45
+ attr_reader :published_instance_methods
46
+
47
+ def publish(options={}, &block)
48
+ @interface = options.delete(:through).to_s || self.to_s
49
+ begin
50
+ @capture = true
51
+ yield
52
+ ensure
53
+ @capture = false
54
+ end
55
+ end
56
+
57
+ def method_added(sym)
58
+ return if not @capture
59
+ if sym.to_s !~ /method_added/
60
+ @published_instance_methods ||= []
61
+ @published_instance_methods << [sym, @interface]
62
+ end
63
+ end
64
+
65
+ def singleton_method_added(sym)
66
+ return if not @capture
67
+ if sym.to_s !~ /method_added/
68
+ DrbDoor.instance.add(@interface, sym.to_s, method(sym.to_s))
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,40 @@
1
+ class Module
2
+
3
+ # In OOP, relationships between classes should be treated as *properties* of those classes. Often, in a complex OO
4
+ # architecture, you'll end up with many relationships that intermingle in monolithic ways, blunting the effectiveness of
5
+ # subclassing.
6
+ #
7
+ # For example, say you have an Automobile class which, in its constructor, instantiates a new Battery class and performs
8
+ # some operations on it such as calling an install() method. Let's also assume the Automobile class exposes a repair()
9
+ # method which uses a class-level method of Battery to diagnose your own instance of Battery. If the result of the
10
+ # diagnosis shows that the Battery is bad, the Automobile will instantiate a new Battery object and replace the old battery
11
+ # with the new one.
12
+ #
13
+ # Now, what if you wish to create a new Automobile derived from existing technology: a HybridAutomobile subclass. For this
14
+ # particular HybridAutomobile class, let's simply say the only difference between it and its parent is which kind of
15
+ # Battery it uses -- it requires its own special subclass of Battery. With Automobile's current implementation, its
16
+ # references to which Battery it instantiates and uses are embedded in the immutable method defintions. This
17
+ # HybridAutomobile needs to override which Battery its superclass' methods use and nothing else.
18
+ #
19
+ # For this reason, the Battery class which Automobile uses is semantically a property which others may want to override.
20
+ # In OOP theory, we define overridable properties in the form of methods and override those methods in the subclasses.
21
+ #
22
+ # This method exposes one method which creates human-readable semantics to defining these relationships as properties. It's
23
+ # used as follows:
24
+ #
25
+ # class Automobile
26
+ # relationship :battery => Battery
27
+ # relationship :chassis => Chassis
28
+ # # Other properties and instance methods here....
29
+ # end
30
+ #
31
+ # class HybridAutomobile < Automobile
32
+ # relationship :battery => HybridBattery
33
+ # end
34
+ def relationships(relationship_mapping)
35
+ relationship_mapping.each_pair do |class_name, class_object|
36
+ define_method(class_name) { class_object }
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,26 @@
1
+ class String
2
+
3
+ def unindent
4
+ gsub(/^\s*/,'')
5
+ end
6
+
7
+ def unindent!
8
+ gsub!(/^\s*/,'')
9
+ end
10
+
11
+ def self.random_char
12
+ case random_digit = rand(62)
13
+ when 0...10 : random_digit.to_s
14
+ when 10...36 : (random_digit + 55).chr
15
+ when 36...62 : (random_digit + 61).chr
16
+ end
17
+ end
18
+
19
+ def self.random(length_of_string=8)
20
+ Array.new(length_of_string) { random_char }.join
21
+ end
22
+
23
+ def nameify() downcase.gsub(/[^\w]/, '') end
24
+ def nameify!() replace nameify end
25
+
26
+ end
@@ -0,0 +1,13 @@
1
+ class Thread
2
+ class << self
3
+
4
+ # Syntactically sugar Thread.current since it's used so much.
5
+ #
6
+ # Allows:
7
+ # Thread.me.extend DSL::Dialplan::ThreadMixin
8
+ # Thread.my.call.io = io
9
+
10
+ alias me current
11
+ alias my current
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ require 'thread'
2
+ class Object
3
+ def synchronize(&block)
4
+ @mutex ||= Mutex.new
5
+ @mutex.synchronize &block
6
+ end
7
+ end
File without changes
@@ -0,0 +1,9 @@
1
+ module Adhearsion
2
+ module Distributed
3
+ module Gateway
4
+ module REST
5
+
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Adhearsion
2
+ module Distributed
3
+ module Gateway
4
+ module SOAP
5
+
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Adhearsion
2
+ module Distributed
3
+ module Gateway
4
+ module XMLRPC
5
+
6
+ end
7
+ end
8
+ end
9
+ end
File without changes
File without changes
@@ -0,0 +1,57 @@
1
+ module Adhearsion
2
+
3
+
4
+ module Hooks
5
+
6
+ class GenericHook
7
+
8
+ def initialize
9
+ @hooks = []
10
+ end
11
+
12
+ def create_hook(&block)
13
+ @hooks.synchronize do
14
+ @hooks << block
15
+ end
16
+ end
17
+
18
+ # TODO: This is hardly thread safe!
19
+ def trigger_hooks
20
+ @hooks.each &:call
21
+ end
22
+
23
+ end
24
+
25
+ class HookWithArguments < GenericHook
26
+ def trigger_hooks(*args)
27
+ @hooks.each { |hook| hook.call(*args) }
28
+ end
29
+ end
30
+
31
+ ThreadsJoinedAfterInitialized = GenericHook.new
32
+
33
+ OnFailedCall = HookWithArguments.new
34
+ OnHungupCall = HookWithArguments.new
35
+ AfterInitialized = GenericHook.new
36
+ BeforeCall = GenericHook.new
37
+ AfterCall = GenericHook.new
38
+ TearDown = GenericHook.new
39
+
40
+ class << TearDown
41
+ def aliases
42
+ [:before_shutdown]
43
+ end
44
+
45
+ def catch_termination_signals
46
+ %w'INT TERM'.each do |sig|
47
+ trap sig do
48
+ ahn_log "Shutting down gracefully at #{Time.now}."
49
+ Adhearsion::Hooks::TearDown.trigger_hooks
50
+ exit
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ end
57
+ end