isomorfeus-data 1.0.0.delta11

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.
@@ -0,0 +1,672 @@
1
+ module LucidGraph
2
+ module Mixin
3
+ def self.included(base)
4
+ if RUBY_ENGINE != 'opal'
5
+ Isomorfeus.add_valid_graph_class(base) unless base == LucidGraph::Base
6
+ end
7
+
8
+ base.extend(Isomorfeus::Data::PropDeclaration)
9
+
10
+ def find_edge(attribute_hash = nil, &block)
11
+ if block_given?
12
+ edges.each do |edge|
13
+ return edge if block.call(edge)
14
+ end
15
+ else
16
+ edge_class = attribute_hash.delete(:class)
17
+ is_a_module = attribute_hash.delete(:is_a)
18
+ edges.each do |edge|
19
+ if edge_class
20
+ next unless edge.class == edge_class
21
+ end
22
+ if is_a_module
23
+ next unless edge.is_a?(is_a_module)
24
+ end
25
+ found = true
26
+ attribute_hash.each do |k,v|
27
+ found &&= (node[k] == v)
28
+ break unless found
29
+ end
30
+ return edge if found
31
+ end
32
+ end
33
+ nil
34
+ end
35
+
36
+ def find_edges(attribute_hash = nil, &block)
37
+ found_edges = Set.new
38
+ if block_given?
39
+ edges.each do |edge|
40
+ found_edges << edge if block.call(edge)
41
+ end
42
+ else
43
+ edge_class = attribute_hash.delete(:class)
44
+ is_a_module = attribute_hash.delete(:is_a)
45
+ edges.each do |edge|
46
+ if edge_class
47
+ next unless edge.class == edge_class
48
+ end
49
+ if is_a_module
50
+ next unless edge.is_a?(is_a_module)
51
+ end
52
+ found = true
53
+ attribute_hash.each do |k,v|
54
+ found &&= (node[k] == v)
55
+ break unless found
56
+ end
57
+ found_edges << edge if found
58
+ end
59
+ end
60
+ found_edges
61
+ end
62
+
63
+ def find_node(attribute_hash = nil, &block)
64
+ if block_given?
65
+ nodes.each do |node|
66
+ return node if block.call(node)
67
+ end
68
+ else
69
+ node_class = attribute_hash.delete(:class)
70
+ is_a_module = attribute_hash.delete(:is_a)
71
+ nodes.each do |node|
72
+ if node_class
73
+ next unless node.class == node_class
74
+ end
75
+ if is_a_module
76
+ next unless node.is_a?(is_a_module)
77
+ end
78
+ found = true
79
+ attribute_hash.each do |k,v|
80
+ found &&= (node[k] == v)
81
+ break unless found
82
+ end
83
+ return node if found
84
+ end
85
+ end
86
+ nil
87
+ end
88
+
89
+ def find_nodes(attribute_hash = nil, &block)
90
+ found_nodes = Set.new
91
+ if block_given?
92
+ nodes.each do |node|
93
+ found_nodes << node if block.call(node)
94
+ end
95
+ else
96
+ node_class = attribute_hash.delete(:class)
97
+ is_a_module = attribute_hash.delete(:is_a)
98
+ nodes.each do |node|
99
+ if node_class
100
+ next unless node.class == node_class
101
+ end
102
+ if is_a_module
103
+ next unless node.is_a?(is_a_module)
104
+ end
105
+ found = true
106
+ attribute_hash.each do |k,v|
107
+ found &&= (node[k] == v)
108
+ break unless found
109
+ end
110
+ found_nodes << node if found
111
+ end
112
+ end
113
+ found_nodes
114
+ end
115
+
116
+ def to_gid
117
+ [@class_name, @props_json]
118
+ end
119
+
120
+ def to_transport(inline: false)
121
+ items_hash = {}
122
+ own_edge_cids = own_edges_as_cids
123
+ own_node_cids = own_nodes_as_cids
124
+ items_hash['edges'] = own_edge_cids.to_a if own_edge_cids.size > 0
125
+ items_hash['nodes'] = own_node_cids.to_a if own_node_cids.size > 0
126
+
127
+ if @included_arrays.size > 0
128
+ items_hash['included_arrays'] = {}
129
+ @included_arrays.each do |name, instance|
130
+ items_hash['included_arrays'][name.to_s] = if self.class.included_arrays[name].key?(:anonymous)
131
+ instance.to_transport(inline: true)
132
+ else
133
+ instance.to_gid
134
+ end
135
+ end
136
+ end
137
+
138
+ if @included_collections.size > 0
139
+ items_hash['included_collections'] = {}
140
+ @included_collections.each do |name, instance|
141
+ items_hash['included_collections'][name.to_s] = if self.class.included_collections[name].key?(:anonymous)
142
+ instance.to_transport(inline: true)
143
+ else
144
+ instance.to_gid
145
+ end
146
+ end
147
+ end
148
+
149
+ if @included_graphs.size > 0
150
+ items_hash['included_graphs'] = {}
151
+ @included_graphs.each do |name, instance|
152
+ items_hash['included_graphs'][name.to_s] = if self.class.included_graphs[name].key?(:anonymous)
153
+ instance.to_transport(inline: true)
154
+ else
155
+ instance.to_gid
156
+ end
157
+ end
158
+ end
159
+
160
+ if @included_hashes.size > 0
161
+ items_hash['included_hashes'] = {}
162
+ @included_hashes.each do |name, instance|
163
+ items_hash['included_hashes'][name.to_s] = if self.class.included_hashes[name].key?(:anonymous)
164
+ instance.to_transport(inline: true)
165
+ else
166
+ instance.to_gid
167
+ end
168
+ end
169
+ end
170
+
171
+ incl_nodes = included_nodes
172
+ if incl_nodes.size > 0
173
+ items_hash['included_nodes'] = {}
174
+ incl_nodes.each do |name, instance|
175
+ items_hash['included_nodes'][name.to_s] = instance.to_cid
176
+ end
177
+ end
178
+
179
+ if inline
180
+ { '_inline' => { @props_json => items_hash }}
181
+ else
182
+ { 'graphs' => { @class_name => { @props_json => items_hash }}}
183
+ end
184
+ end
185
+
186
+ def included_items_to_transport
187
+ result_hash = {}
188
+
189
+ self.class.included_arrays.each do |name, options|
190
+ unless options.key?(:anonymous)
191
+ result_hash.deep_merge!(@included_arrays[name].to_transport)
192
+ end
193
+ end
194
+
195
+ self.class.included_collections.each do |name, options|
196
+ unless options.key?(:anonymous)
197
+ result_hash.deep_merge!(@included_collections[name].to_transport)
198
+ end
199
+ result_hash.deep_merge!(@included_collections[name].included_items_to_transport)
200
+ end
201
+
202
+ self.class.included_graphs.each do |name, options|
203
+ unless options.key?(:anonymous)
204
+ result_hash.deep_merge!(@included_graphs[name].to_transport)
205
+ end
206
+ result_hash.deep_merge!(@included_graphs[name].included_items_to_transport)
207
+ end
208
+
209
+ self.class.included_hashes.each do |name, options|
210
+ unless options.key?(:anonymous)
211
+ result_hash.deep_merge!(@included_hashes[name].to_transport)
212
+ end
213
+ end
214
+
215
+ included_nodes.each_value do |instance|
216
+ result_hash.deep_merge!(instance.to_transport)
217
+ end
218
+
219
+ edges.each do |edge|
220
+ result_hash.deep_merge!(edge.to_transport)
221
+ end
222
+
223
+ nodes.each do |node|
224
+ result_hash.deep_merge!(node.to_transport)
225
+ end
226
+
227
+ result_hash
228
+ end
229
+
230
+ base.instance_exec do
231
+ def included_arrays
232
+ @included_arrays ||= {}
233
+ end
234
+
235
+ def included_collections
236
+ @included_collections ||= {}
237
+ end
238
+
239
+ def included_graphs
240
+ @included_graphs ||= {}
241
+ end
242
+
243
+ def included_hashes
244
+ @included_hashes ||= {}
245
+ end
246
+
247
+ def included_nodes
248
+ @included_nodes ||= {}
249
+ end
250
+
251
+ def on_load_block
252
+ @on_load_block
253
+ end
254
+
255
+ def query_block
256
+ @query_block
257
+ end
258
+ end
259
+
260
+ if RUBY_ENGINE == 'opal'
261
+ def initialize(store_path: nil, validated_props: nil)
262
+ @props = validated_props
263
+ @props_json = @props.to_json if @props
264
+ @class_name = self.class.name
265
+ @class_name = @class_name.split('>::').last if @class_name.start_with?('#<')
266
+ @store_path = store_path ? store_path : [:data_state, :graphs, @class_name, @props_json]
267
+
268
+ @included_arrays = {}
269
+ self.class.included_arrays.each do |name, options|
270
+ @included_arrays[name] = if options.key?(:anonymous)
271
+ options[:class].new(store_path: @store_path + [:included_arrays, name, :_inline],
272
+ validated_props: @props)
273
+ else
274
+ options[:class].new(validated_props: @props)
275
+ end
276
+ end
277
+ @included_collections = {}
278
+ self.class.included_collections.each do |name, options|
279
+ @included_collections[name] = if options.key?(:anonymous)
280
+ options[:class].new(store_path: @store_path + [:included_arrays, name, :_inline],
281
+ validated_props: @props)
282
+ else
283
+ options[:class].new(validated_props: @props)
284
+ end
285
+ end
286
+ @included_graphs = {}
287
+ self.class.included_graphs.each do |name, options|
288
+ @included_graphs[name] = if options.key?(:anonymous)
289
+ options[:class].new(store_path: @store_path + [:included_arrays, name, :_inline],
290
+ validated_props: @props)
291
+ else
292
+ options[:class].new(validated_props: @props)
293
+ end
294
+ end
295
+ @included_hashes = {}
296
+ self.class.included_hashes.each do |name, options|
297
+ @included_hashes[name] = if options.key?(:anonymous)
298
+ options[:class].new(store_path: @store_path + [:included_arrays, name, :_inline],
299
+ validated_props: @props)
300
+ else
301
+ options[:class].new(validated_props: @props)
302
+ end
303
+ end
304
+ end
305
+
306
+ def loaded?
307
+ Redux.fetch_by_path(*@store_path) ? true : false
308
+ end
309
+
310
+ def edges
311
+ edges_as_cids.map { |edge_cid| LucidEdge::Base.edge_from_cid(edge_cid) }
312
+ end
313
+
314
+ def edges_as_cids
315
+ edge_cids = own_edges_as_cids
316
+ @included_graphs.each_value { |graph| edge_cids += graph.edges_as_cids }
317
+ edge_cids
318
+ end
319
+
320
+ def own_edges_as_cids
321
+ path = @store_path + [:edges]
322
+ Redux.register_used_store_path(*path)
323
+ edge_cids = Redux.fetch_by_path(*path)
324
+ edge_cids ? Set.new(edge_cids) : Set.new
325
+ end
326
+
327
+ def find_edge_by_id(edge_id)
328
+ edges_as_cids.each do |edge_cid|
329
+ return LucidNode::Base.edge_from_cid(edge_cid) if edge_cid[1] == edge_id
330
+ end
331
+ nil
332
+ end
333
+
334
+ def find_node_by_id(node_id)
335
+ nodes_as_cids.each do |node_cid|
336
+ return LucidNode::Base.node_from_cid(node_cid) if node_cid[1] == node_id
337
+ end
338
+ nil
339
+ end
340
+
341
+ def included_nodes
342
+ incl_nodes = {}
343
+ path = @store_path + [:included_nodes]
344
+ Redux.register_used_store_path(*path)
345
+ self.class.included_nodes.each_key do |name|
346
+ node_cid = Redux.fetch_by_path(*(path + [name]))
347
+ incl_nodes[name] = LucidNode::Base.node_from_cid(node_cid) if node_cid
348
+ end
349
+ incl_nodes
350
+ end
351
+
352
+ def nodes
353
+ nodes_as_cids.map { |node_cid| LucidNode::Base.node_from_cid(node_cid) }
354
+ end
355
+
356
+ def nodes_as_cids
357
+ node_cids = own_nodes_as_cids
358
+ @included_graphs.each_value { |graph| node_cids += graph.nodes_as_cids }
359
+ @included_collections.each_value { |collection| node_cids += collection.nodes_as_cids }
360
+ included_nodes.each_value { |node| node_cids << node.to_cid }
361
+ node_cids
362
+ end
363
+
364
+ def own_nodes_as_cids
365
+ path = @store_path + [:nodes]
366
+ Redux.register_used_store_path(*path)
367
+ node_cids = Redux.fetch_by_path(*path)
368
+ node_cids ? Set.new(node_cids) : Set.new
369
+ end
370
+
371
+ def method_missing(method_name, *args, &block)
372
+ if method_name.JS.startsWith('find_node_by_')
373
+ attribute = method_name[13..-1] # remove 'find_node_by_'
374
+ value = args[0]
375
+ attribute_hash = { attribute => value }
376
+ attribute_hash.merge!(args[1]) if args[1]
377
+ find_node(attribute_hash)
378
+ elsif method_name.JS.startsWith('find_edge_by_')
379
+ attribute = method_name[13..-1] # remove 'find_edge_by_'
380
+ value = args[0]
381
+ attribute_hash = { attribute => value }
382
+ attribute_hash.merge!(args[1]) if args[1]
383
+ find_edge(attribute_hash)
384
+ else
385
+ super
386
+ end
387
+ end
388
+
389
+ base.instance_exec do
390
+ def include_array(name, array_class = nil, &block)
391
+ included_arrays[name] = if array_class
392
+ { class: array_class }
393
+ else
394
+ new_class = Class.new(LucidArray::Base)
395
+ new_class.instance_exec(&block)
396
+ { anonymous: true, class: new_class }
397
+ end
398
+ define_method(name) do
399
+ @included_arrays[name]
400
+ end
401
+ end
402
+
403
+ def include_collection(name, collection_class = nil, &block)
404
+ included_collections[name] = if collection_class
405
+ { class: collection_class }
406
+ else
407
+ new_class = Class.new(LucidCollection::Base)
408
+ new_class.instance_exec(&block)
409
+ { anonymous: true, class: new_class }
410
+ end
411
+ define_method(name) do
412
+ @included_collections[name]
413
+ end
414
+ end
415
+
416
+ def include_graph(name, graph_class = nil, &block)
417
+ included_graphs[name] = if graph_class
418
+ { class: graph_class }
419
+ else
420
+ new_class = Class.new(LucidGraph::Base)
421
+ new_class.instance_exec(&block)
422
+ { anonymous: true, class: new_class }
423
+ end
424
+ define_method(name) do
425
+ @included_graphs[name]
426
+ end
427
+ end
428
+
429
+ def include_hash(name, hash_class = nil, &block)
430
+ included_hashes[name] = if hash_class
431
+ { class: hash_class }
432
+ else
433
+ new_class = Class.new(LucidHash::Base)
434
+ new_class.instance_exec(&block)
435
+ { anonymous: true, class: new_class }
436
+ end
437
+ define_method(name) do
438
+ @included_hashes[name]
439
+ end
440
+ end
441
+
442
+ def include_node(name, node_class, &block)
443
+ included_nodes[name] = { class: node_class, anonymous: true, block: block }
444
+ define_method(name) do
445
+ path = @store_path + [:included_nodes, name]
446
+ Redux.register_used_store_path(*path)
447
+ node_cid = Redux.fetch_by_path(*path)
448
+ node_cid ? self.class.included_nodes[name][:class].node_from_cid(node_cid) : nil
449
+ end
450
+ end
451
+
452
+ def load(props_hash = {})
453
+ validate_props(props_hash)
454
+ instance = self.new(validated_props: Isomorfeus::Data::Props.new(props_hash))
455
+ self.promise_load(props_hash, instance) unless instance.loaded?
456
+ instance
457
+ end
458
+
459
+ def on_load(&block)
460
+ end
461
+
462
+ def promise_load(props_hash = {}, instance = nil)
463
+ unless instance
464
+ validate_props(props_hash)
465
+ instance = self.new(validated_props: Isomorfeus::Data::Props.new(props_hash))
466
+ end
467
+
468
+ props_json = instance.instance_variable_get(:@props_json)
469
+
470
+ Redux.register_used_store_path(:data_state, :graphs, self.name, props_json)
471
+
472
+ Isomorfeus::Transport.promise_send_path('Isomorfeus::Data::Handler::GraphLoadHandler', self.name, props_json).then do |response|
473
+ if response[:agent_response].key?(:error)
474
+ `console.error(#{response[:agent_response][:error].to_n})`
475
+ raise response[:agent_response][:error]
476
+ end
477
+ Isomorfeus.store.dispatch(type: 'DATA_LOAD', data: response[:full_response][:data])
478
+ instance
479
+ end
480
+ end
481
+
482
+ def query
483
+ nil
484
+ end
485
+ end
486
+ else # RUBY_ENGINE
487
+ unless base == LucidGraph::Base
488
+ base.prop :pub_sub_client, default: nil
489
+ base.prop :session_id, default: nil
490
+ base.prop :current_user, default: nil
491
+ end
492
+
493
+ def initialize(store_path: nil, validated_props: nil)
494
+ @props = validated_props
495
+ @props_json = @props.to_json if @props
496
+ @loaded = false
497
+ @included_arrays = {}
498
+ @included_collections = {}
499
+ @included_hashes = {}
500
+ @included_graphs = {}
501
+ @included_nodes = {}
502
+ @class_name = self.class.name
503
+ @class_name = @class_name.split('>::').last if @class_name.start_with?('#<')
504
+ end
505
+
506
+ def loaded?
507
+ @loaded
508
+ end
509
+
510
+ def edges
511
+ all_edges = @edges.to_a
512
+ @included_graphs.each_value { |graph| all_edges += graph.edges }
513
+ all_edges.uniq!(&:to_cid)
514
+ all_edges
515
+ end
516
+
517
+ def edges_as_cids
518
+ edges.map { |edge| [edge.class.name, edge.id] }
519
+ end
520
+
521
+ def own_edges_as_cids
522
+ @edges.map(&:to_cid).uniq
523
+ end
524
+
525
+ def nodes
526
+ all_nodes = @nodes.to_a
527
+ @included_graphs.each_value { |graph| all_nodes += graph.nodes }
528
+ @included_collections.each_value { |collection| all_nodes += collection.nodes }
529
+ included_nodes.each_value { |node| all_nodes << node }
530
+ all_nodes.uniq!(&:to_cid)
531
+ all_nodes
532
+ end
533
+
534
+ def nodes_as_cids
535
+ nodes.map { |node| [node.class.name, node.id] }
536
+ end
537
+
538
+ def own_nodes_as_cids
539
+ @nodes.map(&:to_cid).uniq
540
+ end
541
+
542
+ def method_missing(method_name, *args, &block)
543
+ if method_name.start_with?('find_node_by_')
544
+ attribute = method_name[13..-1] # remove 'find_node_by_'
545
+ value = args[0]
546
+ attribute_hash = { attribute => value }
547
+ attribute_hash.merge!(args[1]) if args[1]
548
+ find_node(attribute_hash)
549
+ elsif method_name.start_with?('find_edge_by_')
550
+ attribute = method_name[13..-1] # remove 'find_edge_by_'
551
+ value = args[0]
552
+ attribute_hash = { attribute => value }
553
+ attribute_hash.merge!(args[1]) if args[1]
554
+ find_edge(attribute_hash)
555
+ else
556
+ super
557
+ end
558
+ end
559
+
560
+ base.instance_exec do
561
+ attr_accessor :included_nodes
562
+
563
+ def include_array(name, array_class = nil, &block)
564
+ included_arrays[name] = if array_class
565
+ { class: array_class }
566
+ else
567
+ new_class = Class.new(LucidArray::Base)
568
+ new_class.instance_exec(&block)
569
+ { anonymous: true, class: new_class }
570
+ end
571
+ define_method(name) do
572
+ @included_arrays[name]
573
+ end
574
+ end
575
+
576
+ def include_collection(name, collection_class = nil, &block)
577
+ included_collections[name] = if collection_class
578
+ { class: collection_class }
579
+ else
580
+ new_class = Class.new(LucidCollection::Base)
581
+ new_class.instance_exec(&block)
582
+ { anonymous: true, class: new_class }
583
+ end
584
+ define_method(name) do
585
+ @included_collections[name]
586
+ end
587
+ end
588
+
589
+ def include_graph(name, graph_class = nil, &block)
590
+ included_graphs[name] = if graph_class
591
+ { class: graph_class }
592
+ else
593
+ new_class = Class.new(LucidGraph::Base)
594
+ new_class.instance_exec(&block)
595
+ { anonymous: true, class: new_class }
596
+ end
597
+ define_method(name) do
598
+ @included_graphs[name]
599
+ end
600
+ end
601
+
602
+ def include_hash(name, hash_class = nil, &block)
603
+ included_hashes[name] = if hash_class
604
+ { class: hash_class }
605
+ else
606
+ new_class = Class.new(LucidHash::Base)
607
+ new_class.instance_exec(&block)
608
+ { anonymous: true, class: new_class }
609
+ end
610
+ define_method(name) do
611
+ @included_hashes[name]
612
+ end
613
+ end
614
+
615
+ def include_node(name, node_class, &block)
616
+ included_nodes[name] = { class: node_class, anonymous: true, block: block }
617
+ define_method(name) do
618
+ included_nodes[name]
619
+ end
620
+ end
621
+
622
+ def load(props_hash = {})
623
+ validate_props(props_hash)
624
+ instance = self.new(validated_props: Isomorfeus::Data::Props.new(props_hash))
625
+ instance.instance_exec do
626
+ nodes, edges = self.class.query_block.call(props_hash)
627
+ @nodes = Set.new(nodes)
628
+ @edges = Set.new(edges)
629
+ self.class.included_arrays.each do |name, options|
630
+ @included_arrays[name] = options[:class].load(props_hash)
631
+ end
632
+ self.class.included_collections.each do |name, options|
633
+ @included_collections[name] = options[:class].load(props_hash)
634
+ end
635
+ self.class.included_graphs.each do |name, options|
636
+ @included_graphs[name] = options[:class].load(props_hash)
637
+ end
638
+ self.class.included_hashes.each do |name, options|
639
+ @included_hashes[name] = options[:class].load(props_hash)
640
+ end
641
+ self.class.included_nodes.each do |name, options|
642
+ result = options[:block].call(props_hash)
643
+ @included_nodes[name] = if result.class == options[:class]
644
+ result
645
+ elsif result.class == Hash
646
+ options[:class].new(result)
647
+ end
648
+ end
649
+ @loaded = true
650
+ end
651
+ instance
652
+ end
653
+
654
+ def on_load(&block)
655
+ @on_load_block = block
656
+ end
657
+
658
+ def promise_load(props_hash = {}, instance = nil)
659
+ instance = self.load(props_hash)
660
+ result_promise = Promise.new
661
+ result_promise.resolve(instance)
662
+ result_promise
663
+ end
664
+
665
+ def query(&block)
666
+ @query_block = block
667
+ end
668
+ end
669
+ end # RUBY_ENGINE
670
+ end
671
+ end
672
+ end
@@ -0,0 +1,15 @@
1
+ module LucidHash
2
+ class Base
3
+ include LucidHash::Mixin
4
+
5
+ if RUBY_ENGINE != 'opal'
6
+ def self.inherited(base)
7
+ Isomorfeus.add_valid_hash_class(base)
8
+
9
+ base.prop :pub_sub_client, default: nil
10
+ base.prop :session_id, default: nil
11
+ base.prop :current_user, default: nil
12
+ end
13
+ end
14
+ end
15
+ end