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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b6294103b68ebdbf099d1137865856b10baec21683bd648727976f245cf197ad
4
- data.tar.gz: e0500e2c784a46c7a82ca3aa29e44f96d116bd6898e5b35f9174162457208165
3
+ metadata.gz: 70ba8da2cd5c6bd11498fb6cf8a40b04d121940c1693efc525372e3a584758f8
4
+ data.tar.gz: 2aa49af74223a5a03529fd3920714ff9b4f1dcbc636285c6b187acebe9b2e601
5
5
  SHA512:
6
- metadata.gz: eb18fb2a167afeaea5eac0db4e13ee58d64fc348ff1ebd33942ad1bb7120cbb828ad2652ed2d6b165d7a1b53eea097338032690e26ee97f5765fd87ba2f97862
7
- data.tar.gz: 7a61a35335247d39a0e97574f5b2816a92238d93a9f5b23c62b909a92ae03817057752ffc576ae1414c5e6744a6381b473be4cd2cfc2a0fd4e7582cffd97bdd4
6
+ metadata.gz: 75a8af0bc9495b4ba65688914eaf2ac94e4b08756581a3e6f193c390376ae6d905d08321ba8c8017675e1d2991ce1c699176f74c4c7b4b3b7aa91edbd8b37325
7
+ data.tar.gz: ea3368515e1fb001874e4e932902e063bb2e1cbf015cb352c2813313bb97abd80c446e5f9ab623fa11dbc0dc8e9c164c5b1947475d7a43c935da91ac34c9be22
data/.rubocop.yml CHANGED
@@ -1,39 +1,70 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.5.1
2
+ TargetRubyVersion: 2.7.8
3
3
  Exclude:
4
4
  - 'config/routes.rb'
5
+ NewCops: enable
5
6
 
6
- Metrics/LineLength:
7
- Enabled: false
8
- Metrics/BlockLength:
9
- ExcludedMethods: [context, describe]
10
- Max: 50
7
+ Metrics/ClassLength:
8
+ Max: 500
9
+ Metrics/ModuleLength:
10
+ Max: 300
11
11
  Metrics/MethodLength:
12
12
  Max: 50
13
- Metrics/ClassLength:
14
- Max: 200
15
13
  Metrics/AbcSize:
16
14
  Max: 30
17
-
18
- ParameterLists:
15
+ Metrics/ParameterLists:
19
16
  Max: 5
20
17
  CountKeywordArgs: false
18
+ Metrics/BlockLength:
19
+ CountAsOne: ['array', 'heredoc', 'method_call']
20
+ Max: 50
21
+ Metrics/CyclomaticComplexity:
22
+ Max: 30
23
+ Metrics/PerceivedComplexity:
24
+ Max: 30
21
25
 
22
- Style/StringLiterals:
26
+ Style/AccessorGrouping:
27
+ Enabled: false
28
+ Style/ConditionalAssignment:
29
+ Enabled: false
30
+ Style/BlockDelimiters:
31
+ BracesRequiredMethods: ['log']
32
+ Style/ClassAndModuleChildren:
23
33
  Enabled: false
24
34
  Style/FrozenStringLiteralComment:
25
35
  Enabled: false
26
- Style/CommentedKeyword:
36
+ Style/StringLiterals:
27
37
  Enabled: false
28
- Style/MultilineBlockChain:
38
+ Style/StringLiteralsInInterpolation:
29
39
  Enabled: false
30
40
  Style/Documentation:
31
41
  Enabled: false
32
- Style/StringLiteralsInInterpolation:
42
+ Style/CommentedKeyword:
43
+ Enabled: false
44
+ Style/MultilineBlockChain:
33
45
  Enabled: false
34
46
  Style/AndOr:
35
47
  Enabled: false
48
+ Style/Alias:
49
+ EnforcedStyle: prefer_alias_method
50
+ Style/FetchEnvVar:
51
+ Enabled: false
52
+ Style/RegexpLiteral:
53
+ EnforcedStyle: mixed
54
+ AllowInnerSlashes: true
36
55
 
56
+ Layout/HashAlignment:
57
+ EnforcedColonStyle: table
58
+ EnforcedHashRocketStyle: table
59
+ Layout/LeadingCommentSpace:
60
+ Enabled: false
61
+ AllowGemfileRubyComment: true
62
+ Layout/ParameterAlignment:
63
+ Enabled: false
64
+ Layout/MultilineMethodDefinitionBraceLayout:
65
+ EnforcedStyle: symmetrical
66
+ Layout/LineLength:
67
+ Enabled: true
37
68
  Layout/SpaceInsideHashLiteralBraces:
38
69
  Enabled: false
39
70
  Layout/SpaceInsideBlockBraces:
@@ -42,14 +73,22 @@ Layout/SpaceAroundOperators:
42
73
  Enabled: false
43
74
  Layout/ExtraSpacing:
44
75
  AllowForAlignment: true
76
+ AllowBeforeTrailingComments: true
45
77
  Layout/AccessModifierIndentation:
46
78
  EnforcedStyle: indent
47
79
  Layout/DotPosition:
48
80
  EnforcedStyle: trailing
49
81
  Layout/MultilineMethodCallIndentation:
50
82
  EnforcedStyle: indented
51
- Layout/IndentHash:
83
+ Layout/FirstHashElementIndentation:
84
+ Enabled: false
85
+ Layout/EmptyLineAfterGuardClause:
52
86
  Enabled: false
53
87
 
54
88
  Naming/VariableNumber:
55
89
  EnforcedStyle: snake_case
90
+ CheckSymbols: false
91
+ Naming/MethodParameterName:
92
+ AllowedNames: ['x', 'y', 'i', 'j', 'id', 'io']
93
+ Naming/RescuedExceptionsVariableName:
94
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,11 +1,26 @@
1
1
  # Change Log
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
- ## [1.1.7] - 2024-02-xx
4
+ ## [1.1.8] - 2024-03-23
5
5
 
6
- ### Added
7
6
  ### Changed
7
+ - A lot of code tidy up
8
+
9
+ ### Fixed
10
+ - `Ecoportal::API::Common::Conent::CollectionModel`
11
+ - correct `item` `klass` resolution
12
+
13
+ ## [1.1.7] - 2024-03-13
14
+
15
+ ### Added
16
+ - `Ecoportal::API::Common::Concerns::Benchmarkable`
17
+ - `Ecoportal::API::Common::Conent::CollectionModel`
18
+ - added ability to define **default** value for `read_only`
19
+
8
20
  ### Fixed
21
+ - `Ecoportal::API::Common::Content::CollectionModel` tidied up item `klass` scoping
22
+ - **added** some benchmarking too
23
+ - memoize `@_items`
9
24
 
10
25
  ## [1.1.6] - 2024-02-29
11
26
 
@@ -0,0 +1,160 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module Concerns
5
+ # @note While benchmarking with multi-thread gives correct results
6
+ # for top unique block, will calculate the real time of each individual
7
+ # thread, which is way higher than a single thread. However, as they
8
+ # run in parallel, the total time would be the valid reference.
9
+ module Benchmarkable
10
+ private
11
+
12
+ def benchmark_enabled?
13
+ @benchmark_enabled = true if @benchmark_enabled.nil?
14
+ @benchmark_enabled
15
+ end
16
+
17
+ def benchmarking(ref = nil, print: false)
18
+ return yield unless benchmark_enabled?
19
+ benchmark_mem(ref, print: false) do
20
+ benchmark_time(ref, print: false) do
21
+ yield
22
+ end
23
+ end.tap do
24
+ puts "\n#{bench_summary_ref(ref)}" if print
25
+ end
26
+ end
27
+
28
+ def benchmark_mem(ref = nil, print: false)
29
+ return yield unless benchmark_enabled?
30
+ memory_before = memory_usage
31
+ yield.tap do
32
+ memory_after = memory_usage
33
+ mb_footprint = ((memory_after - memory_before) / 1024.0).round(2)
34
+ bench_add_mem(ref, mb_footprint)
35
+ puts bench_str_mem(mb, ref: ref, reffix: true) if print
36
+ end
37
+ end
38
+
39
+ def benchmark_time(ref = nil, print: false)
40
+ return yield unless benchmark_enabled?
41
+ result = nil
42
+ time = benchmark.realtime do
43
+ result = yield
44
+ end.round(2)
45
+ result.tap do
46
+ bench_add_time(ref, time)
47
+ puts bench_str_time(time, ref: ref, reffix: true) if print
48
+ end
49
+ end
50
+
51
+ def benchmark_summary(ref = nil)
52
+ return '' unless benchmark_enabled?
53
+ return bench_summary_ref(ref) unless ref == :all
54
+
55
+ bench.keys.each_with_object([]) do |ref, out|
56
+ out << bench_summary_ref(ref)
57
+ end.join("\n")
58
+ end
59
+
60
+ def bench_summary_ref(ref = nil)
61
+ ref_lines = bench_summary_ref_lines(ref)
62
+ "- #{ref}\n" + " • " + ref_lines.join("\n • ")
63
+ end
64
+
65
+ def bench_summary_ref_lines(ref = nil)
66
+ ref_bench = bench_get(ref)
67
+ [
68
+ bench_str_time(*ref_bench[:time].values_at(:avg, :cnt), ref: ref),
69
+ bench_str_mem(*ref_bench[:mem].values_at(:avg, :cnt), ref: ref)
70
+ ]
71
+ end
72
+
73
+ def benchmark
74
+ require 'benchmark'
75
+ Benchmark
76
+ end
77
+
78
+ def bench
79
+ @bench ||= {}
80
+ end
81
+
82
+ def bench_add_mem(ref, mem)
83
+ bench_data_push(bench_get(ref)[:mem], mem)
84
+ end
85
+
86
+ def bench_add_time(ref, time)
87
+ bench_data_push(bench_get(ref)[:time], time)
88
+ end
89
+
90
+ def bench_get(ref)
91
+ bench[ref] ||= {
92
+ time: bench_data,
93
+ mem: bench_data
94
+ }
95
+ end
96
+
97
+ def bench_data
98
+ {
99
+ avg: nil,
100
+ cnt: 0
101
+ }
102
+ end
103
+
104
+ def bench_data_push(data, value)
105
+ total = value + ( (data[:avg] || 0) * data[:cnt])
106
+ data[:cnt] += 1
107
+ data[:avg] = (total / data[:cnt]).round(3)
108
+ data
109
+ end
110
+
111
+ def bench_str_mem(mem, count = nil, ref: nil, reffix: false)
112
+ ref = reffix ? ref : nil
113
+ msg = [ref, 'Memory'].compact.join(' -- ')
114
+ cnt = count ? " (cnt: #{count})" : ''
115
+ "#{msg}: #{mem} MB#{cnt}"
116
+ end
117
+
118
+ def active_support_duration?
119
+ return false unless Kernel.const_defined?(:ActiveSupport)
120
+ ActiveSupport.const_defined?(:Duration, false)
121
+ end
122
+
123
+ def bench_str_time(time, count = nil, ref: nil, reffix: false)
124
+ ref = reffix ? ref : nil
125
+ msg = [ref, 'Time'].compact.join(' -- ')
126
+ total = (time * count).round(2)
127
+ str_desc = ''
128
+ if active_support_duration? && total >= 60
129
+ duration = ActiveSupport::Duration.build(total.round)
130
+ str_desc = ": #{duration_to_s(duration)}"
131
+ end
132
+ cnt = count ? " (cnt: #{count}; sum: #{total} s#{str_desc})" : ''
133
+ "#{msg}: #{time} s#{cnt}"
134
+ end
135
+
136
+ def duration_to_s(value)
137
+ return "" unless active_support_duration?
138
+ return "" if value.nil?
139
+
140
+ raise ArgumentError, "Expecint ActiveSupport::Duration. Given: #{value.class}" unless value.is_a?(ActiveSupport::Duration)
141
+ parts = value.parts.map {|pair| pair.reverse.join(" ")}
142
+ return parts.first if parts.length == 1
143
+ [parts[..-2].join(", "), parts.last].join(" and ")
144
+ end
145
+
146
+ # @return [Integer] total memory used by this process in KB
147
+ def memory_usage
148
+ if Gem.win_platform?
149
+ wmem = `wmic process where processid=#{Process.pid} get WorkingSetSize | findstr "[0-9]"`
150
+ return 0 unless wmem
151
+ wmem.lines.first.chop.strip.to_i / 1024.0
152
+ else
153
+ `ps -o rss= -p #{Process.pid}`.to_i
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,10 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module Concerns
5
+ end
6
+ end
7
+ end
8
+ end
9
+
10
+ require 'ecoportal/api/common/concerns/benchmarkable'
@@ -7,13 +7,9 @@ module Ecoportal
7
7
  # - Its purpose is to handle an Array of basic objects (i.e. `Date`, `String`, `Number`)
8
8
  class ArrayModel < Content::DoubleModel
9
9
  class TypeMismatchedComparison < StandardError
10
- def initialize (this: nil, that: msg = "Trying to compare objects with different behavior.")
11
- if this
12
- msg += " From object with 'order_matters: #{this.order_matters?}' and 'uniq: #{this.uniq?}'."
13
- end
14
- if that
15
- msg += " To object where 'order_matters: #{that.order_matters?}' and 'uniq: #{that.uniq?}'."
16
- end
10
+ def initialize(this: nil, that: msg = "Trying to compare objects with different behavior.")
11
+ msg += " From object with 'order_matters: #{this.order_matters?}' and 'uniq: #{this.uniq?}'." if this
12
+ msg += " To object where 'order_matters: #{that.order_matters?}' and 'uniq: #{that.uniq?}'." if that
17
13
  super(msg)
18
14
  end
19
15
  end
@@ -27,23 +23,37 @@ module Ecoportal
27
23
  # @param b [ArrayModel]
28
24
  # @return [Boolean] `true` if both elements have same behaviour
29
25
  def same_type?(a, b)
30
- raise "To use this comparison both objects should be `ArrayModel`" unless a.is_a?(ArrayModel) && b.is_a?(ArrayModel)
26
+ msg = "To use this comparison both objects should be `ArrayModel`"
27
+ raise msg unless a.is_a?(ArrayModel) && b.is_a?(ArrayModel)
31
28
  (a.order_matters? == b.order_matters?) && (a.uniq? == b.uniq?)
32
29
  end
33
30
  end
34
31
 
35
32
  inheritable_class_vars :order_matteres, :uniq
36
33
 
37
- def initialize(doc = [], parent: self, key: nil, read_only: false)
34
+ def initialize(doc = [], parent: self, key: nil, read_only: self.class.read_only?)
38
35
  super(doc, parent: parent, key: key, read_only: read_only)
39
36
  end
40
37
 
41
- def order_matters?; self.class.order_matters; end
42
- def uniq?; self.class.uniq; end
38
+ def order_matters?
39
+ self.class.order_matters
40
+ end
41
+
42
+ def uniq?
43
+ self.class.uniq
44
+ end
45
+
46
+ def length
47
+ count
48
+ end
43
49
 
44
- def length; count; end
45
- def empty?; count == 0; end
46
- def present?; count > 0; end
50
+ def empty?
51
+ count&.zero?
52
+ end
53
+
54
+ def present?
55
+ count&.positive?
56
+ end
47
57
 
48
58
  def each(&block)
49
59
  return to_enum(:each) unless block
@@ -66,10 +76,10 @@ module Ecoportal
66
76
  _items.slice(0..-1)
67
77
  end
68
78
 
69
- # @param value [Object, Array<Object>, ArrayModel] the value(s) of the new object
79
+ # @param other [Object, Array<Object>, ArrayModel] the value(s) of the new object
70
80
  # @return [ArrayModel] a new object with the current class
71
- def new_from(value)
72
- self.class.new(into_a(value))
81
+ def new_from(other)
82
+ self.class.new(into_a(other))
73
83
  end
74
84
 
75
85
  # @return [ArrayModel] a copy of the current object
@@ -93,28 +103,28 @@ module Ecoportal
93
103
  # @param pos [Integer] the position of the element
94
104
  # @param value [String, Date, Number] the element
95
105
  # @return [Date, String, Number]
96
- def []=(post, value)
106
+ def []=(pos, value)
97
107
  _items[pos] = value
98
108
  on_change
99
109
  self[pos]
100
110
  end
101
111
 
102
112
  # Compares with an `Array` or another `ArrayModel`
103
- # @param a [ArrayModel, Array]
104
- def ==(a)
105
- return true if self.equal?(a)
106
- return false unless (a.class == self.class) || a.is_a?(Array)
107
- case a
113
+ # @param other [ArrayModel, Array]
114
+ def ==(other)
115
+ return true if equal?(other)
116
+ return false unless (other.class == self.class) || other.is_a?(Array)
117
+
118
+ case other
108
119
  when Array
109
- self == new_from(a)
120
+ self == new_from(other)
110
121
  when ArrayModel
111
- return true if
112
- raise TypeMismatchedComparison.new(this: self, that: a) unless self.class.same_type?(self, a)
122
+ raise TypeMismatchedComparison.new(this: self, that: other) unless self.class.same_type?(self, other)
113
123
 
114
- if self.order_matters?
115
- _items == a.to_a
124
+ if order_matters?
125
+ _items == other.to_a
116
126
  else
117
- (_items - a.to_a).empty? && (a.to_a - _items).empty?
127
+ (_items - other.to_a).empty? && (other.to_a - _items).empty?
118
128
  end
119
129
  end
120
130
  end
@@ -154,11 +164,10 @@ module Ecoportal
154
164
  end
155
165
 
156
166
  # Resets the `Array` by keeping its reference and adds the value(s)
157
- # @param value [Object, Array<Object>, ArrayModel] the value(s) to be added
158
- # @param values [Array]
159
- def <(values)
167
+ # @param other [Object, Array<Object>, ArrayModel] the value(s) to be added
168
+ def <(other)
160
169
  _items.clear
161
- self << values
170
+ self << other
162
171
  end
163
172
 
164
173
  # Clears the `Array` keeping its reference
@@ -169,25 +178,25 @@ module Ecoportal
169
178
  end
170
179
 
171
180
  # Concat to new
172
- def +(value)
173
- new_from(self.to_a + into_a(value))
181
+ def +(other)
182
+ new_from(to_a + into_a(other))
174
183
  end
175
184
 
176
185
  # Join
177
- # @param value [Object, Array<Object>, ArrayModel] the value(s) to be joined
186
+ # @param other [Object, Array<Object>, ArrayModel] the value(s) to be joined
178
187
  # @return [ArrayModel] a new object instance with the intersection done
179
- def |(value)
180
- new = new_from(value) - self
181
- new_from(to_a + new.to_a)
188
+ def |(other)
189
+ oth = new_from(other) - self
190
+ new_from(to_a + oth.to_a)
182
191
  end
183
192
 
184
193
  # Intersect
185
- # @param value [Object, Array<Object>, ArrayModel] the value(s) to be deleted
194
+ # @param other [Object, Array<Object>, ArrayModel] the value(s) to be deleted
186
195
  # @return [ArrayModel] a new object instance with the intersection done
187
- def &(value)
188
- self.dup.tap do |out|
189
- self.dup.tap do |delta|
190
- delta.delete!(*into_a(value))
196
+ def &(other)
197
+ dup.tap do |out|
198
+ dup.tap do |delta|
199
+ delta.delete!(*into_a(other))
191
200
  out.delete!(*into_a(delta))
192
201
  end
193
202
  end
@@ -196,9 +205,9 @@ module Ecoportal
196
205
  # Subtract
197
206
  # @param value [Object, Array<Object>, ArrayModel] the value(s) to be deleted
198
207
  # @return [ArrayModel] a **copy** of the object with the elements subtracted
199
- def -(value)
200
- self.dup.tap do |copy|
201
- copy.delete!(*into_a(value))
208
+ def -(other)
209
+ dup.tap do |copy|
210
+ copy.delete!(*into_a(other))
202
211
  end
203
212
  end
204
213
 
@@ -206,7 +215,7 @@ module Ecoportal
206
215
  def delete!(*values)
207
216
  values.map do |v|
208
217
  deletion!(v)
209
- end.tap do |r|
218
+ end.tap do |_r|
210
219
  on_change
211
220
  end
212
221
  end
@@ -216,43 +225,41 @@ module Ecoportal
216
225
  # @param val1 [Object] the first value to swap
217
226
  # @param val2 [Object] the second value to swap
218
227
  # @return [Integer] the new of `value1`, `nil` if it wasn't moved
219
- def swap(value1, value2)
220
- index(value2).tap do |dest|
221
- if dest && pos = index(value1)
222
- _items[dest] = value1
223
- _items[pos] = value2
228
+ def swap(val_1, val_2)
229
+ index(val_2).tap do |dest|
230
+ if dest && (pos = index(val_1))
231
+ _items[dest] = val_1
232
+ _items[pos] = val_2
224
233
  end
225
234
  end
226
235
  end
227
236
 
228
237
  def insert_one(value, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
229
- i = index(value)
230
- return i if (i && uniq?)
231
- pos = case
232
- when used_param?(pos) && pos
233
- pos
234
- when used_param?(before) && before
235
- index(before)
236
- when used_param?(after) && after
237
- if i = index(after) then i + 1 end
238
- end
238
+ idx = index(value)
239
+ return idx if idx && uniq?
240
+
241
+ pos =
242
+ if used_param?(pos) && pos
243
+ pos
244
+ elsif used_param?(before) && before
245
+ index(before)
246
+ elsif used_param?(after) && after
247
+ if (idx = index(after)) then idx + 1 end
248
+ end
239
249
 
250
+ # use last position as default
240
251
  pos ||= length
241
- pos.tap do |i|
252
+ pos.tap do |_i|
242
253
  _items.insert(pos, value)
243
254
  on_change
244
255
  end
245
256
  end
246
257
 
247
258
  # TODO
248
- def move(value, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
249
- if i = index(value)
250
- unless i == pos
251
-
252
- on_change
253
- end
254
- pos
255
- end
259
+ def move(value, pos: NOT_USED, _before: NOT_USED, _after: NOT_USED)
260
+ return unless (idx = index(value))
261
+ on_change unless idx == pos
262
+ pos
256
263
  end
257
264
 
258
265
  protected
@@ -270,13 +277,9 @@ module Ecoportal
270
277
  end
271
278
 
272
279
  def deletion!(value)
273
- if !uniq?
274
- if i = _items.index(value)
275
- _items.slice!(i)
276
- end
277
- else
278
- _items.delete(value)
279
- end
280
+ return _items.delete(value) if uniq?
281
+ return unless (idx = _items.index(value))
282
+ _items.slice!(idx)
280
283
  end
281
284
  end
282
285
  end