eventbox 0.1.0 → 1.0.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.
@@ -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