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,163 @@
1
+ module LucidArray
2
+ module Mixin
3
+ def self.included(base)
4
+ if RUBY_ENGINE != 'opal'
5
+ Isomorfeus.add_valid_array_class(base) unless base == LucidArray::Base
6
+ end
7
+
8
+ base.extend(Isomorfeus::Data::PropDeclaration)
9
+
10
+ def to_gid
11
+ [@class_name, @props_json]
12
+ end
13
+
14
+ base.instance_exec do
15
+ def on_load_block
16
+ @on_load_block
17
+ end
18
+
19
+ def query_block
20
+ @query_block
21
+ end
22
+ end
23
+
24
+ if RUBY_ENGINE == 'opal'
25
+ def initialize(store_path: nil, validated_props: nil)
26
+ @props = validated_props
27
+ @props_json = @props.to_json if @props
28
+ @class_name = self.class.name
29
+ @class_name = @class_name.split('>::').last if @class_name.start_with?('#<')
30
+ @store_path = store_path ? store_path : [:data_state, :arrays, @class_name, @props_json]
31
+ end
32
+
33
+ def loaded?
34
+ Redux.fetch_by_path(*@store_path) ? true : false
35
+ end
36
+
37
+ def items
38
+ Redux.register_used_store_path(*@store_path)
39
+ raw_array = Redux.fetch_by_path(*@store_path)
40
+ raw_array ? raw_array : []
41
+ end
42
+
43
+ def method_missing(method_name, *args, &block)
44
+ Redux.register_used_store_path(*@store_path)
45
+ raw_array = Redux.fetch_by_path(*@store_path)
46
+ data_array = raw_array ? raw_array : []
47
+ data_array.send(method_name, *args, &block)
48
+ end
49
+
50
+ def to_transport(inline: false)
51
+ Redux.register_used_store_path(*@store_path)
52
+ raw_array = Redux.fetch_by_path(*@store_path)
53
+ if inline
54
+ { '_inline' => { @props_json => (raw_array ? raw_array : []) }}
55
+ else
56
+ { 'arrays' => { @class_name => { @props_json => (raw_array ? raw_array : []) }}}
57
+ end
58
+ end
59
+
60
+ base.instance_exec do
61
+ def load(props_hash = {})
62
+ validate_props(props_hash)
63
+ instance = self.new(validated_props: Isomorfeus::Data::Props.new(props_hash))
64
+ self.promise_load(props_hash, instance) unless instance.loaded?
65
+ instance
66
+ end
67
+
68
+ def on_load(&block)
69
+ end
70
+
71
+ def promise_load(props_hash = {}, instance = nil)
72
+ unless instance
73
+ validate_props(props_hash)
74
+ instance = self.new(validated_props: Isomorfeus::Data::Props.new(props_hash))
75
+ end
76
+
77
+ props_json = instance.instance_variable_get(:@props_json)
78
+
79
+ Redux.register_used_store_path(:data_state, :arrays, self.name, props_json)
80
+
81
+ Isomorfeus::Transport.promise_send_path('Isomorfeus::Data::Handler::ArrayLoadHandler', self.name, props_json).then do |response|
82
+ if response[:agent_response].key?(:error)
83
+ `console.error(#{response[:agent_response][:error].to_n})`
84
+ raise response[:agent_response][:error]
85
+ end
86
+ Isomorfeus.store.dispatch(type: 'DATA_LOAD', data: response[:full_response][:data])
87
+ instance
88
+ end
89
+ end
90
+
91
+ def query
92
+ nil
93
+ end
94
+ end
95
+ else # RUBY_ENGINE
96
+ unless base == LucidArray::Base
97
+ base.prop :pub_sub_client, default: nil
98
+ base.prop :session_id, default: nil
99
+ base.prop :current_user, default: nil
100
+ end
101
+
102
+ def initialize(store_path: nil, validated_props: nil)
103
+ @props = validated_props
104
+ @props_json = @props.to_json if @props
105
+ @loaded = false
106
+ @class_name = self.class.name
107
+ @class_name = @class_name.split('>::').last if @class_name.start_with?('#<')
108
+ end
109
+
110
+ def loaded?
111
+ @loaded
112
+ end
113
+
114
+ def [](name)
115
+ @data_array[name]
116
+ end
117
+
118
+ def items
119
+ @data_array
120
+ end
121
+
122
+ def method_missing(method_name, *args, &block)
123
+ @data_array.send(method_name, *args, &block)
124
+ end
125
+
126
+ def to_transport(inline: false)
127
+ if inline
128
+ { '_inline' => { @props_json => @data_array }}
129
+ else
130
+ { 'arrays' => { @class_name => { @props_json => @data_array }}}
131
+ end
132
+ end
133
+
134
+ base.instance_exec do
135
+ def load(props_hash = {})
136
+ validate_props(props_hash)
137
+ instance = self.new(validated_props: Isomorfeus::Data::Props.new(props_hash))
138
+ instance.instance_exec do
139
+ @data_array = self.class.query_block.call(props_hash)
140
+ @loaded = true
141
+ end
142
+ instance
143
+ end
144
+
145
+ def on_load(&block)
146
+ @on_load_block = block
147
+ end
148
+
149
+ def promise_load(props_hash = {}, instance = nil)
150
+ instance = self.load(props_hash)
151
+ result_promise = Promise.new
152
+ result_promise.resolve(instance)
153
+ result_promise
154
+ end
155
+
156
+ def query(&block)
157
+ @query_block = block
158
+ end
159
+ end
160
+ end # RUBY_ENGINE
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,15 @@
1
+ module LucidCollection
2
+ class Base
3
+ include LucidCollection::Mixin
4
+
5
+ if RUBY_ENGINE != 'opal'
6
+ def self.inherited(base)
7
+ Isomorfeus.add_valid_collection_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
@@ -0,0 +1,241 @@
1
+ module LucidCollection
2
+ module Mixin
3
+ def self.included(base)
4
+ if RUBY_ENGINE != 'opal'
5
+ Isomorfeus.add_valid_collection_class(base) unless base == LucidCollection::Base
6
+ end
7
+
8
+ base.extend(Isomorfeus::Data::PropDeclaration)
9
+
10
+ def find_node(attribute_hash = nil, &block)
11
+ if block_given?
12
+ nodes.each do |node|
13
+ return node if block.call(node)
14
+ end
15
+ else
16
+ node_class = attribute_hash.delete(:class)
17
+ is_a_module = attribute_hash.delete(:is_a)
18
+ nodes.each do |node|
19
+ if node_class
20
+ next unless node.class == node_class
21
+ end
22
+ if is_a_module
23
+ next unless node.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 node if found
31
+ end
32
+ end
33
+ nil
34
+ end
35
+
36
+ def find_nodes(attribute_hash = nil, &block)
37
+ found_nodes = Set.new
38
+ if block_given?
39
+ nodes.each do |node|
40
+ found_nodes << node if block.call(node)
41
+ end
42
+ else
43
+ node_class = attribute_hash.delete(:class)
44
+ is_a_module = attribute_hash.delete(:is_a)
45
+ nodes.each do |node|
46
+ if node_class
47
+ next unless node.class == node_class
48
+ end
49
+ if is_a_module
50
+ next unless node.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_nodes << node if found
58
+ end
59
+ end
60
+ found_nodes
61
+ end
62
+
63
+ def to_gid
64
+ [@class_name, @props_json]
65
+ end
66
+
67
+ def to_transport(inline: false)
68
+ if inline
69
+ { '_inline' => { @props_json => nodes_as_cids }}
70
+ else
71
+ { 'collections' => { @class_name => { @props_json => nodes_as_cids }}}
72
+ end
73
+ end
74
+
75
+ def included_items_to_transport
76
+ nodes_hash = {}
77
+ nodes.each do |node|
78
+ nodes_hash.deep_merge!(node.to_transport)
79
+ end
80
+ nodes_hash
81
+ end
82
+
83
+ base.instance_exec do
84
+ def on_load_block
85
+ @on_load_block
86
+ end
87
+
88
+ def query_block
89
+ @query_block
90
+ end
91
+ end
92
+
93
+ if RUBY_ENGINE == 'opal'
94
+ def initialize(store_path: nil, validated_props: nil)
95
+ @props = validated_props
96
+ @props_json = @props.to_json if @props
97
+ @store_path = store_path
98
+ @class_name = self.class.name
99
+ @class_name = @class_name.split('>::').last if @class_name.start_with?('#<')
100
+ @store_path = store_path ? store_path : [:data_state, :collections, @class_name, @props_json]
101
+ end
102
+
103
+ def loaded?
104
+ Redux.fetch_by_path(*@store_path) ? true : false
105
+ end
106
+
107
+ def find_node_by_id(node_id)
108
+ Redux.register_used_store_path(*@store_path)
109
+ nodes_as_cids.each do |node_cid|
110
+ return LucidNode::Base.node_from_cid(node_cid) if node_cid[1] == node_id
111
+ end
112
+ nil
113
+ end
114
+
115
+ def nodes
116
+ # maybe use a node cache, maybe not:
117
+ # - pro node cache: maybe faster
118
+ # - contra node cache: js garbage collection fails because references are kept forever, memory usage just grows and grows
119
+ nodes_as_cids.map { |node_cid| LucidNode::Base.node_from_cid(node_cid) }
120
+ end
121
+
122
+ def nodes_as_cids
123
+ Redux.register_used_store_path(*@store_path)
124
+ node_cids = Redux.fetch_by_path(*@store_path)
125
+ node_cids ? node_cids : []
126
+ end
127
+
128
+ def method_missing(method_name, *args, &block)
129
+ if method_name.JS.startsWith('find_node_by_')
130
+ attribute = method_name[13..-1] # remove 'find_node_by_'
131
+ value = args[0]
132
+ attribute_hash = { attribute => value }
133
+ attribute_hash.merge!(args[1]) if args[1]
134
+ find_node(attribute_hash)
135
+ else
136
+ super
137
+ end
138
+ end
139
+
140
+ base.instance_exec do
141
+ def load(props_hash = {})
142
+ validate_props(props_hash)
143
+ instance = self.new(validated_props: Isomorfeus::Data::Props.new(props_hash))
144
+ self.promise_load(props_hash, instance) unless instance.loaded?
145
+ instance
146
+ end
147
+
148
+ def on_load(&block)
149
+ end
150
+
151
+ def promise_load(props_hash = {}, instance = nil)
152
+ unless instance
153
+ validate_props(props_hash)
154
+ instance = self.new(validated_props: Isomorfeus::Data::Props.new(props_hash))
155
+ end
156
+
157
+ props_json = instance.instance_variable_get(:@props_json)
158
+
159
+ Redux.register_used_store_path(:data_state, :collections, self.name, props_json)
160
+
161
+ Isomorfeus::Transport.promise_send_path('Isomorfeus::Data::Handler::CollectionLoadHandler', self.name, props_json).then do |response|
162
+ if response[:agent_response].key?(:error)
163
+ `console.error(#{response[:agent_response][:error].to_n})`
164
+ raise response[:agent_response][:error]
165
+ end
166
+ Isomorfeus.store.dispatch(type: 'DATA_LOAD', data: response[:full_response][:data])
167
+ instance
168
+ end
169
+ end
170
+
171
+ def query
172
+ nil
173
+ end
174
+ end
175
+ else # RUBY_ENGINE
176
+ unless base == LucidCollection::Base
177
+ base.prop :pub_sub_client, default: nil
178
+ base.prop :session_id, default: nil
179
+ base.prop :current_user, default: nil
180
+ end
181
+
182
+ def initialize(store_path: nil, validated_props: nil)
183
+ @props = validated_props
184
+ @props_json = @props.to_json if @props
185
+ @loaded = false
186
+ @class_name = self.class.name
187
+ @class_name = @class_name.split('>::').last if @class_name.start_with?('#<')
188
+ end
189
+
190
+ def loaded?
191
+ @loaded
192
+ end
193
+
194
+ def method_missing(method_name, *args, &block)
195
+ if method_name.start_with?('find_node_by_')
196
+ attribute = method_name[13..-1] # remove 'find_node_by_'
197
+ value = args[0]
198
+ attribute_hash = { attribute => value }
199
+ attribute_hash.merge!(args[1]) if args[1]
200
+ find_node(attribute_hash)
201
+ else
202
+ super
203
+ end
204
+ end
205
+
206
+ def nodes_as_cids
207
+ nodes.map { |node| node.to_cid }
208
+ end
209
+
210
+ base.instance_exec do
211
+ attr_reader :nodes
212
+
213
+ def load(props_hash = {})
214
+ validate_props(props_hash)
215
+ instance = self.new(validated_props: Isomorfeus::Data::Props.new(props_hash))
216
+ instance.instance_exec do
217
+ @nodes = self.class.query_block.call(props_hash)
218
+ @loaded = true
219
+ end
220
+ instance
221
+ end
222
+
223
+ def on_load(&block)
224
+ @on_load_block = block
225
+ end
226
+
227
+ def promise_load(props_hash = {}, instance = nil)
228
+ instance = self.load(props_hash)
229
+ result_promise = Promise.new
230
+ result_promise.resolve(instance)
231
+ result_promise
232
+ end
233
+
234
+ def query(&block)
235
+ @query_block = block
236
+ end
237
+ end
238
+ end # RUBY_ENGINE
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,5 @@
1
+ module LucidEdge
2
+ class Base
3
+ include LucidEdge::Mixin
4
+ end
5
+ end
@@ -0,0 +1,243 @@
1
+ module LucidEdge
2
+ module Mixin
3
+ def self.included(base)
4
+ attr_reader :id
5
+
6
+ def ==(other_node)
7
+ eql?(other_node)
8
+ end
9
+
10
+ def eql?(other_node)
11
+ @id == other_node.id && @class_name == other_node.instance_variable_get(:@class_name)
12
+ end
13
+
14
+ def id=(new_id)
15
+ new_id = new_id.to_s
16
+ changed_attributes[:id] = new_id
17
+ @id = new_id
18
+ end
19
+
20
+ def changed_attributes
21
+ @changed_attributes ||= Isomorfeus::Data::Props.new({})
22
+ end
23
+
24
+ def changed?
25
+ changed_attributes.any?
26
+ end
27
+
28
+ def to=(node)
29
+ @changed_to_cid = node.to_cid
30
+ node
31
+ end
32
+
33
+ def to_cid
34
+ [@class_name, @id]
35
+ end
36
+
37
+ def valid_attribute?(attr, value)
38
+ begin
39
+ validate_attribute!(attr, value)
40
+ true
41
+ rescue
42
+ false
43
+ end
44
+ end
45
+
46
+ def validate_attribute!(attr, value)
47
+ attr_options = self.class.attribute_options[attr]
48
+
49
+ if attr_options.key?(:class)
50
+ raise "#{attr}: value class is not #{attr_options[:class]}!" unless value.class == attr_options[:class]
51
+ end
52
+
53
+ if attr_options.key?(:is_a)
54
+ raise "#{attr}: value is not a #{attr_options[:class]}!" unless value.is_a?(attr_options[:is_a])
55
+ end
56
+
57
+ if attr_options.key?(:validate)
58
+ raise "#{attr}: value failed validation!" unless attr_options[:validate].call(value)
59
+ end
60
+ end
61
+
62
+ base.instance_exec do
63
+ def attributes
64
+ attribute_options.keys
65
+ end
66
+
67
+ def attribute_options
68
+ @attribute_options ||= { id: {} }
69
+ end
70
+
71
+ def edge_from_cid(cid)
72
+ Isomorfeus.cached_edge_class(cid[0]).new({id: cid[1]})
73
+ end
74
+ end
75
+
76
+ if RUBY_ENGINE == 'opal'
77
+ def initialize(attributes_hash = nil)
78
+ attributes_hash = {} unless attributes_hash
79
+ self.class.attributes.each do |attr|
80
+ next if attr == :to || attr == :from
81
+ if attributes_hash.key?(attr)
82
+ validate_attribute!(attr, attributes_hash[attr])
83
+ changed_attributes[attr] = attributes_hash[attr]
84
+ elsif self.class.attribute_options[attr].key?(:default)
85
+ changed_attributes[attr] = self.class.attribute_options[attr][:default]
86
+ end
87
+ end
88
+ @id = attributes_hash[:id].to_s
89
+ @id = "new_#{object_id}" if @id.empty?
90
+ @changed_from_cid = attributes_hash[:from]&.to_cid
91
+ @changed_to_cid = attributes_hash[:to]&.to_cid
92
+ @class_name = self.class.name
93
+ @class_name = @class_name.split('>::').last if @class_name.start_with?('#<')
94
+ end
95
+
96
+ def loaded?
97
+ Redux.fetch_by_path(:data_state, :edges, @class_name, @id) ? true : false
98
+ end
99
+
100
+ def from
101
+ cid = from_as_cid
102
+ cid ? LucidNode::Base.node_from_cid(cid) : nil
103
+ end
104
+
105
+ def from_as_cid
106
+ Redux.register_used_store_path(:data_state, :edges, @class_name, @id, :from)
107
+ return @changed_from_cid if @changed_from_cid
108
+ cid = Redux.fetch_by_path(:data_state, :edges, @class_name, @id, :from)
109
+ cid ? cid : nil
110
+ end
111
+
112
+ def from=(node)
113
+ @changed_from_cid = node.to_cid
114
+ node
115
+ end
116
+
117
+ def to
118
+ cid = to_as_cid
119
+ cid ? LucidNode::Base.node_from_cid(cid) : nil
120
+ end
121
+
122
+ def to_as_cid
123
+ Redux.register_used_store_path(:data_state, :edges, @class_name, @id, :to)
124
+ return @changed_to_cid if @changed_to_cid
125
+ cid = Redux.fetch_by_path(:data_state, :edges, @class_name, @id, :to)
126
+ cid ? cid : nil
127
+ end
128
+
129
+ def to_transport(*args)
130
+ Redux.register_used_store_path(:data_state, :edges, @class_name, @id)
131
+ final_attributes = {}
132
+ self.class.attributes.each do |attr|
133
+ next if attr == :id
134
+ final_attributes[attr] = send(attr)
135
+ end
136
+ { 'edges' => { @class_name => { @id => { from: from_as_cid, to: to_as_cid, attributes: final_attributes }}}}
137
+ end
138
+
139
+ base.instance_exec do
140
+ def attribute(name, options = {})
141
+ attribute_options[name] = options
142
+
143
+ define_method(name) do
144
+ Redux.register_used_store_path(:data_state, :edges, @class_name, @id, :attributes, name)
145
+ if changed_attributes.key?(name)
146
+ changed_attributes[name]
147
+ else
148
+ Redux.fetch_by_path(:data_state, :edges, @class_name, @id, :attributes, name)
149
+ end
150
+ end
151
+
152
+ define_method("#{name}=") do |arg|
153
+ validate_attribute!(name, arg)
154
+ Redux.register_used_store_path(:data_state, :edges, @class_name, @id, :attributes, name)
155
+ changed_attributes.set(name, arg)
156
+ end
157
+ end
158
+ end
159
+ else # RUBY_ENGINE
160
+ def initialize(attributes_hash = nil)
161
+ attributes_hash = {} unless attributes_hash
162
+ given_attributes = Isomorfeus::Data::Props.new(attributes_hash)
163
+ valid_attributes_hash = {}
164
+ self.class.attributes.each do |attr|
165
+ next if attr == :to || attr == :from
166
+ if given_attributes.key?(attr)
167
+ validate_attribute!(attr, given_attributes[attr])
168
+ valid_attributes_hash[attr] = given_attributes[attr]
169
+ elsif self.class.attribute_options[attr].key?(:default)
170
+ valid_attributes_hash[attr] = self.class.attribute_options[attr][:default]
171
+ end
172
+ end
173
+ @attributes = Isomorfeus::Data::Props.new(valid_attributes_hash)
174
+ @id = @attributes[:id].to_s
175
+ @id = "new_#{object_id}" if @id.empty?
176
+ @from_cid = given_attributes[:from]&.to_cid
177
+ @changed_from_cid = nil
178
+ @to_cid = given_attributes[:to]&.to_cid
179
+ @changed_to_cid = nil
180
+ @class_name = self.class.name
181
+ @class_name = @class_name.split('>::').last if @class_name.start_with?('#<')
182
+ end
183
+
184
+ def loaded?
185
+ true
186
+ end
187
+
188
+ def from
189
+ from_cid = from_as_cid
190
+ from_cid ? LucidNode::Base.node_from_cid(from_cid) : nil
191
+ end
192
+
193
+ def from_as_cid
194
+ @changed_from_cid ? @changed_from_cid : @from_cid
195
+ end
196
+
197
+ def from=(node)
198
+ @changed_from_cid = node.to_cid
199
+ node
200
+ end
201
+
202
+ def to
203
+ to_cid = to_as_cid
204
+ to_cid ? LucidNode::Base.node_from_cid(to_cid) : nil
205
+ end
206
+
207
+ def to_as_cid
208
+ @changed_to_cid ? @changed_to_cid : @to_cid
209
+ end
210
+
211
+ def to_transport(*args)
212
+ final_attributes = {}
213
+ self.class.attributes.each do |attr|
214
+ next if attr == :id
215
+ include_attribute = @attributes.key?(attr)
216
+ include_attribute = !self.class.attribute_options[attr][:server_only] if self.class.attribute_options[attr].key?(:server_only)
217
+ final_attributes[attr.to_s] = @attributes[attr] if include_attribute
218
+ end
219
+ { 'edges' => { @class_name => { @id => { 'from' => from_as_cid, 'to' => to_as_cid, 'attributes' => final_attributes }}}}
220
+ end
221
+
222
+ base.instance_exec do
223
+ def attribute(name, options = {})
224
+ attribute_options[name] = options
225
+
226
+ define_method(name) do
227
+ if changed_attributes.key?(name)
228
+ changed_attributes[name]
229
+ else
230
+ @attributes[name]
231
+ end
232
+ end
233
+
234
+ define_method("#{name}=") do |arg|
235
+ validate_attribute!(name, arg)
236
+ changed_attributes.set(name, arg)
237
+ end
238
+ end
239
+ end
240
+ end # RUBY_ENGINE
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,15 @@
1
+ module LucidGraph
2
+ class Base
3
+ include LucidGraph::Mixin
4
+
5
+ if RUBY_ENGINE != 'opal'
6
+ def self.inherited(base)
7
+ Isomorfeus.add_valid_graph_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