isomorfeus-data 1.0.0.delta11

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