hakuban 0.5.0 → 0.6.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.
@@ -1,9 +1,13 @@
1
1
  require 'set'
2
+ require 'ostruct'
2
3
 
3
4
  require_relative './ffi.rb'
4
5
 
5
6
  #TODO: explicit drops?
6
7
  #TODO: privative methods
8
+ #TODO: error classes
9
+
10
+ #AutoPointers use proc-wrapped calls because they segfault sometimes when used with FFI::method(:xxx) (why?)
7
11
 
8
12
  module Hakuban
9
13
 
@@ -28,26 +32,73 @@ module Hakuban
28
32
  end
29
33
 
30
34
 
35
+ @@logger_initialized = false
36
+
37
+ def self.logger_initialize(default_level, skip_if_already_initialized = false)
38
+ if @@logger_initialized and !skip_if_already_initialized
39
+ raise "Logger already initialized. This can't be done more than once. Make sure logger_initialize is called before any LocalNode gets constructed."
40
+ end
41
+ if not @@logger_initialized
42
+ raise "Invalid default log level string" if FFI::hakuban_logger_initialize(default_level) != 0
43
+ @@logger_initialized = true
44
+ end
45
+ end
46
+
47
+
31
48
  class LocalNode
32
49
 
33
50
  #TODO: explicit drop
34
51
 
35
52
  attr_reader :local_node_pointer #todo: hide
36
53
 
37
- def initialize(name=nil)
38
- result = FFI::hakuban_local_node_new(name || "local")
54
+ def initialize(name: nil)
55
+ @default_serializer = lambda { |data_type, data|
56
+ [["JSON"]+data_type, JSON.dump(data)]
57
+ }
58
+ @default_deserializer = lambda { |data_type, data|
59
+ raise "Expected JSON serialized data, got: #{dat_type}" if data_type[0] != "JSON"
60
+ [data_type[1..-1], JSON.load(data)]
61
+ }
62
+ Hakuban::logger_initialize("hakuban=warn", true)
63
+ result = FFI::hakuban_local_node_new(name || File.basename(caller_locations(0..1)[1].path))
39
64
  Hakuban::raise_if_error(result)
40
65
  @local_node_pointer = ::FFI::AutoPointer.new(result[:local_node_pointer], FFI::method(:hakuban_local_node_drop))
41
66
  end
42
67
 
68
+ def with_default_serializer(&block)
69
+ @default_serializer = block
70
+ self
71
+ end
72
+
73
+ def with_default_deserializer(&block)
74
+ @default_deserializer = block
75
+ self
76
+ end
77
+
78
+ def default_serializer(&block)
79
+ if block_given?
80
+ @default_serializer = block
81
+ else
82
+ @default_serializer
83
+ end
84
+ end
85
+
86
+ def default_deserializer(&block)
87
+ if block_given?
88
+ @default_deserializer = block
89
+ else
90
+ @default_deserializer
91
+ end
92
+ end
93
+
43
94
  def object(tags, descriptor)
44
95
  #TODO: accept real descriptor too
45
- ObjectBuilder.new(self, ObjectDescriptor.new(tags,descriptor))
96
+ ObjectBuilder.new(self, ObjectDescriptor.new(tags,descriptor), @default_serializer, @default_deserializer)
46
97
  end
47
98
 
48
99
  def tag(descriptor)
49
100
  #TODO: accept real descriptor too
50
- TagBuilder.new(self, TagDescriptor.new(descriptor))
101
+ TagBuilder.new(self, TagDescriptor.new(descriptor), @default_serializer, @default_deserializer)
51
102
  end
52
103
 
53
104
  end
@@ -55,16 +106,24 @@ module Hakuban
55
106
 
56
107
  class ObjectBuilder
57
108
 
58
- def initialize(store, descriptor)
59
- @store, @descriptor = store, descriptor
109
+ def initialize(store, descriptor, serializer, deserializer)
110
+ @store, @descriptor, @serializer, @deserializer = store, descriptor, serializer, deserializer
60
111
  end
61
112
 
62
113
  def observe
63
- ObjectObserve.new(@store, @descriptor)
114
+ ObjectObserve.new(@store, @descriptor, @deserializer)
64
115
  end
65
116
 
66
117
  def expose
67
- ObjectExpose.new(@store, @descriptor)
118
+ ObjectExpose.new(@store, @descriptor, @serializer)
119
+ end
120
+
121
+ def with_serializer(&block)
122
+ @serializer = block
123
+ end
124
+
125
+ def with_deserializer(&block)
126
+ @deserializer = block
68
127
  end
69
128
 
70
129
  end
@@ -72,16 +131,24 @@ module Hakuban
72
131
 
73
132
  class TagBuilder
74
133
 
75
- def initialize(store, descriptor)
76
- @store, @descriptor = store, descriptor
134
+ def initialize(store, descriptor, serializer, deserializer)
135
+ @store, @descriptor, @serializer, @deserializer = store, descriptor, serializer, deserializer
77
136
  end
78
137
 
79
138
  def observe
80
- TagObserve.new(@store, @descriptor)
139
+ TagObserve.new(@store, @descriptor, @deserializer)
81
140
  end
82
141
 
83
142
  def expose
84
- TagExpose.new(@store, @descriptor)
143
+ TagExpose.new(@store, @descriptor, @serializer)
144
+ end
145
+
146
+ def with_serializer(&block)
147
+ @serializer = block
148
+ end
149
+
150
+ def with_deserializer(&block)
151
+ @deserializer = block
85
152
  end
86
153
 
87
154
  end
@@ -91,15 +158,16 @@ module Hakuban
91
158
  attr_reader :tags, :json
92
159
 
93
160
  def initialize(tags,json)
94
- @tags, @json = Set.new(tags), json
161
+ @tags = Set.new(tags.map { |tag| tag.kind_of?(TagDescriptor) ? tag : TagDescriptor.new(tag) })
162
+ @json = json
95
163
  end
96
164
 
97
165
  def to_ffi
98
- FFI::FFIObjectDescriptor.construct(@tags, @json)
166
+ FFI::FFIObjectDescriptor.construct(@tags.map(&:json), @json)
99
167
  end
100
168
 
101
169
  def self.from_ffi(ffi)
102
- ObjectDescriptor.new(ffi.tags, ffi.json)
170
+ ObjectDescriptor.new(ffi.tags.map{ |tag| TagDescriptor.new(tag) }, ffi.json)
103
171
  end
104
172
 
105
173
  def ==(other)
@@ -111,6 +179,11 @@ module Hakuban
111
179
  def hash
112
180
  [@tags.hash, @json.hash].hash
113
181
  end
182
+
183
+ def inspect
184
+ "#<ObjectDescriptor @tags={%s}, @json=%p>"%[self.tags.map(&:inspect).join(","), self.json]
185
+ end
186
+
114
187
  end
115
188
 
116
189
 
@@ -142,68 +215,59 @@ module Hakuban
142
215
  end
143
216
 
144
217
 
145
- class EventQueue
146
-
147
- def initialize(queue, prefix, register_method, pointer, unregister_method)
148
- @queue = queue
149
- @callback = EventQueue.generate_callback(@queue, prefix)
150
- @registered_callback_pointer = ::FFI::AutoPointer.new(
151
- register_method.call(pointer, @callback, ::FFI::Pointer::NULL),
152
- EventQueue.generate_unregister(unregister_method, @callback)
153
- )
154
- #puts "Allocated callback pointer: #{@registered_callback_pointer}"
155
- #puts "Allocated callback queue: #{@events}"
156
- end
218
+ class ObjectDescriptorEvents
219
+ def initialize(pointer)
220
+ @events_pointer = ::FFI::AutoPointer.new(pointer, proc { |ptr| FFI.hakuban_object_descriptor_events_return(ptr) })
221
+ end
157
222
 
158
- def self.generate_callback(queue, prefix)
159
- proc { |_userdata, ffi_descriptor, ffi_action|
160
- #puts "Callback for queue: #{queue}"
223
+ # WARNING: this callback may be run from a separate, non-ruby, thread
224
+ def callback_register(&callback)
225
+ ffi_callback = proc { |_userdata, ffi_descriptor, ffi_action|
161
226
  action = Hakuban::action_int_to_symbol(ffi_action)
162
227
  descriptor = ObjectDescriptor.from_ffi(ffi_descriptor)
163
- queue << prefix + [action, descriptor]
228
+ callback.call(descriptor, action)
164
229
  }
230
+ @callback_pointer = ::FFI::AutoPointer.new(
231
+ FFI::hakuban_object_descriptor_events_callback_register(@events_pointer, ffi_callback, ::FFI::Pointer::NULL),
232
+ proc { |ptr| FFI::hakuban_object_descriptor_events_callback_unregister(ptr) }
233
+ )
165
234
  end
166
235
 
167
- def self.generate_unregister(unregister_method, callback)
168
- proc { |pointer|
169
- #puts "Dropping callback pointer: #{pointer}"
170
- callback # probably not needed, but I want to be sure this is captured
171
- unregister_method.call(pointer)
172
- }
236
+ def callback_unregister
237
+ @callback_pointer.free
238
+ @callback_pointer = nil
173
239
  end
174
240
 
175
- end
241
+ def drop
242
+ @events_pointer.free
243
+ @events_pointer = nil
244
+ end
176
245
 
246
+ end
177
247
 
178
248
  class ObjectObserve
179
249
 
180
250
  attr_reader :descriptor
181
251
 
182
- def initialize(local_node, descriptor)
183
- @local_node, @descriptor = local_node, descriptor
252
+ def initialize(local_node, descriptor, deserializer)
253
+ @local_node, @descriptor, @deserializer = local_node, descriptor, deserializer
184
254
  result = FFI::hakuban_object_observe_new(@local_node.local_node_pointer, descriptor.to_ffi)
185
255
  Hakuban::raise_if_error(result)
186
- @object_observe_pointer = ::FFI::AutoPointer.new(result[:object_observe_pointer], FFI::method(:hakuban_object_observe_drop))
187
- @queues = {}
256
+ @object_observe_pointer = ::FFI::AutoPointer.new(result[:object_observe_pointer], proc { |ptr| FFI::hakuban_object_observe_drop(ptr) })
188
257
  end
189
258
 
190
259
  def object_state
191
260
  raise "Attempt to use after 'drop'" if not @object_observe_pointer
192
- ObjectObserveState.new(FFI::hakuban_object_observe_state_borrow(@object_observe_pointer))
193
- end
194
-
195
- def send_events_to(queue, *prefix)
196
- @queues[queue] = EventQueue.new(
197
- queue,
198
- prefix,
199
- FFI::method(:hakuban_object_observe_changes_callback_register),
200
- @object_observe_pointer,
201
- FFI::method(:hakuban_object_callback_unregister)
202
- )
261
+ if (state_ptr = FFI::hakuban_object_observe_state_borrow(@object_observe_pointer)).null?
262
+ nil
263
+ else
264
+ ObjectObserveState.new(state_ptr, @deserializer)
265
+ end
203
266
  end
204
267
 
205
- def stop_sending_objects_events_to(queue)
206
- @queues.delete(queue)
268
+ def events
269
+ raise "Attempt to use after 'drop'" if not @object_observe_pointer
270
+ ObjectDescriptorEvents.new(FFI::hakuban_object_observe_events_get(@object_observe_pointer))
207
271
  end
208
272
 
209
273
  def inspect
@@ -213,12 +277,10 @@ module Hakuban
213
277
  def drop
214
278
  @object_observe_pointer.free
215
279
  @object_observe_pointer = nil
216
- @queues.values.each { |pointer| pointer.free }
217
- @queues.clear
218
280
  end
219
281
 
220
- def manage(object_class, *object_constructor_params)
221
- object_class::new(self, @descriptor, *object_constructor_params)
282
+ def dropped?
283
+ @object_observe_pointer.nil?
222
284
  end
223
285
 
224
286
  end
@@ -228,51 +290,51 @@ module Hakuban
228
290
 
229
291
  attr_reader :descriptor
230
292
 
231
- def initialize(local_node, descriptor)
232
- @local_node, @descriptor = local_node, descriptor
293
+ def initialize(local_node, descriptor, serializer)
294
+ @local_node, @descriptor, @serializer = local_node, descriptor, serializer
233
295
  result = FFI::hakuban_object_expose_new(@local_node.local_node_pointer, descriptor.to_ffi)
234
296
  Hakuban::raise_if_error(result)
235
297
  @object_expose_pointer = ::FFI::AutoPointer.new(result[:object_expose_pointer], FFI::method(:hakuban_object_expose_drop))
236
- @queues = {}
237
298
  end
238
299
 
239
- def set_object_state(version, data_type, data)
300
+ def set_object_state(version, data, data_type: [], assignment: 0)
240
301
  raise "Attempt to use after 'drop'" if not @object_expose_pointer
241
- data_type = ["MessagePack"] + data_type
242
- serialized = MessagePack.pack(data)
243
- result = FFI::hakuban_object_expose_state(@object_expose_pointer, FFI::FFIObjectExposeState.construct(version, data_type, serialized))
302
+ serialized_data_type, serialized_data = @serializer.call(data_type, data)
303
+ result = FFI::hakuban_object_expose_state(@object_expose_pointer, FFI::FFIObjectExposeState.construct(version, serialized_data_type, serialized_data), assignment)
244
304
  Hakuban::raise_if_error(result)
245
305
  result[:changed] == 1
246
306
  end
247
307
 
248
- def assigned
308
+ def assignment
249
309
  raise "Attempt to use after 'drop'" if not @object_expose_pointer
250
- FFI::hakuban_object_expose_assigned(@object_expose_pointer) == 1
310
+ FFI::hakuban_object_expose_assignment(@object_expose_pointer)
251
311
  end
252
312
 
253
- def send_events_to(queue, *prefix)
254
- @queues[queue] = EventQueue.new(
255
- queue,
256
- prefix,
257
- FFI::method(:hakuban_object_expose_changes_callback_register),
258
- @object_expose_pointer,
259
- FFI::method(:hakuban_object_callback_unregister)
260
- )
313
+ def assigned?
314
+ assignment > 0
315
+ end
316
+
317
+ def desynchronize(assignment)
318
+ raise "Attempt to use after 'drop'" if not @object_expose_pointer
319
+ FFI::hakuban_object_expose_desynchronize(@object_expose_pointer, assignment)
261
320
  end
262
321
 
263
- def stop_sending_objects_events_to(queue)
264
- @queues.delete(queue)
322
+ def events
323
+ raise "Attempt to use after 'drop'" if not @object_expose_pointer
324
+ ObjectDescriptorEvents.new(FFI::hakuban_object_expose_events_get(@object_expose_pointer))
325
+ end
326
+
327
+ def inspect
328
+ "#<ObjectExpose #{@descriptor}>"
265
329
  end
266
330
 
267
331
  def drop
268
332
  @object_expose_pointer.free
269
333
  @object_expose_pointer = nil
270
- @queues.values.each { |pointer| pointer.free }
271
- @queues.clear
272
334
  end
273
335
 
274
- def manage(object_class, *object_constructor_params)
275
- object_class::new(self, @descriptor, *object_constructor_params)
336
+ def dropped?
337
+ @object_expose_pointer.nil?
276
338
  end
277
339
 
278
340
  end
@@ -280,29 +342,45 @@ module Hakuban
280
342
 
281
343
  class ObjectObserveState
282
344
 
283
- attr_reader :data, :data_type, :data_version
284
-
285
- def initialize(raw_state)
286
- @raw_state = raw_state
287
- @synchronized = (raw_state[:synchronized] == 1)
288
- @data_type = raw_state[:data_type].read_array_of_pointer(raw_state[:data_type_length]).map { |string| string.read_string() }
289
- @data_version = raw_state[:version].read_array_of_type(::FFI::TYPE_INT64, :read_int64, raw_state[:version_length]) if not raw_state[:version].null?
290
- @data = MessagePack.unpack(raw_state[:raw].read_string_length(raw_state[:raw_length])) if not raw_state[:raw].null?
291
- ObjectSpace.define_finalizer(self, ObjectObserveState.finalize(@raw_state))
345
+ def initialize(state, deserializer)
346
+ @state, @deserializer = state, deserializer
347
+ ObjectSpace.define_finalizer(self, ObjectObserveState.finalize(@state))
292
348
  end
293
349
 
294
- def ObjectObserveState.finalize(raw_state)
350
+ def ObjectObserveState.finalize(state)
295
351
  proc { |_|
296
- FFI::hakuban_object_observe_state_return(raw_state)
352
+ FFI::hakuban_object_observe_state_return(state)
297
353
  }
298
354
  end
299
355
 
300
356
  def version
301
- @data_version
357
+ return @data_version if @data_version
358
+ ffi_version = FFI::hakuban_object_observe_state_get_data_version(@state)
359
+ @data_version = ffi_version[:version_elements].read_array_of_type(::FFI::TYPE_INT64, :read_int64, ffi_version[:version_length])
360
+ end
361
+
362
+ def data
363
+ return @data if @data
364
+ ffi_data = FFI::hakuban_object_observe_state_get_data(@state)
365
+ ffi_data_type = FFI::hakuban_object_observe_state_get_data_type(@state)
366
+ serialized_data_type = ffi_data_type[:type_elements].read_array_of_pointer(ffi_data_type[:type_length]).map { |string| string.read_string() }
367
+ @data_type, @data = @deserializer.call(serialized_data_type, ffi_data[:data_bytes].read_string_length(ffi_data[:data_length]))
368
+ @data
369
+ end
370
+
371
+ def data_type
372
+ return @data_type if @data_type
373
+ data
374
+ @data_type
375
+ end
376
+
377
+ def synchronized
378
+ return @synchronized if @synchronized
379
+ @synchronized = FFI::hakuban_object_observe_state_get_synchronized(@state)
302
380
  end
303
381
 
304
382
  def inspect
305
- "#<ObjectObserveState version=#{@data_version}>"
383
+ "#<ObjectObserveState @synchronized=%p @version=%p>"%[@synchronized, @data_version]
306
384
  end
307
385
 
308
386
  end
@@ -312,12 +390,11 @@ module Hakuban
312
390
 
313
391
  attr_reader :descriptor
314
392
 
315
- def initialize(local_node, descriptor)
316
- @local_node, @descriptor = local_node, descriptor
393
+ def initialize(local_node, descriptor, deserializer)
394
+ @local_node, @descriptor, @deserializer = local_node, descriptor, deserializer
317
395
  result = FFI::hakuban_tag_observe_new(@local_node.local_node_pointer, @descriptor.to_ffi)
318
396
  Hakuban::raise_if_error(result)
319
397
  @tag_observe_pointer = ::FFI::AutoPointer.new(result[:tag_observe_pointer], FFI::method(:hakuban_tag_observe_drop))
320
- @queues = {}
321
398
  end
322
399
 
323
400
  def object_descriptors
@@ -333,36 +410,21 @@ module Hakuban
333
410
  result = FFI::hakuban_tag_observe_object_state_borrow(@tag_observe_pointer, object_descriptor.to_ffi)
334
411
  return nil if result[:error] == 4
335
412
  Hakuban::raise_if_error(result)
336
- ObjectObserveState.new(result[:state])
413
+ ObjectObserveState.new(result[:state], @deserializer)
337
414
  end
338
415
 
339
- def send_events_to(*params)
340
- self.send_objects_events_to(*params)
341
- end
342
-
343
- def send_objects_events_to(queue, *prefix)
344
- @queues[queue] = EventQueue.new(
345
- queue,
346
- prefix,
347
- FFI::method(:hakuban_tag_observe_objects_changes_callback_register),
348
- @tag_observe_pointer,
349
- FFI::method(:hakuban_object_callback_unregister)
350
- )
351
- end
352
-
353
- def stop_sending_objects_events_to(queue)
354
- @queues.delete(queue)
416
+ def events
417
+ raise "Attempt to use after 'drop'" if not @tag_observe_pointer
418
+ ObjectDescriptorEvents.new(FFI::hakuban_tag_observe_events_get(@tag_observe_pointer))
355
419
  end
356
420
 
357
421
  def drop
358
422
  @tag_observe_pointer.free
359
423
  @tag_observe_pointer = nil
360
- @queues.values.each { |pointer| pointer.free }
361
- @queues.clear
362
424
  end
363
425
 
364
- def manage(object_class, *object_constructor_params)
365
- ObjectManager::new(self, object_class, *object_constructor_params)
426
+ def dropped?
427
+ @tag_observe_pointer.nil?
366
428
  end
367
429
 
368
430
  end
@@ -372,12 +434,11 @@ module Hakuban
372
434
 
373
435
  attr_reader :descriptor
374
436
 
375
- def initialize(local_node, descriptor)
376
- @local_node, @descriptor = local_node, descriptor
437
+ def initialize(local_node, descriptor, serializer)
438
+ @local_node, @descriptor, @serializer = local_node, descriptor, serializer
377
439
  result = FFI::hakuban_tag_expose_new(@local_node.local_node_pointer, descriptor.to_ffi)
378
440
  Hakuban::raise_if_error(result)
379
441
  @tag_expose_pointer = ::FFI::AutoPointer.new(result[:tag_expose_pointer], FFI::method(:hakuban_tag_expose_drop))
380
- @queues = {}
381
442
  end
382
443
 
383
444
  def object_descriptors
@@ -388,42 +449,41 @@ module Hakuban
388
449
  ret
389
450
  end
390
451
 
391
- def set_object_state(object_descriptor, version, data_type, data)
452
+ def set_object_state(object_descriptor, version, data, data_type: [], assignment: 0)
392
453
  raise "Attempt to use after 'drop'" if not @tag_expose_pointer
393
- data_type = ["MessagePack"] + data_type
394
- serialized = MessagePack.pack(data)
395
- result = FFI::hakuban_tag_expose_object_state(@tag_expose_pointer, object_descriptor.to_ffi, FFI::FFIObjectExposeState.construct(version, data_type, serialized))
454
+ raise if not version #TODO: replace this by type check
455
+ serialized_data_type, serialized_data = @serializer.call(data_type, data)
456
+ result = FFI::hakuban_tag_expose_object_state(@tag_expose_pointer, object_descriptor.to_ffi, FFI::FFIObjectExposeState.construct(version, serialized_data_type, serialized_data), assignment)
396
457
  Hakuban::raise_if_error(result)
397
458
  result[:changed] == 1
398
459
  end
399
460
 
400
- def send_events_to(*params)
401
- self.send_objects_events_to(*params)
461
+ def assignment(object_descriptor)
462
+ raise "Attempt to use after 'drop'" if not @tag_expose_pointer
463
+ FFI::hakuban_tag_expose_object_assignment(@tag_expose_pointer, object_descriptor.to_ffi)
402
464
  end
403
465
 
404
- def send_objects_events_to(queue, *prefix)
405
- @queues[queue] = EventQueue.new(
406
- queue,
407
- prefix,
408
- FFI::method(:hakuban_tag_expose_objects_changes_callback_register),
409
- @tag_expose_pointer,
410
- FFI::method(:hakuban_object_callback_unregister)
411
- )
466
+ def assigned?(object_descriptor)
467
+ assignment(object_descriptor) > 0
468
+ end
469
+
470
+ def desynchronize(object_descriptor, assignment)
471
+ raise "Attempt to use after 'drop'" if not @tag_expose_pointer
472
+ FFI::hakuban_tag_expose_object_desynchronize(@tag_expose_pointer, object_descriptor.to_ffi, assignment)
412
473
  end
413
474
 
414
- def stop_sending_objects_events_to(queue)
415
- @queues.delete(queue)
475
+ def events
476
+ raise "Attempt to use after 'drop'" if not @tag_expose_pointer
477
+ ObjectDescriptorEvents.new(FFI::hakuban_tag_expose_events_get(@tag_expose_pointer))
416
478
  end
417
479
 
418
480
  def drop
419
481
  @tag_expose_pointer.free
420
482
  @tag_expose_pointer = nil
421
- @queues.values.each { |pointer| pointer.free }
422
- @queues.clear
423
483
  end
424
484
 
425
- def manage(object_class, *object_constructor_params)
426
- ObjectManager::new(self, object_class, *object_constructor_params)
485
+ def dropped?
486
+ @tag_expose_pointer.nil?
427
487
  end
428
488
 
429
489
  end
@@ -452,104 +512,19 @@ module Hakuban
452
512
 
453
513
  #TODO: drop
454
514
 
455
- def initialize(url)
456
- result = FFI::hakuban_tokio_websocket_connector_new(Tokio.pointer, url)
515
+ def initialize(local_node, url)
516
+ result = FFI::hakuban_tokio_websocket_connector_new(Tokio.pointer, local_node.local_node_pointer, url)
457
517
  Hakuban::raise_if_error(result)
458
- @websocket_connector_pointer = result[:websocket_connector_pointer]
518
+ @websocket_connector_pointer = ::FFI::AutoPointer.new(result[:websocket_connector_pointer], WebsocketConnector.generate_drop)
459
519
  end
460
520
 
461
- def start(local_node)
462
- @local_node = local_node
463
- FFI::hakuban_tokio_websocket_connector_start(Tokio.pointer, @websocket_connector_pointer, @local_node.local_node_pointer)
464
- self
465
- end
466
-
467
- end
468
-
469
-
470
- # convenience utils
471
-
472
- class ObjectManager
473
-
474
- attr_reader :objects, :contract
475
-
476
- def initialize(contract, object_class, *object_constructor_params)
477
- @contract = contract
478
- @objects = {}
479
- @queue = Queue.new
480
- contract.send_events_to(@queue, :object)
481
- @thread = Thread.new {
482
- loop {
483
- event = @queue.shift
484
- case event[0..1]
485
- when [:control, :quit]
486
- break
487
- when [:object, :insert]
488
- @objects[event[2]] = object_class.new(@contract, event[2], *object_constructor_params)
489
- when [:object, :change]
490
- @objects[event[2]].change if @objects[event[2]].respond_to?(:change)
491
- when [:object, :remove]
492
- raise if not deleted = @objects.delete(event[2])
493
- deleted.drop if deleted.respond_to?(:drop)
494
- end
495
- }
521
+ def self.generate_drop
522
+ proc { |pointer|
523
+ FFI::hakuban_tokio_websocket_connector_drop(Tokio.pointer, pointer)
496
524
  }
497
525
  end
498
526
 
499
- def drop
500
- @contract.stop_sending_objects_events_to(@queue)
501
- @queue.push [:control, :quit]
502
- @thread.join
503
- @queue, @contract, @thread, @objects = nil, nil, nil, {}
504
- end
505
-
506
-
507
- class ManagedObject
508
- attr_reader :contract, :descriptor
509
- def initialize(contract, descriptor)
510
- @contract, @descriptor = contract, descriptor
511
- end
512
- end
513
-
514
-
515
- #TODO: fix the arity thing...
516
- module ObservedObject
517
- def state
518
- if @contract.method(:object_state).arity == 1
519
- @contract.object_state(@descriptor)
520
- else
521
- @contract.object_state
522
- end
523
- end
524
- def data
525
- if @contract.method(:object_state).arity == 1
526
- @contract.object_state(@descriptor)&.data
527
- else
528
- @contract.object_state&.data
529
- end
530
- end
531
- end
532
-
533
- module ExposedObject
534
- def state=(state)
535
- if @contract.method(:set_object_state).arity == 4
536
- @contract.set_object_state(@descriptor, *state)
537
- else
538
- @contract.set_object_state(*state)
539
- end
540
- end
541
- def data=(value)
542
- timestamp = Time.new.to_f.floor
543
- if @contract.method(:set_object_state).arity == 4
544
- @contract.set_object_state(@descriptor,[1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],[],value)
545
- else
546
- @contract.set_object_state([1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],[],value)
547
- end
548
- end
549
- end
550
527
  end
551
528
 
552
529
 
553
-
554
-
555
530
  end