ecoportal-api-v2 1.1.7 → 1.1.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|