hakuban 0.5.2 → 0.6.2

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