ecoportal-api-v2 1.1.6 → 1.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
 
@@ -38,6 +38,16 @@ module Ecoportal
38
38
  uid(length)
39
39
  end
40
40
 
41
+ def read_only?
42
+ @read_only = false if @read_only.nil?
43
+ @read_only
44
+ end
45
+
46
+ # Be able to define if a class should be read-only
47
+ def read_only!
48
+ @read_only = true
49
+ end
50
+
41
51
  # Same as `attr_reader` but links to a subjacent `Hash` model property
42
52
  # @note it does **not** create an _instance variable_
43
53
  # @param methods [Array<Symbol>] the method that exposes the value
@@ -108,7 +118,7 @@ module Ecoportal
108
118
  # as well as its `key` in the underlying `Hash` model.
109
119
  # @param default [Value] the default value that
110
120
  # this `key` will be written in the model when it doesn't exixt
111
- def passforced(method, default: , read_only: false)
121
+ def passforced(method, default:, read_only: false)
112
122
  model_forced_keys[method.to_s.freeze] = default
113
123
  passthrough(method, read_only: read_only)
114
124
  end
@@ -116,8 +126,9 @@ module Ecoportal
116
126
  # Ensures `doc` has the `model_forced_keys`. If it doesn't, it adds those missing
117
127
  # with the defined `default` values
118
128
  def enforce!(doc)
119
- return unless doc && doc.is_a?(Hash)
120
- return if model_forced_keys.empty?
129
+ return unless doc.is_a?(Hash)
130
+ return if model_forced_keys.empty?
131
+
121
132
  model_forced_keys.each do |key, default|
122
133
  doc[key] = default unless doc.key?(key)
123
134
  end
@@ -129,8 +140,8 @@ module Ecoportal
129
140
  # as well as its `key` in the underlying `Hash` model.
130
141
  # @param read_only [Boolean] should it only define the reader?
131
142
  def passthrough(*methods, read_only: false)
132
- pass_reader *methods
133
- pass_writer *methods unless read_only
143
+ pass_reader(*methods)
144
+ pass_writer(*methods) unless read_only
134
145
  self
135
146
  end
136
147
 
@@ -141,9 +152,7 @@ module Ecoportal
141
152
  # @param read_only [Boolean] should it only define the reader?
142
153
  def passdate(*methods, read_only: false)
143
154
  pass_reader(*methods) {|value| to_time(value)}
144
- unless read_only
145
- pass_writer(*methods) {|value| to_time(value)&.iso8601}
146
- end
155
+ pass_writer(*methods) {|value| to_time(value)&.iso8601} unless read_only
147
156
  self
148
157
  end
149
158
 
@@ -153,9 +162,7 @@ module Ecoportal
153
162
  # @param read_only [Boolean] should it only define the reader?
154
163
  def passboolean(*methods, read_only: false)
155
164
  pass_reader(*methods) {|value| value}
156
- unless read_only
157
- pass_writer(*methods) {|value| !!value}
158
- end
165
+ pass_writer(*methods) {|value| !!value} unless read_only
159
166
  self
160
167
  end
161
168
 
@@ -176,7 +183,7 @@ module Ecoportal
176
183
 
177
184
  define_method method do
178
185
  return instance_variable_get(var) if instance_variable_defined?(var)
179
- 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?)
180
187
  variable_set(var, new_obj)
181
188
  end
182
189
  end
@@ -187,7 +194,7 @@ module Ecoportal
187
194
  # @param key [Symbol] the `key` that embeds it to the underlying `Hash` model
188
195
  # @nullable [Boolean] to specify if this object can be `nil`
189
196
  # @param klass [Class, String] the class of the embedded object
190
- def embeds_one(method, key: method, nullable: false, klass:)
197
+ def embeds_one(method, klass:, key: method, nullable: false)
191
198
  embed(method, key: key, nullable: nullable, multiple: false, klass: klass)
192
199
  end
193
200
 
@@ -202,73 +209,83 @@ module Ecoportal
202
209
  # @param read_only [Boolean] whether or not should try to **work around** items `klass` missing a `key`
203
210
  # - If set to `true` this is meant only for read purposes (won't be able to successufully insert)
204
211
  def embeds_many(method, key: method, klass: nil, enum_class: nil,
205
- order_matters: false, order_key: nil, read_only: false)
212
+ order_matters: false, order_key: nil, read_only: read_only?)
206
213
  if enum_class
207
214
  eclass = enum_class
208
215
  elsif klass
209
216
  eclass = new_class("#{method}::#{klass}", inherits: Common::Content::CollectionModel) do |dim_class|
210
- 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
211
219
  dim_class.order_matters = order_matters
212
220
  dim_class.order_key = order_key
221
+ dim_class.read_only! if read_only
213
222
  end
214
223
  else
215
224
  raise "You should either specify the 'klass' of the elements or the 'enum_class'"
216
225
  end
217
- 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|
218
232
  # keep reference to the original class to resolve the `klass` dependency
219
233
  # See stackoverflow: https://stackoverflow.com/a/73709529/4352306
220
234
  referrer_class = instance_with_called_method.class
221
- 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
222
238
  end
223
239
  end
224
240
 
225
241
  private
226
242
 
227
- def embed(method, key: method, nullable: false, multiple: false, klass:, read_only: false, &block)
243
+ def embed(
244
+ method, klass:, key: method,
245
+ nullable: false, multiple: false, read_only: read_only?,
246
+ &embed_block
247
+ )
228
248
  method = method.to_s.freeze
229
249
  var = instance_variable_name(method).freeze
230
- k = key.to_s.freeze
250
+ obj_k = key.to_s.freeze
231
251
 
232
252
  # retrieving method (getter)
233
253
  define_method(method) do
234
- yield(self) if block_given?
254
+ # set item klass as referrer to klass (to allow resolve symbol)
255
+ embed_block&.call(self)
235
256
  return instance_variable_get(var) if instance_variable_defined?(var)
236
- unless nullable
237
- doc[k] ||= multiple ? [] : {}
238
- end
239
- return variable_set(var, nil) unless doc[k]
240
257
 
241
- embedded_class = self.class.resolve_class(klass)
258
+ doc[obj_k] ||= (multiple ? [] : {}) unless nullable
259
+ return variable_set(var, nil) unless doc[obj_k]
242
260
 
243
- if multiple && read_only
244
- if doc[k].is_a?(Array) && embedded_class < Common::Content::CollectionModel
245
- if (item_class = embedded_class.klass) && !item_class.key?
246
- item_class.passkey :id
247
- doc[k].each_with_index do |item_doc, i|
248
- item_doc["id"] = "#{i}" unless item_doc.key?("id")
249
- end
250
- end
251
- end
252
- end
253
-
254
- embedded_class.new(doc[k], parent: self, key: k, read_only: self._read_only || read_only).tap do |obj|
255
- 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)
256
271
  end
257
272
  end
258
273
  end
259
274
 
260
275
  # The list of keys that will be forced in the model
261
276
  def model_forced_keys
262
- @forced_model_keys ||= {}
277
+ @model_forced_keys ||= {}
263
278
  end
264
279
  end
265
280
 
266
- inheritable_class_vars :forced_model_keys, :key
281
+ inheritable_class_vars :model_forced_keys, :key, :read_only
267
282
 
268
- # `_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)
269
286
  attr_reader :_parent, :_key, :_read_only
270
287
 
271
- def initialize(doc = {}, parent: self, key: nil, read_only: false)
288
+ def initialize(doc = {}, parent: self, key: nil, read_only: self.class.read_only?) # rubocop:disable Lint/MissingSuper
272
289
  @_dim_vars = []
273
290
  @_parent = parent || self
274
291
  @_key = key || self
@@ -281,10 +298,16 @@ module Ecoportal
281
298
  @original_doc = JSON.parse(@doc.to_json)
282
299
  end
283
300
 
284
- if key_method? && doc && doc.is_a?(Hash)
285
- self.key = doc[key_method]
286
- #puts "\n$(#{self.key}<=>#{self.class})"
287
- 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})"
305
+ end
306
+
307
+ # @note `read_only` allows for some optimizations, such as storing values
308
+ # in instance variables, for optimization purposes
309
+ def read_only?
310
+ @_read_only
288
311
  end
289
312
 
290
313
  def root
@@ -294,15 +317,20 @@ module Ecoportal
294
317
 
295
318
  # @return [String] the `value` of the `key` method (i.e. `id` value)
296
319
  def key
297
- raise NoKeyMethod.new "No key_method defined for #{self.class}" unless key_method?
298
- self.method(key_method).call
320
+ raise NoKeyMethod, "No key_method defined for #{self.class}" unless key_method?
321
+
322
+ method(key_method).call
299
323
  end
300
324
 
301
325
  # @param [String] the `value` of the `key` method (i.e. `id` value)
302
326
  def key=(value)
303
- raise NoKeyMethod.new "No key_method defined for #{self.class}" unless key_method?
304
- method = "#{key_method}="
305
- 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)]
306
334
  end
307
335
 
308
336
  # Offers a method for child classes to transform the key,
@@ -320,25 +348,22 @@ module Ecoportal
320
348
  # @return [nil, Hash] the underlying `Hash` model as is (carrying current changes)
321
349
  def doc
322
350
  return @doc if doc_var?
351
+
323
352
  raise UnlinkedModel.new(from: "#{self.class}#doc", key: _key) unless linked?
324
- if is_root?
325
- @doc
326
- else
327
- # transform parent's `_key` to this object into a
328
- # path key that can rerieve from the parents's doc
329
- _parent.doc.dig(*[_doc_key(_key)].flatten)
330
- 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)
331
358
  end
332
359
 
333
360
  # The `original_doc` holds the model as is now on server-side.
334
361
  # @return [nil, Hash] the underlying `Hash` model as after last `consolidate!` changes
335
362
  def original_doc
336
363
  raise UnlinkedModel.new(from: "#{self.class}#original_doc", key: _key) unless linked?
337
- if is_root?
338
- @original_doc
339
- else
340
- _parent.original_doc.dig(*[_doc_key(_key)].flatten)
341
- end
364
+ return @original_doc if is_root?
365
+
366
+ _parent.original_doc.dig(*resolved_doc_key.flatten)
342
367
  end
343
368
 
344
369
  def as_json
@@ -359,7 +384,7 @@ module Ecoportal
359
384
  # @return [Boolean] stating if there are changes
360
385
  def dirty?
361
386
  au = as_update
362
- !((au == {}) || (au == nil))
387
+ !((au == {}) || au.nil?)
363
388
  end
364
389
 
365
390
  # It makes `original_doc` to be like `doc`
@@ -379,7 +404,7 @@ module Ecoportal
379
404
  if key
380
405
  keys = [key].flatten.compact
381
406
  odoc = original_doc.dig(*keys)
382
- odoc = odoc && JSON.parse(odoc.to_json)
407
+ odoc &&= JSON.parse(odoc.to_json)
383
408
  dig_set(doc, keys, odoc)
384
409
  else
385
410
  replace_doc(JSON.parse(original_doc.to_json))
@@ -393,13 +418,11 @@ module Ecoportal
393
418
 
394
419
  def replace_doc(new_doc)
395
420
  raise UnlinkedModel.new(from: "#{self.class}#replace_doc", key: _key) unless linked?
396
- if is_root?
397
- @doc = new_doc
398
- else
399
- dig_set(_parent.doc, [_doc_key(_key)].flatten, new_doc)
400
- _parent.variable_remove!(_key) unless new_doc
401
- #variables_remove!
402
- 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!
403
426
  end
404
427
 
405
428
  protected
@@ -408,6 +431,9 @@ module Ecoportal
408
431
  !!defined?(@doc)
409
432
  end
410
433
 
434
+ # Both requisites
435
+ # @note that for optimization purposes, `@doc` var may be used when
436
+ # the object is `read_only?`
411
437
  def is_root?
412
438
  _parent == self && doc_var?
413
439
  end
@@ -418,11 +444,9 @@ module Ecoportal
418
444
 
419
445
  def replace_original_doc(new_doc)
420
446
  raise UnlinkedModel.new(from: "#{self.class}#replace_original_doc", key: _key) unless linked?
421
- if is_root?
422
- @original_doc = new_doc
423
- else
424
- dig_set(_parent.original_doc, [_doc_key(_key)].flatten, new_doc)
425
- end
447
+ return (@original_doc = new_doc) if is_root?
448
+
449
+ dig_set(_parent.original_doc, resolved_doc_key.flatten, new_doc)
426
450
  end
427
451
 
428
452
  # Helper to track down persistent variables
@@ -435,16 +459,16 @@ module Ecoportal
435
459
  # Helper to remove tracked down instance variables
436
460
  def variable_remove!(key)
437
461
  var = instance_variable_name(key)
438
- unless !@_dim_vars.include?(var)
439
- @_dim_vars.delete(var)
440
- remove_instance_variable(var)
441
- end
462
+ return unless @_dim_vars.include?(var)
463
+
464
+ @_dim_vars.delete(var)
465
+ remove_instance_variable(var)
442
466
  end
443
467
 
444
468
  # Removes all the persistent variables
445
469
  def variables_remove!
446
470
  #puts "going to remove vars: #{@_dim_vars} on #{self.class} (parent: #{identify_parent(self._parent)})"
447
- @_dim_vars.dup.map {|k| variable_remove!(k)}
471
+ @_dim_vars.dup.map {|var| variable_remove!(var)}
448
472
  end
449
473
 
450
474
  private
@@ -482,6 +506,20 @@ module Ecoportal
482
506
  self.class.key
483
507
  end
484
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
485
523
  end
486
524
  end
487
525
  end
@@ -5,4 +5,5 @@ module Ecoportal
5
5
  end
6
6
  end
7
7
 
8
+ require 'ecoportal/api/common/concerns'
8
9
  require 'ecoportal/api/common/content'
@@ -30,65 +30,64 @@ module Ecoportal
30
30
  class << self
31
31
  def new_doc(type: nil)
32
32
  {
33
- "id" => new_uuid
33
+ "id" => new_uuid
34
34
  }.tap do |base_doc|
35
35
  if type
36
36
  base_doc.merge!({"type" => type})
37
- if klass = get_class(base_doc)
37
+ if (klass = get_class(base_doc))
38
38
  base_doc.merge!(klass.new_doc || {})
39
39
  end
40
40
  end
41
41
  end
42
42
  end
43
43
 
44
- def get_class(doc)
45
- if doc.is_a?(Hash)
46
- case doc["type"]
47
- when "tag_field"
48
- tag_field_class
49
- when "geo"
50
- geo_field_class
51
- when "select"
52
- selection_field_class
53
- when "date"
54
- date_field_class
55
- when "number"
56
- number_field_class
57
- when "gauge"
58
- gauge_field_class
59
- when "plain_text"
60
- plain_text_field_class
61
- when "rich_text"
62
- rich_text_field_class
63
- when "people"
64
- people_field_class
65
- when "contractor_entities"
66
- contractor_entities_field_class
67
- when "checklist"
68
- checklist_field_class
69
- when "page_action","checklist_task"
70
- action_field_class
71
- when "actions_list"
72
- actions_field_class
73
- when "file"
74
- files_field_class
75
- when "image_gallery"
76
- images_field_class
77
- when "signature"
78
- signature_field_class
79
- when "cross_reference"
80
- reference_field_class
81
- when "law"
82
- law_field_class
83
- when "mailbox"
84
- mailbox_field_class
85
- when "chart"
86
- chart_field_class
87
- when "frequency_rate_chart"
88
- chart_fr_field_class
89
- else
90
- self
91
- end
44
+ def get_class(doc) # rubocop:disable Metrics/AbcSize
45
+ return nil unless doc.is_a?(Hash)
46
+ case doc["type"]
47
+ when "tag_field"
48
+ tag_field_class
49
+ when "geo"
50
+ geo_field_class
51
+ when "select"
52
+ selection_field_class
53
+ when "date"
54
+ date_field_class
55
+ when "number"
56
+ number_field_class
57
+ when "gauge"
58
+ gauge_field_class
59
+ when "plain_text"
60
+ plain_text_field_class
61
+ when "rich_text"
62
+ rich_text_field_class
63
+ when "people"
64
+ people_field_class
65
+ when "contractor_entities"
66
+ contractor_entities_field_class
67
+ when "checklist"
68
+ checklist_field_class
69
+ when "page_action", "checklist_task"
70
+ action_field_class
71
+ when "actions_list"
72
+ actions_field_class
73
+ when "file"
74
+ files_field_class
75
+ when "image_gallery"
76
+ images_field_class
77
+ when "signature"
78
+ signature_field_class
79
+ when "cross_reference"
80
+ reference_field_class
81
+ when "law"
82
+ law_field_class
83
+ when "mailbox"
84
+ mailbox_field_class
85
+ when "chart"
86
+ chart_field_class
87
+ when "frequency_rate_chart"
88
+ chart_fr_field_class
89
+ else
90
+ self
92
91
  end
93
92
  end
94
93
  end
@@ -103,7 +102,7 @@ module Ecoportal
103
102
  passarray :refs
104
103
 
105
104
  def ooze
106
- self._parent.ooze
105
+ _parent.ooze
107
106
  end
108
107
 
109
108
  def ref_backend
@@ -111,9 +110,9 @@ module Ecoportal
111
110
  end
112
111
 
113
112
  def ref(any_length: false)
114
- if digest = self.class.hash_label(label, any_length: any_length)
115
- [type, digest].join(".")
116
- end
113
+ digest = self.class.hash_label(label, any_length: any_length)
114
+ return unless digest
115
+ [type, digest].join(".")
117
116
  end
118
117
 
119
118
  # Looks up the section that this component belongs to.
@@ -159,7 +158,7 @@ module Ecoportal
159
158
 
160
159
  # @return [Boolean] `true` if the component is bound to any force, `false` otherwise
161
160
  def bindings?
162
- forces.count > 0
161
+ forces.count&.positive?
163
162
  end
164
163
 
165
164
  # If the field has bindings they are replaced by this new field
@@ -168,10 +167,10 @@ module Ecoportal
168
167
  if fld.section
169
168
  fld.move(section: self.section, before: self)
170
169
  else
171
- self.section.add(fld, before: self)
170
+ section.add(fld, before: self)
172
171
  end
173
172
  replace_bindings(fld)
174
- self.delete!
173
+ delete!
175
174
  end
176
175
 
177
176
  def replace_bindings(fld)
@@ -180,12 +179,12 @@ module Ecoportal
180
179
 
181
180
  def delete!
182
181
  bindings.each {|b| b.delete!}
183
- self.unattach!
184
- self._parent.delete!(self)
182
+ unattach!
183
+ _parent.delete!(self)
185
184
  end
186
185
 
187
186
  def move(section:, before: nil, after: nil, side: nil)
188
- self.unattach!
187
+ unattach!
189
188
  section.add_component(self, before: before, after: after, side: side)
190
189
  end
191
190
 
@@ -212,9 +211,7 @@ module Ecoportal
212
211
  when :hide_reports
213
212
  self.hidden_on_reports = true
214
213
  when Hash
215
- if cnf.key?(:global)
216
- self.global_binding = cnf[:global]
217
- end
214
+ self.global_binding = cnf[:global] if cnf.key?(:global)
218
215
  else
219
216
  unused.push(cnf)
220
217
  end
@@ -222,7 +219,6 @@ module Ecoportal
222
219
  raise "Unsupported configuration options '#{unused}' for #{self.class}" unless unused.empty?
223
220
  end
224
221
  end
225
-
226
222
  end
227
223
  end
228
224
  end
@@ -5,28 +5,28 @@ module Ecoportal
5
5
  class Components < Common::Content::CollectionModel
6
6
  class_resolver :component_class, "Ecoportal::API::V2::Page::Component"
7
7
 
8
- self.klass do |doc|
8
+ klass do |doc|
9
9
  component_class.get_class(doc).tap do |klass|
10
10
  klass.key = :id
11
11
  end
12
12
  end
13
13
 
14
- order_matters = true
14
+ #self.order_matters = true
15
15
 
16
16
  def ooze
17
- self._parent.ooze
17
+ _parent.ooze
18
18
  end
19
19
 
20
20
  # @return [Ecoportal::API::V2::Page::Component] the field with `id`
21
21
  def get_by_id(id)
22
- self.find do |comp|
22
+ find do |comp|
23
23
  comp.id == id
24
24
  end
25
25
  end
26
26
 
27
27
  # @return [Array<Ecoportal::API::V2::Page::Component>] the fields of that `type`
28
28
  def get_by_type(type)
29
- self.select do |comp|
29
+ select do |comp|
30
30
  comp.type.downcase == type.to_s.strip.downcase
31
31
  end
32
32
  end
@@ -53,19 +53,19 @@ module Ecoportal
53
53
  def add(doc: nil, label: doc && doc["label"], type: doc && doc["type"])
54
54
  fld_doc = doc ? JSON.parse(doc.to_json) : component_class.new_doc(type: type)
55
55
  upsert!(fld_doc) do |fld|
56
- fld.label = label if !doc
57
- yield(fld) if block_given?
56
+ fld.label = label unless doc
57
+ yield(fld) if block_given?
58
58
  end
59
59
  end
60
60
 
61
61
  # @return [Array<Ecoportal::API::V2::Page::Component>] **orphaned** fields (with no section).
62
62
  def unattached
63
- select {|comp| !comp.attached?}
63
+ reject(&:attached?)
64
64
  end
65
65
 
66
66
  # @return [Array<Ecoportal::API::V2::Page::Component>] fields belonging to more than one section.
67
67
  def multi_section
68
- select {|comp| comp.multi_section?}
68
+ select(&:multi_section?)
69
69
  end
70
70
  end
71
71
  end