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.
@@ -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 [Klass] the target `class`
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 && !@class.is_a?(Proc)
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 @klass.is_a?(Proc)
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
- return (@new_item = block; nil) if block_given
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
- unless self.class.klass?
109
- raise "Undefined base 'klass' or 'new_item' callback for #{self.class}"
110
- end
111
-
112
- ini_doc = case ini_doc
113
- when Array
114
- ini_doc
115
- when Enumerable
116
- ini_doc.to_a
117
- else
118
- []
119
- end
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; count; end
161
- def empty?; count == 0; end
162
- def present?; count > 0; end
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 |item|
218
- yield(item) if block_given?
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
- self.to_a.each {|item| delete!(item)}
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
- if item = self[value]
237
- _doc_delete(item.doc)
238
- @indexed = false
239
- _items.delete(item)
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?; self.class.order_matters; end
246
- def uniq?; self.class.uniq; end
247
- def items_key; self.class.items_key; end
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(self.to_a[value])
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: 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: 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
- if current_pos = _doc_key(value)
313
- _doc_items.delete_at(current_pos)
314
- end
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 = if elem = self[value]
319
- _doc_key(elem)
320
- end
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 = (pos <= current_pos)? pos : pos - 1
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 |i|
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
- case
339
- when used_param?(pos)
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
- when used_param?(before)
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
- when used_param?(after)
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 (msg = "Something went wrong when linking the document.", from: nil, key: nil)
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: , read_only: false)
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 && doc.is_a?(Hash)
130
- return if model_forced_keys.empty?
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 *methods
143
- pass_writer *methods unless read_only
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: self.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, klass:)
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: self.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
- dim_class.klass = klass
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
- embed(method, key: key, multiple: true, klass: eclass, read_only: read_only) do |instance_with_called_method|
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 = {referrer_class => klass} if 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(method, key: method, nullable: false, multiple: false, klass:, read_only: self.read_only?, &block)
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
- k = key.to_s.freeze
250
+ obj_k = key.to_s.freeze
242
251
 
243
252
  # retrieving method (getter)
244
253
  define_method(method) do
245
- yield(self) if block_given?
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
- embedded_class = self.class.resolve_class(klass)
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.new(doc[k], parent: self, key: k, read_only: self.read_only? || read_only).tap do |obj|
266
- variable_set(var, obj)
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
- @forced_model_keys ||= {}
277
+ @model_forced_keys ||= {}
274
278
  end
275
279
  end
276
280
 
277
- inheritable_class_vars :forced_model_keys, :key, :read_only
281
+ inheritable_class_vars :model_forced_keys, :key, :read_only
278
282
 
279
- # `_key` refers to the parent's property that links to this model
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
- if key_method? && doc && doc.is_a?(Hash)
296
- self.key = doc[key_method]
297
- #puts "\n$(#{self.key}<=>#{self.class})"
298
- end
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.new "No key_method defined for #{self.class}" unless key_method?
315
- self.method(key_method).call
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.new "No key_method defined for #{self.class}" unless key_method?
321
- method = "#{key_method}="
322
- self.method(method).call(value)
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
- @doc
343
- else
344
- # transform parent's `_key` to this object into a
345
- # path key that can rerieve from the parents's doc
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
- @original_doc
356
- else
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 == {}) || (au == nil))
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 = odoc && JSON.parse(odoc.to_json)
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
- @doc = new_doc
415
- else
416
- dig_set(_parent.doc, [_doc_key(_key)].flatten, new_doc)
417
- _parent.variable_remove!(_key) unless new_doc
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
- @original_doc = new_doc
443
- else
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 !@_dim_vars.include?(var)
459
- @_dim_vars.delete(var)
460
- remove_instance_variable(var)
461
- end
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 {|k| variable_remove!(k)}
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