hakuban 0.5.1 → 0.6.1

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,4 +1,5 @@
1
1
  require 'set'
2
+ require 'ostruct'
2
3
 
3
4
  require_relative './ffi.rb'
4
5
 
@@ -6,6 +7,7 @@ require_relative './ffi.rb'
6
7
  #TODO: privative methods
7
8
  #TODO: error classes
8
9
 
10
+ #AutoPointers use proc-wrapped calls because they segfault sometimes when used with FFI::method(:xxx) (why?)
9
11
 
10
12
  module Hakuban
11
13
 
@@ -49,21 +51,54 @@ module Hakuban
49
51
 
50
52
  attr_reader :local_node_pointer #todo: hide
51
53
 
52
- def initialize(name=nil)
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: #{data_type}" if data_type[0] != "JSON"
60
+ [data_type[1..-1], JSON.load(data)]
61
+ }
53
62
  Hakuban::logger_initialize("hakuban=warn", true)
54
63
  result = FFI::hakuban_local_node_new(name || File.basename(caller_locations(0..1)[1].path))
55
64
  Hakuban::raise_if_error(result)
56
65
  @local_node_pointer = ::FFI::AutoPointer.new(result[:local_node_pointer], FFI::method(:hakuban_local_node_drop))
57
66
  end
58
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
+
59
94
  def object(tags, descriptor)
60
95
  #TODO: accept real descriptor too
61
- ObjectBuilder.new(self, ObjectDescriptor.new(tags,descriptor))
96
+ ObjectBuilder.new(self, ObjectDescriptor.new(tags,descriptor), @default_serializer, @default_deserializer)
62
97
  end
63
98
 
64
99
  def tag(descriptor)
65
100
  #TODO: accept real descriptor too
66
- TagBuilder.new(self, TagDescriptor.new(descriptor))
101
+ TagBuilder.new(self, TagDescriptor.new(descriptor), @default_serializer, @default_deserializer)
67
102
  end
68
103
 
69
104
  end
@@ -71,16 +106,24 @@ module Hakuban
71
106
 
72
107
  class ObjectBuilder
73
108
 
74
- def initialize(store, descriptor)
75
- @store, @descriptor = store, descriptor
109
+ def initialize(store, descriptor, serializer, deserializer)
110
+ @store, @descriptor, @serializer, @deserializer = store, descriptor, serializer, deserializer
76
111
  end
77
112
 
78
113
  def observe
79
- ObjectObserve.new(@store, @descriptor)
114
+ ObjectObserve.new(@store, @descriptor, @deserializer)
80
115
  end
81
116
 
82
117
  def expose
83
- 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
84
127
  end
85
128
 
86
129
  end
@@ -88,16 +131,24 @@ module Hakuban
88
131
 
89
132
  class TagBuilder
90
133
 
91
- def initialize(store, descriptor)
92
- @store, @descriptor = store, descriptor
134
+ def initialize(store, descriptor, serializer, deserializer)
135
+ @store, @descriptor, @serializer, @deserializer = store, descriptor, serializer, deserializer
93
136
  end
94
137
 
95
138
  def observe
96
- TagObserve.new(@store, @descriptor)
139
+ TagObserve.new(@store, @descriptor, @deserializer)
97
140
  end
98
141
 
99
142
  def expose
100
- 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
101
152
  end
102
153
 
103
154
  end
@@ -107,15 +158,16 @@ module Hakuban
107
158
  attr_reader :tags, :json
108
159
 
109
160
  def initialize(tags,json)
110
- @tags, @json = Set.new(tags), json
161
+ @tags = Set.new(tags.map { |tag| tag.kind_of?(TagDescriptor) ? tag : TagDescriptor.new(tag) })
162
+ @json = json
111
163
  end
112
164
 
113
165
  def to_ffi
114
- FFI::FFIObjectDescriptor.construct(@tags, @json)
166
+ FFI::FFIObjectDescriptor.construct(@tags.map(&:json), @json)
115
167
  end
116
168
 
117
169
  def self.from_ffi(ffi)
118
- ObjectDescriptor.new(ffi.tags, ffi.json)
170
+ ObjectDescriptor.new(ffi.tags.map{ |tag| TagDescriptor.new(tag) }, ffi.json)
119
171
  end
120
172
 
121
173
  def ==(other)
@@ -163,68 +215,59 @@ module Hakuban
163
215
  end
164
216
 
165
217
 
166
- class EventQueue
167
-
168
- def initialize(queue, prefix, register_method, pointer, unregister_method)
169
- @queue = queue
170
- @callback = EventQueue.generate_callback(@queue, prefix)
171
- @registered_callback_pointer = ::FFI::AutoPointer.new(
172
- register_method.call(pointer, @callback, ::FFI::Pointer::NULL),
173
- EventQueue.generate_unregister(unregister_method, @callback)
174
- )
175
- #puts "Allocated callback pointer: #{@registered_callback_pointer}"
176
- #puts "Allocated callback queue: #{@events}"
177
- 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
178
222
 
179
- def self.generate_callback(queue, prefix)
180
- proc { |_userdata, ffi_descriptor, ffi_action|
181
- #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|
182
226
  action = Hakuban::action_int_to_symbol(ffi_action)
183
227
  descriptor = ObjectDescriptor.from_ffi(ffi_descriptor)
184
- queue << prefix + [action, descriptor]
228
+ callback.call(descriptor, action)
185
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
+ )
186
234
  end
187
235
 
188
- def self.generate_unregister(unregister_method, callback)
189
- proc { |pointer|
190
- #puts "Dropping callback pointer: #{pointer}"
191
- callback # probably not needed, but I want to be sure this is captured
192
- unregister_method.call(pointer)
193
- }
236
+ def callback_unregister
237
+ @callback_pointer.free
238
+ @callback_pointer = nil
194
239
  end
195
240
 
196
- end
241
+ def drop
242
+ @events_pointer.free
243
+ @events_pointer = nil
244
+ end
197
245
 
246
+ end
198
247
 
199
248
  class ObjectObserve
200
249
 
201
250
  attr_reader :descriptor
202
251
 
203
- def initialize(local_node, descriptor)
204
- @local_node, @descriptor = local_node, descriptor
252
+ def initialize(local_node, descriptor, deserializer)
253
+ @local_node, @descriptor, @deserializer = local_node, descriptor, deserializer
205
254
  result = FFI::hakuban_object_observe_new(@local_node.local_node_pointer, descriptor.to_ffi)
206
255
  Hakuban::raise_if_error(result)
207
- @object_observe_pointer = ::FFI::AutoPointer.new(result[:object_observe_pointer], FFI::method(:hakuban_object_observe_drop))
208
- @queues = {}
256
+ @object_observe_pointer = ::FFI::AutoPointer.new(result[:object_observe_pointer], proc { |ptr| FFI::hakuban_object_observe_drop(ptr) })
209
257
  end
210
258
 
211
259
  def object_state
212
260
  raise "Attempt to use after 'drop'" if not @object_observe_pointer
213
- ObjectObserveState.new(FFI::hakuban_object_observe_state_borrow(@object_observe_pointer))
214
- end
215
-
216
- def send_events_to(queue, *prefix)
217
- @queues[queue] = EventQueue.new(
218
- queue,
219
- prefix,
220
- FFI::method(:hakuban_object_observe_changes_callback_register),
221
- @object_observe_pointer,
222
- FFI::method(:hakuban_object_callback_unregister)
223
- )
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
224
266
  end
225
267
 
226
- def stop_sending_objects_events_to(queue)
227
- @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))
228
271
  end
229
272
 
230
273
  def inspect
@@ -234,13 +277,10 @@ module Hakuban
234
277
  def drop
235
278
  @object_observe_pointer.free
236
279
  @object_observe_pointer = nil
237
- @queues.clear
238
280
  end
239
281
 
240
- def manage(manager=nil)
241
- manager ||= ObjectManager.new
242
- manager.contract = self
243
- manager
282
+ def dropped?
283
+ @object_observe_pointer.nil?
244
284
  end
245
285
 
246
286
  end
@@ -250,40 +290,38 @@ module Hakuban
250
290
 
251
291
  attr_reader :descriptor
252
292
 
253
- def initialize(local_node, descriptor)
254
- @local_node, @descriptor = local_node, descriptor
293
+ def initialize(local_node, descriptor, serializer)
294
+ @local_node, @descriptor, @serializer = local_node, descriptor, serializer
255
295
  result = FFI::hakuban_object_expose_new(@local_node.local_node_pointer, descriptor.to_ffi)
256
296
  Hakuban::raise_if_error(result)
257
297
  @object_expose_pointer = ::FFI::AutoPointer.new(result[:object_expose_pointer], FFI::method(:hakuban_object_expose_drop))
258
- @queues = {}
259
298
  end
260
299
 
261
- def set_object_state(version, data_type, data)
300
+ def set_object_state(version, data, data_type: [], assignment: 0)
262
301
  raise "Attempt to use after 'drop'" if not @object_expose_pointer
263
- data_type = ["MessagePack"] + data_type
264
- serialized = MessagePack.pack(data)
265
- 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)
266
304
  Hakuban::raise_if_error(result)
267
305
  result[:changed] == 1
268
306
  end
269
307
 
270
- def assigned
308
+ def assignment
271
309
  raise "Attempt to use after 'drop'" if not @object_expose_pointer
272
- FFI::hakuban_object_expose_assigned(@object_expose_pointer) == 1
310
+ FFI::hakuban_object_expose_assignment(@object_expose_pointer)
273
311
  end
274
312
 
275
- def send_events_to(queue, *prefix)
276
- @queues[queue] = EventQueue.new(
277
- queue,
278
- prefix,
279
- FFI::method(:hakuban_object_expose_changes_callback_register),
280
- @object_expose_pointer,
281
- FFI::method(:hakuban_object_callback_unregister)
282
- )
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)
283
320
  end
284
321
 
285
- def stop_sending_objects_events_to(queue)
286
- @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))
287
325
  end
288
326
 
289
327
  def inspect
@@ -293,13 +331,10 @@ module Hakuban
293
331
  def drop
294
332
  @object_expose_pointer.free
295
333
  @object_expose_pointer = nil
296
- @queues.clear
297
334
  end
298
335
 
299
- def manage(manager=nil)
300
- manager ||= ObjectManager.new
301
- manager.contract = self
302
- manager
336
+ def dropped?
337
+ @object_expose_pointer.nil?
303
338
  end
304
339
 
305
340
  end
@@ -307,25 +342,41 @@ module Hakuban
307
342
 
308
343
  class ObjectObserveState
309
344
 
310
- attr_reader :data, :data_type, :data_version, :synchronized
311
-
312
- def initialize(raw_state)
313
- @raw_state = raw_state
314
- @synchronized = (raw_state[:synchronized] == 1)
315
- @data_type = raw_state[:data_type].read_array_of_pointer(raw_state[:data_type_length]).map { |string| string.read_string() }
316
- @data_version = raw_state[:version].read_array_of_type(::FFI::TYPE_INT64, :read_int64, raw_state[:version_length]) if not raw_state[:version].null?
317
- @data = MessagePack.unpack(raw_state[:raw].read_string_length(raw_state[:raw_length])) if not raw_state[:raw].null?
318
- 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))
319
348
  end
320
349
 
321
- def ObjectObserveState.finalize(raw_state)
350
+ def ObjectObserveState.finalize(state)
322
351
  proc { |_|
323
- FFI::hakuban_object_observe_state_return(raw_state)
352
+ FFI::hakuban_object_observe_state_return(state)
324
353
  }
325
354
  end
326
355
 
327
356
  def version
328
- @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)
329
380
  end
330
381
 
331
382
  def inspect
@@ -339,12 +390,11 @@ module Hakuban
339
390
 
340
391
  attr_reader :descriptor
341
392
 
342
- def initialize(local_node, descriptor)
343
- @local_node, @descriptor = local_node, descriptor
393
+ def initialize(local_node, descriptor, deserializer)
394
+ @local_node, @descriptor, @deserializer = local_node, descriptor, deserializer
344
395
  result = FFI::hakuban_tag_observe_new(@local_node.local_node_pointer, @descriptor.to_ffi)
345
396
  Hakuban::raise_if_error(result)
346
397
  @tag_observe_pointer = ::FFI::AutoPointer.new(result[:tag_observe_pointer], FFI::method(:hakuban_tag_observe_drop))
347
- @queues = {}
348
398
  end
349
399
 
350
400
  def object_descriptors
@@ -360,37 +410,21 @@ module Hakuban
360
410
  result = FFI::hakuban_tag_observe_object_state_borrow(@tag_observe_pointer, object_descriptor.to_ffi)
361
411
  return nil if result[:error] == 4
362
412
  Hakuban::raise_if_error(result)
363
- ObjectObserveState.new(result[:state])
364
- end
365
-
366
- def send_events_to(*params)
367
- self.send_objects_events_to(*params)
368
- end
369
-
370
- def send_objects_events_to(queue, *prefix)
371
- @queues[queue] = EventQueue.new(
372
- queue,
373
- prefix,
374
- FFI::method(:hakuban_tag_observe_objects_changes_callback_register),
375
- @tag_observe_pointer,
376
- FFI::method(:hakuban_object_callback_unregister)
377
- )
413
+ ObjectObserveState.new(result[:state], @deserializer)
378
414
  end
379
415
 
380
- def stop_sending_objects_events_to(queue)
381
- @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))
382
419
  end
383
420
 
384
421
  def drop
385
422
  @tag_observe_pointer.free
386
423
  @tag_observe_pointer = nil
387
- @queues.clear
388
424
  end
389
425
 
390
- def manage(manager=nil)
391
- manager ||= ObjectManager.new
392
- manager.contract = self
393
- manager
426
+ def dropped?
427
+ @tag_observe_pointer.nil?
394
428
  end
395
429
 
396
430
  end
@@ -400,12 +434,11 @@ module Hakuban
400
434
 
401
435
  attr_reader :descriptor
402
436
 
403
- def initialize(local_node, descriptor)
404
- @local_node, @descriptor = local_node, descriptor
437
+ def initialize(local_node, descriptor, serializer)
438
+ @local_node, @descriptor, @serializer = local_node, descriptor, serializer
405
439
  result = FFI::hakuban_tag_expose_new(@local_node.local_node_pointer, descriptor.to_ffi)
406
440
  Hakuban::raise_if_error(result)
407
441
  @tag_expose_pointer = ::FFI::AutoPointer.new(result[:tag_expose_pointer], FFI::method(:hakuban_tag_expose_drop))
408
- @queues = {}
409
442
  end
410
443
 
411
444
  def object_descriptors
@@ -416,43 +449,41 @@ module Hakuban
416
449
  ret
417
450
  end
418
451
 
419
- def set_object_state(object_descriptor, version, data_type, data)
452
+ def set_object_state(object_descriptor, version, data, data_type: [], assignment: 0)
420
453
  raise "Attempt to use after 'drop'" if not @tag_expose_pointer
421
- data_type = ["MessagePack"] + data_type
422
- serialized = MessagePack.pack(data)
423
- 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)
424
457
  Hakuban::raise_if_error(result)
425
458
  result[:changed] == 1
426
459
  end
427
460
 
428
- def send_events_to(*params)
429
- 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)
430
464
  end
431
465
 
432
- def send_objects_events_to(queue, *prefix)
433
- @queues[queue] = EventQueue.new(
434
- queue,
435
- prefix,
436
- FFI::method(:hakuban_tag_expose_objects_changes_callback_register),
437
- @tag_expose_pointer,
438
- FFI::method(:hakuban_object_callback_unregister)
439
- )
466
+ def assigned?(object_descriptor)
467
+ assignment(object_descriptor) > 0
440
468
  end
441
469
 
442
- def stop_sending_objects_events_to(queue)
443
- @queues.delete(queue)
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)
473
+ end
474
+
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))
444
478
  end
445
479
 
446
480
  def drop
447
481
  @tag_expose_pointer.free
448
482
  @tag_expose_pointer = nil
449
- @queues.clear
450
483
  end
451
484
 
452
- def manage(manager=nil)
453
- manager ||= ObjectManager.new
454
- manager.contract = self
455
- manager
485
+ def dropped?
486
+ @tag_expose_pointer.nil?
456
487
  end
457
488
 
458
489
  end
@@ -487,12 +518,6 @@ module Hakuban
487
518
  @websocket_connector_pointer = ::FFI::AutoPointer.new(result[:websocket_connector_pointer], WebsocketConnector.generate_drop)
488
519
  end
489
520
 
490
- def start(local_node)
491
- @local_node = local_node
492
- FFI::hakuban_tokio_websocket_connector_start(Tokio.pointer, @websocket_connector_pointer, @local_node.local_node_pointer)
493
- self
494
- end
495
-
496
521
  def self.generate_drop
497
522
  proc { |pointer|
498
523
  FFI::hakuban_tokio_websocket_connector_drop(Tokio.pointer, pointer)
@@ -502,149 +527,4 @@ module Hakuban
502
527
  end
503
528
 
504
529
 
505
- # convenience utils
506
-
507
- #TODO: replace this mess (<Hash, dynamic mixings) with some "deref"
508
- class ObjectManager < Hash
509
-
510
- module ObservedObjectManager
511
- def descriptor; self.values.first.descriptor; end
512
- def state; self.values.first.state; end
513
- def data; self.values.first.data; end
514
- def version; self.values.first.verison; end
515
- def synchronized; self.values.first.synchronized; end
516
- def construct_object(contract, descriptor); ObservedObject.new(contract, descriptor); end
517
- end
518
-
519
- module ExposedObjectManager
520
- def descriptor; self.values.first.descriptor; end
521
- def state=(params); self.values.first.state = params; end
522
- def data=(params); self.values.first.data = params; end
523
- def state_set(params); self.values.first.state_set(params); end
524
- def data_set(params); self.values.first.data_set(params); end
525
- def construct_object(contract, descriptor); ExposedObject.new(contract, descriptor); end
526
- end
527
-
528
- module ObservedTagManager
529
- def construct_object(contract, descriptor); ObservedObject.new(contract, descriptor); end
530
- end
531
-
532
- module ExposedTagManager
533
- def construct_object(contract, descriptor); ExposedObject.new(contract, descriptor); end
534
- end
535
-
536
- attr_reader :objects, :contract
537
-
538
- def contract=(contract)
539
- raise "You can't change contracts, sorry." if @contract
540
- @contract = contract
541
- case @contract.class.name
542
- when "Hakuban::ObjectObserve" then singleton_class.include(ObservedObjectManager)
543
- when "Hakuban::ObjectExpose" then singleton_class.include(ExposedObjectManager)
544
- when "Hakuban::TagObserve" then singleton_class.include(ObservedTagManager)
545
- when "Hakuban::TagExpose" then singleton_class.include(ExposedTagManager)
546
- else raise
547
- end
548
-
549
- @queue = Queue.new
550
- @contract.send_events_to(@queue, :object)
551
- if @contract.kind_of?(ObjectObserve) or @contract.kind_of?(ObjectExpose)
552
- event, param, descriptor = @queue.pop
553
- raise if event != :object or param != :insert or not descriptor
554
- self[descriptor] = self.construct_object(@contract, descriptor)
555
- end
556
-
557
- @thread = Thread.new {
558
- loop {
559
- event,param,descriptor = @queue.shift
560
- case [event, param]
561
- when [:control, :quit]
562
- break
563
- when [:object, :insert]
564
- self[descriptor] = construct_object(@contract, descriptor)
565
- object_insert(self[descriptor])
566
- when [:object, :change]
567
- object_change(self[descriptor])
568
- when [:object, :remove]
569
- object_remove(delete(descriptor))
570
- end
571
- }
572
- }
573
- end
574
-
575
-
576
- def object_insert(object)
577
- # noop default
578
- end
579
-
580
- def object_change(object)
581
- # noop default
582
- end
583
-
584
- def object_remove(object)
585
- # noop default
586
- end
587
-
588
-
589
- def drop
590
- @contract.stop_sending_objects_events_to(@queue)
591
- @queue.push [:control, :quit]
592
- @thread.join
593
- @queue, @contract, @thread, @objects = nil, nil, nil, {}
594
- end
595
-
596
-
597
- class ManagedObject
598
- attr_reader :contract, :descriptor
599
- def initialize(contract, descriptor)
600
- @contract, @descriptor = contract, descriptor
601
- end
602
- end
603
-
604
- #TODO: remove the arity thing...
605
- class ObservedObject < ManagedObject
606
- def state
607
- if @contract.method(:object_state).arity == 1
608
- @contract.object_state(@descriptor)
609
- else
610
- @contract.object_state
611
- end
612
- end
613
- def data
614
- if @contract.method(:object_state).arity == 1
615
- @contract.object_state(@descriptor)&.data
616
- else
617
- @contract.object_state&.data
618
- end
619
- end
620
- end
621
-
622
- class ExposedObject < ManagedObject
623
- def state_set(state)
624
- if @contract.method(:set_object_state).arity == 4
625
- @contract.set_object_state(@descriptor, *state)
626
- else
627
- @contract.set_object_state(*state)
628
- end
629
- end
630
- def state=(state)
631
- state_set(state)
632
- end
633
- def data_set(value)
634
- timestamp = Time.new.to_f
635
- if @contract.method(:set_object_state).arity == 4
636
- @contract.set_object_state(@descriptor,[1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],[],value)
637
- else
638
- @contract.set_object_state([1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],[],value)
639
- end
640
- end
641
- def data=(value)
642
- data_set(value)
643
- end
644
- end
645
- end
646
-
647
-
648
-
649
-
650
530
  end