ecoportal-api-v2 1.1.6 → 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.
@@ -5,7 +5,7 @@ module Ecoportal
5
5
  module Content
6
6
  module ClassHelpers
7
7
  include Common::BaseClass
8
- NOT_USED = "no_used!"
8
+ NOT_USED = "no_used!".freeze
9
9
 
10
10
  # Class resolver
11
11
  # @note it caches the resolved `klass`es
@@ -18,21 +18,21 @@ module Ecoportal
18
18
  @resolved ||= {}
19
19
  @resolved[klass] ||=
20
20
  case klass
21
- when Class
22
- klass
23
- when String
24
- begin
25
- Kernel.const_get(klass)
26
- rescue NameError => e
27
- raise if exception
28
- end
29
- when Symbol
30
- source_class.resolve_class(source_class.send(klass))
31
- when Hash
32
- referrer, referred = klass.first
33
- resolve_class(referred, source_class: referrer, exception: exception)
34
- else
35
- raise "Unknown class: #{klass}" if exception
21
+ when Class
22
+ klass
23
+ when String
24
+ begin
25
+ Kernel.const_get(klass)
26
+ rescue NameError
27
+ raise if exception
28
+ end
29
+ when Symbol
30
+ source_class.resolve_class(source_class.send(klass))
31
+ when Hash
32
+ referrer, referred = klass.first
33
+ resolve_class(referred, source_class: referrer, exception: exception)
34
+ else
35
+ raise "Unknown class: #{klass}" if exception
36
36
  end
37
37
  end
38
38
 
@@ -41,11 +41,11 @@ module Ecoportal
41
41
  # @param key [String, Symbol] to be normalized
42
42
  # @return [String] a correct constant name
43
43
  def to_constant(key)
44
- str_name = key.to_s.strip.split(/::/).compact.map do |str|
44
+ key.to_s.strip.split(/::/).compact.map do |str|
45
45
  str.slice(0).upcase + str.slice(1..-1)
46
- end.join("").split(/[\-\_ :]+/i).compact.map do |str|
46
+ end.join.split(/[\-\_ :]+/i).compact.map do |str|
47
47
  str.slice(0).upcase + str.slice(1..-1)
48
- end.join("")
48
+ end.join
49
49
  end
50
50
 
51
51
  # Helper to create an instance variable `name`
@@ -60,7 +60,7 @@ module Ecoportal
60
60
  # Generates random ids in hexadecimal to use in class name generation.
61
61
  # @param len [Integeter] length of the `uid`
62
62
  # @return [String] a random unique id of length `len`
63
- def uid(len = 8);
63
+ def uid(len = 8)
64
64
  SecureRandom.hex(len/2)
65
65
  end
66
66
 
@@ -72,8 +72,8 @@ module Ecoportal
72
72
  # @yieldparam child_class [Class] the new class
73
73
  # @return [Class] the new generated class
74
74
  def new_class(name = "Child#{uid}", inherits: self, namespace: inherits)
75
- name = name.to_s.to_sym.freeze
76
- class_name = to_constant(name)
75
+ name = name.to_s.to_sym.freeze
76
+ class_name = to_constant(name)
77
77
 
78
78
  unless target_class = resolve_class("#{namespace}::#{class_name}", exception: false)
79
79
  target_class = Class.new(inherits)
@@ -92,19 +92,17 @@ module Ecoportal
92
92
  # @return
93
93
  def to_time(value, exception: true)
94
94
  case value
95
- when NilClass
95
+ when Time, NilClass
96
96
  value
97
97
  when String
98
98
  begin
99
99
  Time.parse(value)
100
- rescue ArgumentArgument => e
100
+ rescue ArgumentArgument
101
101
  raise if exception
102
102
  nil
103
103
  end
104
104
  when Date
105
105
  Time.parse(value.to_s)
106
- when Time
107
- value
108
106
  else
109
107
  to_time(value.to_s) if value.respond_to?(:to_s)
110
108
  end
@@ -147,6 +145,7 @@ module Ecoportal
147
145
  # - mutating methods would reflect the changes on other classes as well
148
146
  # - therefore, `freeze` will be called on the values that are inherited.
149
147
  def inherited(subclass)
148
+ super
150
149
  inheritable_class_vars.each do |var|
151
150
  instance_var = instance_variable_name(var)
152
151
  value = instance_variable_get(instance_var)
@@ -6,7 +6,6 @@ module Ecoportal
6
6
  # @note to be able to refer to the correct element of the Collection,
7
7
  # it is required that those elements have a unique `key` that allows to identify them
8
8
  class CollectionModel < Content::DoubleModel
9
-
10
9
  class << self
11
10
  attr_writer :klass
12
11
  attr_accessor :order_matters, :order_key
@@ -26,24 +25,27 @@ module Ecoportal
26
25
  # - use block to define `klass` callback
27
26
  # @note When `klass` is resolved, if the items are of type
28
27
  # `DoubleModel`, it sets on the collection class the `items_key`
28
+ # @note when `klass` is directly resolved (not via doc) only once
29
+ # it will set @klass as resolved and will use this class from now on.
30
+ # This is an optimization to cut class lookups
29
31
  # @param value [Hash] base `doc` (raw object) to create the object with
30
32
  # @yield [doc] identifies the target `class` of the raw object
31
33
  # @yieldparam doc [Hash]
32
34
  # @yieldreturn [Klass] the target `class`
33
- # @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
34
39
  def klass(value = NOT_USED, &block)
35
- if block
36
- @klass = block
37
- block.call(value) if value != NOT_USED
40
+ @klass = block if block_given?
41
+
42
+ if @klass.is_a?(Proc) && used_param?(value)
43
+ @klass.call(value)
44
+ elsif @klass && !@klass.is_a?(Proc) && !@klass.is_a?(Class)
45
+ @klass = resolve_class(@klass, exception: false)
38
46
  @klass
39
- elsif used_param?(value)
40
- if @klass.is_a?(Proc)
41
- @klass.call(value)
42
- else
43
- resolve_class(@klass, exception: false)
44
- end
45
47
  else
46
- resolve_class(@klass, exception: false)
48
+ @klass
47
49
  end.tap do |result|
48
50
  next unless result.is_a?(Class)
49
51
  next unless result < Ecoportal::API::Common::Content::DoubleModel
@@ -51,6 +53,19 @@ module Ecoportal
51
53
  end
52
54
  end
53
55
 
56
+ # @return [Boolean] are there the factory logics to build item objects defined?
57
+ def klass?
58
+ @klass || @new_item
59
+ end
60
+
61
+ # Optimization
62
+ def new_item_class_based?
63
+ return false if @new_item.is_a?(Proc)
64
+ return false if klass.is_a?(Proc)
65
+ return true if klass.is_a?(Class)
66
+ false
67
+ end
68
+
54
69
  # Generates a new object of the target class
55
70
  # @note
56
71
  # - use block to define `new_item` callback, which will prevail over `klass`
@@ -60,36 +75,27 @@ module Ecoportal
60
75
  # @yield [doc, parent, key] creates an object instance of the target `klass`
61
76
  # @yieldparam doc [Hash]
62
77
  # @yieldreturn [Klass] instance object of the target `klass`
78
+ # @parent [CollectionModel] the parent of the new item
79
+ # @key [Symbol, String] the key value to access the item within collection
80
+ # Please observe that items in a CollectionModel are identified via their key attr.
81
+ # Meaning that there is actually no need to define this argument.
63
82
  # @return [Klass] instance object of the target `klass`
64
83
  def new_item(doc = NOT_USED, parent: nil, key: nil, read_only: false, &block)
65
- if block
84
+ if block_given?
66
85
  @new_item = block
67
- elsif used_param?(doc)
68
- raise "You should define either a 'klass' or a 'new_item' callback first" unless klass?
69
- if @new_item
70
- @new_item.call(doc, parent, key)
71
- else
72
- if target_class = self.klass(doc)
73
- doc.is_a?(target_class) ? doc : target_class.new(doc, parent: parent, key: key, read_only: read_only)
74
- else
75
- raise "Could not find a class for: #{doc}"
76
- end
77
- end
78
- else
79
- raise "To define the 'new_item' callback (factory), you need to use a block"
86
+ return
80
87
  end
81
- end
82
88
 
83
- # @return [Boolean] are there the factory logics to build item objects defined?
84
- def klass?
85
- @klass || @new_item
86
- end
89
+ msg = "To define the 'new_item' callback (factory), you need to use a block"
90
+ raise msg unless used_param?(doc)
91
+ msg = "You should define either a 'klass' or a 'new_item' callback first"
92
+ raise msg unless klass?
93
+ return @new_item.call(doc, parent, key) if @new_item.is_a?(Proc)
87
94
 
88
- def doc_class(name)
89
- dim_class = new_class(name, inherits: Common::Content::ArrayModel) do |klass|
90
- klass.order_matters = order_matters
91
- klass.uniq = uniq
92
- end
95
+ raise "Could not find a class for: #{doc}" unless (target_class = klass(doc))
96
+ return doc if doc.is_a?(target_class)
97
+
98
+ target_class.new(doc, parent: parent, key: key, read_only: read_only)
93
99
  end
94
100
  end
95
101
 
@@ -98,18 +104,18 @@ module Ecoportal
98
104
  inheritable_class_vars :klass, :order_matters, :order_key, :items_key, :new_item
99
105
 
100
106
  def initialize(ini_doc = [], parent: self, key: nil, read_only: false)
101
- unless self.class.klass?
102
- raise "Undefined base 'klass' or 'new_item' callback for #{self.class}"
103
- end
104
-
105
- ini_doc = case ini_doc
106
- when Array
107
- ini_doc
108
- when Enumerable
109
- ini_doc.to_a
110
- else
111
- []
112
- 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
113
119
 
114
120
  super(ini_doc, parent: parent, key: key, read_only: read_only)
115
121
  end
@@ -130,7 +136,7 @@ module Ecoportal
130
136
  def _doc_key(value)
131
137
  #print "*(#{value.class})"
132
138
  return super(value) unless value.is_a?(Hash) || value.is_a?(Content::DoubleModel)
133
- if id = get_key(value)
139
+ if (id = get_key(value))
134
140
  #print "^"
135
141
  _doc_items.index {|item| get_key(item) == id}.tap do |p|
136
142
  #print "{{#{p}}}"
@@ -150,9 +156,17 @@ module Ecoportal
150
156
  end
151
157
  end
152
158
 
153
- def length; count; end
154
- def empty?; count == 0; end
155
- 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
156
170
 
157
171
  def each(&block)
158
172
  return to_enum(:each) unless block
@@ -166,6 +180,7 @@ module Ecoportal
166
180
  _doc_items.each do |item_doc|
167
181
  elements << new_item(item_doc)
168
182
  end
183
+ @_items = elements if read_only?
169
184
  end
170
185
  end
171
186
 
@@ -198,7 +213,7 @@ module Ecoportal
198
213
  end
199
214
  item_doc = value.is_a?(Content::DoubleModel)? value.doc : value
200
215
  item_doc = JSON.parse(item_doc.to_json)
201
- if item = self[value]
216
+ if (item = self[value])
202
217
  item.replace_doc(item_doc)
203
218
  else
204
219
  _doc_upsert(item_doc, pos: pos, before: before, after: after).tap do |pos_idx|
@@ -206,14 +221,14 @@ module Ecoportal
206
221
  @indexed = false
207
222
  end
208
223
  end
209
- (item || self[item_doc]).tap do |item|
210
- yield(item) if block_given?
224
+ (item || self[item_doc]).tap do |itm|
225
+ yield(itm) if block_given?
211
226
  end
212
227
  end
213
228
 
214
229
  # Deletes all the elements of this `CollectionModel` instance
215
230
  def clear
216
- self.to_a.each {|item| delete!(item)}
231
+ to_a.each {|item| delete!(item)}
217
232
  end
218
233
 
219
234
  # Deletes `value` from this `CollectionModel` instance
@@ -225,18 +240,25 @@ module Ecoportal
225
240
  unless value.is_a?(Hash) || value.is_a?(Content::DoubleModel) || value.is_a?(String)
226
241
  raise "'Content::DoubleModel' or 'Hash' doc required"
227
242
  end
228
- if item = self[value]
229
- _doc_delete(item.doc)
230
- @indexed = false
231
- _items.delete(item)
232
- end
243
+ return unless (item = self[value])
244
+ _doc_delete(item.doc)
245
+ @indexed = false
246
+ _items.delete(item)
233
247
  end
234
248
 
235
249
  protected
236
250
 
237
- def order_matters?; self.class.order_matters; end
238
- def uniq?; self.class.uniq; end
239
- 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
240
262
 
241
263
  def on_change
242
264
  @indexed = false
@@ -253,7 +275,7 @@ module Ecoportal
253
275
  when String
254
276
  value
255
277
  when Numeric
256
- get_key(self.to_a[value])
278
+ get_key(to_a[value])
257
279
  end
258
280
  end
259
281
 
@@ -275,7 +297,11 @@ module Ecoportal
275
297
  private
276
298
 
277
299
  def new_item(value)
278
- self.class.new_item(value, parent: self, read_only: self._read_only)
300
+ if self.class.new_item_class_based?
301
+ self.class.klass.new(value, parent: self, read_only: _read_only)
302
+ else
303
+ self.class.new_item(value, parent: self, read_only: _read_only)
304
+ end
279
305
  end
280
306
 
281
307
  # Helper to remove tracked down instance variables
@@ -297,48 +323,46 @@ module Ecoportal
297
323
  # Deletes `value` from `doc` (here referred as `_doc_items`)
298
324
  # @return [Object] the element deleted from `doc`
299
325
  def _doc_delete(value)
300
- if current_pos = _doc_key(value)
301
- _doc_items.delete_at(current_pos)
302
- end
326
+ return unless (current_pos = _doc_key(value))
327
+
328
+ _doc_items.delete_at(current_pos)
303
329
  end
304
330
 
305
331
  def _doc_upsert(value, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
306
- current_pos = if elem = self[value]
307
- _doc_key(elem)
308
- end
332
+ current_pos =
333
+ if (elem = self[value])
334
+ _doc_key(elem)
335
+ end
309
336
 
310
337
  pos = scope_position(pos: pos, before: before, after: after)
311
338
  pos ||= current_pos
312
339
 
313
340
  if current_pos && pos
314
341
  _doc_items.delete_at(current_pos)
315
- pos = (pos <= current_pos)? pos : pos - 1
342
+ pos = pos <= current_pos ? pos : pos - 1
316
343
  end
317
344
 
318
- pos = (pos && pos < _doc_items.length)? pos : _doc_items.length
319
- pos.tap do |i|
345
+ pos = (pos && pos < _doc_items.length)? pos : _doc_items.length # rubocop:disable Style/TernaryParentheses
346
+ pos.tap do |_i|
320
347
  _doc_items.insert(pos, value)
321
348
  end
322
-
323
349
  end
324
350
 
325
351
  def scope_position(pos: NOT_USED, before: NOT_USED, after: NOT_USED)
326
- case
327
- when used_param?(pos)
328
- if elem = self[pos]
352
+ if used_param?(pos)
353
+ if (elem = self[pos])
329
354
  _doc_key(elem) - 1
330
355
  end
331
- when used_param?(before)
332
- if elem = self[before]
356
+ elsif used_param?(before)
357
+ if (elem = self[before])
333
358
  _doc_key(elem) - 1
334
359
  end
335
- when used_param?(after)
336
- if elem = self[after]
360
+ elsif used_param?(after)
361
+ if (elem = self[after])
337
362
  _doc_key(elem)
338
363
  end
339
364
  end
340
365
  end
341
-
342
366
  end
343
367
  end
344
368
  end