ecoportal-api-v2 1.1.7 → 1.1.8
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +54 -15
- data/CHANGELOG.md +6 -3
- data/lib/ecoportal/api/common/content/array_model.rb +81 -78
- data/lib/ecoportal/api/common/content/class_helpers.rb +25 -26
- data/lib/ecoportal/api/common/content/collection_model.rb +76 -64
- data/lib/ecoportal/api/common/content/double_model.rb +105 -87
- data/lib/ecoportal/api/v2/page/component.rb +60 -64
- data/lib/ecoportal/api/v2/page/components.rb +9 -9
- data/lib/ecoportal/api/v2/page/force.rb +5 -6
- data/lib/ecoportal/api/v2/page.rb +13 -14
- data/lib/ecoportal/api/v2_version.rb +1 -1
- metadata +2 -2
@@ -32,15 +32,18 @@ module Ecoportal
|
|
32
32
|
# @yield [doc] identifies the target `class` of the raw object
|
33
33
|
# @yieldparam doc [Hash]
|
34
34
|
# @yieldreturn [Klass] the target `class`
|
35
|
-
# @return [
|
35
|
+
# @return [Class, Proc, Hash] the target `class`
|
36
|
+
# - `Hash` tracks a symbol pending to be resovle from its referrer
|
37
|
+
# - `Class` an already resolve class
|
38
|
+
# - `Proc` a forker that pivots between multiple classes
|
36
39
|
def klass(value = NOT_USED, &block)
|
37
40
|
@klass = block if block_given?
|
38
41
|
|
39
|
-
if @klass
|
40
|
-
@klass = resolve_class(@klass, exception: false) unless @klass.is_a?(Class)
|
41
|
-
@klass
|
42
|
-
elsif @klass.is_a?(Proc) && used_param?(value)
|
42
|
+
if @klass.is_a?(Proc) && used_param?(value)
|
43
43
|
@klass.call(value)
|
44
|
+
elsif @klass && !@klass.is_a?(Proc) && !@klass.is_a?(Class)
|
45
|
+
@klass = resolve_class(@klass, exception: false)
|
46
|
+
@klass
|
44
47
|
else
|
45
48
|
@klass
|
46
49
|
end.tap do |result|
|
@@ -58,7 +61,7 @@ module Ecoportal
|
|
58
61
|
# Optimization
|
59
62
|
def new_item_class_based?
|
60
63
|
return false if @new_item.is_a?(Proc)
|
61
|
-
return false if
|
64
|
+
return false if klass.is_a?(Proc)
|
62
65
|
return true if klass.is_a?(Class)
|
63
66
|
false
|
64
67
|
end
|
@@ -78,7 +81,10 @@ module Ecoportal
|
|
78
81
|
# Meaning that there is actually no need to define this argument.
|
79
82
|
# @return [Klass] instance object of the target `klass`
|
80
83
|
def new_item(doc = NOT_USED, parent: nil, key: nil, read_only: false, &block)
|
81
|
-
|
84
|
+
if block_given?
|
85
|
+
@new_item = block
|
86
|
+
return
|
87
|
+
end
|
82
88
|
|
83
89
|
msg = "To define the 'new_item' callback (factory), you need to use a block"
|
84
90
|
raise msg unless used_param?(doc)
|
@@ -86,18 +92,11 @@ module Ecoportal
|
|
86
92
|
raise msg unless klass?
|
87
93
|
return @new_item.call(doc, parent, key) if @new_item.is_a?(Proc)
|
88
94
|
|
89
|
-
raise "Could not find a class for: #{doc}" unless target_class = klass(doc)
|
95
|
+
raise "Could not find a class for: #{doc}" unless (target_class = klass(doc))
|
90
96
|
return doc if doc.is_a?(target_class)
|
91
97
|
|
92
98
|
target_class.new(doc, parent: parent, key: key, read_only: read_only)
|
93
99
|
end
|
94
|
-
|
95
|
-
def doc_class(name)
|
96
|
-
dim_class = new_class(name, inherits: Common::Content::ArrayModel) do |klass|
|
97
|
-
klass.order_matters = order_matters
|
98
|
-
klass.uniq = uniq
|
99
|
-
end
|
100
|
-
end
|
101
100
|
end
|
102
101
|
|
103
102
|
include Enumerable
|
@@ -105,18 +104,18 @@ module Ecoportal
|
|
105
104
|
inheritable_class_vars :klass, :order_matters, :order_key, :items_key, :new_item
|
106
105
|
|
107
106
|
def initialize(ini_doc = [], parent: self, key: nil, read_only: false)
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
107
|
+
msg = "Undefined base 'klass' or 'new_item' callback for #{self.class}"
|
108
|
+
raise msg unless self.class.klass?
|
109
|
+
|
110
|
+
ini_doc =
|
111
|
+
case ini_doc
|
112
|
+
when Array
|
113
|
+
ini_doc
|
114
|
+
when Enumerable
|
115
|
+
ini_doc.to_a
|
116
|
+
else
|
117
|
+
[]
|
118
|
+
end
|
120
119
|
|
121
120
|
super(ini_doc, parent: parent, key: key, read_only: read_only)
|
122
121
|
end
|
@@ -137,7 +136,7 @@ module Ecoportal
|
|
137
136
|
def _doc_key(value)
|
138
137
|
#print "*(#{value.class})"
|
139
138
|
return super(value) unless value.is_a?(Hash) || value.is_a?(Content::DoubleModel)
|
140
|
-
if id = get_key(value)
|
139
|
+
if (id = get_key(value))
|
141
140
|
#print "^"
|
142
141
|
_doc_items.index {|item| get_key(item) == id}.tap do |p|
|
143
142
|
#print "{{#{p}}}"
|
@@ -157,9 +156,17 @@ module Ecoportal
|
|
157
156
|
end
|
158
157
|
end
|
159
158
|
|
160
|
-
def length
|
161
|
-
|
162
|
-
|
159
|
+
def length
|
160
|
+
count
|
161
|
+
end
|
162
|
+
|
163
|
+
def empty?
|
164
|
+
count&.zero?
|
165
|
+
end
|
166
|
+
|
167
|
+
def present?
|
168
|
+
count&.positive?
|
169
|
+
end
|
163
170
|
|
164
171
|
def each(&block)
|
165
172
|
return to_enum(:each) unless block
|
@@ -206,7 +213,7 @@ module Ecoportal
|
|
206
213
|
end
|
207
214
|
item_doc = value.is_a?(Content::DoubleModel)? value.doc : value
|
208
215
|
item_doc = JSON.parse(item_doc.to_json)
|
209
|
-
if item = self[value]
|
216
|
+
if (item = self[value])
|
210
217
|
item.replace_doc(item_doc)
|
211
218
|
else
|
212
219
|
_doc_upsert(item_doc, pos: pos, before: before, after: after).tap do |pos_idx|
|
@@ -214,14 +221,14 @@ module Ecoportal
|
|
214
221
|
@indexed = false
|
215
222
|
end
|
216
223
|
end
|
217
|
-
(item || self[item_doc]).tap do |
|
218
|
-
yield(
|
224
|
+
(item || self[item_doc]).tap do |itm|
|
225
|
+
yield(itm) if block_given?
|
219
226
|
end
|
220
227
|
end
|
221
228
|
|
222
229
|
# Deletes all the elements of this `CollectionModel` instance
|
223
230
|
def clear
|
224
|
-
|
231
|
+
to_a.each {|item| delete!(item)}
|
225
232
|
end
|
226
233
|
|
227
234
|
# Deletes `value` from this `CollectionModel` instance
|
@@ -233,18 +240,25 @@ module Ecoportal
|
|
233
240
|
unless value.is_a?(Hash) || value.is_a?(Content::DoubleModel) || value.is_a?(String)
|
234
241
|
raise "'Content::DoubleModel' or 'Hash' doc required"
|
235
242
|
end
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
end
|
243
|
+
return unless (item = self[value])
|
244
|
+
_doc_delete(item.doc)
|
245
|
+
@indexed = false
|
246
|
+
_items.delete(item)
|
241
247
|
end
|
242
248
|
|
243
249
|
protected
|
244
250
|
|
245
|
-
def order_matters
|
246
|
-
|
247
|
-
|
251
|
+
def order_matters?
|
252
|
+
self.class.order_matters
|
253
|
+
end
|
254
|
+
|
255
|
+
def uniq?
|
256
|
+
self.class.uniq
|
257
|
+
end
|
258
|
+
|
259
|
+
def items_key
|
260
|
+
self.class.items_key
|
261
|
+
end
|
248
262
|
|
249
263
|
def on_change
|
250
264
|
@indexed = false
|
@@ -261,7 +275,7 @@ module Ecoportal
|
|
261
275
|
when String
|
262
276
|
value
|
263
277
|
when Numeric
|
264
|
-
get_key(
|
278
|
+
get_key(to_a[value])
|
265
279
|
end
|
266
280
|
end
|
267
281
|
|
@@ -284,9 +298,9 @@ module Ecoportal
|
|
284
298
|
|
285
299
|
def new_item(value)
|
286
300
|
if self.class.new_item_class_based?
|
287
|
-
self.class.klass.new(value, parent: self, read_only:
|
301
|
+
self.class.klass.new(value, parent: self, read_only: _read_only)
|
288
302
|
else
|
289
|
-
self.class.new_item(value, parent: self, read_only:
|
303
|
+
self.class.new_item(value, parent: self, read_only: _read_only)
|
290
304
|
end
|
291
305
|
end
|
292
306
|
|
@@ -309,48 +323,46 @@ module Ecoportal
|
|
309
323
|
# Deletes `value` from `doc` (here referred as `_doc_items`)
|
310
324
|
# @return [Object] the element deleted from `doc`
|
311
325
|
def _doc_delete(value)
|
312
|
-
|
313
|
-
|
314
|
-
|
326
|
+
return unless (current_pos = _doc_key(value))
|
327
|
+
|
328
|
+
_doc_items.delete_at(current_pos)
|
315
329
|
end
|
316
330
|
|
317
331
|
def _doc_upsert(value, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
|
318
|
-
current_pos =
|
319
|
-
|
320
|
-
|
332
|
+
current_pos =
|
333
|
+
if (elem = self[value])
|
334
|
+
_doc_key(elem)
|
335
|
+
end
|
321
336
|
|
322
337
|
pos = scope_position(pos: pos, before: before, after: after)
|
323
338
|
pos ||= current_pos
|
324
339
|
|
325
340
|
if current_pos && pos
|
326
341
|
_doc_items.delete_at(current_pos)
|
327
|
-
pos =
|
342
|
+
pos = pos <= current_pos ? pos : pos - 1
|
328
343
|
end
|
329
344
|
|
330
|
-
pos = (pos && pos < _doc_items.length)? pos : _doc_items.length
|
331
|
-
pos.tap do |
|
345
|
+
pos = (pos && pos < _doc_items.length)? pos : _doc_items.length # rubocop:disable Style/TernaryParentheses
|
346
|
+
pos.tap do |_i|
|
332
347
|
_doc_items.insert(pos, value)
|
333
348
|
end
|
334
|
-
|
335
349
|
end
|
336
350
|
|
337
351
|
def scope_position(pos: NOT_USED, before: NOT_USED, after: NOT_USED)
|
338
|
-
|
339
|
-
|
340
|
-
if elem = self[pos]
|
352
|
+
if used_param?(pos)
|
353
|
+
if (elem = self[pos])
|
341
354
|
_doc_key(elem) - 1
|
342
355
|
end
|
343
|
-
|
344
|
-
if elem = self[before]
|
356
|
+
elsif used_param?(before)
|
357
|
+
if (elem = self[before])
|
345
358
|
_doc_key(elem) - 1
|
346
359
|
end
|
347
|
-
|
348
|
-
if elem = self[after]
|
360
|
+
elsif used_param?(after)
|
361
|
+
if (elem = self[after])
|
349
362
|
_doc_key(elem)
|
350
363
|
end
|
351
364
|
end
|
352
365
|
end
|
353
|
-
|
354
366
|
end
|
355
367
|
end
|
356
368
|
end
|
@@ -6,12 +6,8 @@ module Ecoportal
|
|
6
6
|
# which differs of `attr_*` ruby native class methods because `pass*`
|
7
7
|
# completelly **links** the methods **to a subjacent `Hash` model**
|
8
8
|
class DoubleModel < Common::BaseModel
|
9
|
-
NOT_USED = Common::Content::ClassHelpers::NOT_USED
|
10
|
-
extend Common::Content::ClassHelpers
|
11
|
-
include Common::Content::ModelHelpers
|
12
|
-
|
13
9
|
class UnlinkedModel < StandardError
|
14
|
-
def initialize
|
10
|
+
def initialize(msg = "Something went wrong when linking the document.", from: nil, key: nil)
|
15
11
|
msg += " From: #{from}." if from
|
16
12
|
msg += " key: #{key}." if key
|
17
13
|
super(msg)
|
@@ -21,6 +17,10 @@ module Ecoportal
|
|
21
17
|
class NoKeyMethod < StandardError
|
22
18
|
end
|
23
19
|
|
20
|
+
NOT_USED = Common::Content::ClassHelpers::NOT_USED
|
21
|
+
extend Common::Content::ClassHelpers
|
22
|
+
include Common::Content::ModelHelpers
|
23
|
+
|
24
24
|
class << self
|
25
25
|
attr_reader :key
|
26
26
|
|
@@ -118,7 +118,7 @@ module Ecoportal
|
|
118
118
|
# as well as its `key` in the underlying `Hash` model.
|
119
119
|
# @param default [Value] the default value that
|
120
120
|
# this `key` will be written in the model when it doesn't exixt
|
121
|
-
def passforced(method, default
|
121
|
+
def passforced(method, default:, read_only: false)
|
122
122
|
model_forced_keys[method.to_s.freeze] = default
|
123
123
|
passthrough(method, read_only: read_only)
|
124
124
|
end
|
@@ -126,8 +126,9 @@ module Ecoportal
|
|
126
126
|
# Ensures `doc` has the `model_forced_keys`. If it doesn't, it adds those missing
|
127
127
|
# with the defined `default` values
|
128
128
|
def enforce!(doc)
|
129
|
-
return unless doc
|
130
|
-
return if
|
129
|
+
return unless doc.is_a?(Hash)
|
130
|
+
return if model_forced_keys.empty?
|
131
|
+
|
131
132
|
model_forced_keys.each do |key, default|
|
132
133
|
doc[key] = default unless doc.key?(key)
|
133
134
|
end
|
@@ -139,8 +140,8 @@ module Ecoportal
|
|
139
140
|
# as well as its `key` in the underlying `Hash` model.
|
140
141
|
# @param read_only [Boolean] should it only define the reader?
|
141
142
|
def passthrough(*methods, read_only: false)
|
142
|
-
pass_reader
|
143
|
-
pass_writer
|
143
|
+
pass_reader(*methods)
|
144
|
+
pass_writer(*methods) unless read_only
|
144
145
|
self
|
145
146
|
end
|
146
147
|
|
@@ -151,9 +152,7 @@ module Ecoportal
|
|
151
152
|
# @param read_only [Boolean] should it only define the reader?
|
152
153
|
def passdate(*methods, read_only: false)
|
153
154
|
pass_reader(*methods) {|value| to_time(value)}
|
154
|
-
unless read_only
|
155
|
-
pass_writer(*methods) {|value| to_time(value)&.iso8601}
|
156
|
-
end
|
155
|
+
pass_writer(*methods) {|value| to_time(value)&.iso8601} unless read_only
|
157
156
|
self
|
158
157
|
end
|
159
158
|
|
@@ -163,9 +162,7 @@ module Ecoportal
|
|
163
162
|
# @param read_only [Boolean] should it only define the reader?
|
164
163
|
def passboolean(*methods, read_only: false)
|
165
164
|
pass_reader(*methods) {|value| value}
|
166
|
-
unless read_only
|
167
|
-
pass_writer(*methods) {|value| !!value}
|
168
|
-
end
|
165
|
+
pass_writer(*methods) {|value| !!value} unless read_only
|
169
166
|
self
|
170
167
|
end
|
171
168
|
|
@@ -186,7 +183,7 @@ module Ecoportal
|
|
186
183
|
|
187
184
|
define_method method do
|
188
185
|
return instance_variable_get(var) if instance_variable_defined?(var)
|
189
|
-
new_obj = dim_class.new(parent: self, key: method, read_only:
|
186
|
+
new_obj = dim_class.new(parent: self, key: method, read_only: read_only?)
|
190
187
|
variable_set(var, new_obj)
|
191
188
|
end
|
192
189
|
end
|
@@ -197,7 +194,7 @@ module Ecoportal
|
|
197
194
|
# @param key [Symbol] the `key` that embeds it to the underlying `Hash` model
|
198
195
|
# @nullable [Boolean] to specify if this object can be `nil`
|
199
196
|
# @param klass [Class, String] the class of the embedded object
|
200
|
-
def embeds_one(method, key: method, nullable: false
|
197
|
+
def embeds_one(method, klass:, key: method, nullable: false)
|
201
198
|
embed(method, key: key, nullable: nullable, multiple: false, klass: klass)
|
202
199
|
end
|
203
200
|
|
@@ -212,12 +209,13 @@ module Ecoportal
|
|
212
209
|
# @param read_only [Boolean] whether or not should try to **work around** items `klass` missing a `key`
|
213
210
|
# - If set to `true` this is meant only for read purposes (won't be able to successufully insert)
|
214
211
|
def embeds_many(method, key: method, klass: nil, enum_class: nil,
|
215
|
-
order_matters: false, order_key: nil, read_only:
|
212
|
+
order_matters: false, order_key: nil, read_only: read_only?)
|
216
213
|
if enum_class
|
217
214
|
eclass = enum_class
|
218
215
|
elsif klass
|
219
216
|
eclass = new_class("#{method}::#{klass}", inherits: Common::Content::CollectionModel) do |dim_class|
|
220
|
-
|
217
|
+
# NOTE: new_class may resolve the namespace of the class to an already existing class
|
218
|
+
dim_class.klass ||= klass
|
221
219
|
dim_class.order_matters = order_matters
|
222
220
|
dim_class.order_key = order_key
|
223
221
|
dim_class.read_only! if read_only
|
@@ -225,61 +223,69 @@ module Ecoportal
|
|
225
223
|
else
|
226
224
|
raise "You should either specify the 'klass' of the elements or the 'enum_class'"
|
227
225
|
end
|
228
|
-
|
226
|
+
|
227
|
+
embed(
|
228
|
+
method, key: key,
|
229
|
+
multiple: true, klass: eclass,
|
230
|
+
read_only: read_only
|
231
|
+
) do |instance_with_called_method|
|
229
232
|
# keep reference to the original class to resolve the `klass` dependency
|
230
233
|
# See stackoverflow: https://stackoverflow.com/a/73709529/4352306
|
231
234
|
referrer_class = instance_with_called_method.class
|
232
|
-
eclass.klass
|
235
|
+
eclass.klass = {referrer_class => klass} if klass
|
236
|
+
# This helps `resolve_class` to correctly resolve a symbol
|
237
|
+
# by using referrer_class as a base module to resolve it
|
233
238
|
end
|
234
239
|
end
|
235
240
|
|
236
241
|
private
|
237
242
|
|
238
|
-
def embed(
|
243
|
+
def embed(
|
244
|
+
method, klass:, key: method,
|
245
|
+
nullable: false, multiple: false, read_only: read_only?,
|
246
|
+
&embed_block
|
247
|
+
)
|
239
248
|
method = method.to_s.freeze
|
240
249
|
var = instance_variable_name(method).freeze
|
241
|
-
|
250
|
+
obj_k = key.to_s.freeze
|
242
251
|
|
243
252
|
# retrieving method (getter)
|
244
253
|
define_method(method) do
|
245
|
-
|
254
|
+
# set item klass as referrer to klass (to allow resolve symbol)
|
255
|
+
embed_block&.call(self)
|
246
256
|
return instance_variable_get(var) if instance_variable_defined?(var)
|
247
|
-
unless nullable
|
248
|
-
doc[k] ||= multiple ? [] : {}
|
249
|
-
end
|
250
|
-
return variable_set(var, nil) unless doc[k]
|
251
257
|
|
252
|
-
|
253
|
-
|
254
|
-
if multiple && read_only
|
255
|
-
if doc[k].is_a?(Array) && embedded_class < Common::Content::CollectionModel
|
256
|
-
if (item_class = embedded_class.klass) && !item_class.key?
|
257
|
-
item_class.passkey :id
|
258
|
-
doc[k].each_with_index do |item_doc, i|
|
259
|
-
item_doc["id"] = "#{i}" unless item_doc.key?("id")
|
260
|
-
end
|
261
|
-
end
|
262
|
-
end
|
263
|
-
end
|
258
|
+
doc[obj_k] ||= (multiple ? [] : {}) unless nullable
|
259
|
+
return variable_set(var, nil) unless doc[obj_k]
|
264
260
|
|
265
|
-
embedded_class
|
266
|
-
|
261
|
+
embedded_class = self.class.resolve_class(klass)
|
262
|
+
setup_items_key(embedded_class, doc[obj_k]) if multiple && read_only
|
263
|
+
|
264
|
+
embedded_class.new(
|
265
|
+
doc[obj_k],
|
266
|
+
parent: self,
|
267
|
+
key: obj_k,
|
268
|
+
read_only: read_only? || read_only
|
269
|
+
).tap do |collection|
|
270
|
+
variable_set(var, collection)
|
267
271
|
end
|
268
272
|
end
|
269
273
|
end
|
270
274
|
|
271
275
|
# The list of keys that will be forced in the model
|
272
276
|
def model_forced_keys
|
273
|
-
@
|
277
|
+
@model_forced_keys ||= {}
|
274
278
|
end
|
275
279
|
end
|
276
280
|
|
277
|
-
inheritable_class_vars :
|
281
|
+
inheritable_class_vars :model_forced_keys, :key, :read_only
|
278
282
|
|
279
|
-
# `_key` refers to the
|
283
|
+
# `_key` refers to the `_parent`'s property that links to this model
|
284
|
+
# @note while `key` refers to the value of theproperty of this model
|
285
|
+
# that is key (identifies an item in a set of elements)
|
280
286
|
attr_reader :_parent, :_key, :_read_only
|
281
287
|
|
282
|
-
def initialize(doc = {}, parent: self, key: nil, read_only: self.class.read_only?)
|
288
|
+
def initialize(doc = {}, parent: self, key: nil, read_only: self.class.read_only?) # rubocop:disable Lint/MissingSuper
|
283
289
|
@_dim_vars = []
|
284
290
|
@_parent = parent || self
|
285
291
|
@_key = key || self
|
@@ -292,10 +298,10 @@ module Ecoportal
|
|
292
298
|
@original_doc = JSON.parse(@doc.to_json)
|
293
299
|
end
|
294
300
|
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
301
|
+
return unless key_method? && doc && doc.is_a?(Hash)
|
302
|
+
|
303
|
+
self.key = doc[key_method]
|
304
|
+
#puts "\n$(#{self.key}<=>#{self.class})"
|
299
305
|
end
|
300
306
|
|
301
307
|
# @note `read_only` allows for some optimizations, such as storing values
|
@@ -311,15 +317,20 @@ module Ecoportal
|
|
311
317
|
|
312
318
|
# @return [String] the `value` of the `key` method (i.e. `id` value)
|
313
319
|
def key
|
314
|
-
raise NoKeyMethod
|
315
|
-
|
320
|
+
raise NoKeyMethod, "No key_method defined for #{self.class}" unless key_method?
|
321
|
+
|
322
|
+
method(key_method).call
|
316
323
|
end
|
317
324
|
|
318
325
|
# @param [String] the `value` of the `key` method (i.e. `id` value)
|
319
326
|
def key=(value)
|
320
|
-
raise NoKeyMethod
|
321
|
-
|
322
|
-
|
327
|
+
raise NoKeyMethod, "No key_method defined for #{self.class}" unless key_method?
|
328
|
+
|
329
|
+
method("#{key_method}=").call(value)
|
330
|
+
end
|
331
|
+
|
332
|
+
def resolved_doc_key
|
333
|
+
[_doc_key(_key)]
|
323
334
|
end
|
324
335
|
|
325
336
|
# Offers a method for child classes to transform the key,
|
@@ -337,25 +348,22 @@ module Ecoportal
|
|
337
348
|
# @return [nil, Hash] the underlying `Hash` model as is (carrying current changes)
|
338
349
|
def doc
|
339
350
|
return @doc if doc_var?
|
351
|
+
|
340
352
|
raise UnlinkedModel.new(from: "#{self.class}#doc", key: _key) unless linked?
|
341
|
-
if is_root?
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
_parent.doc.dig(*[_doc_key(_key)].flatten)
|
347
|
-
end
|
353
|
+
return @doc if is_root?
|
354
|
+
|
355
|
+
# transform parent's `_key` to this object into a
|
356
|
+
# path key that can rerieve from the parents's doc
|
357
|
+
_parent.doc.dig(*resolved_doc_key.flatten)
|
348
358
|
end
|
349
359
|
|
350
360
|
# The `original_doc` holds the model as is now on server-side.
|
351
361
|
# @return [nil, Hash] the underlying `Hash` model as after last `consolidate!` changes
|
352
362
|
def original_doc
|
353
363
|
raise UnlinkedModel.new(from: "#{self.class}#original_doc", key: _key) unless linked?
|
354
|
-
if is_root?
|
355
|
-
|
356
|
-
|
357
|
-
_parent.original_doc.dig(*[_doc_key(_key)].flatten)
|
358
|
-
end
|
364
|
+
return @original_doc if is_root?
|
365
|
+
|
366
|
+
_parent.original_doc.dig(*resolved_doc_key.flatten)
|
359
367
|
end
|
360
368
|
|
361
369
|
def as_json
|
@@ -376,7 +384,7 @@ module Ecoportal
|
|
376
384
|
# @return [Boolean] stating if there are changes
|
377
385
|
def dirty?
|
378
386
|
au = as_update
|
379
|
-
!((au == {}) ||
|
387
|
+
!((au == {}) || au.nil?)
|
380
388
|
end
|
381
389
|
|
382
390
|
# It makes `original_doc` to be like `doc`
|
@@ -396,7 +404,7 @@ module Ecoportal
|
|
396
404
|
if key
|
397
405
|
keys = [key].flatten.compact
|
398
406
|
odoc = original_doc.dig(*keys)
|
399
|
-
odoc
|
407
|
+
odoc &&= JSON.parse(odoc.to_json)
|
400
408
|
dig_set(doc, keys, odoc)
|
401
409
|
else
|
402
410
|
replace_doc(JSON.parse(original_doc.to_json))
|
@@ -410,13 +418,11 @@ module Ecoportal
|
|
410
418
|
|
411
419
|
def replace_doc(new_doc)
|
412
420
|
raise UnlinkedModel.new(from: "#{self.class}#replace_doc", key: _key) unless linked?
|
413
|
-
if is_root?
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
#variables_remove!
|
419
|
-
end
|
421
|
+
return (@doc = new_doc) if is_root?
|
422
|
+
|
423
|
+
dig_set(_parent.doc, resolved_doc_key.flatten, new_doc)
|
424
|
+
_parent.variable_remove!(_key) unless new_doc
|
425
|
+
#variables_remove!
|
420
426
|
end
|
421
427
|
|
422
428
|
protected
|
@@ -438,11 +444,9 @@ module Ecoportal
|
|
438
444
|
|
439
445
|
def replace_original_doc(new_doc)
|
440
446
|
raise UnlinkedModel.new(from: "#{self.class}#replace_original_doc", key: _key) unless linked?
|
441
|
-
if is_root?
|
442
|
-
|
443
|
-
|
444
|
-
dig_set(_parent.original_doc, [_doc_key(_key)].flatten, new_doc)
|
445
|
-
end
|
447
|
+
return (@original_doc = new_doc) if is_root?
|
448
|
+
|
449
|
+
dig_set(_parent.original_doc, resolved_doc_key.flatten, new_doc)
|
446
450
|
end
|
447
451
|
|
448
452
|
# Helper to track down persistent variables
|
@@ -455,16 +459,16 @@ module Ecoportal
|
|
455
459
|
# Helper to remove tracked down instance variables
|
456
460
|
def variable_remove!(key)
|
457
461
|
var = instance_variable_name(key)
|
458
|
-
unless
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
+
return unless @_dim_vars.include?(var)
|
463
|
+
|
464
|
+
@_dim_vars.delete(var)
|
465
|
+
remove_instance_variable(var)
|
462
466
|
end
|
463
467
|
|
464
468
|
# Removes all the persistent variables
|
465
469
|
def variables_remove!
|
466
470
|
#puts "going to remove vars: #{@_dim_vars} on #{self.class} (parent: #{identify_parent(self._parent)})"
|
467
|
-
@_dim_vars.dup.map {|
|
471
|
+
@_dim_vars.dup.map {|var| variable_remove!(var)}
|
468
472
|
end
|
469
473
|
|
470
474
|
private
|
@@ -502,6 +506,20 @@ module Ecoportal
|
|
502
506
|
self.class.key
|
503
507
|
end
|
504
508
|
|
509
|
+
# It allows to work-around missing item_key
|
510
|
+
def setup_items_key(embedded_class, obj_doc)
|
511
|
+
# only if is going to be a collection
|
512
|
+
return unless obj_doc.is_a?(Array) && embedded_class < Common::Content::CollectionModel
|
513
|
+
return unless (item_class = embedded_class.klass)
|
514
|
+
# if already has key don't need to work around
|
515
|
+
return if item_class&.key
|
516
|
+
|
517
|
+
# apply work around
|
518
|
+
item_class.passkey :id
|
519
|
+
obj_doc.each_with_index do |item_doc, idx|
|
520
|
+
item_doc["id"] = idx.to_s unless item_doc.key?("id")
|
521
|
+
end
|
522
|
+
end
|
505
523
|
end
|
506
524
|
end
|
507
525
|
end
|