hakuban 0.5.1 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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