kulesa-celluloid 0.10.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +116 -0
- data/lib/celluloid/actor.rb +279 -0
- data/lib/celluloid/actor_proxy.rb +95 -0
- data/lib/celluloid/calls.rb +105 -0
- data/lib/celluloid/core_ext.rb +25 -0
- data/lib/celluloid/cpu_counter.rb +16 -0
- data/lib/celluloid/events.rb +26 -0
- data/lib/celluloid/fiber.rb +32 -0
- data/lib/celluloid/fsm.rb +151 -0
- data/lib/celluloid/future.rb +110 -0
- data/lib/celluloid/group.rb +90 -0
- data/lib/celluloid/internal_pool.rb +62 -0
- data/lib/celluloid/links.rb +61 -0
- data/lib/celluloid/logger.rb +53 -0
- data/lib/celluloid/mailbox.rb +134 -0
- data/lib/celluloid/pool.rb +105 -0
- data/lib/celluloid/receivers.rb +70 -0
- data/lib/celluloid/registry.rb +35 -0
- data/lib/celluloid/responses.rb +26 -0
- data/lib/celluloid/rspec.rb +2 -0
- data/lib/celluloid/signals.rb +51 -0
- data/lib/celluloid/supervisor.rb +69 -0
- data/lib/celluloid/task.rb +81 -0
- data/lib/celluloid/thread_handle.rb +35 -0
- data/lib/celluloid/timers.rb +110 -0
- data/lib/celluloid/uuid.rb +38 -0
- data/lib/celluloid/version.rb +4 -0
- data/lib/celluloid/worker.rb +78 -0
- data/lib/celluloid.rb +355 -0
- data/spec/support/actor_examples.rb +565 -0
- data/spec/support/mailbox_examples.rb +52 -0
- metadata +142 -0
@@ -0,0 +1,565 @@
|
|
1
|
+
shared_context "a Celluloid Actor" do |included_module|
|
2
|
+
class ExampleCrash < StandardError
|
3
|
+
attr_accessor :foo
|
4
|
+
end
|
5
|
+
|
6
|
+
let :actor_class do
|
7
|
+
Class.new do
|
8
|
+
include included_module
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
def initialize(name)
|
12
|
+
@name = name
|
13
|
+
@delegate = [:bar]
|
14
|
+
end
|
15
|
+
|
16
|
+
def change_name(new_name)
|
17
|
+
@name = new_name
|
18
|
+
end
|
19
|
+
|
20
|
+
def change_name_async(new_name)
|
21
|
+
change_name! new_name
|
22
|
+
end
|
23
|
+
|
24
|
+
def greet
|
25
|
+
"Hi, I'm #{@name}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def run(*args)
|
29
|
+
yield(*args)
|
30
|
+
end
|
31
|
+
|
32
|
+
def crash
|
33
|
+
raise ExampleCrash, "the spec purposely crashed me :("
|
34
|
+
end
|
35
|
+
|
36
|
+
def crash_with_abort(reason, foo = nil)
|
37
|
+
example_crash = ExampleCrash.new(reason)
|
38
|
+
example_crash.foo = foo
|
39
|
+
abort example_crash
|
40
|
+
end
|
41
|
+
|
42
|
+
def internal_hello
|
43
|
+
external_hello
|
44
|
+
end
|
45
|
+
|
46
|
+
def external_hello
|
47
|
+
"Hello"
|
48
|
+
end
|
49
|
+
|
50
|
+
def method_missing(method_name, *args, &block)
|
51
|
+
if delegates?(method_name)
|
52
|
+
@delegate.send method_name, *args, &block
|
53
|
+
else
|
54
|
+
super
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def respond_to?(method_name)
|
59
|
+
super || delegates?(method_name)
|
60
|
+
end
|
61
|
+
|
62
|
+
def call_private
|
63
|
+
zomg_private!
|
64
|
+
end
|
65
|
+
|
66
|
+
def zomg_private
|
67
|
+
@private_called = true
|
68
|
+
end
|
69
|
+
private :zomg_private
|
70
|
+
attr_reader :private_called
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def delegates?(method_name)
|
75
|
+
@delegate.respond_to?(method_name)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
it "returns the actor's class, not the proxy's" do
|
81
|
+
actor = actor_class.new "Troy McClure"
|
82
|
+
actor.class.should == actor_class
|
83
|
+
end
|
84
|
+
|
85
|
+
it "compares with the actor's class in a case statement" do
|
86
|
+
case actor_class.new("Troy McClure")
|
87
|
+
when actor_class
|
88
|
+
true
|
89
|
+
else
|
90
|
+
false
|
91
|
+
end.should be_true
|
92
|
+
end
|
93
|
+
|
94
|
+
it "handles synchronous calls" do
|
95
|
+
actor = actor_class.new "Troy McClure"
|
96
|
+
actor.greet.should == "Hi, I'm Troy McClure"
|
97
|
+
end
|
98
|
+
|
99
|
+
it "handles futures for synchronous calls" do
|
100
|
+
actor = actor_class.new "Troy McClure"
|
101
|
+
future = actor.future :greet
|
102
|
+
future.value.should == "Hi, I'm Troy McClure"
|
103
|
+
end
|
104
|
+
|
105
|
+
it "handles circular synchronous calls" do
|
106
|
+
klass = Class.new do
|
107
|
+
include included_module
|
108
|
+
|
109
|
+
def greet_by_proxy(actor)
|
110
|
+
actor.greet
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_s
|
114
|
+
"a ponycopter!"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
ponycopter = klass.new
|
119
|
+
actor = actor_class.new ponycopter
|
120
|
+
ponycopter.greet_by_proxy(actor).should == "Hi, I'm a ponycopter!"
|
121
|
+
end
|
122
|
+
|
123
|
+
it "properly handles method_missing" do
|
124
|
+
actor = actor_class.new "Method Missing"
|
125
|
+
actor.should respond_to(:first)
|
126
|
+
actor.first.should be == :bar
|
127
|
+
end
|
128
|
+
|
129
|
+
it "raises NoMethodError when a nonexistent method is called" do
|
130
|
+
actor = actor_class.new "Billy Bob Thornton"
|
131
|
+
|
132
|
+
expect do
|
133
|
+
actor.the_method_that_wasnt_there
|
134
|
+
end.to raise_exception(NoMethodError)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "reraises exceptions which occur during synchronous calls in the caller" do
|
138
|
+
actor = actor_class.new "James Dean" # is this in bad taste?
|
139
|
+
|
140
|
+
expect do
|
141
|
+
actor.crash
|
142
|
+
end.to raise_exception(ExampleCrash)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "raises exceptions in the caller when abort is called, but keeps running" do
|
146
|
+
actor = actor_class.new "Al Pacino"
|
147
|
+
|
148
|
+
e = nil
|
149
|
+
line_no = nil
|
150
|
+
|
151
|
+
expect do
|
152
|
+
begin
|
153
|
+
line_no = __LINE__; actor.crash_with_abort "You die motherfucker!", :bar
|
154
|
+
rescue => ex
|
155
|
+
e = ex
|
156
|
+
raise
|
157
|
+
end
|
158
|
+
end.to raise_exception(ExampleCrash, "You die motherfucker!")
|
159
|
+
|
160
|
+
e.backtrace.any? { |line| line.include?([__FILE__, line_no].join(':')) }.should be_true # Check the backtrace is appropriate to the caller
|
161
|
+
e.foo.should be == :bar # Check the exception maintains instance variables
|
162
|
+
|
163
|
+
actor.should be_alive
|
164
|
+
end
|
165
|
+
|
166
|
+
it "raises DeadActorError if methods are synchronously called on a dead actor" do
|
167
|
+
actor = actor_class.new "James Dean"
|
168
|
+
actor.crash rescue nil
|
169
|
+
|
170
|
+
expect do
|
171
|
+
actor.greet
|
172
|
+
end.to raise_exception(Celluloid::DeadActorError)
|
173
|
+
end
|
174
|
+
|
175
|
+
it "handles asynchronous calls" do
|
176
|
+
actor = actor_class.new "Troy McClure"
|
177
|
+
actor.change_name! "Charlie Sheen"
|
178
|
+
actor.greet.should == "Hi, I'm Charlie Sheen"
|
179
|
+
end
|
180
|
+
|
181
|
+
it "handles asynchronous calls via #async" do
|
182
|
+
actor = actor_class.new "Troy McClure"
|
183
|
+
actor.async :change_name, "Charlie Sheen"
|
184
|
+
actor.greet.should == "Hi, I'm Charlie Sheen"
|
185
|
+
end
|
186
|
+
|
187
|
+
it "handles asynchronous calls to itself" do
|
188
|
+
actor = actor_class.new "Troy McClure"
|
189
|
+
actor.change_name_async "Charlie Sheen"
|
190
|
+
actor.greet.should == "Hi, I'm Charlie Sheen"
|
191
|
+
end
|
192
|
+
|
193
|
+
it "allows an actor to call private methods asynchronously with a bang" do
|
194
|
+
actor = actor_class.new "Troy McClure"
|
195
|
+
actor.call_private
|
196
|
+
actor.private_called.should be_true
|
197
|
+
end
|
198
|
+
|
199
|
+
it "knows if it's inside actor scope" do
|
200
|
+
Celluloid.should_not be_actor
|
201
|
+
actor = actor_class.new "Troy McClure"
|
202
|
+
actor.run do
|
203
|
+
Celluloid.actor?
|
204
|
+
end.should be_true
|
205
|
+
end
|
206
|
+
|
207
|
+
it "inspects properly" do
|
208
|
+
actor = actor_class.new "Troy McClure"
|
209
|
+
actor.inspect.should match(/Celluloid::Actor\(/)
|
210
|
+
actor.inspect.should match(/#{actor_class}/)
|
211
|
+
actor.inspect.should include('@name="Troy McClure"')
|
212
|
+
actor.inspect.should_not include("@celluloid")
|
213
|
+
end
|
214
|
+
|
215
|
+
it "inspects properly when dead" do
|
216
|
+
actor = actor_class.new "Troy McClure"
|
217
|
+
actor.terminate
|
218
|
+
actor.inspect.should match(/Celluloid::Actor\(/)
|
219
|
+
actor.inspect.should match(/#{actor_class}/)
|
220
|
+
actor.inspect.should include('dead')
|
221
|
+
end
|
222
|
+
|
223
|
+
it "allows access to the wrapped object" do
|
224
|
+
actor = actor_class.new "Troy McClure"
|
225
|
+
actor.wrapped_object.should be_a actor_class
|
226
|
+
end
|
227
|
+
|
228
|
+
describe 'mocking methods' do
|
229
|
+
let(:actor) { actor_class.new "Troy McClure" }
|
230
|
+
|
231
|
+
before do
|
232
|
+
actor.wrapped_object.should_receive(:external_hello).once.and_return "World"
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'works externally via the proxy' do
|
236
|
+
actor.external_hello.should == "World"
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'works internally when called on self' do
|
240
|
+
actor.internal_hello.should == "World"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
context :termination do
|
245
|
+
it "terminates" do
|
246
|
+
actor = actor_class.new "Arnold Schwarzenegger"
|
247
|
+
actor.should be_alive
|
248
|
+
actor.terminate
|
249
|
+
sleep 0.5 # hax
|
250
|
+
actor.should_not be_alive
|
251
|
+
end
|
252
|
+
|
253
|
+
it "raises DeadActorError if called after terminated" do
|
254
|
+
actor = actor_class.new "Arnold Schwarzenegger"
|
255
|
+
actor.terminate
|
256
|
+
|
257
|
+
expect do
|
258
|
+
actor.greet
|
259
|
+
end.to raise_exception(Celluloid::DeadActorError)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
context :current_actor do
|
264
|
+
it "knows the current actor" do
|
265
|
+
actor = actor_class.new "Roger Daltrey"
|
266
|
+
actor.current_actor.should == actor
|
267
|
+
end
|
268
|
+
|
269
|
+
it "raises NotActorError if called outside an actor" do
|
270
|
+
expect do
|
271
|
+
Celluloid.current_actor
|
272
|
+
end.to raise_exception(Celluloid::NotActorError)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
context :linking do
|
277
|
+
before :each do
|
278
|
+
@kevin = actor_class.new "Kevin Bacon" # Some six degrees action here
|
279
|
+
@charlie = actor_class.new "Charlie Sheen"
|
280
|
+
end
|
281
|
+
|
282
|
+
it "links to other actors" do
|
283
|
+
@kevin.link @charlie
|
284
|
+
@kevin.linked_to?(@charlie).should be_true
|
285
|
+
@charlie.linked_to?(@kevin).should be_true
|
286
|
+
end
|
287
|
+
|
288
|
+
it "unlinks from other actors" do
|
289
|
+
@kevin.link @charlie
|
290
|
+
@kevin.unlink @charlie
|
291
|
+
|
292
|
+
@kevin.linked_to?(@charlie).should be_false
|
293
|
+
@charlie.linked_to?(@kevin).should be_false
|
294
|
+
end
|
295
|
+
|
296
|
+
it "traps exit messages from other actors" do
|
297
|
+
boss = Class.new do # like a boss
|
298
|
+
include included_module
|
299
|
+
trap_exit :lambaste_subordinate
|
300
|
+
|
301
|
+
def initialize(name)
|
302
|
+
@name = name
|
303
|
+
@subordinate_lambasted = false
|
304
|
+
end
|
305
|
+
|
306
|
+
def subordinate_lambasted?; @subordinate_lambasted; end
|
307
|
+
|
308
|
+
def lambaste_subordinate(actor, reason)
|
309
|
+
@subordinate_lambasted = true
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
chuck = boss.new "Chuck Lorre"
|
314
|
+
chuck.link @charlie
|
315
|
+
|
316
|
+
expect do
|
317
|
+
@charlie.crash
|
318
|
+
end.to raise_exception(ExampleCrash)
|
319
|
+
|
320
|
+
sleep 0.1 # hax to prevent a race between exit handling and the next call
|
321
|
+
chuck.should be_subordinate_lambasted
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
context :signaling do
|
326
|
+
before do
|
327
|
+
@signaler = Class.new do
|
328
|
+
include included_module
|
329
|
+
|
330
|
+
def initialize
|
331
|
+
@waiting = false
|
332
|
+
@signaled = false
|
333
|
+
end
|
334
|
+
|
335
|
+
def wait_for_signal
|
336
|
+
raise "already signaled" if @signaled
|
337
|
+
|
338
|
+
@waiting = true
|
339
|
+
signal :future
|
340
|
+
|
341
|
+
value = wait :ponycopter
|
342
|
+
|
343
|
+
@waiting = false
|
344
|
+
@signaled = true
|
345
|
+
value
|
346
|
+
end
|
347
|
+
|
348
|
+
def wait_for_future
|
349
|
+
return true if @waiting
|
350
|
+
wait :future
|
351
|
+
end
|
352
|
+
|
353
|
+
def send_signal(value)
|
354
|
+
signal :ponycopter, value
|
355
|
+
end
|
356
|
+
|
357
|
+
def waiting?; @waiting end
|
358
|
+
def signaled?; @signaled end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
it "allows methods within the same object to signal each other" do
|
363
|
+
obj = @signaler.new
|
364
|
+
obj.should_not be_signaled
|
365
|
+
|
366
|
+
obj.wait_for_signal!
|
367
|
+
obj.should_not be_signaled
|
368
|
+
|
369
|
+
obj.send_signal :foobar
|
370
|
+
obj.should be_signaled
|
371
|
+
end
|
372
|
+
|
373
|
+
# FIXME: This is deadlocking on Travis, and may still have issues
|
374
|
+
it "sends values along with signals" do
|
375
|
+
obj = @signaler.new
|
376
|
+
obj.should_not be_signaled
|
377
|
+
|
378
|
+
future = obj.future(:wait_for_signal)
|
379
|
+
|
380
|
+
obj.wait_for_future
|
381
|
+
obj.should be_waiting
|
382
|
+
obj.should_not be_signaled
|
383
|
+
|
384
|
+
obj.send_signal(:foobar).should be_true
|
385
|
+
future.value.should == :foobar
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
context :receiving do
|
390
|
+
before do
|
391
|
+
@receiver = Class.new do
|
392
|
+
include included_module
|
393
|
+
|
394
|
+
def signal_myself(obj, &block)
|
395
|
+
current_actor.mailbox << obj
|
396
|
+
receive(&block)
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
let(:receiver) { @receiver.new }
|
402
|
+
let(:message) { Object.new }
|
403
|
+
|
404
|
+
it "allows unconditional receive" do
|
405
|
+
receiver.signal_myself(message).should == message
|
406
|
+
end
|
407
|
+
|
408
|
+
it "allows arbitrary selective receive" do
|
409
|
+
received_obj = receiver.signal_myself(message) { |o| o == message }
|
410
|
+
received_obj.should == message
|
411
|
+
end
|
412
|
+
|
413
|
+
it "times out after the given interval" do
|
414
|
+
interval = 0.1
|
415
|
+
started_at = Time.now
|
416
|
+
|
417
|
+
receiver.receive(interval) { false }.should be_nil
|
418
|
+
(Time.now - started_at).should be_within(Celluloid::Timer::QUANTUM).of interval
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
context :timers do
|
423
|
+
before do
|
424
|
+
@klass = Class.new do
|
425
|
+
include included_module
|
426
|
+
|
427
|
+
def initialize
|
428
|
+
@sleeping = false
|
429
|
+
@fired = false
|
430
|
+
end
|
431
|
+
|
432
|
+
def do_sleep(n)
|
433
|
+
@sleeping = true
|
434
|
+
sleep n
|
435
|
+
@sleeping = false
|
436
|
+
end
|
437
|
+
|
438
|
+
def sleeping?; @sleeping end
|
439
|
+
|
440
|
+
def fire_after(n)
|
441
|
+
after(n) { @fired = true }
|
442
|
+
end
|
443
|
+
|
444
|
+
def fire_every(n)
|
445
|
+
@fired = 0
|
446
|
+
every(n) { @fired += 1 }
|
447
|
+
end
|
448
|
+
|
449
|
+
def fired?; !!@fired end
|
450
|
+
def fired; @fired end
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
it "suspends execution of a method (but not the actor) for a given time" do
|
455
|
+
actor = @klass.new
|
456
|
+
|
457
|
+
# Sleep long enough to ensure we're actually seeing behavior when asleep
|
458
|
+
# but not so long as to delay the test suite unnecessarily
|
459
|
+
interval = Celluloid::Timer::QUANTUM * 10
|
460
|
+
started_at = Time.now
|
461
|
+
|
462
|
+
future = actor.future(:do_sleep, interval)
|
463
|
+
sleep(interval / 2) # wonky! :/
|
464
|
+
actor.should be_sleeping
|
465
|
+
|
466
|
+
future.value
|
467
|
+
(Time.now - started_at).should be_within(Celluloid::Timer::QUANTUM).of interval
|
468
|
+
end
|
469
|
+
|
470
|
+
it "schedules timers which fire in the future" do
|
471
|
+
actor = @klass.new
|
472
|
+
|
473
|
+
interval = Celluloid::Timer::QUANTUM * 10
|
474
|
+
started_at = Time.now
|
475
|
+
|
476
|
+
timer = actor.fire_after(interval)
|
477
|
+
actor.should_not be_fired
|
478
|
+
|
479
|
+
sleep(interval + Celluloid::Timer::QUANTUM) # wonky! #/
|
480
|
+
actor.should be_fired
|
481
|
+
end
|
482
|
+
|
483
|
+
it "schedules recurring timers which fire in the future" do
|
484
|
+
actor = @klass.new
|
485
|
+
|
486
|
+
interval = Celluloid::Timer::QUANTUM * 10
|
487
|
+
started_at = Time.now
|
488
|
+
|
489
|
+
timer = actor.fire_every(interval)
|
490
|
+
actor.fired.should be == 0
|
491
|
+
|
492
|
+
sleep(interval + Celluloid::Timer::QUANTUM) # wonky! #/
|
493
|
+
actor.fired.should be == 1
|
494
|
+
|
495
|
+
2.times { sleep(interval + Celluloid::Timer::QUANTUM) } # wonky! #/
|
496
|
+
actor.fired.should be == 3
|
497
|
+
end
|
498
|
+
|
499
|
+
it "cancels timers before they fire" do
|
500
|
+
actor = @klass.new
|
501
|
+
|
502
|
+
interval = Celluloid::Timer::QUANTUM * 10
|
503
|
+
started_at = Time.now
|
504
|
+
|
505
|
+
timer = actor.fire_after(interval)
|
506
|
+
actor.should_not be_fired
|
507
|
+
timer.cancel
|
508
|
+
|
509
|
+
sleep(interval + Celluloid::Timer::QUANTUM) # wonky! #/
|
510
|
+
actor.should_not be_fired
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
context :tasks do
|
515
|
+
before do
|
516
|
+
@klass = Class.new do
|
517
|
+
include included_module
|
518
|
+
attr_reader :blocker
|
519
|
+
|
520
|
+
def initialize
|
521
|
+
@blocker = Blocker.new
|
522
|
+
end
|
523
|
+
|
524
|
+
def blocking_call
|
525
|
+
@blocker.block
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
class Blocker
|
530
|
+
include Celluloid
|
531
|
+
|
532
|
+
def block
|
533
|
+
wait :unblock
|
534
|
+
end
|
535
|
+
|
536
|
+
def unblock
|
537
|
+
signal :unblock
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
it "knows which tasks are waiting on calls to other actors" do
|
543
|
+
actor = @klass.new
|
544
|
+
|
545
|
+
# an alias for Celluloid::Actor#waiting_tasks
|
546
|
+
tasks = actor.tasks
|
547
|
+
tasks.size.should == 1
|
548
|
+
tasks.first.status.should == :running
|
549
|
+
|
550
|
+
future = actor.future(:blocking_call)
|
551
|
+
sleep 0.1 # hax! waiting for ^^^ call to actually start
|
552
|
+
|
553
|
+
tasks = actor.tasks
|
554
|
+
tasks.size.should == 2
|
555
|
+
|
556
|
+
blocking_task = tasks.find { |t| t.status != :running }
|
557
|
+
blocking_task.should be_a Celluloid::Task
|
558
|
+
blocking_task.status.should == :callwait
|
559
|
+
|
560
|
+
actor.blocker.unblock
|
561
|
+
sleep 0.1 # hax again :(
|
562
|
+
actor.tasks.size.should == 1
|
563
|
+
end
|
564
|
+
end
|
565
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
shared_context "a Celluloid Mailbox" do
|
2
|
+
class TestEvent < Celluloid::SystemEvent; end
|
3
|
+
|
4
|
+
it "receives messages" do
|
5
|
+
message = :ohai
|
6
|
+
|
7
|
+
subject << message
|
8
|
+
subject.receive.should == message
|
9
|
+
end
|
10
|
+
|
11
|
+
it "raises system events when received" do
|
12
|
+
subject.system_event TestEvent.new("example")
|
13
|
+
|
14
|
+
expect do
|
15
|
+
subject.receive
|
16
|
+
end.to raise_exception(TestEvent)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "prioritizes system events over other messages" do
|
20
|
+
subject << :dummy1
|
21
|
+
subject << :dummy2
|
22
|
+
subject.system_event TestEvent.new("example")
|
23
|
+
|
24
|
+
expect do
|
25
|
+
subject.receive
|
26
|
+
end.to raise_exception(TestEvent)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "selectively receives messages with a block" do
|
30
|
+
class Foo; end
|
31
|
+
class Bar; end
|
32
|
+
class Baz; end
|
33
|
+
|
34
|
+
foo, bar, baz = Foo.new, Bar.new, Baz.new
|
35
|
+
|
36
|
+
subject << baz
|
37
|
+
subject << foo
|
38
|
+
subject << bar
|
39
|
+
|
40
|
+
subject.receive { |msg| msg.is_a? Foo }.should == foo
|
41
|
+
subject.receive { |msg| msg.is_a? Bar }.should == bar
|
42
|
+
subject.receive.should == baz
|
43
|
+
end
|
44
|
+
|
45
|
+
it "waits for a given timeout interval" do
|
46
|
+
interval = 0.1
|
47
|
+
started_at = Time.now
|
48
|
+
|
49
|
+
subject.receive(interval) { false }
|
50
|
+
(Time.now - started_at).should be_within(Celluloid::Timer::QUANTUM).of interval
|
51
|
+
end
|
52
|
+
end
|