ecoportal-api-v2 0.8.8 → 0.8.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +85 -3
  3. data/lib/ecoportal/api/common/content/array_model.rb +8 -6
  4. data/lib/ecoportal/api/common/content/collection_model.rb +45 -20
  5. data/lib/ecoportal/api/common/content/doc_helpers.rb +2 -2
  6. data/lib/ecoportal/api/common/content/double_model.rb +94 -5
  7. data/lib/ecoportal/api/common/content/hash_diff_patch.rb +44 -22
  8. data/lib/ecoportal/api/common/content/model_helpers.rb +36 -0
  9. data/lib/ecoportal/api/common/content.rb +1 -0
  10. data/lib/ecoportal/api/v2/page/component/action.rb +17 -8
  11. data/lib/ecoportal/api/v2/page/component/action_field.rb +37 -2
  12. data/lib/ecoportal/api/v2/page/component/chart_field/benchmark.rb +9 -7
  13. data/lib/ecoportal/api/v2/page/component/chart_field/config.rb +23 -0
  14. data/lib/ecoportal/api/v2/page/component/chart_field/frequency.rb +3 -4
  15. data/lib/ecoportal/api/v2/page/component/chart_field/heatmap.rb +1 -3
  16. data/lib/ecoportal/api/v2/page/component/chart_field/indicator.rb +4 -5
  17. data/lib/ecoportal/api/v2/page/component/chart_field/multiseries.rb +3 -5
  18. data/lib/ecoportal/api/v2/page/component/chart_field/sankey.rb +1 -3
  19. data/lib/ecoportal/api/v2/page/component/chart_field/serie.rb +3 -4
  20. data/lib/ecoportal/api/v2/page/component/chart_field/series_config.rb +5 -7
  21. data/lib/ecoportal/api/v2/page/component/chart_field.rb +39 -5
  22. data/lib/ecoportal/api/v2/page/component/chart_fr_field.rb +7 -5
  23. data/lib/ecoportal/api/v2/page/component/checklist_field.rb +1 -1
  24. data/lib/ecoportal/api/v2/page/component/checklist_item.rb +7 -5
  25. data/lib/ecoportal/api/v2/page/component/date_field.rb +71 -4
  26. data/lib/ecoportal/api/v2/page/component/file.rb +14 -3
  27. data/lib/ecoportal/api/v2/page/component/files_field.rb +37 -1
  28. data/lib/ecoportal/api/v2/page/component/gauge_field.rb +2 -2
  29. data/lib/ecoportal/api/v2/page/component/gauge_stop.rb +5 -5
  30. data/lib/ecoportal/api/v2/page/component/geo_coordinates.rb +13 -0
  31. data/lib/ecoportal/api/v2/page/component/geo_field.rb +4 -1
  32. data/lib/ecoportal/api/v2/page/component/image.rb +2 -1
  33. data/lib/ecoportal/api/v2/page/component/images_field.rb +57 -1
  34. data/lib/ecoportal/api/v2/page/component/number_field.rb +1 -0
  35. data/lib/ecoportal/api/v2/page/component/people_field.rb +115 -5
  36. data/lib/ecoportal/api/v2/page/component/people_viewable_field.rb +14 -0
  37. data/lib/ecoportal/api/v2/page/component/plain_text_field.rb +34 -2
  38. data/lib/ecoportal/api/v2/page/component/reference_field.rb +32 -3
  39. data/lib/ecoportal/api/v2/page/component/selection_field.rb +59 -3
  40. data/lib/ecoportal/api/v2/page/component/selection_option.rb +6 -4
  41. data/lib/ecoportal/api/v2/page/component/signature_field.rb +3 -2
  42. data/lib/ecoportal/api/v2/page/component/tag_field.rb +31 -1
  43. data/lib/ecoportal/api/v2/page/component.rb +44 -10
  44. data/lib/ecoportal/api/v2/page/components.rb +8 -3
  45. data/lib/ecoportal/api/v2/page/permission_flags.rb +67 -0
  46. data/lib/ecoportal/api/v2/page/permit.rb +2 -1
  47. data/lib/ecoportal/api/v2/page/section.rb +70 -9
  48. data/lib/ecoportal/api/v2/page/sections.rb +64 -6
  49. data/lib/ecoportal/api/v2/page/stage.rb +11 -7
  50. data/lib/ecoportal/api/v2/page/stages.rb +2 -2
  51. data/lib/ecoportal/api/v2/page.rb +18 -2
  52. data/lib/ecoportal/api/v2/pages/page_stage.rb +13 -1
  53. data/lib/ecoportal/api/v2/pages.rb +3 -0
  54. data/lib/ecoportal/api/v2/registers/search_results.rb +13 -0
  55. data/lib/ecoportal/api/v2/registers.rb +15 -2
  56. data/lib/ecoportal/api/v2_version.rb +1 -1
  57. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d7fd0373344854c22b4397e7e2d2645dd799695424f3fa30d006fca2081676d
4
- data.tar.gz: 2d839f8ecb67512c2c5d00a2bd6832e53d304334f2c5a6bbc291958661e21130
3
+ metadata.gz: 692fe87a2a9dd089ee091bae7133915ac5c9ae4b0643be6086b57206569a1044
4
+ data.tar.gz: 4d9866b6e5727b176caaff881cb44a3cd13ca08418884e69283f8c64c0e80272
5
5
  SHA512:
6
- metadata.gz: 27425f592041428b0d07f9593c78f2a80095311e4890a3569ce212e37f067a3b36f2a3b147459e7dca368f7a737671ac9325df6ed40cec3d4a18f75d0ea18d28
7
- data.tar.gz: c4cee01feb2c89179da4eda1d3699e5900812b52fb7d198a1447935ac4e89c347bee56ba69c5f6d1a407f968da21112cf0a490855811ddfd5048caace430c422
6
+ metadata.gz: b812fc350ccdfd173d8ca7b02b05bd0676eda8dac97e2a981cd6c87e1e62c7170744a352ef962144ffb24b3bf9eb43011156c78c09f22a00db154431fd2be5ca
7
+ data.tar.gz: 00c551099e991a339125e5a985f98aa918b8f9844e03b7b6ba79c6cc2fc5d9ffa4303eaee51c2acc6205932472cdf5324692a73d875e647ba0bcd685f676c60c
data/CHANGELOG.md CHANGED
@@ -1,7 +1,90 @@
1
1
  # Change Log
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
- ## [0.8.8] - 2021-06-xx
4
+ ## [0.8.12] - 2021-08-xx
5
+
6
+ ### Added
7
+ - `Ecoportal::API::V2::Pages::PageStage#current_stage`
8
+
9
+ ### Changed
10
+
11
+ ### Fixed
12
+ - `Ecoportal::API::Common::Content::DocHelpers#get_body` typo and wrong parameters on call.
13
+ - `Ecoportal::API::V2::Pages#update` should always return `Result` object.
14
+ - `Ecoportal::API::V2::Registers#search` was not using `cursor_id` to iterate.
15
+
16
+ ## [0.8.11] - 2021-08-27
17
+
18
+ ### Added
19
+ - `Ecoportal::API::V2::Page#stages?`
20
+ - `Ecoportal::API::V2::Pages::PageStage#current_stage_id`
21
+ - `Ecoportal::API::V2::Registers#search` added option keyed parameter `:only_first`
22
+ - which will make the method return the first `Ecoportal::API::V2::Registers::SearchResults`
23
+ - This has been **added** with the aim of knowing how many results out of how many entries.
24
+
25
+ ### Changed
26
+ - `Ecoportal::API::V2::Pages::PageStage#update` won't update unless there's something to update.
27
+
28
+ ### Fixed
29
+
30
+
31
+ ## [0.8.10] - 2021-08-22
32
+
33
+ ### Added
34
+ - `Ecoportal::API::Common::Content::CollectionModel#[]` now supports position as well
35
+ - `Ecoportal::API::V2::Page::Component::FilesField#add_file`: support for adding files
36
+ - `Ecoportal::API::V2::Page::Component::PeopleField#viewable_fields`: support for managing `viewable_fields`
37
+
38
+ ### Changed
39
+
40
+ ### Fixed
41
+ - `Ecoportal::API::V2::Page::Component::FilesField`: was not requiring `File`
42
+ - `Ecoportal::API::V2::Page::Component::NumberField#value`: it was missing :/
43
+ - `Ecoportal::API::Common::Content::HashDiffPatch` did not support nested objects in Arrays, where the nested object wouldn't have a `patch_ver`. This **fix** allows for it
44
+ - `Ecoportal::API::Common::Content::DoubleModel.passforced` to define `methods` that should always be present. This allows to define `patch_ver` as a forced `key` in models that have it
45
+ - The enforcement (`self.class.enforce!`) happens on `initialize`
46
+ - `passenforced` subjacent model `forced_model_keys` is inheritable
47
+ - `Ecoportal::API::Common::Content::CollectionModel`:
48
+ - Method `upsert!` was not working fine the parameters `pos`, `before` and `after`
49
+ - Method `delete!` did not support position
50
+
51
+ ## [0.8.9] - 2021-08-16
52
+
53
+ ### Added
54
+ - `Ecoportal::API::Common::Content::ModelHelpers`
55
+ - Starting with `#same_string?`, this lib aims to offer a full set of helper comparers.
56
+ - `Ecoportal::API::V2::Page::Components#get_by_id`
57
+ - In future changes, method `[]` might be overriding the method of the parent class `CollectionModel`.
58
+ - `Ecoportal::API::V2::Page::PermissionFlags` to offer support to `can_permission`, `subscribed`, etc.
59
+ - `Ecoportal::API::V2::Page::Section#add_component` super handy helper to add fields to sections.
60
+ - `Ecoportal::API::V2::Page::Component::PeopleField#delete` to remove people using their ids.
61
+ - `Ecoportal::API::V2::Page::Component::GeoField` **added** suport for `#coordinates`
62
+ - With embedded object `Ecoportal::API::V2::Page::Component::GeoCoordinates`
63
+ - `#configure` method, to all the `Component` types (but `ChartField` s)
64
+ - This method allows to quickly set field properties.
65
+ - `Ecoportal::API::V2::Page::Sections#between`, aiming to obtain sections between two other ones
66
+
67
+
68
+ ### Changed
69
+ - `Ecoportal::API::V2::Page::Section` real support for `split` sections (right vs left)
70
+ - This is actually a **FIX**
71
+ - `Ecoportal::API::V2::Page::Component::DateField#create_event`
72
+ - Now it switches to _allow future dates_
73
+ - `Ecoportal::API::V2::Page::Component::SelectionField#add_option`, the `name` parameter is now optional
74
+ - This change aims to make it consistent with the back-end.
75
+ - To its actual `Boolean` type some properties
76
+ - All `Component` types
77
+
78
+ ### Fixed
79
+ - `Ecoportal::API::Common::Content::HashDiffPatch` did not support `Hash` values without `id` or `patch_ver`
80
+ - This fix should allow them to flow to the update
81
+ - `Ecoportal::API::V2::Page::Component::ChartField` did not have `doc` for `mode` based configuration
82
+ - `Ecoportal::API::Common::Content::ArrayModel#insert_one`
83
+ - When `pos`, `before` & `after` were `nil` it was failing to attach the element.
84
+ - `Ecoportal::API::V2::Page::Sections#add` was not adding in the correct order
85
+ - `Ecoportal::API::V2::Page::Section.new_doc` was unnecessarily giving `9999` as default weight
86
+
87
+ ## [0.8.8] - 2021-08-03
5
88
 
6
89
  ### Added
7
90
  - `Ecoportal::API::V2::Page::Component::ChartField::Benchmark`
@@ -10,8 +93,7 @@ All notable changes to this project will be documented in this file.
10
93
 
11
94
  ### Changed
12
95
  - Removed `Stages.ordered_stages`: detected bug with Enumerable iteration
13
-
14
- ### Fixed
96
+
15
97
 
16
98
  ## [0.8.7] - 2021-05-25
17
99
 
@@ -3,7 +3,8 @@ module Ecoportal
3
3
  module Common
4
4
  module Content
5
5
  # Class to handle a plain Array embedded in a Hashed model.
6
- # @note its purpose is to handle an Array of basic objects (i.e. `Date`, `String`, `Number`)
6
+ # @note
7
+ # - Its purpose is to handle an Array of basic objects (i.e. `Date`, `String`, `Number`)
7
8
  class ArrayModel < Content::DoubleModel
8
9
  class TypeMismatchedComparison < Exception
9
10
  def initialize (this: nil, that: msg = "Trying to compare objects with different behavior.")
@@ -222,15 +223,16 @@ module Ecoportal
222
223
  def insert_one(value, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
223
224
  i = index(value)
224
225
  return i if (i && uniq?)
225
-
226
226
  pos = case
227
227
  when used_param?(pos)
228
- pos
228
+ pos || length
229
229
  when used_param?(before)
230
- index(before)
230
+ before ? index(before) : length
231
231
  when used_param?(after)
232
- if i = index(after)
233
- i + 1
232
+ if after
233
+ if i = index(after) then i + 1 end
234
+ else
235
+ length
234
236
  end
235
237
  else
236
238
  length
@@ -2,6 +2,9 @@ module Ecoportal
2
2
  module API
3
3
  module Common
4
4
  module Content
5
+ # CollectionModel aims to deal with Arrays of actual objects.
6
+ # @note to be able to refer to the correct element of the Collection,
7
+ # it is required that those elements have a unique `key` that allows to identify them
5
8
  class CollectionModel < Content::DoubleModel
6
9
 
7
10
  class << self
@@ -140,17 +143,21 @@ module Ecoportal
140
143
  end
141
144
 
142
145
  # Get an element usign the `key`.
146
+ # @param value [String, Hash, Ecoportal::API::Common::Content::DoubleModel]
147
+ # @return [Object] the `items_class` element object
143
148
  def [](value)
144
149
  items_by_key[get_key(value)]
145
150
  end
146
151
 
152
+ # @return [Array<Object>] the `items_class` element object
147
153
  def values_at(*keys)
148
154
  keys.map {|key| self[key]}
149
155
  end
150
156
 
151
157
  # Tries to find the element `value`, if it exists, it updates it
152
- # Otherwise it pushes it to the end
153
- # @return the element
158
+ # Otherwise it pushes it to the end
159
+ # @value [Hash, Ecoportal::API::Common::Content::DoubleModel] the eleement to be added
160
+ # @return [Object] the `items_class` element object
154
161
  def upsert!(value, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
155
162
  unless value.is_a?(Hash) || value.is_a?(Content::DoubleModel)
156
163
  raise "'Content::DoubleModel' or 'Hash' doc required"
@@ -160,15 +167,20 @@ module Ecoportal
160
167
  if item = self[value]
161
168
  item.replace_doc(item_doc)
162
169
  else
163
- _doc_upsert(item_doc, pos: pos, before: before, after: after)
170
+ pos_idx = _doc_upsert(item_doc, pos: pos, before: before, after: after)
164
171
  end
165
172
  (item || self[item_doc]).tap do |item|
166
173
  yield(item) if block_given?
167
174
  end
168
175
  end
169
176
 
177
+ # Deletes `value` from this `CollectionModel` instance
178
+ # @param value [String, Hash, Ecoportal::API::Common::Content::DoubleModel]
179
+ # - When used as `String`, the `key` value (i.e. `id` value) is expected
180
+ # - When used as `Hash`, it should be the `doc` of the target element
181
+ # - When used as `DoubleModel`, it should be the specific object to be deleted
170
182
  def delete!(value)
171
- unless value.is_a?(Hash) || value.is_a?(Content::DoubleModel)
183
+ unless value.is_a?(Hash) || value.is_a?(Content::DoubleModel) || value.is_a?(String)
172
184
  raise "'Content::DoubleModel' or 'Hash' doc required"
173
185
  end
174
186
  if item = self[value]
@@ -186,7 +198,7 @@ module Ecoportal
186
198
  variables_remove!
187
199
  end
188
200
 
189
- # Gets the `key` of the object
201
+ # Gets the `key` of the object `value`
190
202
  def get_key(value)
191
203
  case value
192
204
  when Content::DoubleModel
@@ -195,6 +207,8 @@ module Ecoportal
195
207
  value[items_key]
196
208
  when String
197
209
  value
210
+ when Numeric
211
+ get_key(self.to_a[value])
198
212
  end
199
213
  end
200
214
 
@@ -235,27 +249,22 @@ module Ecoportal
235
249
  super
236
250
  end
237
251
 
252
+ # Deletes `value` from `doc` (here referred as `_doc_items`)
253
+ # @return [Object] the element deleted from `doc`
238
254
  def _doc_delete(value)
239
255
  if current_pos = _doc_key(value)
240
- _doc_items.delete_at(current_pos)
241
- on_change
256
+ _doc_items.delete_at(current_pos).tap do |deleted|
257
+ on_change
258
+ end
242
259
  end
243
260
  end
244
261
 
245
262
  def _doc_upsert(value, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
246
- current_pos = _doc_key(value)
247
- pos = case
248
- when used_param?(pos)
249
- pos
250
- when used_param?(before)
251
- _doc_key(before)
252
- when used_param?(after)
253
- #puts "to add after #{after.id}"
254
- if i = _doc_key(after)
255
- i + 1
256
- end
257
- end
263
+ current_pos = if elem = self[value]
264
+ _doc_key(elem)
265
+ end
258
266
 
267
+ pos = scope_position(pos: pos, before: before, after: after)
259
268
  pos ||= current_pos
260
269
 
261
270
  if current_pos && pos
@@ -264,7 +273,6 @@ module Ecoportal
264
273
  end
265
274
 
266
275
  pos = (pos && pos < _doc_items.length)? pos : _doc_items.length
267
-
268
276
  pos.tap do |i|
269
277
  _doc_items.insert(pos, value)
270
278
  on_change
@@ -272,6 +280,23 @@ module Ecoportal
272
280
 
273
281
  end
274
282
 
283
+ def scope_position(pos: NOT_USED, before: NOT_USED, after: NOT_USED)
284
+ case
285
+ when used_param?(pos)
286
+ if elem = self[pos]
287
+ _doc_key(elem) - 1
288
+ end
289
+ when used_param?(before)
290
+ if elem = self[before]
291
+ _doc_key(elem) - 1
292
+ end
293
+ when used_param?(after)
294
+ if elem = self[after]
295
+ _doc_key(elem)
296
+ end
297
+ end
298
+ end
299
+
275
300
  end
276
301
  end
277
302
  end
@@ -13,9 +13,9 @@ module Ecoportal
13
13
  when doc.respond_to?(:as_update)
14
14
  doc.as_update
15
15
  when doc.respond_to?(:as_json)
16
- Common::Content::HashPatchDiff.patch_diff(doc.as_json)
16
+ Common::Content::HashDiffPatch.patch_diff(doc.as_json, nil)
17
17
  when doc.is_a?(Hash)
18
- Common::Content::HashPatchDiff.patch_diff(doc)
18
+ Common::Content::HashDiffPatch.patch_diff(doc, nil)
19
19
  else
20
20
  raise "Could not get body for doc: #{doc}"
21
21
  end
@@ -10,6 +10,7 @@ module Ecoportal
10
10
  class DoubleModel < Common::BaseModel
11
11
  NOT_USED = Common::Content::ClassHelpers::NOT_USED
12
12
  extend Common::Content::ClassHelpers
13
+ include Common::Content::ModelHelpers
13
14
 
14
15
  class UnlinkedModel < Exception
15
16
  def initialize (msg = "Something went wrong when linking the document.", from: nil, key: nil)
@@ -36,6 +37,8 @@ module Ecoportal
36
37
 
37
38
  # Same as `attr_reader` but links to a subjacent `Hash` model property
38
39
  # @note it does **not** create an _instance variable_
40
+ # @param methods [Array<Symbol>] the method that exposes the value
41
+ # as well as its `key` in the underlying `Hash` model.
39
42
  def pass_reader(*methods)
40
43
  methods.each do |method|
41
44
  method = method.to_s.freeze
@@ -51,6 +54,8 @@ module Ecoportal
51
54
 
52
55
  # Same as `attr_writer` but links to a subjacent `Hash` model property
53
56
  # @note it does **not** create an _instance variable_
57
+ # @param methods [Array<Symbol>] the method that exposes the value
58
+ # as well as its `key` in the underlying `Hash` model.
54
59
  def pass_writer(*methods)
55
60
  methods.each do |method|
56
61
  method = method.to_s.freeze
@@ -69,6 +74,8 @@ module Ecoportal
69
74
  # no chance you can avoid invinite loop for `get_key` without setting an
70
75
  # instance variable key at the moment of the object creation, when the
71
76
  # `doc` is firstly received
77
+ # @param method [Symbol] the method that exposes the value
78
+ # as well as its `key` in the underlying `Hash` model.
72
79
  def passkey(method)
73
80
  method = method.to_s.freeze
74
81
  var = instance_variable_name(method)
@@ -90,7 +97,33 @@ module Ecoportal
90
97
  self
91
98
  end
92
99
 
100
+ # These are methods that should always be present in patch update
101
+ # @note
102
+ # - `DoubleModel` can be used with objects that do not use `patch_ver`
103
+ # - This ensures that does that do, will get the correct patch update model
104
+ # @param method [Symbol] the method that exposes the value
105
+ # as well as its `key` in the underlying `Hash` model.
106
+ # @param default [Value] the default value that
107
+ # this `key` will be written in the model when it doesn't exixt
108
+ def passforced(method, default: , read_only: false)
109
+ model_forced_keys[method.to_s.freeze] = default
110
+ passthrough(method, read_only: read_only)
111
+ end
112
+
113
+ # Ensures `doc` has the `model_forced_keys`. If it doesn't, it adds those missing
114
+ # with the defined `default` values
115
+ def enforce!(doc)
116
+ return unless doc && doc.is_a?(Hash)
117
+ return if model_forced_keys.empty?
118
+ model_forced_keys.each do |key, default|
119
+ doc[key] = default unless doc.key?(key)
120
+ end
121
+ doc
122
+ end
123
+
93
124
  # Same as `attr_accessor` but links to a subjacent `Hash` model property
125
+ # @param methods [Array<Symbol>] the method that exposes the value
126
+ # as well as its `key` in the underlying `Hash` model.
94
127
  # @param read_only [Boolean] should it only define the reader?
95
128
  def passthrough(*methods, read_only: false)
96
129
  pass_reader *methods
@@ -100,6 +133,8 @@ module Ecoportal
100
133
 
101
134
  # To link as a `Time` date to a subjacent `Hash` model property
102
135
  # @see Ecoportal::API::Common::Content::DoubleModel#passthrough
136
+ # @param methods [Array<Symbol>] the method that exposes the value
137
+ # as well as its `key` in the underlying `Hash` model.
103
138
  # @param read_only [Boolean] should it only define the reader?
104
139
  def passdate(*methods, read_only: false)
105
140
  pass_reader(*methods) {|value| to_time(value)}
@@ -109,7 +144,21 @@ module Ecoportal
109
144
  self
110
145
  end
111
146
 
147
+ # To link as a `Boolean` to a subjacent `Hash` model property
148
+ # @param methods [Array<Symbol>] the method that exposes the value
149
+ # as well as its `key` in the underlying `Hash` model.
150
+ # @param read_only [Boolean] should it only define the reader?
151
+ def passboolean(*methods, read_only: false)
152
+ pass_reader(*methods) {|value| value}
153
+ unless read_only
154
+ pass_writer(*methods) {|value| !!value}
155
+ end
156
+ self
157
+ end
158
+
112
159
  # To link as plain `Array` 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.
113
162
  # @param order_matters [Boolean] does the order matter
114
163
  # @param uniq [Boolean] should it contain unique elements
115
164
  def passarray(*methods, order_matters: true, uniq: true)
@@ -131,6 +180,10 @@ module Ecoportal
131
180
  end
132
181
 
133
182
  # Helper to embed one nested object under one property
183
+ # @param method [Symbol] the method that exposes the embeded object
184
+ # @param key [Symbol] the `key` that embeds it to the underlying `Hash` model
185
+ # @nullable [Boolean] to specify if this object can be `nil`
186
+ # @param klass [Class, String] the class of the embedded object
134
187
  def embeds_one(method, key: method, nullable: false, klass:)
135
188
  embed(method, key: key, nullable: nullable, multiple: false, klass: klass)
136
189
  end
@@ -138,7 +191,11 @@ module Ecoportal
138
191
  # @note
139
192
  # - if you have a dedicated `Enumerable` class to manage `many`, you should use `:enum_class`
140
193
  # - otherwise, just indicate the child class in `:klass` and it will auto generate the class
141
- # @param
194
+ # @param method [Symbol] the method that exposes the embeded object
195
+ # @param key [Symbol] the `key` that embeds it to the underlying `Hash` model
196
+ # @param order_matters [Boolean] to state if the order will matter
197
+ # @param klass [Class, String] the class of the individual elements it embeds
198
+ # @param enum_class [Class, String] the class of the collection that will hold the individual elements
142
199
  def embeds_many(method, key: method, order_matters: false, order_key: nil, klass: nil, enum_class: nil)
143
200
  if enum_class
144
201
  eclass = enum_class
@@ -175,8 +232,15 @@ module Ecoportal
175
232
  end
176
233
  end
177
234
 
235
+ # The list of keys that will be forced in the model
236
+ def model_forced_keys
237
+ @forced_model_keys ||= {}
238
+ end
239
+
178
240
  end
179
241
 
242
+ inheritable_class_vars :forced_model_keys
243
+
180
244
  attr_reader :_parent, :_key
181
245
 
182
246
  def initialize(doc = {}, parent: self, key: nil)
@@ -184,6 +248,8 @@ module Ecoportal
184
248
  @_parent = parent || self
185
249
  @_key = key || self
186
250
 
251
+ self.class.enforce!(doc)
252
+
187
253
  if _parent == self
188
254
  @doc = doc
189
255
  @original_doc = JSON.parse(@doc.to_json)
@@ -200,11 +266,13 @@ module Ecoportal
200
266
  _parent.root
201
267
  end
202
268
 
269
+ # @return [String] the `value` of the `key` method (i.e. `id` value)
203
270
  def key
204
271
  raise "No key_method defined for #{self.class}" unless key_method?
205
272
  self.method(key_method).call
206
273
  end
207
274
 
275
+ # @param [String] the `value` of the `key` method (i.e. `id` value)
208
276
  def key=(value)
209
277
  raise "No key_method defined for #{self.class}" unless key_method?
210
278
  method = "#{key_method}="
@@ -223,16 +291,25 @@ module Ecoportal
223
291
  end
224
292
  end
225
293
 
294
+ # @return [nil, Hash] the underlying `Hash` model as is (carrying current changes)
226
295
  def doc
227
296
  raise UnlinkedModel.new(from: "#{self.class}#doc", key: _key) unless linked?
228
- return @doc if is_root?
229
- _parent.doc.dig(*[_doc_key(_key)].flatten)
297
+ if is_root?
298
+ @doc
299
+ else
300
+ _parent.doc.dig(*[_doc_key(_key)].flatten)
301
+ end
230
302
  end
231
303
 
304
+ # The `original_doc` holds the model as is now on server-side.
305
+ # @return [nil, Hash] the underlying `Hash` model as after last `consolidate!` changes
232
306
  def original_doc
233
307
  raise UnlinkedModel.new(from: "#{self.class}#original_doc", key: _key) unless linked?
234
- return @original_doc if is_root?
235
- _parent.original_doc.dig(*[_doc_key(_key)].flatten)
308
+ if is_root?
309
+ @original_doc
310
+ else
311
+ _parent.original_doc.dig(*[_doc_key(_key)].flatten)
312
+ end
236
313
  end
237
314
 
238
315
  def as_json
@@ -243,19 +320,31 @@ module Ecoportal
243
320
  doc.to_json(*args)
244
321
  end
245
322
 
323
+ # @return [nil, Hash] the patch `Hash` model including only the changes between
324
+ # `original_doc` and `doc`
246
325
  def as_update
247
326
  new_doc = as_json
248
327
  Common::Content::HashDiffPatch.patch_diff(new_doc, original_doc)
249
328
  end
250
329
 
330
+ # @return [Boolean] stating if there are changes
251
331
  def dirty?
252
332
  as_update != {}
253
333
  end
254
334
 
335
+ # It makes `original_doc` to be like `doc`
336
+ # @note
337
+ # - after executing it, there will be no pending changes
338
+ # - you should technically run this command, after a successful update request to the server
255
339
  def consolidate!
256
340
  replace_original_doc(JSON.parse(doc.to_json))
257
341
  end
258
342
 
343
+ # It makes `doc` to be like `original`
344
+ # @note
345
+ # - after executing it, changes in `doc` will be lost
346
+ # - you should technically run this command only if you want to remove certain changes
347
+ # @key [Symbol] the specific part of the model you want to `reset`
259
348
  def reset!(key = nil)
260
349
  if key
261
350
  keys = [key].flatten.compact