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.
@@ -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