eventbox 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,18 +18,18 @@ class Eventbox
18
18
  #
19
19
  # In detail this works as following.
20
20
  # Objects which are passed through unchanged are:
21
- # * {Eventbox}, {Eventbox::Action} and `Module` objects
21
+ # * {Eventbox}, {Eventbox::Action} and +Module+ objects
22
22
  # * Proc objects created by {Eventbox#async_proc}, {Eventbox#sync_proc} and {Eventbox#yield_proc}
23
23
  #
24
24
  # The following rules apply for wrapping/unwrapping:
25
- # * If the object has been marked as {Eventbox#shared_object}, it is wrapped as {WrappedObject} depending on the direction of the data flow (return value or call argument).
25
+ # * If the object has been marked as {Eventbox#shared_object}, it is wrapped as {WrappedObject} or {ExternalObject} depending on the direction of the data flow (return value or call argument).
26
26
  # * If the object is a {WrappedObject} or {ExternalProc} and fits to the target scope, it is unwrapped.
27
27
  # Both cases even work if the object is encapsulated by another object.
28
28
  #
29
29
  # In all other cases the following rules apply:
30
- # * If the object is marshalable, it is passed as a deep copy through `Marshal.dump` and `Marshal.load` .
31
- # * An object which failed to marshal as a whole is tried to be dissected and values are sanitized recursively.
32
- # * If the object can't be marshaled or dissected, it is wrapped as {WrappedObject}.
30
+ # * If the object is marshalable, it is passed as a deep copy through +Marshal.dump+ and +Marshal.load+ .
31
+ # * An object which failed to marshal as a whole is tried to be dissected and values are sanitized separately and recursively.
32
+ # * If the object can't be marshaled or dissected, it is wrapped as {ExternalObject} when passed from external scope to event scope and wrapped as {WrappedObject} when passed from the event scope.
33
33
  # They are unwrapped when passed back to origin scope.
34
34
  # * Proc objects passed from event scope to external are wrapped as {WrappedObject}.
35
35
  # They are unwrapped when passed back to event scope.
@@ -46,24 +46,25 @@ class Eventbox
46
46
  # Separate the instance variables from the object
47
47
  ivns = arg.instance_variables
48
48
  ivvs = ivns.map do |ivn|
49
- arg.instance_variable_get(ivn)
50
- end
51
-
52
- # Temporary set all instance variables to nil
53
- ivns.each do |ivn|
49
+ ivv = arg.instance_variable_get(ivn)
50
+ # Temporary set all instance variables to nil
54
51
  arg.instance_variable_set(ivn, nil)
52
+ ivv
55
53
  end
56
54
 
57
55
  # Copy the object
58
56
  arg2 = yield(arg)
59
-
60
- # Restore the original object
57
+ rescue
58
+ # Restore the original object in case of Marshal.dump failure
61
59
  ivns.each_with_index do |ivn, ivni|
62
60
  arg.instance_variable_set(ivn, ivvs[ivni])
63
61
  end
64
-
65
- # sanitize instance variables independently and write them to the copied object
62
+ raise
63
+ else
66
64
  ivns.each_with_index do |ivn, ivni|
65
+ # Restore the original object
66
+ arg.instance_variable_set(ivn, ivvs[ivni])
67
+ # sanitize instance variables independently and write them to the copied object
67
68
  ivv = sanitize_value(ivvs[ivni], source_event_loop, target_event_loop, ivn)
68
69
  arg2.instance_variable_set(ivn, ivv)
69
70
  end
@@ -81,12 +82,15 @@ class Eventbox
81
82
  end
82
83
 
83
84
  arg2 = yield(arg)
84
-
85
+ rescue
86
+ # Restore the original object in case of Marshal.dump failure
85
87
  ms.each_with_index do |m, i|
86
88
  arg[m] = vs[i]
87
89
  end
88
-
90
+ raise
91
+ else
89
92
  ms.each_with_index do |m, i|
93
+ arg[m] = vs[i]
90
94
  v2 = sanitize_value(vs[i], source_event_loop, target_event_loop, m)
91
95
  arg2[m] = v2
92
96
  end
@@ -102,12 +106,15 @@ class Eventbox
102
106
  end
103
107
 
104
108
  arg2 = yield(arg)
105
-
109
+ rescue
110
+ # Restore the original object in case of Marshal.dump failure
106
111
  h.each do |k, v|
107
112
  arg[k] = v
108
113
  end
109
-
114
+ raise
115
+ else
110
116
  h.each do |k, v|
117
+ arg[k] = v
111
118
  arg2[k] = sanitize_value(v, source_event_loop, target_event_loop, k)
112
119
  end
113
120
 
@@ -122,12 +129,15 @@ class Eventbox
122
129
  end
123
130
 
124
131
  arg2 = yield(arg)
125
-
126
- vs.each_index do |i|
132
+ rescue
133
+ # Restore the original object in case of Marshal.dump failure
134
+ vs.each_with_index do |v, i|
127
135
  arg[i] = vs[i]
128
136
  end
129
-
137
+ raise
138
+ else
130
139
  vs.each_with_index do |v, i|
140
+ arg[i] = vs[i]
131
141
  v2 = sanitize_value(v, source_event_loop, target_event_loop, name)
132
142
  arg2[i] = v2
133
143
  end
@@ -158,9 +168,9 @@ class Eventbox
158
168
  unless mel == source_event_loop
159
169
  raise InvalidAccess, "object #{arg.inspect} #{"wrapped by #{name} " if name} was marked as shared_object in a different eventbox object than the calling eventbox"
160
170
  end
161
- WrappedObject.new(arg, mel, name)
171
+ wrap_object(arg, mel, target_event_loop, name)
162
172
  when ExternalSharedObject # External object marked as shared_object
163
- WrappedObject.new(arg, source_event_loop, name)
173
+ wrap_object(arg, source_event_loop, target_event_loop, name)
164
174
  else
165
175
  # Not tagged -> try to deep copy the object
166
176
  begin
@@ -219,16 +229,23 @@ class Eventbox
219
229
  args.map { |arg| sanitize_value(arg, source_event_loop, target_event_loop, name) }
220
230
  end
221
231
 
232
+ def sanitize_kwargs(args, source_event_loop, target_event_loop, name=nil)
233
+ args.transform_values { |arg| sanitize_value(arg, source_event_loop, target_event_loop, name) }
234
+ end
235
+
222
236
  def wrap_proc(arg, name, source_event_loop, target_event_loop)
223
237
  if target_event_loop&.event_scope?
224
- ExternalProc.new(arg, source_event_loop, name) do |*args, &block|
238
+ ExternalProc.new(arg, source_event_loop, name) do |*args, **kwargs, &block|
225
239
  if target_event_loop&.event_scope?
226
240
  # called in the event scope
241
+
227
242
  if block && !(WrappedProc === block)
228
243
  raise InvalidAccess, "calling #{arg.inspect} with block argument #{block.inspect} is not allowed - use async_proc, sync_proc, yield_proc or an external proc instead"
229
244
  end
230
- cbblock = args.last if Proc === args.last
231
- target_event_loop._external_proc_call(arg, name, args, block, cbblock, source_event_loop)
245
+
246
+ call_context = args.shift if CallContext === args.first
247
+ cbblock = args.pop if Proc === args.last
248
+ target_event_loop._external_object_call(arg, :call, name, args, kwargs, block, cbblock, source_event_loop, call_context)
232
249
  else
233
250
  # called externally
234
251
  raise InvalidAccess, "external proc #{arg.inspect} #{"wrapped by #{name} " if name} can not be called in a different eventbox instance"
@@ -238,6 +255,14 @@ class Eventbox
238
255
  WrappedObject.new(arg, source_event_loop, name)
239
256
  end
240
257
  end
258
+
259
+ def wrap_object(object, source_event_loop, target_event_loop, name)
260
+ if target_event_loop&.event_scope?
261
+ ExternalObject.new(object, source_event_loop, target_event_loop, name)
262
+ else
263
+ WrappedObject.new(object, source_event_loop, name)
264
+ end
265
+ end
241
266
  end
242
267
 
243
268
  # Generic wrapper for objects that are passed through a foreign scope as reference.
@@ -245,19 +270,87 @@ class Eventbox
245
270
  # Access to the object from a different scope is denied, but the wrapper object can be stored and passed back to the origin scope to unwrap it.
246
271
  class WrappedObject
247
272
  attr_reader :name
273
+ # @private
248
274
  def initialize(object, event_loop, name=nil)
249
- @object = object
250
- @event_loop = event_loop
251
- @name = name
275
+ @object = object # the object to be wrapped
276
+ @event_loop = event_loop # the event_loop @object originates from, nil if external scope
277
+ @name = name # some information to get more meaningful error messages
252
278
  @dont_marshal = ExternalSharedObject # protect self from being marshaled
253
279
  end
254
280
 
281
+ # @private
255
282
  def object_for(target_event_loop)
256
283
  @event_loop == target_event_loop ? @object : self
257
284
  end
258
285
 
259
286
  def inspect
260
- "#<#{self.class} @object=#{@object.inspect} @name=#{@name.inspect}>"
287
+ el = case @event_loop
288
+ when EventLoop then @event_loop.object_id.to_s(16)
289
+ else @event_loop.inspect
290
+ end
291
+ "#<#{self.class} @object=#{@object.inspect} @name=#{@name.inspect} @event_loop=#{el}>"
292
+ end
293
+ end
294
+
295
+ # Wrapper for objects created external or in the action scope of some Eventbox instance.
296
+ #
297
+ # External objects can be called from event scope by {ExternalObject#send}.
298
+ #
299
+ # External objects can also be passed to action or to external scope.
300
+ # In this case a {ExternalObject} is unwrapped back to the ordinary object.
301
+ #
302
+ # @see ExternalProc
303
+ class ExternalObject < WrappedObject
304
+ # @private
305
+ def initialize(object, event_loop, target_event_loop, name=nil)
306
+ super(object, event_loop, name)
307
+ @target_event_loop = target_event_loop
308
+ end
309
+
310
+ # Invoke the external objects within the event scope.
311
+ #
312
+ # It can be called within {Eventbox::Boxable#sync_call sync_call} and {Eventbox::Boxable#yield_call yield_call} methods and from {Eventbox#sync_proc} and {Eventbox#yield_proc} closures.
313
+ # The method then runs in the background on the thread that called the event scope method in execution.
314
+ #
315
+ # It's also possible to invoke the external object with an explicit {Eventbox::CallContext} instead of the implicit call context of a sync or yield call.
316
+ # The explicit {Eventbox::CallContext} must be given as the very first parameter and it is not passed to the object call.
317
+ # The object call is then done in the given context of an arbitrary event scope method or closure that didn't return yet.
318
+ # In this case the method runs in the background on the thread that is waiting for the call to return.
319
+ #
320
+ # If the call to the external object doesn't return immediately, it blocks the calling thread or the thread of the {Eventbox::CallContext}.
321
+ # If this is not desired, an {Eventbox::Boxable#action action} can be used instead, to invoke the method of the object on a dedicated thread.
322
+ # However in any case calling the external object doesn't block the Eventbox instance itself.
323
+ # It still keeps responsive to calls from other threads.
324
+ #
325
+ # Optionally a proc can be provided as the last argument which acts as a completion callback.
326
+ # This proc is invoked, when the call has finished, with the result value as argument.
327
+ #
328
+ # class Sender < Eventbox
329
+ # sync_call def init(€obj) # €-variables are passed as reference instead of copy
330
+ # # invoke the object given to Sender.new
331
+ # # and when completed, print the result of strip
332
+ # €obj.send :strip, ->(res){ p res }
333
+ # end
334
+ # end
335
+ # Sender.new(" a b c ") # Output: "a b c"
336
+ def send(method, *args, **kwargs, &block)
337
+ if @target_event_loop&.event_scope?
338
+ # called in the event scope
339
+ if CallContext === method
340
+ call_context = method
341
+ method = args.shift
342
+ end
343
+
344
+ if block && !(WrappedProc === block)
345
+ raise InvalidAccess, "calling `#{method}' with block argument #{block.inspect} is not allowed - use async_proc, sync_proc, yield_proc or an external proc instead"
346
+ end
347
+
348
+ cbblock = args.pop if Proc === args.last
349
+ @target_event_loop._external_object_call(@object, method, @name, args, kwargs, block, cbblock, @event_loop, call_context)
350
+ else
351
+ # called externally
352
+ raise InvalidAccess, "external object #{self.inspect} #{"wrapped by #{name} " if name} can not be called in a different eventbox instance"
353
+ end
261
354
  end
262
355
  end
263
356
 
@@ -283,9 +376,21 @@ class Eventbox
283
376
 
284
377
  WrappedException = Struct.new(:exc)
285
378
 
286
- # Proc object provided as the last argument of {Eventbox.yield_call} and {Eventbox#yield_proc}.
379
+ # Proc object provided as the last argument of {Eventbox::Boxable#yield_call yield_call} and {Eventbox#yield_proc}.
380
+ #
381
+ # Used to let the corresponding yield call return a value:
382
+ # class MyBox < Eventbox
383
+ # yield_call def number(result)
384
+ # result.yield 42
385
+ # end
386
+ # end
387
+ # MyBox.new.number # => 42
388
+ #
389
+ # Alternatively the yield call can respond with an exception by {CompletionProc#raise}.
287
390
  class CompletionProc < AsyncProc
288
- # Raise an exception in the context of the waiting {Eventbox.yield_call} or {Eventbox#yield_proc} method.
391
+ include CallContext
392
+
393
+ # Raise an exception in the context of the waiting {Eventbox::Boxable#yield_call yield_call} or {Eventbox#yield_proc} method.
289
394
  #
290
395
  # This allows to raise an exception to the calling scope from external or action scope:
291
396
  #
@@ -300,38 +405,49 @@ class Eventbox
300
405
  # end
301
406
  # MyBox.new # => raises RuntimeError (raise from action MyBox#process)
302
407
  #
303
- # In contrast to a direct call of `Kernel.raise`, calling this method doesn't abort the current context.
408
+ # In contrast to a direct call of +Kernel.raise+, calling this method doesn't abort the current context.
304
409
  # Instead when in the event scope, raising the exception is deferred until returning to the calling external or action scope.
305
410
  def raise(*args)
306
411
  self.call(WrappedException.new(args))
307
412
  end
308
413
  end
309
414
 
310
- # Wrapper for Proc objects created external of some Eventbox instance.
415
+ # Wrapper for Proc objects created external or in the action scope of some Eventbox instance.
416
+ #
417
+ # External Proc objects can be invoked from event scope by {ExternalProc#call}.
418
+ # It can be called within {Eventbox::Boxable#sync_call sync_call} and {Eventbox::Boxable#yield_call yield_call} methods and from {Eventbox#sync_proc} and {Eventbox#yield_proc} closures.
419
+ # The proc then runs in the background on the thread that called the event scope method in execution.
420
+ #
421
+ # It's also possible to invoke it within a {Eventbox::Boxable#async_call async_call} or {Eventbox#async_proc}, when the method or proc that brought the external proc into the event scope, is a yield call that didn't return yet.
422
+ # In this case the proc runs in the background on the thread that is waiting for the yield call to return.
311
423
  #
312
- # External Proc objects can be invoked from event scope through {Eventbox.sync_call} and {Eventbox.yield_call} methods.
313
424
  # Optionally a proc can be provided as the last argument which acts as a completion callback.
314
425
  # This proc is invoked, when the call has finished, with the result value as argument.
315
426
  #
316
427
  # class Callback < Eventbox
317
428
  # sync_call def init(&block)
318
- # block.call(5, proc do |res| # invoke the block given to Callback.new
319
- # p res # print the block result (5 + 1)
320
- # end)
429
+ # # invoke the block given to Callback.new
430
+ # # and when completed, print the result of the block
431
+ # block.call 5, ->(res){ p res }
321
432
  # end
322
433
  # end
323
434
  # Callback.new {|num| num + 1 } # Output: 6
324
435
  #
325
436
  # External Proc objects can also be passed to action or to external scope.
326
- # In this case a {ExternalProc} is unwrapped back to an ordinary Proc object.
437
+ # In this case a {ExternalProc} is unwrapped back to the ordinary Proc object.
438
+ #
439
+ # @see ExternalObject
327
440
  class ExternalProc < WrappedProc
441
+ # @private
328
442
  attr_reader :name
443
+ # @private
329
444
  def initialize(object, event_loop, name=nil)
330
445
  @object = object
331
446
  @event_loop = event_loop
332
447
  @name = name
333
448
  end
334
449
 
450
+ # @private
335
451
  def object_for(target_event_loop)
336
452
  @event_loop == target_event_loop ? @object : self
337
453
  end
@@ -68,6 +68,10 @@ class Eventbox
68
68
  end
69
69
  end
70
70
 
71
+ async_call def terminate
72
+ @action.abort
73
+ end
74
+
71
75
  # @private
72
76
  async_call def __start__(action, input)
73
77
  # Send the block to the start_pool_thread as result of next_job
@@ -120,6 +124,8 @@ class Eventbox
120
124
  Thread.handle_interrupt(Exception => :immediate) do
121
125
  sleep # Aborted by the exception
122
126
  end
127
+ rescue Eventbox::AbortAction
128
+ raise # The thread-pool was requested to shutdown
123
129
  rescue Exception
124
130
  end
125
131
  end
@@ -166,5 +172,9 @@ class Eventbox
166
172
  private async_call def gc_finished
167
173
  @run_gc_when_busy = true
168
174
  end
175
+
176
+ def inspect
177
+ "#<#{self.class}:#{self.object_id} @requests=#{@requests.length} @jobless=#{@jobless.length} @run_gc_when_busy=#{@run_gc_when_busy.inspect}>"
178
+ end
169
179
  end
170
180
  end
@@ -9,28 +9,36 @@ class Eventbox
9
9
  # include Eventbox::Timer
10
10
  #
11
11
  # async_call def init
12
- # super # make sure Timer#init is called
12
+ # super # Make sure Timer#init is called
13
13
  # timer_after(1) do
14
14
  # puts "one second elapsed"
15
15
  # end
16
16
  # end
17
17
  # end
18
18
  #
19
+ # MyBox.new # Schedule the alarm after 1 sec
20
+ # sleep 2 # Wait for the timer to be triggered
21
+ #
19
22
  # The main functions are timer_after and timer_every.
20
23
  # They schedule asynchronous calls to the given block:
21
- # timer_after(3) do
24
+ # timer_after(3.0) do
22
25
  # # executed once after 3 seconds
23
26
  # end
24
27
  #
25
- # timer_every(3) do
26
- # # executed repeatedly every 3 seconds
28
+ # timer_every(1.5) do
29
+ # # executed repeatedly every 1.5 seconds
27
30
  # end
28
31
  #
29
- # Both functions return an Alarm object which can be used to cancel the alarm through timer_cancel.
32
+ # Both functions return an {Alarm} object which can be used to cancel the alarm through {timer_cancel}.
30
33
  #
31
- # timer_after, timer_every and timer_cancel can be used within the event scope, in actions and from external scope.
34
+ # {Timer} always uses one {Eventbox::Boxable#action action} thread per Eventbox object, regardless of the number of scheduled timers.
35
+ # All alarms are called from this thread.
36
+ # {timer_after}, {timer_every} and {timer_cancel} can be used within the {file:README.md#event-scope event scope}, in actions and from external scope.
37
+ # Alarms defined within the event scope must be non-blocking, as any other code in the event scope.
38
+ # Alarms defined in action or external scope should also avoid blocking code, otherwise one alarm can delay the next alarm.
32
39
  #
33
- # {Timer} always uses one action thread per Eventbox object, regardless of the number of scheduled timers.
40
+ # *Note:* Each object that includes the {Timer} module must be explicit terminated by {Eventbox#shutdown!}.
41
+ # It is (currently) not freed by the garbarge collector.
34
42
  module Timer
35
43
  class Reload < RuntimeError
36
44
  end
@@ -124,6 +132,8 @@ class Eventbox
124
132
  end
125
133
 
126
134
  # Cancel an alarm previously scheduled per timer_after or timer_every
135
+ #
136
+ # The method does nothing, if the alarm is no longer active.
127
137
  async_call def timer_cancel(alarm)
128
138
  a = @timer_alarms.delete(alarm)
129
139
  if a
@@ -1,5 +1,5 @@
1
1
  # frozen-string-literal: true
2
2
 
3
3
  class Eventbox
4
- VERSION = "0.1.0"
4
+ VERSION = "1.0.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eventbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lars Kanis
@@ -10,26 +10,26 @@ bindir: exe
10
10
  cert_chain:
11
11
  - |
12
12
  -----BEGIN CERTIFICATE-----
13
- MIIDPDCCAiSgAwIBAgIBBTANBgkqhkiG9w0BAQsFADBEMQ0wCwYDVQQDDARsYXJz
14
- MR8wHQYKCZImiZPyLGQBGRYPZ3JlaXotcmVpbnNkb3JmMRIwEAYKCZImiZPyLGQB
15
- GRYCZGUwHhcNMTgwOTE0MjE0NjIyWhcNMTkwOTE0MjE0NjIyWjBEMQ0wCwYDVQQD
16
- DARsYXJzMR8wHQYKCZImiZPyLGQBGRYPZ3JlaXotcmVpbnNkb3JmMRIwEAYKCZIm
17
- iZPyLGQBGRYCZGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZb4Uv
18
- RFJfRu/VEWiy3psh2jinETjiuBrL0NeRFGf8H7iU9+gx/DI/FFhfHGLrDeIskrJx
19
- YIWDMmEjVO10UUdj7wu4ZhmU++0Cd7Kq9/TyP/shIP3IjqHjVLCnJ3P6f1cl5rxZ
20
- gqo+d3BAoDrmPk0rtaf6QopwUw9RBiF8V4HqvpiY+ruJotP5UQDP4/lVOKvA8PI9
21
- P0GmVbFBrbc7Zt5h78N3UyOK0u+nvOC23BvyHXzCtcFsXCoEkt+Wwh0RFqVZdnjM
22
- LMO2vULHKKHDdX54K/sbVCj9pN9h1aotNzrEyo55zxn0G9PHg/G3P8nMvAXPkUTe
23
- brhXrfCwWRvOXA4TAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0G
24
- A1UdDgQWBBRAHK81igrXodaDj8a8/BIKsaZrETANBgkqhkiG9w0BAQsFAAOCAQEA
25
- ZNTM80Pn9caTOcDYCmJi28qV0H8x6k5zeEd8PckEr9gYCFrUtVvtQPBi364PWo82
26
- Img1GQle3nbhYuiNPRsjfr0uaTgUeA7JXw5OCEtgck2w93KzFISBt+jLyoprAJDh
27
- uHImSFGzjRwHVi2Kihh1y4VR0ZkofNWaPqhxhxN7JCTVrV8MLqe2ZgHoe1WMGAyT
28
- mmzdbSP3kmz9L2eshisCKPSi0qKk4VyBhdeeIf+3xMim/DK1gTz44BwHSCtGkaJT
29
- NOQ0Qq2j8NtFfBHqGHBKaqj/Q0AciwGWIMfXJh3fTsRuMonkEu6TgB6M79HzgqQ8
30
- sXOGZvvDWUBn5GWztfhdZw==
13
+ MIIDLjCCAhagAwIBAgIBCTANBgkqhkiG9w0BAQsFADA9MQ4wDAYDVQQDDAVrYW5p
14
+ czEXMBUGCgmSJomT8ixkARkWB2NvbWNhcmQxEjAQBgoJkiaJk/IsZAEZFgJkZTAe
15
+ Fw0yMTA0MDcxMzQzNTZaFw0yMjA0MDcxMzQzNTZaMD0xDjAMBgNVBAMMBWthbmlz
16
+ MRcwFQYKCZImiZPyLGQBGRYHY29tY2FyZDESMBAGCgmSJomT8ixkARkWAmRlMIIB
17
+ IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApop+rNmg35bzRugZ21VMGqI6
18
+ HGzPLO4VHYncWn/xmgPU/ZMcZdfj6MzIaZJ/czXyt4eHpBk1r8QOV3gBXnRXEjVW
19
+ 9xi+EdVOkTV2/AVFKThcbTAQGiF/bT1n2M+B1GTybRzMg6hyhOJeGPqIhLfJEpxn
20
+ lJi4+ENAVT4MpqHEAGB8yFoPC0GqiOHQsdHxQV3P3c2OZqG+yJey74QtwA2tLcLn
21
+ Q53c63+VLGsOjODl1yPn/2ejyq8qWu6ahfTxiIlSar2UbwtaQGBDFdb2CXgEufXT
22
+ L7oaPxlmj+Q2oLOfOnInd2Oxop59HoJCQPsg8f921J43NCQGA8VHK6paxIRDLQID
23
+ AQABozkwNzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUvgTdT7fe
24
+ x17ugO3IOsjEJwW7KP4wDQYJKoZIhvcNAQELBQADggEBAGCQhS4TBqUG1bSY5gw5
25
+ emj2GNePHFNlXTZ/W0/7FlnXQz/LyBZeYmy4AIHcdY0w9xsu3bPNGk8kLBkHgK3Y
26
+ l/yWiUK0NYRI3K3yI2EoTfrHPDT8XIgBPeUUGv5Nje+SUYMQWsfYWKo3+vLEG64a
27
+ n1xP+1+g2Za39WCS5LwnDWMiIk47NnxR9yXErKd0Iau/Q/IarYsHFX6kWWmlMoln
28
+ W1lMomCcOJFwIPnsy6aqq7YfS0YcqyHjcvs1h5k3zPaIRWhoPlQivniMVMa3Txh+
29
+ NEF/4atY64rruzkyfxGEcrFFOHJIkWnWQjRGaiZdgULxf7ira2gEFvV/ZtamqJWF
30
+ c+I=
31
31
  -----END CERTIFICATE-----
32
- date: 2018-12-04 00:00:00.000000000 Z
32
+ date: 2021-07-07 00:00:00.000000000 Z
33
33
  dependencies:
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: bundler
@@ -57,14 +57,14 @@ dependencies:
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '10.0'
60
+ version: '13.0'
61
61
  type: :development
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '10.0'
67
+ version: '13.0'
68
68
  - !ruby/object:Gem::Dependency
69
69
  name: minitest
70
70
  requirement: !ruby/object:Gem::Requirement
@@ -79,6 +79,20 @@ dependencies:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
81
  version: '5.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: minitest-hooks
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
82
96
  - !ruby/object:Gem::Dependency
83
97
  name: yard
84
98
  requirement: !ruby/object:Gem::Requirement
@@ -94,10 +108,9 @@ dependencies:
94
108
  - !ruby/object:Gem::Version
95
109
  version: '0.9'
96
110
  description: |-
97
- {Eventbox} objects are event based and single threaded from the inside but thread-safe and blocking from the outside.
98
- Eventbox enforces a separation of code for event processing and code running blocking operations.
99
- Code inside an {Eventbox} object is executed non-concurrently and hence shouldn't do any blocking operations.
100
- This is similar to the typical JavaScript programming style.
111
+ Eventbox is a model of concurrent computation that is used to build thread-safe objects with arbitrary interfaces.
112
+ It is [kind of advancement](#comparison-threading-abstractions) of the well known [actor model](https://en.wikipedia.org/wiki/Actor_model) leveraging the possibilities of the ruby language.
113
+ It is a small, consistent but powerful threading abstraction which **integrates well into existing environments**.
101
114
  email:
102
115
  - lars@greiz-reinsdorf.de
103
116
  executables: []
@@ -108,6 +121,7 @@ files:
108
121
  - ".gitignore"
109
122
  - ".travis.yml"
110
123
  - ".yardopts"
124
+ - CHANGELOG.md
111
125
  - CODE_OF_CONDUCT.md
112
126
  - Gemfile
113
127
  - LICENSE.txt
@@ -116,12 +130,16 @@ files:
116
130
  - bin/console
117
131
  - bin/setup
118
132
  - docs/downloads.md
133
+ - docs/images/my_queue_calls.svg
134
+ - docs/my_queue_calls_github.md
135
+ - docs/my_queue_calls_local.md
119
136
  - docs/server.md
120
137
  - docs/threadpool.md
121
138
  - eventbox.gemspec
122
139
  - lib/eventbox.rb
123
140
  - lib/eventbox/argument_wrapper.rb
124
141
  - lib/eventbox/boxable.rb
142
+ - lib/eventbox/call_context.rb
125
143
  - lib/eventbox/event_loop.rb
126
144
  - lib/eventbox/object_registry.rb
127
145
  - lib/eventbox/sanitizer.rb
@@ -139,17 +157,19 @@ require_paths:
139
157
  - lib
140
158
  required_ruby_version: !ruby/object:Gem::Requirement
141
159
  requirements:
142
- - - "~>"
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: '3.0'
163
+ - - "<"
143
164
  - !ruby/object:Gem::Version
144
- version: '2.3'
165
+ version: '4.0'
145
166
  required_rubygems_version: !ruby/object:Gem::Requirement
146
167
  requirements:
147
168
  - - ">="
148
169
  - !ruby/object:Gem::Version
149
170
  version: '0'
150
171
  requirements: []
151
- rubyforge_project:
152
- rubygems_version: 2.7.7
172
+ rubygems_version: 3.3.0.dev
153
173
  signing_key:
154
174
  specification_version: 4
155
175
  summary: Manage multithreading with the safety of event based programming