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,259 +1,81 @@
1
- require 'thread'
1
+ %w[theatre jicksta-theatre].each do |theatre_gem_name|
2
+ # Some older versions of the master branch required the theatre gem be installed. This is deprecation logic to inform the
3
+ # user to uninstall the gem if they happened to have used theatre in the past.
4
+ begin
5
+ gem theatre_gem_name
6
+ rescue LoadError
7
+ # Good. It should not be installed.
8
+ else
9
+ abort <<-MESSAGE
10
+ It seems you have the "#{theatre_gem_name}" gem installed. As of Dec. 7th, 2008 Theatre has been rolled into Adhearsion
11
+ and will be distributed with it.
12
+
13
+ Please uninstall the gem by doing "sudo gem uninstall #{theatre_gem_name}".
14
+
15
+ You will not need to install it again in the future. Sorry for the inconvenience.
16
+ MESSAGE
17
+ end
18
+ end
19
+
20
+ require 'theatre'
21
+
22
+
2
23
  module Adhearsion
3
24
  module Events
4
25
 
5
- class << self
6
-
7
- def framework_events_container
8
- defined?(@@framework_events_container) ? @@framework_events_container : reinitialize_framework_events_container!
9
- end
10
-
11
- def load_definitions_from_files(*files)
12
- files.each do |file|
13
- framework_events_container.instance_eval File.read(file)
14
- end
15
- end
16
- alias load_definitions_from_file load_definitions_from_files
17
-
18
- def namespace_registered?(*paths)
19
- framework_events_container.namespace_registered?(*paths)
20
- end
21
-
22
- def reinitialize_framework_events_container!
23
- @@framework_events_container = EventsDefinitionContainer.new
24
- end
25
-
26
- def register_namespace_path(*paths)
27
- framework_events_container.register_namespace_path(*paths)
28
- end
29
-
30
- end
26
+ DEFAULT_FRAMEWORK_EVENT_NAMESPACES = %w[
27
+ /after_initialized
28
+ /shutdown
29
+ /asterisk/manager_interface
30
+ /asterisk/before_call
31
+ /asterisk/after_call
32
+ /asterisk/hungup_call
33
+ /asterisk/failed_call
34
+ ]
31
35
 
32
- class EventsDefinitionContainer
33
-
34
- attr_reader :root
35
- def initialize
36
- @root = RootEventNamespace.new
37
- end
38
-
39
- def register_namespace_path(*paths)
40
- inject_across_path(*paths) do |namespace, path|
41
- namespace.namespace_registered?(path) ? namespace[path] : namespace.register_namespace(path)
42
- end
43
- end
44
-
45
- def events
46
- root.capturer
47
- end
48
-
49
- def namespace_registered?(*paths)
50
- !! inject_across_path(*paths) { |namespace,path| namespace[path] }
51
- rescue UndefinedEventNamespace
52
- false
53
- end
54
-
55
- def callbacks_at_path(*paths)
56
- inject_across_path(*paths) { |namespace,path| namespace[path] }.callbacks
57
- end
58
-
59
- private
60
-
61
- def inject_across_path(*paths, &block)
62
- paths.map(&:to_sym).inject(root, &block)
63
- end
64
- end
65
-
66
- class NamespaceDefinitionCapturer
67
-
68
- attr_reader :namespace
69
- def initialize(namespace)
70
- @namespace = namespace
71
- end
72
-
73
- def method_missing(name, *args)
74
- super if name == :each # Added to prevent confusion
75
- nested_namespace_or_registrar = namespace[name.to_sym]
76
- raise UndefinedEventNamespace.new(name) unless nested_namespace_or_registrar
77
- case nested_namespace_or_registrar
78
- when EventCallbackRegistrar
79
- nested_namespace_or_registrar
80
- when RegisteredEventNamespace
81
- nested_namespace_or_registrar.capturer
82
- end
83
- end
84
- end
85
-
86
- class UndefinedEventNamespace < Exception
87
- def initialize(name)
88
- super "Undefined namespace '#{name}'"
89
- end
90
- end
91
-
92
- class AbstractEventNamespace
93
-
94
- attr_reader :children
95
- def initialize
96
- @children = {}
97
- end
98
-
99
- def [](namespace_name)
100
- raise UndefinedEventNamespace.new(namespace_name) unless namespace_registered? namespace_name
101
- children[namespace_name]
102
- end
103
-
104
- def namespace_registered?(namespace_name)
105
- children.has_key?(namespace_name)
106
- end
107
-
108
- def register_namespace(namespace)
109
- children[namespace] = RegisteredEventNamespace.new(self)
110
- end
111
-
112
- def root?
113
- false
114
- end
115
-
116
- def capturer
117
- @capturer ||= NamespaceDefinitionCapturer.new(self)
118
- end
119
-
120
- end
121
-
122
- class RootEventNamespace < AbstractEventNamespace
123
-
124
- def parent
125
- nil
126
- end
127
-
128
- def root?
129
- true
130
- end
131
- end
132
-
133
- class RegisteredEventNamespace < AbstractEventNamespace
134
- attr_reader :parent
135
- def initialize(parent)
136
- super()
137
- @parent = parent
138
- end
139
-
140
- def register_callback_name(name, mode=:sync, &block)
141
- children[name] = case mode
142
- when :sync then SynchronousEventCallbackRegistrar
143
- when :async then AsynchronousEventCallbackRegistrar
144
- else
145
- raise ArgumentError, "Unsupported mode #{mode.inspect} !"
146
- end.new(self, &block)
147
- end
148
-
149
- end
150
-
151
- class EventCallbackRegistrar
152
-
153
- attr_reader :namespace, :callbacks
154
- attr_accessor :notified_on_new_callback
155
-
156
-
157
- def initialize(namespace, &notified_on_new_callback)
158
- @namespace = namespace
159
- @callbacks = []
160
- @mutex = Mutex.new
161
- @notified_on_new_callback = notified_on_new_callback
162
- end
163
-
164
- # This is effectively called when you define a new callback with each()
165
- def register_callback(&block)
166
- returning RegisteredEventCallback.new(self, &block) do |callback|
167
- with_lock { callbacks << callback }
168
- notified_on_new_callback.call callback if notified_on_new_callback
169
- end
170
- end
171
- alias each register_callback
36
+ class << self
172
37
 
173
- def <<(message)
174
- raise NotImplementedError
38
+ def framework_theatre
39
+ defined?(@@framework_theatre) ? @@framework_theatre : reinitialize_theatre!
175
40
  end
176
41
 
177
- def remove_callback(callback)
178
- with_lock { callbacks.delete callback }
42
+ def trigger(*args)
43
+ framework_theatre.trigger(*args)
179
44
  end
180
45
 
181
- protected
182
-
183
- def with_lock(&block)
184
- @mutex.synchronize(&block)
46
+ def trigger_immediately(*args)
47
+ framework_theatre.trigger_immediately(*args)
185
48
  end
186
49
 
187
- def threadsafe_callback_collection
188
- with_lock { callbacks.clone }
189
- end
190
-
191
- end
192
-
193
- class SynchronousEventCallbackRegistrar < EventCallbackRegistrar
194
- def <<(event)
195
- threadsafe_callback_collection.each do |callback|
196
- callback.run_with_event event
50
+ def reinitialize_theatre!
51
+ @@framework_theatre.gracefully_stop! if defined? @@framework_theatre
52
+ rescue
53
+ # Recover and reinitalize
54
+ ensure
55
+ # TODO: Extract number of threads to use from AHN_CONFIG
56
+ @@framework_theatre = Theatre::Theatre.new
57
+ DEFAULT_FRAMEWORK_EVENT_NAMESPACES.each do |namespace|
58
+ register_namespace_name namespace
197
59
  end
60
+ return @@framework_theatre
198
61
  end
199
- end
200
-
201
- class AsynchronousEventCallbackRegistrar < EventCallbackRegistrar
202
62
 
203
- attr_reader :thread
204
- def initialize(namespace, &notified_on_new_callback)
205
- super
206
- @thread = AsynchronousEventCallbackRegistrar.new
63
+ def register_namespace_name(name)
64
+ framework_theatre.register_namespace_name name
207
65
  end
208
66
 
209
- def <<(event)
210
- threadsafe_callback_collection.each do |callback|
211
-
212
- end
67
+ def stop!
68
+ Events.trigger :shutdown
69
+ framework_theatre.graceful_stop!
70
+ framework_theatre.join
213
71
  end
214
72
 
215
- protected
216
-
217
- attr_reader :queue
218
-
219
- class AsynchronousEventHandlerThread < Thread
220
-
221
- def initialize(&block)
222
- @queue = Queue.new
223
- super do
224
- loop { block.call @queue.pop }
225
- end
226
- end
227
-
228
- def <<(event)
229
- @queue << event
230
- end
73
+ def register_callback(namespace, block_arg=nil, &method_block)
74
+ raise ArgumentError, "Cannot supply two blocks!" if block_arg && block_given?
75
+ block = method_block || block_arg
76
+ raise ArgumentError, "Must supply a callback!" unless block
231
77
 
232
- end
233
-
234
- end
235
-
236
- # A RegisteredEventCallback is stored away each time you call each() on an event namespace.
237
- # It keeps a copy of the namespace
238
- class RegisteredEventCallback
239
-
240
- attr_reader :registrar, :args, :block
241
- def initialize(registrar, *args, &block)
242
- raise ArgumentError, "Must supply a callback in the form of a block!" unless block_given?
243
- @registrar, @args, @block = registrar, args, block
244
- end
245
-
246
- def run_with_event(event)
247
- begin
248
- block.call event
249
- rescue => e
250
- indenter = "\n" + (" " * 5)
251
- ahn_log.events.error e.message + indenter + e.backtrace.join(indenter)
252
- end
253
- end
254
-
255
- def namespace
256
- registrar.namespace
78
+ framework_theatre.register_callback_at_namespace(namespace, block)
257
79
  end
258
80
 
259
81
  end
@@ -0,0 +1,203 @@
1
+ ##
2
+ # EventSocket is a small abstraction of TCPSocket which causes it to behave much like an EventMachine Connection object for
3
+ # the sake of better testability. The EventMachine Connection paradigm (as well as other networking libraries such as the
4
+ # Objective-C HTTP library) uses callbacks to signal different stages of a socket's lifecycle.
5
+ #
6
+ # A handler can be registered in one of two ways: through registrations on an object yielded by the constructor or
7
+ # pre-defined on the object given as a constructor parameter. Below is an example definition which uses the block way:
8
+ #
9
+ # EventSocket.new do |handler|
10
+ # def handler.receive_data(data)
11
+ # # Do something here
12
+ # end
13
+ # def handler.disconnected
14
+ # # Do something here
15
+ # end
16
+ # def handler.connected
17
+ # # Do something here
18
+ # end
19
+ # end
20
+ #
21
+ # Note: this is also a valid way of defining block callbacks:
22
+ #
23
+ # EventSocket.new do |handler|
24
+ # handler.receive_data { |data| do_something }
25
+ # handler.disconnected { do_something }
26
+ # handler.connected { do_something }
27
+ # end
28
+ #
29
+ # and here is an example of using a handler object:
30
+ #
31
+ # class MyCallbackHandler
32
+ # def receive_data(data) end
33
+ # def connected() end
34
+ # def disconnected() end
35
+ # end
36
+ # EventSocket.new(MyCallbackHandler.new)
37
+ #
38
+ # If you wish to ask the EventSocket what state it is in, you can call the Thread-safe EventSocket#state method. The
39
+ # supported states are:
40
+ #
41
+ # - :new
42
+ # - :connected
43
+ # - :stopped
44
+ # - :connection_dropped
45
+ #
46
+ # Note: the EventSocket's state will be changed before these callbacks are executed. For example, if your "connected"
47
+ # callback queried its own EventSocket for its state, it will have already transitioned to the connected() state.
48
+ #
49
+ # Warning: If an exception occurs in your EventSocket callbacks, they will be "eaten" and never bubbled up the call stack.
50
+ # You should always wrap your callbacks in a begin/rescue clause and handle exceptions explicitly.
51
+ #
52
+ require "thread"
53
+ require "socket"
54
+
55
+ class EventSocket
56
+
57
+ class << self
58
+
59
+ ##
60
+ # Creates and returns a connected EventSocket instance.
61
+ #
62
+ def connect(*args, &block)
63
+ instance = new(*args, &block)
64
+ instance.connect!
65
+ instance
66
+ end
67
+ end
68
+
69
+ MAX_CHUNK_SIZE = 256 * 1024
70
+
71
+ def initialize(host, port, handler=nil, &block)
72
+ raise ArgumentError, "Cannot supply both a handler object and a block" if handler && block_given?
73
+ raise ArgumentError, "Must supply either a handler object or a block" if !handler && !block_given?
74
+
75
+ @state_lock = Mutex.new
76
+ @host = host
77
+ @port = port
78
+
79
+ @state = :new
80
+ @handler = handler || new_handler_from_block(&block)
81
+ end
82
+
83
+ def state
84
+ @state_lock.synchronize { @state }
85
+ end
86
+
87
+ def connect!
88
+ @state_lock.synchronize do
89
+ if @state.equal? :connected
90
+ raise ConnectionError, "Already connected!"
91
+ else
92
+ @socket = TCPSocket.new(@host, @port)
93
+ @state = :connected
94
+ end
95
+ end
96
+ @handler.connected rescue nil
97
+ @reader_thread = spawn_reader_thread
98
+ self
99
+ rescue => error
100
+ @state = :failed
101
+ raise error
102
+ end
103
+
104
+ ##
105
+ # Thread-safe implementation of write.
106
+ #
107
+ # @param [String] data Data to write
108
+ #
109
+ def send_data(data)
110
+ # Note: TCPSocket#write is intrinsically Thread-safe
111
+ @socket.write data
112
+ rescue
113
+ connection_dropped!
114
+ end
115
+ ##
116
+ # Disconnects this EventSocket and sets the state to :stopped
117
+ #
118
+ def disconnect!
119
+ @state_lock.synchronize do
120
+ @socket.close rescue nil
121
+ @state = :stopped
122
+ end
123
+ end
124
+
125
+ ##
126
+ # Joins the Thread which reads data off the socket.
127
+ #
128
+ def join
129
+ @state_lock.synchronize do
130
+ if @state.equal? :connected
131
+ @reader_thread.join
132
+ else
133
+ nil
134
+ end
135
+ end
136
+ end
137
+
138
+ def receive_data(data)
139
+ @handler.receive_data(data)
140
+ end
141
+
142
+ protected
143
+
144
+ def connection_dropped!
145
+ @state_lock.synchronize do
146
+ unless @state.equal? :connection_dropped
147
+ @state = :connection_dropped
148
+ @handler.disconnected
149
+ end
150
+ end
151
+ end
152
+
153
+ def spawn_reader_thread
154
+ Thread.new(&method(:reader_loop))
155
+ end
156
+
157
+ def reader_loop
158
+ until state.equal? :stopped
159
+ data = @socket.readpartial(MAX_CHUNK_SIZE)
160
+ @handler.receive_data data
161
+ end
162
+ rescue EOFError
163
+ connection_dropped!
164
+ end
165
+
166
+ def new_handler_from_block(&handler_block)
167
+ handler = Object.new
168
+ handler.metaclass.send :attr_accessor, :set_callbacks
169
+ handler.set_callbacks = {:receive_data => false, :disconnected => false, :connected => false }
170
+
171
+ def handler.receive_data(&block)
172
+ self.metaclass.send(:remove_method, :receive_data)
173
+ self.metaclass.send(:define_method, :receive_data) { |data| block.call data }
174
+ set_callbacks[:receive_data] = true
175
+ end
176
+ def handler.connected(&block)
177
+ self.metaclass.send(:remove_method, :connected)
178
+ self.metaclass.send(:define_method, :connected) { block.call }
179
+ set_callbacks[:connected] = true
180
+ end
181
+ def handler.disconnected(&block)
182
+ self.metaclass.send(:remove_method, :disconnected)
183
+ self.metaclass.send(:define_method, :disconnected) { block.call }
184
+ set_callbacks[:disconnected] = true
185
+ end
186
+
187
+ def handler.singleton_method_added(name)
188
+ set_callbacks[name.to_sym] = true
189
+ end
190
+
191
+ yield handler
192
+
193
+ handler.set_callbacks.each_pair do |callback_name,was_set|
194
+ handler.send(callback_name) {} unless was_set
195
+ end
196
+
197
+ handler
198
+
199
+ end
200
+
201
+ class ConnectionError < Exception; end
202
+
203
+ end
@@ -0,0 +1,36 @@
1
+ require "thread"
2
+
3
+ class FutureResource
4
+
5
+ def initialize
6
+ @resource_lock = Monitor.new
7
+ @resource_value_blocker = @resource_lock.new_cond
8
+ end
9
+
10
+ def set_yet?
11
+ @resource_lock.synchronize { defined? @resource }
12
+ end
13
+
14
+ def resource
15
+ @resource_lock.synchronize do
16
+ @resource_value_blocker.wait unless defined? @resource
17
+ @resource
18
+ end
19
+ end
20
+
21
+ def resource=(resource)
22
+ @resource_lock.synchronize do
23
+ raise ResourceAlreadySetException if defined? @resource
24
+ @resource = resource
25
+ @resource_value_blocker.broadcast
26
+ @resource_value_blocker = nil # Don't really need it anymore.
27
+ end
28
+ end
29
+
30
+ class ResourceAlreadySetException < Exception
31
+ def initialize
32
+ super "Cannot set this resource twice!"
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,13 @@
1
+ class Numeric
2
+
3
+ def digit()
4
+ ahn_log.deprecation 'Please do not use Fixnum#digit() and Fixnum#digits() in the future! These will be deprecated soon'
5
+ self
6
+ end
7
+
8
+ def digits()
9
+ ahn_log.deprecation 'Please do not use Fixnum#digit() and Fixnum#digits() in the future! These will be deprecated soon'
10
+ self
11
+ end
12
+
13
+ end
@@ -0,0 +1,10 @@
1
+ module PseudoGuidGenerator
2
+ ##
3
+ # Generates a new 128-bit Globally Unique Identifier. It is a "pseudo" in that it does not adhere to the RFC which mandates
4
+ # that a certain section be reserved for a fragment of the NIC MAC address.
5
+ def new_guid(separator="-")
6
+ [8,4,4,4,12].map { |segment_length| String.random(segment_length) }.join(separator)
7
+ end
8
+ end
9
+
10
+ include PseudoGuidGenerator
@@ -1,5 +1,6 @@
1
1
  class Module
2
2
 
3
+ ##
3
4
  # In OOP, relationships between classes should be treated as *properties* of those classes. Often, in a complex OO
4
5
  # architecture, you'll end up with many relationships that intermingle in monolithic ways, blunting the effectiveness of
5
6
  # subclassing.
@@ -31,6 +32,7 @@ class Module
31
32
  # class HybridAutomobile < Automobile
32
33
  # relationship :battery => HybridBattery
33
34
  # end
35
+ #
34
36
  def relationships(relationship_mapping)
35
37
  relationship_mapping.each_pair do |class_name, class_object|
36
38
  define_method(class_name) { class_object }
@@ -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