ecoportal-api-v2 2.0.11 → 2.0.14
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/.gitignore +3 -0
- data/CHANGELOG.md +35 -1
- data/ecoportal-api-v2.gemspec +1 -1
- data/lib/ecoportal/api/common/content/array_model.rb +20 -3
- data/lib/ecoportal/api/common/content/class_helpers.rb +4 -2
- data/lib/ecoportal/api/common/content/client.rb +12 -6
- data/lib/ecoportal/api/common/content/collection_model/doc/items.rb +33 -0
- data/lib/ecoportal/api/common/content/collection_model/doc/rooted_key.rb +68 -0
- data/lib/ecoportal/api/common/content/collection_model/doc.rb +25 -0
- data/lib/ecoportal/api/common/content/collection_model/doc_mutation/delete.rb +33 -0
- data/lib/ecoportal/api/common/content/collection_model/doc_mutation/position.rb +42 -0
- data/lib/ecoportal/api/common/content/collection_model/doc_mutation/upsert.rb +45 -0
- data/lib/ecoportal/api/common/content/collection_model/doc_mutation.rb +27 -0
- data/lib/ecoportal/api/common/content/collection_model/model/cache.rb +44 -0
- data/lib/ecoportal/api/common/content/collection_model/model/items.rb +43 -0
- data/lib/ecoportal/api/common/content/collection_model/model/iterable.rb +44 -0
- data/lib/ecoportal/api/common/content/collection_model/model/lookup.rb +45 -0
- data/lib/ecoportal/api/common/content/collection_model/model/numeric_key.rb +40 -0
- data/lib/ecoportal/api/common/content/collection_model/model/var_tracking.rb +50 -0
- data/lib/ecoportal/api/common/content/collection_model/model.rb +33 -0
- data/lib/ecoportal/api/common/content/collection_model/modifiers/items_key.rb +57 -0
- data/lib/ecoportal/api/common/content/collection_model/modifiers/items_klass.rb +144 -0
- data/lib/ecoportal/api/common/content/collection_model/modifiers/items_order.rb +38 -0
- data/lib/ecoportal/api/common/content/collection_model/modifiers.rb +27 -0
- data/lib/ecoportal/api/common/content/collection_model/mutation/clear.rb +30 -0
- data/lib/ecoportal/api/common/content/collection_model/mutation/delete.rb +42 -0
- data/lib/ecoportal/api/common/content/collection_model/mutation/upsert.rb +56 -0
- data/lib/ecoportal/api/common/content/collection_model/mutation.rb +27 -0
- data/lib/ecoportal/api/common/content/collection_model.rb +19 -351
- data/lib/ecoportal/api/common/content/doc_helpers.rb +18 -16
- data/lib/ecoportal/api/common/content/double_model/attributable/base.rb +23 -0
- data/lib/ecoportal/api/common/content/double_model/attributable/enforce.rb +62 -0
- data/lib/ecoportal/api/common/content/double_model/attributable/nesting/cascaded_callback.rb +104 -0
- data/lib/ecoportal/api/common/content/double_model/attributable/nesting/embeddable.rb +76 -0
- data/lib/ecoportal/api/common/content/double_model/attributable/nesting/keyable.rb +119 -0
- data/lib/ecoportal/api/common/content/double_model/attributable/nesting.rb +136 -0
- data/lib/ecoportal/api/common/content/double_model/attributable/passthrough.rb +70 -0
- data/lib/ecoportal/api/common/content/double_model/attributable/simple.rb +48 -0
- data/lib/ecoportal/api/common/content/double_model/attributable.rb +30 -0
- data/lib/ecoportal/api/common/content/double_model/base.rb +29 -0
- data/lib/ecoportal/api/common/content/double_model/diffable_model.rb +43 -0
- data/lib/ecoportal/api/common/content/double_model/double_doc/base.rb +23 -0
- data/lib/ecoportal/api/common/content/double_model/double_doc/linkable_doc.rb +87 -0
- data/lib/ecoportal/api/common/content/double_model/double_doc/replaceable_doc.rb +61 -0
- data/lib/ecoportal/api/common/content/double_model/double_doc/reset_consolidate.rb +54 -0
- data/lib/ecoportal/api/common/content/double_model/double_doc/rooted_key.rb +49 -0
- data/lib/ecoportal/api/common/content/double_model/double_doc.rb +31 -0
- data/lib/ecoportal/api/common/content/double_model/hash_helpers.rb +40 -0
- data/lib/ecoportal/api/common/content/double_model/modifiers/read_only_able.rb +73 -0
- data/lib/ecoportal/api/common/content/double_model/modifiers/rootable.rb +64 -0
- data/lib/ecoportal/api/common/content/double_model/modifiers.rb +24 -0
- data/lib/ecoportal/api/common/content/double_model/parented.rb +22 -0
- data/lib/ecoportal/api/common/content/double_model/var_tracking.rb +45 -0
- data/lib/ecoportal/api/common/content/double_model.rb +28 -486
- data/lib/ecoportal/api/common/content/hash_diff_patch.rb +2 -1
- data/lib/ecoportal/api/common/content/includer.rb +16 -0
- data/lib/ecoportal/api/common/content/model_helpers.rb +14 -16
- data/lib/ecoportal/api/common/content.rb +1 -0
- data/lib/ecoportal/api/v2/page/component.rb +46 -46
- data/lib/ecoportal/api/v2/page/components.rb +2 -2
- data/lib/ecoportal/api/v2/page.rb +14 -14
- data/lib/ecoportal/api/v2/pages/page_stage/task.rb +2 -2
- data/lib/ecoportal/api/v2/pages/page_stage/tasks.rb +3 -3
- data/lib/ecoportal/api/v2/pages/page_stage.rb +8 -8
- data/lib/ecoportal/api/v2/pages/stages.rb +2 -2
- data/lib/ecoportal/api/v2/pages.rb +15 -15
- data/lib/ecoportal/api/v2/registers.rb +20 -19
- data/lib/ecoportal/api/v2/s3/files/batch_upload.rb +1 -0
- data/lib/ecoportal/api/v2/s3/files/poll.rb +5 -5
- data/lib/ecoportal/api/v2/s3/files/poll_status.rb +3 -3
- data/lib/ecoportal/api/v2/s3/files.rb +6 -6
- data/lib/ecoportal/api/v2/s3.rb +5 -5
- data/lib/ecoportal/api/v2.rb +18 -8
- data/lib/ecoportal/api/v2_version.rb +1 -1
- metadata +51 -5
@@ -6,471 +6,52 @@ 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 < Ecoportal::API::Common::BaseModel
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
include
|
9
|
+
require 'ecoportal/api/common/content/double_model/base'
|
10
|
+
require 'ecoportal/api/common/content/double_model/parented'
|
11
|
+
require 'ecoportal/api/common/content/double_model/modifiers'
|
12
|
+
require 'ecoportal/api/common/content/double_model/var_tracking'
|
13
|
+
require 'ecoportal/api/common/content/double_model/hash_helpers'
|
14
|
+
require 'ecoportal/api/common/content/double_model/double_doc'
|
15
|
+
require 'ecoportal/api/common/content/double_model/attributable'
|
16
|
+
require 'ecoportal/api/common/content/double_model/diffable_model'
|
17
|
+
|
18
|
+
include Base
|
19
|
+
include Parented
|
20
|
+
include Modifiers
|
21
|
+
include VarTracking
|
22
|
+
include HashHelpers
|
23
|
+
|
24
|
+
include DoubleDoc
|
25
|
+
include Attributable
|
26
|
+
include DiffableModel
|
23
27
|
|
24
28
|
class << self
|
25
|
-
attr_reader :key
|
26
|
-
|
27
|
-
def key?
|
28
|
-
!!key
|
29
|
-
end
|
30
|
-
|
31
|
-
# key property (and method) of this model
|
32
|
-
# @note this is auto-set when `passkey` is used
|
33
|
-
def key=(value)
|
34
|
-
@key = value.to_s.freeze
|
35
|
-
end
|
36
|
-
|
37
29
|
def new_uuid(length: 24)
|
38
30
|
uid(length)
|
39
31
|
end
|
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
|
-
|
51
|
-
# Same as `attr_reader` but links to a subjacent `Hash` model property
|
52
|
-
# @note it does **not** create an _instance variable_
|
53
|
-
# @param methods [Array<Symbol>] the method that exposes the value
|
54
|
-
# as well as its `key` in the underlying `Hash` model.
|
55
|
-
def pass_reader(*methods)
|
56
|
-
methods.each do |method|
|
57
|
-
method = method.to_s.freeze
|
58
|
-
|
59
|
-
define_method method do
|
60
|
-
value = send(:doc)[method]
|
61
|
-
value = yield(value) if block_given?
|
62
|
-
value
|
63
|
-
end
|
64
|
-
end
|
65
|
-
self
|
66
|
-
end
|
67
|
-
|
68
|
-
# Same as `attr_writer` but links to a subjacent `Hash` model property
|
69
|
-
# @note it does **not** create an _instance variable_
|
70
|
-
# @param methods [Array<Symbol>] the method that exposes the value
|
71
|
-
# as well as its `key` in the underlying `Hash` model.
|
72
|
-
def pass_writer(*methods)
|
73
|
-
methods.each do |method|
|
74
|
-
method = method.to_s.freeze
|
75
|
-
|
76
|
-
define_method "#{method}=" do |value|
|
77
|
-
value = yield(value) if block_given?
|
78
|
-
send(:doc)[method] = value
|
79
|
-
end
|
80
|
-
end
|
81
|
-
self
|
82
|
-
end
|
83
|
-
|
84
|
-
# This method is essential to give stability to the model
|
85
|
-
# @note `Content::CollectionModel` needs to find elements in the doc `Array`.
|
86
|
-
# The only way to do it is via the access key (i.e. `id`). However, there is
|
87
|
-
# no chance you can avoid infinite loop for `get_key` without setting an
|
88
|
-
# instance variable key at the moment of the object creation, when the
|
89
|
-
# `doc` is firstly received
|
90
|
-
# @param method [Symbol] the method that exposes the value
|
91
|
-
# as well as its `key` in the underlying `Hash` model.
|
92
|
-
def passkey(method)
|
93
|
-
method = method.to_s.freeze
|
94
|
-
var = instance_variable_name(method)
|
95
|
-
self.key = method
|
96
|
-
|
97
|
-
define_method method do
|
98
|
-
return instance_variable_get(var) if instance_variable_defined?(var)
|
99
|
-
value = send(:doc)[method]
|
100
|
-
value = yield(value) if block_given?
|
101
|
-
value
|
102
|
-
end
|
103
|
-
|
104
|
-
define_method "#{method}=" do |value|
|
105
|
-
variable_set(var, value)
|
106
|
-
value = yield(value) if block_given?
|
107
|
-
send(:doc)[method] = value
|
108
|
-
end
|
109
|
-
|
110
|
-
self
|
111
|
-
end
|
112
|
-
|
113
|
-
# These are methods that should always be present in patch update
|
114
|
-
# @note
|
115
|
-
# - `DoubleModel` can be used with objects that do not use `patch_ver`
|
116
|
-
# - This ensures that does that do, will get the correct patch update model
|
117
|
-
# @param method [Symbol] the method that exposes the value
|
118
|
-
# as well as its `key` in the underlying `Hash` model.
|
119
|
-
# @param default [Value] the default value that
|
120
|
-
# this `key` will be written in the model when it doesn't exixt
|
121
|
-
def passforced(method, default:, read_only: false)
|
122
|
-
model_forced_keys[method.to_s.freeze] = default
|
123
|
-
passthrough(method, read_only: read_only)
|
124
|
-
end
|
125
|
-
|
126
|
-
# Ensures `doc` has the `model_forced_keys`. If it doesn't, it adds those missing
|
127
|
-
# with the defined `default` values
|
128
|
-
def enforce!(doc)
|
129
|
-
return unless doc.is_a?(Hash)
|
130
|
-
return if model_forced_keys.empty?
|
131
|
-
|
132
|
-
model_forced_keys.each do |key, default|
|
133
|
-
doc[key] = default unless doc.key?(key)
|
134
|
-
end
|
135
|
-
doc
|
136
|
-
end
|
137
|
-
|
138
|
-
# Same as `attr_accessor` but links to a subjacent `Hash` model property
|
139
|
-
# @param methods [Array<Symbol>] the method that exposes the value
|
140
|
-
# as well as its `key` in the underlying `Hash` model.
|
141
|
-
# @param read_only [Boolean] should it only define the reader?
|
142
|
-
def passthrough(*methods, read_only: false)
|
143
|
-
pass_reader(*methods)
|
144
|
-
pass_writer(*methods) unless read_only
|
145
|
-
self
|
146
|
-
end
|
147
|
-
|
148
|
-
# To link as a `Time` date to a subjacent `Hash` model property
|
149
|
-
# @see Ecoportal::API::Common::Content::DoubleModel#passthrough
|
150
|
-
# @param methods [Array<Symbol>] the method that exposes the value
|
151
|
-
# as well as its `key` in the underlying `Hash` model.
|
152
|
-
# @param read_only [Boolean] should it only define the reader?
|
153
|
-
def passdate(*methods, read_only: false)
|
154
|
-
pass_reader(*methods) {|value| to_time(value)}
|
155
|
-
pass_writer(*methods) {|value| to_time(value)&.iso8601} unless read_only
|
156
|
-
self
|
157
|
-
end
|
158
|
-
|
159
|
-
# To link as a `Boolean` to a subjacent `Hash` model property
|
160
|
-
# @param methods [Array<Symbol>] the method that exposes the value
|
161
|
-
# as well as its `key` in the underlying `Hash` model.
|
162
|
-
# @param read_only [Boolean] should it only define the reader?
|
163
|
-
def passboolean(*methods, read_only: false)
|
164
|
-
pass_reader(*methods) {|value| value}
|
165
|
-
pass_writer(*methods) {|value| !!value} unless read_only
|
166
|
-
self
|
167
|
-
end
|
168
|
-
|
169
|
-
# To link as plain `Array` to a subjacent `Hash` model property
|
170
|
-
# @param methods [Array<Symbol>] the method that exposes the value
|
171
|
-
# as well as its `key` in the underlying `Hash` model.
|
172
|
-
# @param order_matters [Boolean] does the order matter
|
173
|
-
# @param uniq [Boolean] should it contain unique elements
|
174
|
-
def passarray(*methods, order_matters: true, uniq: true)
|
175
|
-
methods.each do |method|
|
176
|
-
method = method.to_s.freeze
|
177
|
-
var = instance_variable_name(method)
|
178
|
-
|
179
|
-
dim_class = new_class(method, inherits: ArrayModel) do |klass|
|
180
|
-
klass.order_matters = order_matters
|
181
|
-
klass.uniq = uniq
|
182
|
-
end
|
183
|
-
|
184
|
-
define_method method do
|
185
|
-
return instance_variable_get(var) if instance_variable_defined?(var)
|
186
|
-
new_obj = dim_class.new(parent: self, key: method, read_only: read_only?)
|
187
|
-
variable_set(var, new_obj)
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
# Helper to embed one nested object under one property
|
193
|
-
# @param method [Symbol] the method that exposes the embeded object
|
194
|
-
# @param key [Symbol] the `key` that embeds it to the underlying `Hash` model
|
195
|
-
# @nullable [Boolean] to specify if this object can be `nil`
|
196
|
-
# @param klass [Class, String] the class of the embedded object
|
197
|
-
def embeds_one(method, klass:, key: method, nullable: false)
|
198
|
-
embed(method, key: key, nullable: nullable, multiple: false, klass: klass)
|
199
|
-
end
|
200
|
-
|
201
|
-
# @note
|
202
|
-
# - if you have a dedicated `Enumerable` class to manage `many`, you should use `:enum_class`
|
203
|
-
# - otherwise, just indicate the child class in `:klass` and it will auto generate the class
|
204
|
-
# @param method [Symbol] the method that exposes the embeded object
|
205
|
-
# @param key [Symbol] the `key` that embeds it to the underlying `Hash` model
|
206
|
-
# @param order_matters [Boolean] to state if the order will matter
|
207
|
-
# @param klass [Class, String] the class of the individual elements it embeds
|
208
|
-
# @param enum_class [Class, String] the class of the collection that will hold the individual elements
|
209
|
-
# @param read_only [Boolean] whether or not should try to **work around** items `klass` missing a `key`
|
210
|
-
# - If set to `true` this is meant only for read purposes (won't be able to successufully insert)
|
211
|
-
def embeds_many(method, key: method, klass: nil, enum_class: nil,
|
212
|
-
order_matters: false, order_key: nil, read_only: read_only?)
|
213
|
-
if enum_class
|
214
|
-
eclass = enum_class
|
215
|
-
elsif klass
|
216
|
-
eclass = new_class("#{method}::#{klass}", inherits: CollectionModel) do |dim_class|
|
217
|
-
# NOTE: new_class may resolve the namespace of the class to an already existing class
|
218
|
-
dim_class.klass ||= klass
|
219
|
-
dim_class.order_matters = order_matters
|
220
|
-
dim_class.order_key = order_key
|
221
|
-
dim_class.read_only! if read_only
|
222
|
-
end
|
223
|
-
else
|
224
|
-
raise "You should either specify the 'klass' of the elements or the 'enum_class'"
|
225
|
-
end
|
226
|
-
|
227
|
-
embed(
|
228
|
-
method, key: key,
|
229
|
-
multiple: true, klass: eclass,
|
230
|
-
read_only: read_only
|
231
|
-
) do |instance_with_called_method|
|
232
|
-
# keep reference to the original class to resolve the `klass` dependency
|
233
|
-
# See stackoverflow: https://stackoverflow.com/a/73709529/4352306
|
234
|
-
referrer_class = instance_with_called_method.class
|
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
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
private
|
242
|
-
|
243
|
-
def embed(
|
244
|
-
method, klass:, key: method,
|
245
|
-
nullable: false, multiple: false, read_only: read_only?,
|
246
|
-
&embed_block
|
247
|
-
)
|
248
|
-
method = method.to_s.freeze
|
249
|
-
var = instance_variable_name(method).freeze
|
250
|
-
obj_k = key.to_s.freeze
|
251
|
-
|
252
|
-
# retrieving method (getter)
|
253
|
-
define_method(method) do
|
254
|
-
# set item klass as referrer to klass (to allow resolve symbol)
|
255
|
-
embed_block&.call(self)
|
256
|
-
return instance_variable_get(var) if instance_variable_defined?(var)
|
257
|
-
|
258
|
-
doc[obj_k] ||= (multiple ? [] : {}) unless nullable
|
259
|
-
return variable_set(var, nil) unless doc[obj_k]
|
260
|
-
|
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)
|
271
|
-
end
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
# The list of keys that will be forced in the model
|
276
|
-
def model_forced_keys
|
277
|
-
@model_forced_keys ||= {}
|
278
|
-
end
|
279
32
|
end
|
280
33
|
|
281
|
-
inheritable_class_vars :model_forced_keys, :key, :read_only
|
282
|
-
|
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)
|
286
|
-
attr_reader :_parent, :_key, :_read_only
|
287
|
-
|
288
34
|
def initialize(doc = {}, parent: self, key: nil, read_only: self.class.read_only?) # rubocop:disable Lint/MissingSuper
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
35
|
+
self._parent = parent || self
|
36
|
+
self._parent_key = key || self
|
37
|
+
|
38
|
+
read_only! if read_only
|
293
39
|
|
294
40
|
self.class.enforce!(doc)
|
295
41
|
|
296
|
-
if
|
297
|
-
@doc
|
298
|
-
@original_doc
|
42
|
+
if to_be_root?
|
43
|
+
@doc = JSON.parse(doc.to_json)
|
44
|
+
@original_doc = JSON.parse(@doc.to_json)
|
299
45
|
end
|
300
46
|
|
301
|
-
return unless key_method? && doc
|
47
|
+
return unless key_method? && doc.is_a?(Hash)
|
302
48
|
|
49
|
+
# ensure it is well linked! (so: throw error if something is wrong!)
|
50
|
+
# and ensure the below does not create an infinite loop
|
303
51
|
self.key = doc[key_method]
|
304
52
|
#puts "\n$(#{self.key}<=>#{self.class})"
|
305
53
|
end
|
306
54
|
|
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
|
311
|
-
end
|
312
|
-
|
313
|
-
def root
|
314
|
-
return self if is_root?
|
315
|
-
_parent.root
|
316
|
-
end
|
317
|
-
|
318
|
-
# @return [String] the `value` of the `key` method (i.e. `id` value)
|
319
|
-
def key
|
320
|
-
raise NoKeyMethod, "No key_method defined for #{self.class}" unless key_method?
|
321
|
-
|
322
|
-
method(key_method).call
|
323
|
-
end
|
324
|
-
|
325
|
-
# @param [String] the `value` of the `key` method (i.e. `id` value)
|
326
|
-
def key=(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)]
|
334
|
-
end
|
335
|
-
|
336
|
-
# Offers a method for child classes to transform the key,
|
337
|
-
# provided that the child's `doc` can be accessed
|
338
|
-
def _doc_key(value)
|
339
|
-
if value.is_a?(Content::DoubleModel) && !value.is_root?
|
340
|
-
#print "?(#{value.class}<=#{value._parent.class})"
|
341
|
-
value._parent._doc_key(value)
|
342
|
-
else
|
343
|
-
#print "!(#{value}<=#{self.class})"
|
344
|
-
value
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
# @return [nil, Hash] the underlying `Hash` model as is (carrying current changes)
|
349
|
-
def doc
|
350
|
-
return @doc if doc_var?
|
351
|
-
|
352
|
-
raise UnlinkedModel.new(from: "#{self.class}#doc", key: _key) unless linked?
|
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)
|
358
|
-
end
|
359
|
-
|
360
|
-
# The `original_doc` holds the model as is now on server-side.
|
361
|
-
# @return [nil, Hash] the underlying `Hash` model as after last `consolidate!` changes
|
362
|
-
def original_doc
|
363
|
-
raise UnlinkedModel.new(from: "#{self.class}#original_doc", key: _key) unless linked?
|
364
|
-
return @original_doc if is_root?
|
365
|
-
|
366
|
-
_parent.original_doc.dig(*resolved_doc_key.flatten)
|
367
|
-
end
|
368
|
-
|
369
|
-
def as_json
|
370
|
-
doc
|
371
|
-
end
|
372
|
-
|
373
|
-
def to_json(*args)
|
374
|
-
doc.to_json(*args)
|
375
|
-
end
|
376
|
-
|
377
|
-
# @return [nil, Hash] the patch `Hash` model including only the changes between
|
378
|
-
# `original_doc` and `doc`
|
379
|
-
def as_update
|
380
|
-
new_doc = as_json
|
381
|
-
HashDiffPatch.patch_diff(new_doc, original_doc)
|
382
|
-
end
|
383
|
-
|
384
|
-
# @return [Boolean] stating if there are changes
|
385
|
-
def dirty?
|
386
|
-
au = as_update
|
387
|
-
!((au == {}) || au.nil?)
|
388
|
-
end
|
389
|
-
|
390
|
-
# It makes `original_doc` to be like `doc`
|
391
|
-
# @note
|
392
|
-
# - after executing it, there will be no pending changes
|
393
|
-
# - you should technically run this command, after a successful update request to the server
|
394
|
-
def consolidate!
|
395
|
-
replace_original_doc(JSON.parse(doc.to_json))
|
396
|
-
end
|
397
|
-
|
398
|
-
# It makes `doc` to be like `original`
|
399
|
-
# @note
|
400
|
-
# - after executing it, changes in `doc` will be lost
|
401
|
-
# - you should technically run this command only if you want to remove certain changes
|
402
|
-
# @key [Symbol] the specific part of the model you want to `reset`
|
403
|
-
def reset!(key = nil)
|
404
|
-
if key
|
405
|
-
keys = [key].flatten.compact
|
406
|
-
odoc = original_doc.dig(*keys)
|
407
|
-
odoc &&= JSON.parse(odoc.to_json)
|
408
|
-
dig_set(doc, keys, odoc)
|
409
|
-
else
|
410
|
-
replace_doc(JSON.parse(original_doc.to_json))
|
411
|
-
end
|
412
|
-
end
|
413
|
-
|
414
|
-
def print_pretty
|
415
|
-
puts JSON.pretty_generate(as_json)
|
416
|
-
self
|
417
|
-
end
|
418
|
-
|
419
|
-
def replace_doc(new_doc)
|
420
|
-
raise UnlinkedModel.new(from: "#{self.class}#replace_doc", key: _key) unless linked?
|
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!
|
426
|
-
end
|
427
|
-
|
428
|
-
protected
|
429
|
-
|
430
|
-
def doc_var?
|
431
|
-
!!defined?(@doc)
|
432
|
-
end
|
433
|
-
|
434
|
-
# Both requisites
|
435
|
-
# @note that for optimization purposes, `@doc` var may be used when
|
436
|
-
# the object is `read_only?`
|
437
|
-
def is_root?
|
438
|
-
_parent == self && doc_var?
|
439
|
-
end
|
440
|
-
|
441
|
-
def linked?
|
442
|
-
is_root? || !!_parent.doc
|
443
|
-
end
|
444
|
-
|
445
|
-
def replace_original_doc(new_doc)
|
446
|
-
raise UnlinkedModel.new(from: "#{self.class}#replace_original_doc", key: _key) unless linked?
|
447
|
-
return (@original_doc = new_doc) if is_root?
|
448
|
-
|
449
|
-
dig_set(_parent.original_doc, resolved_doc_key.flatten, new_doc)
|
450
|
-
end
|
451
|
-
|
452
|
-
# Helper to track down persistent variables
|
453
|
-
def variable_set(key, value)
|
454
|
-
var = instance_variable_name(key)
|
455
|
-
@_dim_vars.push(var).uniq!
|
456
|
-
instance_variable_set(var, value)
|
457
|
-
end
|
458
|
-
|
459
|
-
# Helper to remove tracked down instance variables
|
460
|
-
def variable_remove!(key)
|
461
|
-
var = instance_variable_name(key)
|
462
|
-
return unless @_dim_vars.include?(var)
|
463
|
-
|
464
|
-
@_dim_vars.delete(var)
|
465
|
-
remove_instance_variable(var)
|
466
|
-
end
|
467
|
-
|
468
|
-
# Removes all the persistent variables
|
469
|
-
def variables_remove!
|
470
|
-
#puts "going to remove vars: #{@_dim_vars} on #{self.class} (parent: #{identify_parent(self._parent)})"
|
471
|
-
@_dim_vars.dup.map {|var| variable_remove!(var)}
|
472
|
-
end
|
473
|
-
|
474
55
|
private
|
475
56
|
|
476
57
|
def identify_parent(object)
|
@@ -481,45 +62,6 @@ module Ecoportal
|
|
481
62
|
"section #{object.heading}"
|
482
63
|
end
|
483
64
|
end
|
484
|
-
|
485
|
-
def instance_variable_name(key)
|
486
|
-
self.class.instance_variable_name(key)
|
487
|
-
end
|
488
|
-
|
489
|
-
def dig_set(obj, keys, value)
|
490
|
-
if keys.length == 1
|
491
|
-
obj[keys.first] = value
|
492
|
-
else
|
493
|
-
dig_set(obj[keys.first], keys.slice(1..-1), value)
|
494
|
-
end
|
495
|
-
end
|
496
|
-
|
497
|
-
def used_param?(val)
|
498
|
-
self.class.used_param?(val)
|
499
|
-
end
|
500
|
-
|
501
|
-
def key_method?
|
502
|
-
self.class.key?
|
503
|
-
end
|
504
|
-
|
505
|
-
def key_method
|
506
|
-
self.class.key
|
507
|
-
end
|
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 < 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
|
523
65
|
end
|
524
66
|
end
|
525
67
|
end
|
@@ -61,6 +61,7 @@ module Ecoportal
|
|
61
61
|
|
62
62
|
return true if a_empty && b_empty
|
63
63
|
end
|
64
|
+
|
64
65
|
a == b
|
65
66
|
end
|
66
67
|
|
@@ -98,7 +99,7 @@ module Ecoportal
|
|
98
99
|
data_hash['patch_ver'] = a['patch_ver']
|
99
100
|
end
|
100
101
|
|
101
|
-
data_hash.delete(
|
102
|
+
data_hash.delete('force_patch')
|
102
103
|
end
|
103
104
|
end
|
104
105
|
|
@@ -3,32 +3,31 @@ module Ecoportal
|
|
3
3
|
module Common
|
4
4
|
module Content
|
5
5
|
module ModelHelpers
|
6
|
-
|
7
6
|
private
|
8
7
|
|
9
8
|
# Offers multiple ways to compare two strings
|
10
9
|
# Offers multiple ways to compare two strings
|
11
|
-
def same_string?(
|
12
|
-
|
13
|
-
when value1.is_a?(String) && value2.is_a?(String)
|
10
|
+
def same_string?(value_1, value_2, exact: false, mild: false)
|
11
|
+
if value_1.is_a?(String) && value_2.is_a?(String)
|
14
12
|
if exact
|
15
|
-
|
13
|
+
value_1 == value_2
|
16
14
|
else
|
17
|
-
|
18
|
-
|
15
|
+
v_1 = value_1.to_s.strip.downcase
|
16
|
+
v_2 = value_2.to_s.strip.downcase
|
19
17
|
|
20
18
|
if mild
|
21
|
-
|
22
|
-
|
19
|
+
v_1 = v_1.gsub(/[^a-z ]+/, ' ').gsub(/\s+/, ' ').strip
|
20
|
+
v_2 = v_2.gsub(/[^a-z ]+/, ' ').gsub(/\s+/, ' ').strip
|
23
21
|
end
|
24
|
-
|
22
|
+
|
23
|
+
v_1 == v_2
|
25
24
|
end
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
elsif value_1.is_a?(Regexp) && value_2.is_a?(String)
|
26
|
+
value_2 =~ value_1
|
27
|
+
elsif value_1.is_a?(String) && value_2.is_a?(Regexp)
|
28
|
+
value_1 =~ value_2
|
30
29
|
else
|
31
|
-
|
30
|
+
value_1 == value_2
|
32
31
|
end
|
33
32
|
end
|
34
33
|
|
@@ -36,7 +35,6 @@ module Ecoportal
|
|
36
35
|
keys.each {|key| hash.delete(key)}
|
37
36
|
hash
|
38
37
|
end
|
39
|
-
|
40
38
|
end
|
41
39
|
end
|
42
40
|
end
|