hakuban 0.5.2 → 0.6.2

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,11 +1,14 @@
1
1
  require 'set'
2
+ require 'ostruct'
2
3
 
3
4
  require_relative './ffi.rb'
5
+ require_relative './event-queue.rb'
4
6
 
5
7
  #TODO: explicit drops?
6
8
  #TODO: privative methods
7
9
  #TODO: error classes
8
10
 
11
+ #AutoPointers use proc-wrapped calls because they segfault sometimes when used with FFI::method(:xxx) (why?)
9
12
 
10
13
  module Hakuban
11
14
 
@@ -49,21 +52,54 @@ module Hakuban
49
52
 
50
53
  attr_reader :local_node_pointer #todo: hide
51
54
 
52
- def initialize(name=nil)
55
+ def initialize(name: nil)
56
+ @default_serializer = lambda { |data_type, data|
57
+ [["JSON"]+data_type, JSON.dump(data)]
58
+ }
59
+ @default_deserializer = lambda { |data_type, data|
60
+ raise "Expected JSON serialized data, got: #{data_type}" if data_type[0] != "JSON"
61
+ [data_type[1..-1], JSON.load(data)]
62
+ }
53
63
  Hakuban::logger_initialize("hakuban=warn", true)
54
64
  result = FFI::hakuban_local_node_new(name || File.basename(caller_locations(0..1)[1].path))
55
65
  Hakuban::raise_if_error(result)
56
66
  @local_node_pointer = ::FFI::AutoPointer.new(result[:local_node_pointer], FFI::method(:hakuban_local_node_drop))
57
67
  end
58
68
 
69
+ def with_default_serializer(&block)
70
+ @default_serializer = block
71
+ self
72
+ end
73
+
74
+ def with_default_deserializer(&block)
75
+ @default_deserializer = block
76
+ self
77
+ end
78
+
79
+ def default_serializer(&block)
80
+ if block_given?
81
+ @default_serializer = block
82
+ else
83
+ @default_serializer
84
+ end
85
+ end
86
+
87
+ def default_deserializer(&block)
88
+ if block_given?
89
+ @default_deserializer = block
90
+ else
91
+ @default_deserializer
92
+ end
93
+ end
94
+
59
95
  def object(tags, descriptor)
60
96
  #TODO: accept real descriptor too
61
- ObjectBuilder.new(self, ObjectDescriptor.new(tags,descriptor))
97
+ ObjectBuilder.new(self, ObjectDescriptor.new(tags,descriptor), @default_serializer, @default_deserializer)
62
98
  end
63
99
 
64
100
  def tag(descriptor)
65
101
  #TODO: accept real descriptor too
66
- TagBuilder.new(self, TagDescriptor.new(descriptor))
102
+ TagBuilder.new(self, TagDescriptor.new(descriptor), @default_serializer, @default_deserializer)
67
103
  end
68
104
 
69
105
  end
@@ -71,16 +107,24 @@ module Hakuban
71
107
 
72
108
  class ObjectBuilder
73
109
 
74
- def initialize(store, descriptor)
75
- @store, @descriptor = store, descriptor
110
+ def initialize(store, descriptor, serializer, deserializer)
111
+ @store, @descriptor, @serializer, @deserializer = store, descriptor, serializer, deserializer
76
112
  end
77
113
 
78
114
  def observe
79
- ObjectObserve.new(@store, @descriptor)
115
+ ObjectObserve.new(@store, @descriptor, @deserializer)
80
116
  end
81
117
 
82
118
  def expose
83
- ObjectExpose.new(@store, @descriptor)
119
+ ObjectExpose.new(@store, @descriptor, @serializer)
120
+ end
121
+
122
+ def with_serializer(&block)
123
+ @serializer = block
124
+ end
125
+
126
+ def with_deserializer(&block)
127
+ @deserializer = block
84
128
  end
85
129
 
86
130
  end
@@ -88,16 +132,24 @@ module Hakuban
88
132
 
89
133
  class TagBuilder
90
134
 
91
- def initialize(store, descriptor)
92
- @store, @descriptor = store, descriptor
135
+ def initialize(store, descriptor, serializer, deserializer)
136
+ @store, @descriptor, @serializer, @deserializer = store, descriptor, serializer, deserializer
93
137
  end
94
138
 
95
139
  def observe
96
- TagObserve.new(@store, @descriptor)
140
+ TagObserve.new(@store, @descriptor, @deserializer)
97
141
  end
98
142
 
99
143
  def expose
100
- TagExpose.new(@store, @descriptor)
144
+ TagExpose.new(@store, @descriptor, @serializer)
145
+ end
146
+
147
+ def with_serializer(&block)
148
+ @serializer = block
149
+ end
150
+
151
+ def with_deserializer(&block)
152
+ @deserializer = block
101
153
  end
102
154
 
103
155
  end
@@ -107,15 +159,16 @@ module Hakuban
107
159
  attr_reader :tags, :json
108
160
 
109
161
  def initialize(tags,json)
110
- @tags, @json = Set.new(tags), json
162
+ @tags = Set.new(tags.map { |tag| tag.kind_of?(TagDescriptor) ? tag : TagDescriptor.new(tag) })
163
+ @json = json
111
164
  end
112
165
 
113
166
  def to_ffi
114
- FFI::FFIObjectDescriptor.construct(@tags, @json)
167
+ FFI::FFIObjectDescriptor.construct(@tags.map(&:json), @json)
115
168
  end
116
169
 
117
170
  def self.from_ffi(ffi)
118
- ObjectDescriptor.new(ffi.tags, ffi.json)
171
+ ObjectDescriptor.new(ffi.tags.map{ |tag| TagDescriptor.new(tag) }, ffi.json)
119
172
  end
120
173
 
121
174
  def ==(other)
@@ -163,68 +216,37 @@ module Hakuban
163
216
  end
164
217
 
165
218
 
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
178
-
179
- def self.generate_callback(queue, prefix)
180
- proc { |_userdata, ffi_descriptor, ffi_action|
181
- #puts "Callback for queue: #{queue}"
182
- action = Hakuban::action_int_to_symbol(ffi_action)
183
- descriptor = ObjectDescriptor.from_ffi(ffi_descriptor)
184
- queue << prefix + [action, descriptor]
185
- }
186
- end
187
-
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
- }
194
- end
195
-
196
- end
197
-
198
-
199
219
  class ObjectObserve
200
220
 
201
221
  attr_reader :descriptor
202
222
 
203
- def initialize(local_node, descriptor)
204
- @local_node, @descriptor = local_node, descriptor
223
+ def initialize(local_node, descriptor, deserializer)
224
+ @local_node, @descriptor, @deserializer = local_node, descriptor, deserializer
205
225
  result = FFI::hakuban_object_observe_new(@local_node.local_node_pointer, descriptor.to_ffi)
206
226
  Hakuban::raise_if_error(result)
207
- @object_observe_pointer = ::FFI::AutoPointer.new(result[:object_observe_pointer], FFI::method(:hakuban_object_observe_drop))
208
- @queues = {}
227
+ @queues = []
228
+ @object_observe_pointer = ::FFI::AutoPointer.new(result[:object_observe_pointer], proc { |ptr| FFI::hakuban_object_observe_drop(ptr) })
209
229
  end
210
230
 
211
231
  def object_state
212
232
  raise "Attempt to use after 'drop'" if not @object_observe_pointer
213
- ObjectObserveState.new(FFI::hakuban_object_observe_state_borrow(@object_observe_pointer))
233
+ if (state_ptr = FFI::hakuban_object_observe_state_borrow(@object_observe_pointer)).null?
234
+ nil
235
+ else
236
+ ObjectObserveState.new(state_ptr, @deserializer)
237
+ end
214
238
  end
215
239
 
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
- )
224
- end
240
+ def new_callback_event_queue
241
+ raise "Attempt to use after 'drop'" if not @object_observe_pointer
242
+ ObjectDescriptorCallbackEventQueue.new(FFI::hakuban_object_observe_events_get(@object_observe_pointer))
243
+ end
225
244
 
226
- def stop_sending_objects_events_to(queue)
227
- @queues.delete(queue)
245
+ def new_event_queue
246
+ raise "Attempt to use after 'drop'" if not @object_observe_pointer
247
+ queue = ObjectDescriptorEventQueue.new(self)
248
+ @queues << queue
249
+ queue
228
250
  end
229
251
 
230
252
  def inspect
@@ -234,13 +256,12 @@ module Hakuban
234
256
  def drop
235
257
  @object_observe_pointer.free
236
258
  @object_observe_pointer = nil
259
+ @queues.each(&:close) #is this atomic?
237
260
  @queues.clear
238
261
  end
239
262
 
240
- def manage(manager=nil)
241
- manager ||= ObjectManager.new
242
- manager.contract = self
243
- manager
263
+ def dropped?
264
+ @object_observe_pointer.nil?
244
265
  end
245
266
 
246
267
  end
@@ -250,40 +271,46 @@ module Hakuban
250
271
 
251
272
  attr_reader :descriptor
252
273
 
253
- def initialize(local_node, descriptor)
254
- @local_node, @descriptor = local_node, descriptor
274
+ def initialize(local_node, descriptor, serializer)
275
+ @local_node, @descriptor, @serializer = local_node, descriptor, serializer
255
276
  result = FFI::hakuban_object_expose_new(@local_node.local_node_pointer, descriptor.to_ffi)
256
277
  Hakuban::raise_if_error(result)
278
+ @queues = []
257
279
  @object_expose_pointer = ::FFI::AutoPointer.new(result[:object_expose_pointer], FFI::method(:hakuban_object_expose_drop))
258
- @queues = {}
259
280
  end
260
281
 
261
- def set_object_state(version, data_type, data)
282
+ def set_object_state(version, data, data_type: [], assignment: 0)
262
283
  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))
284
+ serialized_data_type, serialized_data = @serializer.call(data_type, data)
285
+ result = FFI::hakuban_object_expose_state(@object_expose_pointer, FFI::FFIObjectExposeState.construct(version, serialized_data_type, serialized_data), assignment)
266
286
  Hakuban::raise_if_error(result)
267
287
  result[:changed] == 1
268
288
  end
269
289
 
270
- def assigned
290
+ def assignment
291
+ raise "Attempt to use after 'drop'" if not @object_expose_pointer
292
+ FFI::hakuban_object_expose_assignment(@object_expose_pointer)
293
+ end
294
+
295
+ def assigned?
296
+ assignment > 0
297
+ end
298
+
299
+ def desynchronize(assignment)
271
300
  raise "Attempt to use after 'drop'" if not @object_expose_pointer
272
- FFI::hakuban_object_expose_assigned(@object_expose_pointer) == 1
301
+ FFI::hakuban_object_expose_desynchronize(@object_expose_pointer, assignment)
273
302
  end
274
303
 
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
- )
304
+ def new_callback_event_queue
305
+ raise "Attempt to use after 'drop'" if not @object_expose_pointer
306
+ ObjectDescriptorCallbackEventQueue.new(FFI::hakuban_object_expose_events_get(@object_expose_pointer))
283
307
  end
284
308
 
285
- def stop_sending_objects_events_to(queue)
286
- @queues.delete(queue)
309
+ def new_event_queue
310
+ raise "Attempt to use after 'drop'" if not @object_expose_pointer
311
+ queue = ObjectDescriptorEventQueue.new(self)
312
+ @queues << queue
313
+ queue
287
314
  end
288
315
 
289
316
  def inspect
@@ -293,13 +320,11 @@ module Hakuban
293
320
  def drop
294
321
  @object_expose_pointer.free
295
322
  @object_expose_pointer = nil
296
- @queues.clear
297
- end
323
+ @queues.each(&:close) #is this atomic?
324
+ @queues.clear end
298
325
 
299
- def manage(manager=nil)
300
- manager ||= ObjectManager.new
301
- manager.contract = self
302
- manager
326
+ def dropped?
327
+ @object_expose_pointer.nil?
303
328
  end
304
329
 
305
330
  end
@@ -307,25 +332,41 @@ module Hakuban
307
332
 
308
333
  class ObjectObserveState
309
334
 
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))
335
+ def initialize(state, deserializer)
336
+ @state, @deserializer = state, deserializer
337
+ ObjectSpace.define_finalizer(self, ObjectObserveState.finalize(@state))
319
338
  end
320
339
 
321
- def ObjectObserveState.finalize(raw_state)
340
+ def ObjectObserveState.finalize(state)
322
341
  proc { |_|
323
- FFI::hakuban_object_observe_state_return(raw_state)
342
+ FFI::hakuban_object_observe_state_return(state)
324
343
  }
325
344
  end
326
345
 
327
346
  def version
328
- @data_version
347
+ return @data_version if @data_version
348
+ ffi_version = FFI::hakuban_object_observe_state_get_data_version(@state)
349
+ @data_version = ffi_version[:version_elements].read_array_of_type(::FFI::TYPE_INT64, :read_int64, ffi_version[:version_length])
350
+ end
351
+
352
+ def data
353
+ return @data if @data
354
+ ffi_data = FFI::hakuban_object_observe_state_get_data(@state)
355
+ ffi_data_type = FFI::hakuban_object_observe_state_get_data_type(@state)
356
+ serialized_data_type = ffi_data_type[:type_elements].read_array_of_pointer(ffi_data_type[:type_length]).map { |string| string.read_string() }
357
+ @data_type, @data = @deserializer.call(serialized_data_type, ffi_data[:data_bytes].read_string_length(ffi_data[:data_length]))
358
+ @data
359
+ end
360
+
361
+ def data_type
362
+ return @data_type if @data_type
363
+ data
364
+ @data_type
365
+ end
366
+
367
+ def synchronized
368
+ return @synchronized if @synchronized
369
+ @synchronized = FFI::hakuban_object_observe_state_get_synchronized(@state)
329
370
  end
330
371
 
331
372
  def inspect
@@ -339,12 +380,12 @@ module Hakuban
339
380
 
340
381
  attr_reader :descriptor
341
382
 
342
- def initialize(local_node, descriptor)
343
- @local_node, @descriptor = local_node, descriptor
383
+ def initialize(local_node, descriptor, deserializer)
384
+ @local_node, @descriptor, @deserializer = local_node, descriptor, deserializer
344
385
  result = FFI::hakuban_tag_observe_new(@local_node.local_node_pointer, @descriptor.to_ffi)
345
386
  Hakuban::raise_if_error(result)
387
+ @queues = []
346
388
  @tag_observe_pointer = ::FFI::AutoPointer.new(result[:tag_observe_pointer], FFI::method(:hakuban_tag_observe_drop))
347
- @queues = {}
348
389
  end
349
390
 
350
391
  def object_descriptors
@@ -360,37 +401,30 @@ module Hakuban
360
401
  result = FFI::hakuban_tag_observe_object_state_borrow(@tag_observe_pointer, object_descriptor.to_ffi)
361
402
  return nil if result[:error] == 4
362
403
  Hakuban::raise_if_error(result)
363
- ObjectObserveState.new(result[:state])
404
+ ObjectObserveState.new(result[:state], @deserializer)
364
405
  end
365
406
 
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
- )
407
+ def new_callback_event_queue
408
+ raise "Attempt to use after 'drop'" if not @tag_observe_pointer
409
+ ObjectDescriptorCallbackEventQueue.new(FFI::hakuban_tag_observe_events_get(@tag_observe_pointer))
378
410
  end
379
411
 
380
- def stop_sending_objects_events_to(queue)
381
- @queues.delete(queue)
412
+ def new_event_queue
413
+ raise "Attempt to use after 'drop'" if not @tag_observe_pointer
414
+ queue = ObjectDescriptorEventQueue.new(self)
415
+ @queues << queue
416
+ queue
382
417
  end
383
418
 
384
419
  def drop
385
420
  @tag_observe_pointer.free
386
421
  @tag_observe_pointer = nil
422
+ @queues.each(&:close) #is this atomic?
387
423
  @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,12 @@ 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)
441
+ @queues = []
407
442
  @tag_expose_pointer = ::FFI::AutoPointer.new(result[:tag_expose_pointer], FFI::method(:hakuban_tag_expose_drop))
408
- @queues = {}
409
443
  end
410
444
 
411
445
  def object_descriptors
@@ -416,43 +450,50 @@ module Hakuban
416
450
  ret
417
451
  end
418
452
 
419
- def set_object_state(object_descriptor, version, data_type, data)
453
+ def set_object_state(object_descriptor, version, data, data_type: [], assignment: 0)
420
454
  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))
455
+ raise if not version #TODO: replace this by type check
456
+ serialized_data_type, serialized_data = @serializer.call(data_type, data)
457
+ 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
458
  Hakuban::raise_if_error(result)
425
459
  result[:changed] == 1
426
460
  end
427
461
 
428
- def send_events_to(*params)
429
- self.send_objects_events_to(*params)
462
+ def assignment(object_descriptor)
463
+ raise "Attempt to use after 'drop'" if not @tag_expose_pointer
464
+ FFI::hakuban_tag_expose_object_assignment(@tag_expose_pointer, object_descriptor.to_ffi)
465
+ end
466
+
467
+ def assigned?(object_descriptor)
468
+ assignment(object_descriptor) > 0
469
+ end
470
+
471
+ def desynchronize(object_descriptor, assignment)
472
+ raise "Attempt to use after 'drop'" if not @tag_expose_pointer
473
+ FFI::hakuban_tag_expose_object_desynchronize(@tag_expose_pointer, object_descriptor.to_ffi, assignment)
430
474
  end
431
475
 
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
- )
476
+ def new_callback_event_queue
477
+ raise "Attempt to use after 'drop'" if not @tag_expose_pointer
478
+ ObjectDescriptorCallbackEventQueue.new(FFI::hakuban_tag_expose_events_get(@tag_expose_pointer))
440
479
  end
441
480
 
442
- def stop_sending_objects_events_to(queue)
443
- @queues.delete(queue)
481
+ def new_event_queue
482
+ raise "Attempt to use after 'drop'" if not @tag_expose_pointer
483
+ queue = ObjectDescriptorEventQueue.new(self)
484
+ @queues << queue
485
+ queue
444
486
  end
445
487
 
446
488
  def drop
447
489
  @tag_expose_pointer.free
448
490
  @tag_expose_pointer = nil
491
+ @queues.each(&:close) #is this atomic?
449
492
  @queues.clear
450
493
  end
451
494
 
452
- def manage(manager=nil)
453
- manager ||= ObjectManager.new
454
- manager.contract = self
455
- manager
495
+ def dropped?
496
+ @tag_expose_pointer.nil?
456
497
  end
457
498
 
458
499
  end
@@ -487,12 +528,6 @@ module Hakuban
487
528
  @websocket_connector_pointer = ::FFI::AutoPointer.new(result[:websocket_connector_pointer], WebsocketConnector.generate_drop)
488
529
  end
489
530
 
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
531
  def self.generate_drop
497
532
  proc { |pointer|
498
533
  FFI::hakuban_tokio_websocket_connector_drop(Tokio.pointer, pointer)
@@ -502,149 +537,4 @@ module Hakuban
502
537
  end
503
538
 
504
539
 
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
540
  end