carnivore 0.2.0 → 0.2.2
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.md +7 -0
- data/carnivore.gemspec +2 -0
- data/lib/carnivore/callback.rb +31 -15
- data/lib/carnivore/config.rb +22 -12
- data/lib/carnivore/container.rb +8 -0
- data/lib/carnivore/errors.rb +2 -0
- data/lib/carnivore/message.rb +15 -8
- data/lib/carnivore/runner.rb +8 -4
- data/lib/carnivore/source/test.rb +11 -3
- data/lib/carnivore/source.rb +181 -79
- data/lib/carnivore/source_container.rb +14 -4
- data/lib/carnivore/spec_helper.rb +65 -3
- data/lib/carnivore/supervisor.rb +40 -22
- data/lib/carnivore/utils/logging.rb +4 -0
- data/lib/carnivore/utils/message_registry.rb +16 -7
- data/lib/carnivore/utils/params.rb +9 -5
- data/lib/carnivore/utils/smash.rb +90 -0
- data/lib/carnivore/utils.rb +2 -0
- data/lib/carnivore/version.rb +3 -3
- data/lib/carnivore.rb +16 -2
- metadata +21 -4
- data/lib/carnivore/autoloader.rb +0 -13
data/lib/carnivore/source.rb
CHANGED
@@ -3,20 +3,24 @@ require 'celluloid'
|
|
3
3
|
require 'carnivore'
|
4
4
|
|
5
5
|
module Carnivore
|
6
|
+
# Message source
|
7
|
+
# @abstract
|
6
8
|
class Source
|
7
9
|
|
8
10
|
autoload :SourceContainer, 'carnivore/source_container'
|
9
11
|
|
10
12
|
class << self
|
11
13
|
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
14
|
+
# Builds a source container
|
15
|
+
#
|
16
|
+
# @param args [Hash] source configuration
|
17
|
+
# @option args [String, Symbol] :type type of source to build
|
18
|
+
# @option args [Hash] :args configuration hash for source initialization
|
19
|
+
# @return [SourceContainer]
|
16
20
|
def build(args={})
|
17
21
|
[:args, :type].each do |key|
|
18
22
|
unless(args.has_key?(key))
|
19
|
-
|
23
|
+
abort ArgumentError.new "Missing required parameter `:#{key}`"
|
20
24
|
end
|
21
25
|
end
|
22
26
|
require Source.require_path(args[:type]) || "carnivore/source/#{args[:type]}"
|
@@ -28,46 +32,56 @@ module Carnivore
|
|
28
32
|
inst
|
29
33
|
end
|
30
34
|
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
35
|
+
# Register a new source type
|
36
|
+
#
|
37
|
+
# @param type [Symbol] name of source type
|
38
|
+
# @param require_path [String] path to require when requested
|
39
|
+
# @return [TrueClass]
|
34
40
|
def provide(type, require_path)
|
35
|
-
@source_klass ||=
|
36
|
-
@source_klass[type
|
41
|
+
@source_klass ||= Smash.new
|
42
|
+
@source_klass[type] = require_path
|
37
43
|
true
|
38
44
|
end
|
39
45
|
|
40
|
-
#
|
41
|
-
#
|
46
|
+
# Registered path for given source type
|
47
|
+
#
|
48
|
+
# @param type [String, Symbol] name of source type
|
49
|
+
# @return [String, NilClass]
|
42
50
|
def require_path(type)
|
43
|
-
@source_klass ||=
|
44
|
-
@source_klass[type
|
51
|
+
@source_klass ||= Smash.new
|
52
|
+
@source_klass[type]
|
45
53
|
end
|
46
54
|
|
47
|
-
# name:: Name of source
|
48
|
-
# inst:: SourceContainer
|
49
55
|
# Register the container
|
56
|
+
#
|
57
|
+
# @param name [String, Symbol] name of source
|
58
|
+
# @param inst [SourceContainer]
|
59
|
+
# @return [TrueClass]
|
50
60
|
def register(name, inst)
|
51
|
-
@sources ||=
|
52
|
-
@sources[name
|
61
|
+
@sources ||= Smash.new
|
62
|
+
@sources[name] = inst
|
53
63
|
true
|
54
64
|
end
|
55
65
|
|
56
|
-
#
|
57
|
-
#
|
66
|
+
# Source container with given name
|
67
|
+
#
|
68
|
+
# @param name [String, Symbol] name of source
|
69
|
+
# @return [SourceContainer]
|
58
70
|
def source(name)
|
59
71
|
if(@sources && @sources[name.to_sym])
|
60
72
|
@sources[name.to_sym]
|
61
73
|
else
|
62
|
-
|
74
|
+
Celluloid.logger.error "Source lookup failed (name: #{name})"
|
75
|
+
abort KeyError.new("Requested named source is not registered: #{name}")
|
63
76
|
end
|
64
77
|
end
|
65
78
|
|
66
|
-
#
|
79
|
+
# @return [Array<SourceContainer>] registered source containers
|
67
80
|
def sources
|
68
81
|
@sources ? @sources.values : []
|
69
82
|
end
|
70
83
|
|
84
|
+
# Reset communication methods within class
|
71
85
|
def reset_comms!
|
72
86
|
self.class_eval do
|
73
87
|
unless(method_defined?(:reset_communications?))
|
@@ -84,25 +98,56 @@ module Carnivore
|
|
84
98
|
|
85
99
|
include Celluloid
|
86
100
|
include Utils::Logging
|
101
|
+
# @!parse include Carnivore::Utils::Logging
|
87
102
|
|
103
|
+
finalizer :teardown_cleanup
|
104
|
+
|
105
|
+
# @return [String, Symbol] name of source
|
88
106
|
attr_reader :name
|
107
|
+
# @return [Array<Callback>] registered callbacks
|
89
108
|
attr_reader :callbacks
|
109
|
+
# @return [TrueClass, FalseClass] auto confirm received messages
|
90
110
|
attr_reader :auto_confirm
|
111
|
+
# @return [TrueClass, FalseClass] start source processing on initialization
|
91
112
|
attr_reader :auto_process
|
113
|
+
# @return [TrueClass, FalseClass] message processing control switch
|
92
114
|
attr_reader :run_process
|
115
|
+
# @return [Carnivore::Supervisor] supervisor maintaining callback instances
|
93
116
|
attr_reader :callback_supervisor
|
117
|
+
# @return [Hash] registry of processed messages
|
94
118
|
attr_reader :message_registry
|
119
|
+
# @return [Queue] local loop message queue
|
95
120
|
attr_reader :message_loop
|
121
|
+
# @return [Queue] remote message queue
|
122
|
+
attr_reader :message_remote
|
123
|
+
# @return [TrueClass, FalseClass] currently processing a message
|
96
124
|
attr_reader :processing
|
97
125
|
|
126
|
+
# Create new Source
|
127
|
+
#
|
128
|
+
# @param args [Hash]
|
129
|
+
# @option args [String, Symbol] :name name of source
|
130
|
+
# @option args [TrueClass, FalseClass] :auto_process start processing on initialization
|
131
|
+
# @option args [TrueClass, FalseClass] :auto_confirm confirm messages automatically on receive
|
132
|
+
# @option args [Proc] :orphan_callback execute block when no callbacks are valid for message
|
133
|
+
# @option args [TrueClass, FalseClass] :prevent_duplicates setup and use message registry
|
134
|
+
# @option args [Array<Callback>] :callbacks callbacks to register on this source
|
98
135
|
def initialize(args={})
|
136
|
+
@args = Smash.new(args)
|
99
137
|
@callbacks = []
|
100
|
-
@message_loop =
|
138
|
+
@message_loop = Queue.new
|
139
|
+
@message_remote = Queue.new
|
101
140
|
@callback_names = {}
|
102
|
-
@auto_process = args.fetch(:auto_process, true)
|
141
|
+
@auto_process = !!args.fetch(:auto_process, true)
|
103
142
|
@run_process = true
|
104
143
|
@auto_confirm = !!args[:auto_confirm]
|
105
144
|
@callback_supervisor = Carnivore::Supervisor.create!.last
|
145
|
+
if(args[:orphan_callback])
|
146
|
+
unless(args[:orphan_callback].is_a?(Proc))
|
147
|
+
raise TypeError.new("Expected `Proc` type for `orphan_callback` but received `#{args[:orphan_callback].class}`")
|
148
|
+
end
|
149
|
+
define_singleton_method(:orphan_callback, &args[:orphan_callback])
|
150
|
+
end
|
106
151
|
if(args[:prevent_duplicates])
|
107
152
|
init_registry
|
108
153
|
end
|
@@ -124,61 +169,70 @@ module Carnivore
|
|
124
169
|
end
|
125
170
|
|
126
171
|
# Ensure we cleanup our internal supervisor before bailing out
|
127
|
-
def
|
172
|
+
def teardown_cleanup
|
173
|
+
warn 'Termination request received. Tearing down!'
|
128
174
|
callback_supervisor.terminate
|
129
|
-
super
|
130
175
|
end
|
131
176
|
|
132
|
-
#
|
177
|
+
# @return [TrueClass, FalseClass] automatic message confirmation enabled
|
133
178
|
def auto_confirm?
|
134
179
|
@auto_confirm
|
135
180
|
end
|
136
181
|
|
137
|
-
#
|
182
|
+
# @return [String] inspection formatted string
|
138
183
|
def inspect
|
139
184
|
"<#{self.class.name}:#{object_id} @name=#{name} @callbacks=#{Hash[*callbacks.map{|k,v| [k,v.object_id]}.flatten]}>"
|
140
185
|
end
|
141
186
|
|
142
|
-
#
|
187
|
+
# @return [String] stringified instance
|
143
188
|
def to_s
|
144
189
|
"<#{self.class.name}:#{object_id} @name=#{name}>"
|
145
190
|
end
|
146
191
|
|
147
|
-
#
|
148
|
-
#
|
192
|
+
# Setup hook for source requiring customized setup
|
193
|
+
#
|
194
|
+
# @param args [Hash] initialization hash
|
149
195
|
def setup(args={})
|
150
196
|
debug 'No custom setup declared'
|
151
197
|
end
|
152
198
|
|
153
|
-
#
|
154
|
-
#
|
155
|
-
|
199
|
+
# Connection hook for sources requiring customized connect
|
200
|
+
#
|
201
|
+
# @param args [Hash] initialization hash
|
202
|
+
def connect
|
156
203
|
debug 'No custom connect declared'
|
157
204
|
end
|
158
205
|
|
159
|
-
#
|
160
|
-
#
|
206
|
+
# Receive messages from source
|
207
|
+
# @abstract
|
208
|
+
#
|
209
|
+
# @param n [Integer] number of messages
|
210
|
+
# @return [Object, Array<Object>] payload or array of payloads
|
161
211
|
def receive(n=1)
|
162
|
-
raise
|
212
|
+
raise NotImplementedError.new('Abstract method not valid for runtime')
|
163
213
|
end
|
164
214
|
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
#
|
215
|
+
# Send payload to source
|
216
|
+
#
|
217
|
+
# @param message [Object] payload
|
218
|
+
# @param original_message [Carnviore::Message] original message if reply to extract optional metadata
|
219
|
+
# @param args [Hash] optional extra arguments
|
169
220
|
def transmit(message, original_message=nil, args={})
|
170
|
-
raise
|
221
|
+
raise NotImplemented.new('Abstract method not valid for runtime')
|
171
222
|
end
|
172
223
|
|
173
|
-
# message:: Carnivore::Message
|
174
224
|
# Confirm receipt of the message on source
|
225
|
+
#
|
226
|
+
# @param message [Carnivore::Message]
|
175
227
|
def confirm(message)
|
176
228
|
debug 'No custom confirm declared'
|
177
229
|
end
|
178
230
|
|
179
|
-
# callback_name:: Name of callback
|
180
|
-
# block_or_class:: Carnivore::Callback class or a block
|
181
231
|
# Adds the given callback to the source for message processing
|
232
|
+
#
|
233
|
+
# @param callback_name [String, Symbol] name of callback
|
234
|
+
# @param block_or_class [Carnivore::Callback, Proc]
|
235
|
+
# @return [self]
|
182
236
|
def add_callback(callback_name, block_or_class)
|
183
237
|
name = "#{self.name}:#{callback_name}"
|
184
238
|
if(block_or_class.is_a?(Class))
|
@@ -188,32 +242,36 @@ module Carnivore
|
|
188
242
|
return self
|
189
243
|
elsif(size == 1)
|
190
244
|
debug "Adding callback class (#{block_or_class}) under supervision. Name: #{callback_name(name)}"
|
191
|
-
callback_supervisor.supervise_as callback_name(name), block_or_class, name
|
245
|
+
callback_supervisor.supervise_as callback_name(name), block_or_class, name, current_actor
|
192
246
|
else
|
193
247
|
debug "Adding callback class (#{block_or_class}) under supervision pool (#{size} workers). Name: #{callback_name(name)}"
|
194
|
-
callback_supervisor.pool block_or_class, as: callback_name(name), size: size, args: [name]
|
248
|
+
callback_supervisor.pool block_or_class, as: callback_name(name), size: size, args: [name, current_actor]
|
195
249
|
end
|
196
250
|
else
|
197
251
|
debug "Adding custom callback class from block (#{block_or_class}) under supervision. Name: #{callback_name(name)}"
|
198
|
-
callback_supervisor.supervise_as callback_name(name), Callback, name, block_or_class
|
252
|
+
callback_supervisor.supervise_as callback_name(name), Callback, name, current_actor, block_or_class
|
199
253
|
end
|
200
254
|
callbacks.push(name).uniq!
|
201
255
|
self
|
202
256
|
end
|
203
257
|
|
204
|
-
# name:: Name of callback
|
205
258
|
# Remove the named callback from the source
|
259
|
+
#
|
260
|
+
# @param name [String, Symbol]
|
261
|
+
# @return [self]
|
206
262
|
def remove_callback(name)
|
207
263
|
unless(@callbacks.include?(callback_name(name)))
|
208
|
-
|
264
|
+
abort NameError.new("Failed to locate callback named: #{name}")
|
209
265
|
end
|
210
266
|
actors[callback_name(name)].terminate
|
211
267
|
@callbacks.delete(name)
|
212
268
|
self
|
213
269
|
end
|
214
270
|
|
215
|
-
# name:: Name of callback
|
216
271
|
# Returns namespaced name (prefixed with source name and instance id)
|
272
|
+
#
|
273
|
+
# @param name [String, Symbol] name of callback
|
274
|
+
# @return [Carnivore::Callback, NilClass]
|
217
275
|
def callback_name(name)
|
218
276
|
unless(@callback_names[name])
|
219
277
|
@callback_names[name] = [@name, self.object_id, name].join(':').to_sym
|
@@ -221,22 +279,28 @@ module Carnivore
|
|
221
279
|
@callback_names[name]
|
222
280
|
end
|
223
281
|
|
224
|
-
#
|
225
|
-
#
|
282
|
+
# Create new Message from received payload
|
283
|
+
#
|
284
|
+
# @param msg [Object] received payload
|
285
|
+
# @return [Carnivore::Message]
|
226
286
|
def format(msg)
|
227
287
|
actor = Carnivore::Supervisor.supervisor[name]
|
228
288
|
if(actor)
|
229
289
|
Message.new(
|
230
290
|
:message => msg,
|
231
|
-
:source => actor
|
291
|
+
:source => actor.current_actor
|
232
292
|
)
|
233
293
|
else
|
234
294
|
abort "Failed to locate self in registry (#{name})"
|
235
295
|
end
|
236
296
|
end
|
237
297
|
|
238
|
-
#
|
239
|
-
#
|
298
|
+
# Validate message is allowed before processing. This is currently
|
299
|
+
# only used when the message registry is enabled to prevent
|
300
|
+
# duplicate message processing.
|
301
|
+
#
|
302
|
+
# @param m [Carnivore::Message]
|
303
|
+
# @return [TrueClass, FalseClass]
|
240
304
|
def valid_message?(m)
|
241
305
|
if(message_registry)
|
242
306
|
if(message_registry.valid?(m))
|
@@ -250,66 +314,104 @@ module Carnivore
|
|
250
314
|
end
|
251
315
|
end
|
252
316
|
|
253
|
-
#
|
254
|
-
#
|
317
|
+
# Process incoming messages from this source
|
318
|
+
#
|
319
|
+
# @param args [Object] list of arguments
|
320
|
+
# @return [TrueClass]
|
255
321
|
def process(*args)
|
256
322
|
begin
|
257
323
|
while(run_process && !callbacks.empty?)
|
258
324
|
@processing = true
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
msgs = loop_msgs.value
|
263
|
-
loop_msgs = nil
|
264
|
-
elsif(remote_msgs.ready?)
|
265
|
-
msgs = remote_msgs.value
|
266
|
-
remote_msgs = nil
|
267
|
-
else
|
268
|
-
msgs = []
|
325
|
+
async.receive_messages
|
326
|
+
if(message_loop.empty? && message_remote.empty?)
|
327
|
+
wait(:messages_available)
|
269
328
|
end
|
329
|
+
msgs = []
|
330
|
+
msgs.push message_loop.pop unless message_loop.empty?
|
331
|
+
msgs.push message_remote.pop unless message_remote.empty?
|
270
332
|
msgs = [msgs].flatten.compact.map do |m|
|
271
333
|
if(valid_message?(m))
|
272
334
|
format(m)
|
273
335
|
end
|
274
336
|
end.compact
|
275
337
|
msgs.each do |msg|
|
276
|
-
|
338
|
+
if(respond_to?(:orphan_callback))
|
339
|
+
valid_callbacks = callbacks.find_all do |name|
|
340
|
+
callback_supervisor[callback_name(name)].valid?(msg)
|
341
|
+
end
|
342
|
+
else
|
343
|
+
valid_callbacks = callbacks
|
344
|
+
end
|
345
|
+
valid_callbacks.each do |name|
|
277
346
|
debug "Dispatching message<#{msg[:message].object_id}> to callback<#{name} (#{callback_name(name)})>"
|
278
347
|
callback_supervisor[callback_name(name)].async.call(msg)
|
279
348
|
end
|
349
|
+
if(valid_callbacks.empty?)
|
350
|
+
warn "Received message was not processed through any callbacks on this source: #{msg}"
|
351
|
+
orphan_callback(current_actor, msg) if respond_to?(:orphan_callback)
|
352
|
+
end
|
280
353
|
end
|
281
|
-
sleep(1) if msgs.empty?
|
282
354
|
end
|
283
355
|
ensure
|
284
356
|
@processing = false
|
285
357
|
end
|
358
|
+
true
|
286
359
|
end
|
287
360
|
|
288
|
-
#
|
289
|
-
#
|
361
|
+
# Receive messages from source
|
362
|
+
# @return [TrueClass]
|
363
|
+
def receive_messages
|
364
|
+
loop do
|
365
|
+
message_remote.push receive
|
366
|
+
signal(:messages_available)
|
367
|
+
end
|
368
|
+
true
|
369
|
+
end
|
370
|
+
|
371
|
+
# Get received message on local loopback
|
372
|
+
#
|
373
|
+
# @param args [Object] argument list (unused)
|
374
|
+
# @return [Carnivore::Message, NilClass]
|
290
375
|
def loop_receive(*args)
|
291
|
-
|
376
|
+
message_loop.shift
|
292
377
|
end
|
293
378
|
|
294
|
-
# message:: Message for delivery
|
295
|
-
# original_message:: unused
|
296
|
-
# args:: unused
|
297
379
|
# Push message onto internal loop queue
|
380
|
+
#
|
381
|
+
# @param message [Carnivore::Message]
|
382
|
+
# @param original_message [Object] unused
|
383
|
+
# @param args [Hash] unused
|
384
|
+
# @return [TrueClass]
|
298
385
|
def loop_transmit(message, original_message=nil, args={})
|
299
|
-
|
386
|
+
message_loop.push message
|
387
|
+
signal(:messages_available)
|
388
|
+
true
|
300
389
|
end
|
301
390
|
|
302
|
-
# args:: transmit args
|
303
391
|
# Send to local loop if processing otherwise use regular transmit
|
392
|
+
#
|
393
|
+
# @param args [Object] argument list
|
394
|
+
# @return [TrueClass]
|
304
395
|
def _transmit(*args)
|
305
|
-
if(processing)
|
396
|
+
if(loop_enabled? && processing)
|
306
397
|
loop_transmit(*args)
|
307
398
|
else
|
308
399
|
custom_transmit(*args)
|
309
400
|
end
|
401
|
+
true
|
402
|
+
end
|
403
|
+
|
404
|
+
# Local message loopback is enabled. Custom sources should
|
405
|
+
# override this method to allow loopback delivery if desired
|
406
|
+
#
|
407
|
+
# @return [TrueClass, FalseClass]
|
408
|
+
def loop_enabled?
|
409
|
+
false
|
310
410
|
end
|
311
411
|
|
312
412
|
# Load and initialize the message registry
|
413
|
+
#
|
414
|
+
# @return [MessageRegistry] new registry
|
313
415
|
def init_registry
|
314
416
|
require 'carnivore/message_registry'
|
315
417
|
@message_registry = MessageRegistry.new
|
@@ -7,20 +7,30 @@ module Carnivore
|
|
7
7
|
# occur prior to the supervisor actually starting the sources
|
8
8
|
class SourceContainer
|
9
9
|
|
10
|
+
# @return [Class] class of Source
|
10
11
|
attr_reader :klass
|
12
|
+
# @return [Hash] configuration hash for Source
|
11
13
|
attr_reader :source_hash
|
12
14
|
|
13
|
-
#
|
14
|
-
#
|
15
|
+
# Create a new source container
|
16
|
+
#
|
17
|
+
# @param class_name [Class] class of Source
|
18
|
+
# @param args [Hash] configuration hash for source
|
15
19
|
def initialize(class_name, args={})
|
16
20
|
@klass = class_name
|
17
|
-
@source_hash = args || {}
|
18
|
-
@source_hash[:callbacks] =
|
21
|
+
@source_hash = Smash.new(args || {})
|
22
|
+
@source_hash[:callbacks] = Smash.new
|
19
23
|
end
|
20
24
|
|
21
25
|
# name:: Name of callback
|
22
26
|
# klass:: Class of callback (optional)
|
23
27
|
# Add a callback to a source via Class or block
|
28
|
+
#
|
29
|
+
# @param name [String, Symbol] name of callback
|
30
|
+
# @param klass [Class] class of callback
|
31
|
+
# @yield callback block
|
32
|
+
# @yieldparam message [Carnivore::Message] message to process
|
33
|
+
# @return [Class, Proc] callback registered
|
24
34
|
def add_callback(name, klass=nil, &block)
|
25
35
|
@source_hash[:callbacks][name] = klass || block
|
26
36
|
end
|
@@ -19,18 +19,36 @@ MiniTest::Spec.before do
|
|
19
19
|
end
|
20
20
|
|
21
21
|
# Simple waiter method to stall testing
|
22
|
+
#
|
23
|
+
# @param name [String, Symbol] fetch wait time from environment variable
|
24
|
+
# @return [Numeric] seconds sleeping
|
22
25
|
def source_wait(name='wait')
|
23
|
-
|
26
|
+
total = ENV.fetch("CARNIVORE_SOURCE_#{name.to_s.upcase}", 1.0).to_f
|
27
|
+
if(block_given?)
|
28
|
+
elapsed = 0.0
|
29
|
+
until(yield || elapsed >= total)
|
30
|
+
sleep(0.1)
|
31
|
+
elapsed += 0.1
|
32
|
+
end
|
33
|
+
elapsed
|
34
|
+
else
|
35
|
+
sleep(total)
|
36
|
+
total
|
37
|
+
end
|
24
38
|
end
|
25
39
|
|
26
|
-
#
|
40
|
+
# Dummy message store used for testing
|
27
41
|
class MessageStore
|
28
42
|
class << self
|
29
43
|
|
44
|
+
# Initialize message storage
|
45
|
+
#
|
46
|
+
# @return [Array]
|
30
47
|
def init
|
31
48
|
@messages = []
|
32
49
|
end
|
33
50
|
|
51
|
+
# @return [Array] messages
|
34
52
|
def messages
|
35
53
|
@messages
|
36
54
|
end
|
@@ -38,21 +56,65 @@ class MessageStore
|
|
38
56
|
end
|
39
57
|
end
|
40
58
|
|
41
|
-
# dummy source to hold final tranmission and stuff payload in store
|
42
59
|
module Carnivore
|
43
60
|
class Source
|
61
|
+
# Dummy source for testing used to capture payloads for inspection
|
44
62
|
class Spec < Source
|
63
|
+
|
64
|
+
# @return [Array] messages confirmed
|
65
|
+
attr_reader :confirmed
|
66
|
+
|
67
|
+
# Creates new spec source
|
68
|
+
#
|
69
|
+
# @param args [Object] argument list (passed to Source)
|
70
|
+
# @yield source block (passed to Source)
|
71
|
+
def initialize(*args, &block)
|
72
|
+
super
|
73
|
+
@confirmed = []
|
74
|
+
end
|
75
|
+
|
76
|
+
# Setup the message store for payload storage
|
77
|
+
#
|
78
|
+
# @return [Array] message storage
|
45
79
|
def setup(*args)
|
46
80
|
MessageStore.init
|
47
81
|
end
|
48
82
|
|
83
|
+
# Dummy receiver
|
49
84
|
def receive(*args)
|
50
85
|
wait(:forever)
|
51
86
|
end
|
52
87
|
|
88
|
+
# Capture messages transmitted
|
89
|
+
#
|
90
|
+
# @param args [Object] argument list
|
91
|
+
# @return [TrueClass]
|
53
92
|
def transmit(*args)
|
54
93
|
MessageStore.messages << args.first
|
94
|
+
true
|
55
95
|
end
|
96
|
+
|
97
|
+
# Format the message
|
98
|
+
#
|
99
|
+
# @param msg [Object] message payload
|
100
|
+
# @return [Carnivore::Message]
|
101
|
+
def format(msg)
|
102
|
+
Message.new(
|
103
|
+
:message => msg,
|
104
|
+
:source => self
|
105
|
+
)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Capture confirmed messages
|
109
|
+
#
|
110
|
+
# @param payload [Object] payload of message
|
111
|
+
# @param args [Object] argument list (unused)
|
112
|
+
# @return [TrueClass]
|
113
|
+
def confirm(payload, *args)
|
114
|
+
confirmed << payload
|
115
|
+
true
|
116
|
+
end
|
117
|
+
|
56
118
|
end
|
57
119
|
end
|
58
120
|
end
|
data/lib/carnivore/supervisor.rb
CHANGED
@@ -6,27 +6,51 @@ module Carnivore
|
|
6
6
|
|
7
7
|
class << self
|
8
8
|
|
9
|
-
attr_reader :registry, :supervisor
|
10
|
-
|
11
9
|
# Build a new supervisor
|
10
|
+
#
|
11
|
+
# @return [Carinvore::Supervisor]
|
12
12
|
def build!
|
13
|
-
|
14
|
-
|
13
|
+
_, s = create!
|
14
|
+
supervisor(s)
|
15
15
|
end
|
16
16
|
|
17
17
|
# Create a new supervisor
|
18
|
-
#
|
18
|
+
#
|
19
|
+
# @return [Array<[Celluloid::Registry, Carnivore::Supervisor]>]
|
19
20
|
def create!
|
20
21
|
registry = Celluloid::Registry.new
|
21
22
|
[registry, run!(registry)]
|
22
23
|
end
|
23
24
|
|
24
|
-
#
|
25
|
+
# Get/set the default supervisor
|
26
|
+
#
|
27
|
+
# @param sup [Carnivore::Supervisor]
|
28
|
+
# @return [Carnivore::Supervisor]
|
29
|
+
def supervisor(sup=nil)
|
30
|
+
if(sup)
|
31
|
+
Celluloid::Actor[:carnivore_supervisor] = sup
|
32
|
+
end
|
33
|
+
Celluloid::Actor[:carnivore_supervisor]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get the registry of the default supervisor
|
37
|
+
#
|
38
|
+
# @return [Celluloid::Registry, NilClass]
|
39
|
+
def registry
|
40
|
+
if(supervisor)
|
41
|
+
supervisor.registry
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Destroy the registered default supervisor
|
46
|
+
#
|
47
|
+
# @return [TrueClass]
|
25
48
|
def terminate!
|
26
49
|
if(supervisor)
|
27
50
|
begin
|
28
51
|
supervisor.terminate
|
29
|
-
rescue Celluloid::DeadActorError
|
52
|
+
rescue Celluloid::DeadActorError => e
|
53
|
+
Celluloid::Logger.warn "Default supervisor is already in dead state (#{e.class}: #{e})"
|
30
54
|
end
|
31
55
|
@supervisor = nil
|
32
56
|
@registry = nil
|
@@ -36,21 +60,15 @@ module Carnivore
|
|
36
60
|
|
37
61
|
end
|
38
62
|
|
39
|
-
#
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
unless(instance)
|
49
|
-
Celluloid::Logger.error "Actor restart failed to make it available in the registry! (#{name})"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
instance
|
63
|
+
# @return [Celluloid::Registry]
|
64
|
+
attr_reader :registry
|
65
|
+
|
66
|
+
# Fetch actor from registry
|
67
|
+
#
|
68
|
+
# @param k [String, Symbol] identifier
|
69
|
+
# @return [Celluloid::Actor, NilClass]
|
70
|
+
def [](k)
|
71
|
+
registry[k]
|
54
72
|
end
|
55
73
|
|
56
74
|
end
|