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.
- checksums.yaml +4 -4
- data/.rubocop.yml +54 -15
- data/CHANGELOG.md +17 -2
- data/lib/ecoportal/api/common/concerns/benchmarkable.rb +160 -0
- data/lib/ecoportal/api/common/concerns.rb +10 -0
- data/lib/ecoportal/api/common/content/array_model.rb +82 -79
- data/lib/ecoportal/api/common/content/class_helpers.rb +25 -26
- data/lib/ecoportal/api/common/content/collection_model.rb +108 -84
- data/lib/ecoportal/api/common/content/double_model.rb +125 -87
- data/lib/ecoportal/api/common.v2.rb +1 -0
- 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 +4 -2
@@ -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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
44
|
+
key.to_s.strip.split(/::/).compact.map do |str|
|
45
45
|
str.slice(0).upcase + str.slice(1..-1)
|
46
|
-
end.join
|
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
|
76
|
-
class_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
|
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 [
|
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
|
36
|
-
|
37
|
-
|
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
|
-
|
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
|
84
|
+
if block_given?
|
66
85
|
@new_item = block
|
67
|
-
|
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
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
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
|
154
|
-
|
155
|
-
|
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 |
|
210
|
-
yield(
|
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
|
-
|
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
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
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
|
238
|
-
|
239
|
-
|
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(
|
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.
|
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
|
-
|
301
|
-
|
302
|
-
|
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 =
|
307
|
-
|
308
|
-
|
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 =
|
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 |
|
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
|
-
|
327
|
-
|
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
|
-
|
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
|
-
|
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
|