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.
- data/CHANGELOG +8 -2
- data/EVENTS +11 -0
- data/Rakefile +92 -26
- data/adhearsion.gemspec +131 -23
- data/app_generators/ahn/ahn_generator.rb +21 -7
- data/app_generators/ahn/templates/.ahnrc +10 -4
- data/app_generators/ahn/templates/Rakefile +7 -2
- 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/simon_game/{lib/simon_game.rb → simon_game.rb} +14 -19
- data/app_generators/ahn/templates/config/startup.rb +3 -6
- data/app_generators/ahn/templates/dialplan.rb +2 -3
- data/app_generators/ahn/templates/events.rb +32 -6
- data/bin/jahn +10 -0
- data/examples/asterisk_manager_interface/standalone.rb +51 -0
- data/lib/adhearsion/cli.rb +140 -23
- data/lib/adhearsion/component_manager/component_tester.rb +55 -0
- data/lib/adhearsion/component_manager/spec_framework.rb +24 -0
- data/lib/adhearsion/component_manager.rb +169 -238
- data/lib/adhearsion/events_support.rb +59 -237
- data/lib/adhearsion/{core_extensions → foundation}/all.rb +0 -0
- data/lib/adhearsion/{blank_slate.rb → foundation/blank_slate.rb} +0 -0
- data/lib/adhearsion/{core_extensions → foundation}/custom_daemonizer.rb +0 -0
- data/lib/adhearsion/foundation/event_socket.rb +203 -0
- data/lib/adhearsion/foundation/future_resource.rb +36 -0
- data/lib/adhearsion/{core_extensions → foundation}/global.rb +0 -0
- data/lib/adhearsion/{core_extensions → foundation}/metaprogramming.rb +0 -0
- data/lib/adhearsion/foundation/numeric.rb +13 -0
- data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
- data/lib/adhearsion/{core_extensions → foundation}/relationship_properties.rb +2 -0
- data/lib/adhearsion/foundation/string.rb +26 -0
- data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
- data/lib/adhearsion/{core_extensions → foundation}/thread_safety.rb +0 -0
- data/lib/adhearsion/host_definitions.rb +5 -1
- data/lib/adhearsion/initializer/asterisk.rb +33 -11
- data/lib/adhearsion/initializer/configuration.rb +58 -6
- data/lib/adhearsion/initializer/database.rb +3 -46
- data/lib/adhearsion/initializer/drb.rb +9 -3
- data/lib/adhearsion/initializer/freeswitch.rb +3 -3
- data/lib/adhearsion/initializer/rails.rb +1 -1
- data/lib/adhearsion/initializer.rb +213 -87
- data/lib/adhearsion/tasks/deprecations.rb +59 -0
- data/lib/adhearsion/tasks.rb +2 -1
- data/lib/adhearsion/version.rb +3 -3
- data/lib/adhearsion/voip/asterisk/agi_server.rb +6 -6
- data/lib/adhearsion/voip/asterisk/commands.rb +100 -2
- 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/super_manager.rb +19 -0
- data/lib/adhearsion/voip/asterisk.rb +1 -8
- data/lib/adhearsion/voip/call.rb +5 -1
- data/lib/adhearsion/voip/dial_plan.rb +74 -61
- data/lib/adhearsion/voip/dsl/dialing_dsl.rb +1 -1
- data/lib/adhearsion/voip/dsl/dialplan/parser.rb +2 -6
- data/lib/adhearsion/voip/dsl/numerical_string.rb +2 -2
- data/lib/adhearsion/voip/freeswitch/oes_server.rb +2 -2
- data/lib/adhearsion.rb +16 -11
- 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 +60 -147
- data/Manifest.txt +0 -151
- data/README.txt +0 -5
- data/ahn_generators/component/USAGE +0 -5
- data/ahn_generators/component/component_generator.rb +0 -57
- data/ahn_generators/component/templates/configuration.rb +0 -0
- data/ahn_generators/component/templates/lib/lib.rb.erb +0 -3
- data/ahn_generators/component/templates/test/test.rb.erb +0 -12
- data/ahn_generators/component/templates/test/test_helper.rb +0 -14
- data/app_generators/ahn/templates/components/simon_game/configuration.rb +0 -0
- data/app_generators/ahn/templates/components/simon_game/test/test_helper.rb +0 -14
- data/app_generators/ahn/templates/components/simon_game/test/test_simon_game.rb +0 -31
- data/lib/adhearsion/core_extensions/array.rb +0 -0
- data/lib/adhearsion/core_extensions/guid.rb +0 -5
- data/lib/adhearsion/core_extensions/hash.rb +0 -0
- data/lib/adhearsion/core_extensions/numeric.rb +0 -4
- data/lib/adhearsion/core_extensions/proc.rb +0 -0
- data/lib/adhearsion/core_extensions/pseudo_uuid.rb +0 -11
- data/lib/adhearsion/core_extensions/publishable.rb +0 -73
- data/lib/adhearsion/core_extensions/string.rb +0 -26
- data/lib/adhearsion/core_extensions/thread.rb +0 -13
- data/lib/adhearsion/core_extensions/time.rb +0 -0
- data/lib/adhearsion/distributed/gateways/dbus_gateway.rb +0 -0
- data/lib/adhearsion/distributed/gateways/osa_gateway.rb +0 -0
- data/lib/adhearsion/distributed/gateways/rest_gateway.rb +0 -9
- data/lib/adhearsion/distributed/gateways/soap_gateway.rb +0 -9
- data/lib/adhearsion/distributed/gateways/xmlrpc_gateway.rb +0 -9
- data/lib/adhearsion/distributed/peer_finder.rb +0 -0
- data/lib/adhearsion/distributed/remote_cli.rb +0 -0
- data/lib/adhearsion/hooks.rb +0 -57
- data/lib/adhearsion/initializer/paths.rb +0 -55
- data/lib/adhearsion/voip/asterisk/ami/actions.rb +0 -238
- data/lib/adhearsion/voip/asterisk/ami/machine.rb +0 -871
- data/lib/adhearsion/voip/asterisk/ami/machine.rl +0 -109
- data/lib/adhearsion/voip/asterisk/ami/parser.rb +0 -262
- data/lib/adhearsion/voip/asterisk/ami.rb +0 -147
- data/spec/fixtures/dialplan.rb +0 -3
- data/spec/initializer/test_configuration.rb +0 -267
- data/spec/initializer/test_loading.rb +0 -162
- data/spec/initializer/test_paths.rb +0 -43
- data/spec/sample.rb +0 -9
- data/spec/silence.rb +0 -10
- data/spec/test_ahn_command.rb +0 -149
- data/spec/test_code_quality.rb +0 -87
- data/spec/test_component_manager.rb +0 -97
- data/spec/test_constants.rb +0 -8
- data/spec/test_drb.rb +0 -104
- data/spec/test_events.rb +0 -136
- data/spec/test_helper.rb +0 -106
- data/spec/test_hooks.rb +0 -15
- data/spec/test_host_definitions.rb +0 -79
- data/spec/test_initialization.rb +0 -124
- data/spec/test_logging.rb +0 -80
- data/spec/test_relationship_properties.rb +0 -54
- data/spec/voip/asterisk/ami_response_definitions.rb +0 -23
- data/spec/voip/asterisk/config_file_generators/test_agents.rb +0 -253
- data/spec/voip/asterisk/config_file_generators/test_queues.rb +0 -325
- data/spec/voip/asterisk/config_file_generators/test_voicemail.rb +0 -306
- data/spec/voip/asterisk/menu_command/test_calculated_match.rb +0 -111
- data/spec/voip/asterisk/menu_command/test_matchers.rb +0 -98
- data/spec/voip/asterisk/mock_ami_server.rb +0 -176
- data/spec/voip/asterisk/test_agi_server.rb +0 -453
- data/spec/voip/asterisk/test_ami.rb +0 -227
- data/spec/voip/asterisk/test_commands.rb +0 -2006
- data/spec/voip/asterisk/test_config_manager.rb +0 -129
- data/spec/voip/dsl/dispatcher_spec_helper.rb +0 -45
- data/spec/voip/dsl/test_dialing_dsl.rb +0 -268
- data/spec/voip/dsl/test_dispatcher.rb +0 -82
- data/spec/voip/dsl/test_parser.rb +0 -87
- data/spec/voip/freeswitch/test_basic_connection_manager.rb +0 -39
- data/spec/voip/freeswitch/test_inbound_connection_manager.rb +0 -39
- data/spec/voip/freeswitch/test_oes_server.rb +0 -9
- data/spec/voip/test_call_routing.rb +0 -127
- data/spec/voip/test_dialplan_manager.rb +0 -442
- data/spec/voip/test_numerical_string.rb +0 -48
- data/spec/voip/test_phone_number.rb +0 -36
- data/test/test_ahn_generator.rb +0 -59
- data/test/test_component_generator.rb +0 -52
- data/test/test_generator_helper.rb +0 -20
@@ -1,259 +1,81 @@
|
|
1
|
-
|
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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
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, ¬ified_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
|
174
|
-
|
38
|
+
def framework_theatre
|
39
|
+
defined?(@@framework_theatre) ? @@framework_theatre : reinitialize_theatre!
|
175
40
|
end
|
176
41
|
|
177
|
-
def
|
178
|
-
|
42
|
+
def trigger(*args)
|
43
|
+
framework_theatre.trigger(*args)
|
179
44
|
end
|
180
45
|
|
181
|
-
|
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
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
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
|
-
|
204
|
-
|
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
|
210
|
-
|
211
|
-
|
212
|
-
|
67
|
+
def stop!
|
68
|
+
Events.trigger :shutdown
|
69
|
+
framework_theatre.graceful_stop!
|
70
|
+
framework_theatre.join
|
213
71
|
end
|
214
72
|
|
215
|
-
|
216
|
-
|
217
|
-
|
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
|
-
|
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
|
File without changes
|
File without changes
|
File without changes
|
@@ -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
|
File without changes
|
File without changes
|
@@ -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
|