ecoportal-api-v2 0.8.13 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 146b0998371f28683143b8a95ae1b68e11f8f079698502bede31c8e29369fc97
4
- data.tar.gz: 4ce0f3dd75e4e1d822d4540fa0ff1e3e18a7c7dca1c54d7030bdb475adc21a93
3
+ metadata.gz: 3480ab100855337eef9de50609f4f4776418de1b09a5a1eac3ee31f936f4cd00
4
+ data.tar.gz: 2520bf687a1d00df3d925788600f78c266fcc6599ac816dac1f7df08703bc9e9
5
5
  SHA512:
6
- metadata.gz: 21605169ab88cb159ee32a2f6f36e3100b183a03a7f9c895b33631934811bb9e3f16c6bc6101ddfac562854f8762f545a743b68c55d662c97be54ff35fc5b9e8
7
- data.tar.gz: d2a66296e4b9194667983ba5c37c646fd6a12e037138870aa2771c743fc75dbf9b2ee91d07af7b1e4b501ad1afd846b427a8b92f5dc99ee4551200cc742dcd20
6
+ metadata.gz: 011752c7161177026fdcd59a01ee7eb048094eb29c5688e2b68e6ad2aec0a263e93990726c1fca86eb1bfc86d000d360db4d8cf685783f7c2ab8257570258740
7
+ data.tar.gz: 012ed55b7472c56f76b2e1bdafd3ce8cb926c67d5d94e6b3349a7c21ade9ff3a20c2caa4974fe9cdc30a6dba8312663edc633ae7d0a66b31117b57e795727dde
data/CHANGELOG.md CHANGED
@@ -1,12 +1,49 @@
1
1
  # Change Log
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
- ## [0.8.13] - 2021-08-xx
4
+ ## [0.8.14] - 2021-09-xx
5
5
 
6
6
  ### Added
7
+ - `Ecoportal::API::Common::Content::CollectionModel`
8
+ - `#_doc_pos` for clarity
9
+ - `#include?` to check if an element is present in the collection
10
+ - `Ecoportal::API::V2::Page::Stage#section?` check if section belongs to stage
11
+ - `Ecoportal::API::V2::Page::Sections`
12
+ - `#get_by_id`
13
+ - `#unattached` sections that are not attached to any stage
14
+ - `Ecoportal::API::V2::Page::Section`
15
+ - `#add_component` **added** parameter `before`: to cover case where you want to add it at the beginning
16
+ - `#component?` **widened** parameter type
17
+ - `#stages` stages where this section is attached
18
+ - `attached?` whether or not the section is attached
19
+ - `Ecoportal::API::V2::Page::Component`
20
+ - `#attached?` whether or not the field is attached to some section
21
+ - `#multi_section?` if the field is attached to more than one section :/
22
+ - **added** support for `forces`
23
+ - `Ecoportal::API::V2::Page::Force`
24
+ - `Ecoportal::API::V2::Page::Forces`
25
+ - `Ecoportal::API::V2::Page::Force::Binding`
26
+ - `Ecoportal::API::V2::Page::Force::Bindings`
27
+ - **added** shortcut to `#ooze` throughout all the model
28
+ - **validations** in some methods
29
+ - `Ecoportal::API::V2::Page::Stage#add_section`: section should be in `ooze.sections`
30
+ - `Ecoportal::API::V2::Page::Section#add_component`: component should be in `ooze.components`
31
+ - `Ecoportal::API::V2::Page::Force::Bindings#add`:
32
+ - section to be in `ooze.sections`
33
+ - component to be in `ooze.components`
34
+ - `Ecoportal::API::V2::Pages::PageStage#as_update` on page instances, it checks that
35
+ - Fields do not belong to more than one section
36
+ - Fields belong at least to one section
37
+ - Sections belong to at least one stage
38
+
7
39
  ### Changed
8
40
  ### Fixed
9
- - `Ecoportal::API::V2::Stage::Sections`
41
+ - `Ecoportal::API::Common::Content::ArrayModel#insert_one` was not inserting when not found. Should insert at least at the end.
42
+
43
+ ## [0.8.13] - 2021-09-03
44
+
45
+ ### Fixed
46
+ - `Ecoportal::API::V2::Page::Sections`
10
47
  - `weight` fixing should only happen on entire page, **not** on stage sections that could change the order of section shared with other stages
11
48
  - `#scope_weight` was not excluding the section when using `weight` of the last one
12
49
  - Several classes with `embeds_many` were performing the `ordering` of those elements in the wrong way
@@ -224,25 +224,18 @@ module Ecoportal
224
224
  i = index(value)
225
225
  return i if (i && uniq?)
226
226
  pos = case
227
- when used_param?(pos)
228
- pos || length
229
- when used_param?(before)
230
- before ? index(before) : length
231
- when used_param?(after)
232
- if after
233
- if i = index(after) then i + 1 end
234
- else
235
- length
236
- end
237
- else
238
- length
227
+ when used_param?(pos) && pos
228
+ pos
229
+ when used_param?(before) && before
230
+ index(before)
231
+ when used_param?(after) && after
232
+ if i = index(after) then i + 1 end
239
233
  end
240
234
 
235
+ pos ||= length
241
236
  pos.tap do |i|
242
- unless !i
243
- _items.insert(pos, value)
244
- on_change
245
- end
237
+ _items.insert(pos, value)
238
+ on_change
246
239
  end
247
240
  end
248
241
 
@@ -109,6 +109,10 @@ module Ecoportal
109
109
  self.class.klass
110
110
  end
111
111
 
112
+ def _doc_pos(value)
113
+ _doc_key(value)
114
+ end
115
+
112
116
  # Transforms `value` into the actual `key` to access the object in the doc `Array`
113
117
  # @note
114
118
  # - The name of the method is after the paren't class method
@@ -152,6 +156,13 @@ module Ecoportal
152
156
  items_by_key[get_key(value)]
153
157
  end
154
158
 
159
+ # Checks if an element exists in the collection
160
+ # @param value [String, Hash, Ecoportal::API::Common::Content::DoubleModel]
161
+ # @return [Boolean] whether or not it is included
162
+ def include?(value)
163
+ items_by_key.key?(get_key(value))
164
+ end
165
+
155
166
  # @return [Array<Object>] the `items_class` element object
156
167
  def values_at(*keys)
157
168
  keys.map {|key| self[key]}
@@ -60,7 +60,7 @@ module Ecoportal
60
60
  a == b
61
61
  end
62
62
 
63
- # Compares `a` as charring changes of `b`
63
+ # Compares `a` as carrying changes of `b`
64
64
  # @return [Hash] patch data object with only changes
65
65
  def patch_data(a, b = nil, delete: false)
66
66
  {}.tap do |data_hash|
@@ -93,6 +93,10 @@ module Ecoportal
93
93
  passboolean :hide_view, :hidden_on_reports, :hidden_on_mobile
94
94
  passarray :refs
95
95
 
96
+ def ooze
97
+ self._parent.ooze
98
+ end
99
+
96
100
  def ref_backend
97
101
  refs.first
98
102
  end
@@ -103,10 +107,23 @@ module Ecoportal
103
107
  end
104
108
  end
105
109
 
110
+ # Looks up the section that this component belongs to.
111
+ # @return [Ecoportal::API::V2::Page::Section, nil] the section where this field is attached to.
106
112
  def section
107
113
  root.sections.find {|sec| sec.component?(id)}
108
114
  end
109
115
 
116
+ # @return [Boolean] whether or not this field is attached to any section.
117
+ def attached?
118
+ !!section
119
+ end
120
+
121
+ # @return [Boolean] whether or not the component has been attached to more than one section.
122
+ def multi_section?
123
+ secs = ooze.sections.select {|sec| sec.component?(id)}
124
+ secs.length > 1
125
+ end
126
+
110
127
  def indexable_label
111
128
  self.class.indexable_label(label)
112
129
  end
@@ -13,18 +13,27 @@ module Ecoportal
13
13
 
14
14
  order_matters = true
15
15
 
16
+ def ooze
17
+ self._parent.ooze
18
+ end
19
+
20
+ # @return [Ecoportal::API::V2::Page::Component] the field with `id`
16
21
  def get_by_id(id)
17
22
  self.find do |comp|
18
23
  comp.id == id
19
24
  end
20
25
  end
21
26
 
27
+ # @return [Array<Ecoportal::API::V2::Page::Component>] the fields of that `type`
22
28
  def get_by_type(type)
23
29
  self.select do |comp|
24
30
  comp.type.downcase == type.to_s.strip.downcase
25
31
  end
26
32
  end
27
33
 
34
+ # @param name [String, Regexp] the `name` to search the field based on their `label`
35
+ # @param type [String] the `type` of the fields to be included
36
+ # @return [Array<Ecoportal::API::V2::Page::Component>] the fields that match `name`.
28
37
  def get_by_name(name, type: nil)
29
38
  pool = type ? get_by_type(type) : self
30
39
  pool.select do |comp|
@@ -32,6 +41,12 @@ module Ecoportal
32
41
  end
33
42
  end
34
43
 
44
+ # It creates a **new** component
45
+ # @param label [String, nil]
46
+ # @param type [String] the type of the field
47
+ # @yield [field] do some stuff with field
48
+ # @yieldparam [Ecoportal::API::V2::Page::Component] the created field
49
+ # @return [Ecoportal::API::V2::Page::Component] the created field.
35
50
  def add(label:, type:)
36
51
  fld_doc = component_class.new_doc(type: type)
37
52
  upsert!(fld_doc) do |fld|
@@ -40,6 +55,15 @@ module Ecoportal
40
55
  end
41
56
  end
42
57
 
58
+ # @return [Array<Ecoportal::API::V2::Page::Component>] **orphaned** fields (with no section).
59
+ def unattached
60
+ select {|comp| !comp.attached?}
61
+ end
62
+
63
+ # @return [Array<Ecoportal::API::V2::Page::Component>] fields belonging to more than one section.
64
+ def multi_section
65
+ select {|comp| comp.multi_section?}
66
+ end
43
67
  end
44
68
  end
45
69
  end
@@ -0,0 +1,54 @@
1
+ module Ecoportal
2
+ module API
3
+ class V2
4
+ class Page
5
+ class Force
6
+ class Binding < Common::Content::DoubleModel
7
+ class << self
8
+ def new_doc
9
+ {
10
+ "id" => new_uuid
11
+ }
12
+ end
13
+ end
14
+ COMPONENT_TYPE = "membrane_binding"
15
+ SECTION_TYPE = "flow_node_binding"
16
+
17
+ passkey :id
18
+ passforced :patch_ver, default: 1
19
+ passthrough :name, :reference_id, :type
20
+
21
+ def ooze
22
+ self._parent.ooze
23
+ end
24
+
25
+ # @return [Boolean] whether or not this the binding refers to a Component
26
+ def component?
27
+ type == COMPONENT_TYPE
28
+ end
29
+
30
+ # @return [Boolean] whether or not this the binding refers to a Section
31
+ def section?
32
+ type == SECTION_TYPE
33
+ end
34
+
35
+ # @return [Ecoportal::API::V2::Page::Component] field referenced by this binding.
36
+ def component
37
+ raise "This is not a component binding, but a section binding" unless component?
38
+ ooze.components.get_by_id(reference_id)
39
+ end
40
+
41
+ # @return [Ecoportal::API::V2::Page::Section] section referenced by this binding.
42
+ def section
43
+ raise "This is not a section binding, but a component binding" unless section?
44
+ ooze.sections.get_by_id(reference_id)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ require 'ecoportal/api/v2/page/force/binding'
54
+ require 'ecoportal/api/v2/page/force/bindings'
@@ -0,0 +1,126 @@
1
+ module Ecoportal
2
+ module API
3
+ class V2
4
+ class Page
5
+ class Force
6
+ class Bindings < Common::Content::CollectionModel
7
+ class_resolver :binding_class, "Ecoportal::API::V2::Page::Force::Binding"
8
+
9
+ self.klass = :binding_class
10
+ order_matters = true
11
+
12
+ def ooze
13
+ self._parent.ooze
14
+ end
15
+
16
+ # @param id [String] the `id` of the binding to find.
17
+ # @return [Ecoportal::API::V2::Page::Force::Binding] binding with `id`
18
+ def get_by_id(id)
19
+ self.find do |bind|
20
+ bind.id == id
21
+ end
22
+ end
23
+
24
+ # @param type [String] target `type` of binding
25
+ # 1. Ecoportal::API::V2::Page::Force::Binding::COMPONENT_TYPE
26
+ # 2. Ecoportal::API::V2::Page::Force::Binding::SECTION_TYPE
27
+ # @return [Array<Ecoportal::API::V2::Page::Force::Binding>] the bindings of type `type`
28
+ def get_by_type(type = Ecoportal::API::V2::Page::Force::Binding::COMPONENT_TYPE)
29
+ self.select do |bind|
30
+ bind.type.downcase == type.to_s.strip.downcase
31
+ end
32
+ end
33
+
34
+ # @param type [String] target `type` of binding
35
+ # 1. Ecoportal::API::V2::Page::Force::Binding::COMPONENT_TYPE
36
+ # 2. Ecoportal::API::V2::Page::Force::Binding::SECTION_TYPE
37
+ # @return [Array<Ecoportal::API::V2::Page::Force::Binding>] the bindings matching `name`
38
+ def get_by_name(name, type: nil)
39
+ pool = type ? get_by_type(type) : self
40
+ pool.select do |bind|
41
+ same_string?(bind.name, name)
42
+ end
43
+ end
44
+
45
+ # Creates a new `binding`
46
+ # @note
47
+ # - As there's no position property, it will upsert to the array of bindings
48
+ # @raise [ArgumentError] when `reference` is neither of Component or Section
49
+ # @raise [Exception] when
50
+ # 1. `reference` is a field missing in ooze.components
51
+ # 2. `reference` is a section missing in ooze.sections
52
+ # @param reference [Ecoportal::API::V2::Page::Component, Ecoportal::API::V2::Page::Section]
53
+ # The field or the section to bind.
54
+ # @yield [binding] do some stuff with binding
55
+ # @yieldparam binding [Ecoportal::API::V2::Page::Force::Binding] the created binding
56
+ # @return [Ecoportal::API::V2::Page::Force::Binding] the created binding.
57
+ def add(reference, name:, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
58
+ binding_doc = binding_class.new_doc
59
+
60
+ type = case reference
61
+ when Ecoportal::API::V2::Page::Component
62
+ unless ooze.components.include?(reference)
63
+ msg = "The field '#{reference.label}' (#{reference.id}) is not present in ooze.components.\n"
64
+ msg += "Review your script (i.e. @var where you store previous ooze runs)."
65
+ raise msg
66
+ end
67
+ Ecoportal::API::V2::Page::Force::Binding::COMPONENT_TYPE
68
+ when Ecoportal::API::V2::Page::Section
69
+ unless ooze.sections.include?(reference)
70
+ msg = "The section '#{reference.heading}' (#{reference.id}) is not present in ooze.sections.\n"
71
+ msg += "Review your script (i.e. @var where you store previous ooze runs)."
72
+ raise msg
73
+ end
74
+ Ecoportal::API::V2::Page::Force::Binding::SECTION_TYPE
75
+ else
76
+ msg = "You can only create bindings with Component and Section. Given: #{reference.class}"
77
+ raise ArgumentError.new(msg)
78
+ end
79
+
80
+ position = scope_position(pos: pos, before: before, after: after)
81
+ upsert!(binding_doc, pos: position) do |bind|
82
+ bind.name = name
83
+ bind.reference_id = reference.id
84
+ bind.type = type
85
+ yield(bind) if block_given?
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def scope_position(pos: NOT_USED, before: NOT_USED, after: NOT_USED)
92
+ case
93
+ when used_param?(pos)
94
+ if pos = to_binding(pos)
95
+ _doc_pos(pos) - 1
96
+ end
97
+ when used_param?(before)
98
+ if before = to_binding(before)
99
+ _doc_pos(before) - 1
100
+ end
101
+ when used_param?(after)
102
+ if after = to_binding(after)
103
+ _doc_pos(after) - 1
104
+ end
105
+ end.yield_self do |position|
106
+ position ||= self.count
107
+ end
108
+ end
109
+
110
+ def to_binding(value)
111
+ case value
112
+ when Ecoportal::API::V2::Page::Force::Binding
113
+ value
114
+ when Numeric
115
+ self[value]
116
+ else
117
+ get_by_name(value).first
118
+ end
119
+ end
120
+
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,34 @@
1
+ module Ecoportal
2
+ module API
3
+ class V2
4
+ class Page
5
+ class Force < Common::Content::DoubleModel
6
+ INITIAL_WEIGHT = 9999
7
+
8
+ class << self
9
+ def new_doc
10
+ {
11
+ "id" => new_uuid,
12
+ "weight" => INITIAL_WEIGHT
13
+ }
14
+ end
15
+ end
16
+
17
+ class_resolver :bindings_class, "Ecoportal::API::V2::Page::Force::Bindings"
18
+
19
+ passkey :id
20
+ passforced :patch_ver, default: 1
21
+ passthrough :name, :weight, :script
22
+ embeds_many :bindings, enum_class: :bindings_class
23
+
24
+ def ooze
25
+ self._parent.ooze
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ require 'ecoportal/api/v2/page/force/binding'
34
+ require 'ecoportal/api/v2/page/force/bindings'
@@ -0,0 +1,103 @@
1
+ module Ecoportal
2
+ module API
3
+ class V2
4
+ class Page
5
+ class Forces < Common::Content::CollectionModel
6
+ class_resolver :force_class, "Ecoportal::API::V2::Page::Force"
7
+
8
+ self.klass = :force_class
9
+ order_matters = true
10
+
11
+ def ooze
12
+ self._parent.ooze
13
+ end
14
+
15
+ # @return [Ecoportal::API::V2::Page::Force]
16
+ def get_by_id(id)
17
+ self.find do |force|
18
+ force.id == id
19
+ end
20
+ end
21
+
22
+ # @return [Array<Ecoportal::API::V2::Page::Force>]
23
+ def get_by_name(name)
24
+ self.select do |force|
25
+ same_string?(force.name, name)
26
+ end
27
+ end
28
+
29
+ # Creates a new `force`
30
+ # @note
31
+ # - It won't fix weights unless all the forces of the ooze are present
32
+ # - This means that it doesn't fix forces weights on stages,
33
+ # as shared forces could change order in other stages
34
+ # @yield [force] to do some stuff with the `force`
35
+ # @yieldparam force [Ecoportal::API::V2::Page::Force] the created force
36
+ # @return [Ecoportal::API::V2::Page::Force]
37
+ def add(name, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
38
+ force_doc = force_class.new_doc
39
+ upsert!(force_doc) do |force| #, pos: pos, before: before, after: after) do |section|
40
+ force.name = name
41
+ if weight = scope_weight(force, pos: pos, before: before, after: after)
42
+ force.weight = weight
43
+ end
44
+ fix_weights!
45
+ yield(force) if block_given?
46
+ end
47
+ end
48
+
49
+ # Gets the forces ordered by `weight` (as they appear in the page)
50
+ # @return [Array<Ecoportal::API::V2::Page::Force>] forces ordered by `weight`
51
+ def ordered
52
+ self.sort_by.with_index do |force, index|
53
+ [force.weight, index]
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def fix_weights!
60
+ unless self._parent.is_a?(Ecoportal::API::V2::Pages::PageStage)
61
+ ordered.each_with_index do |force, index|
62
+ force.weight = index
63
+ end
64
+ end
65
+ end
66
+
67
+ def scope_weight(force, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
68
+ case
69
+ when used_param?(pos)
70
+ if pos = to_force(pos)
71
+ pos.weight - 1
72
+ end
73
+ when used_param?(before)
74
+ if before = to_force(before)
75
+ before.weight - 1
76
+ end
77
+ when used_param?(after)
78
+ if after = to_force(after)
79
+ after.weight
80
+ end
81
+ end.yield_self do |weight|
82
+ weight = ordered.reject do |frc|
83
+ frc.id == force.id
84
+ end.last&.weight
85
+ weight ||= force_class.const_get(:INITIAL_WEIGHT)
86
+ end
87
+ end
88
+
89
+ def to_force(value)
90
+ case value
91
+ when Ecoportal::API::V2::Page::Force
92
+ value
93
+ when Numeric
94
+ ordered[value]
95
+ else
96
+ get_by_name(value).first
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -57,9 +57,7 @@ module Ecoportal
57
57
  end.yield_self do |unused|
58
58
  raise "Unknown configuaration options #{unused}" unless unused.empty?
59
59
  end
60
-
61
60
  end
62
-
63
61
  end
64
62
  end
65
63
  end
@@ -34,19 +34,48 @@ module Ecoportal
34
34
  passarray :component_ids, :left_component_ids, :right_component_ids
35
35
  passboolean :minimized
36
36
 
37
+ def ooze
38
+ self._parent.ooze
39
+ end
40
+
41
+ # @return [Array<Ecoportal::API::V2::Page::Stage>] the stage(s) this section belongs to.
42
+ def stages
43
+ ooze.stages.select {|stg| stg.has_section?(self)}
44
+ end
45
+
46
+ # @return [Boolean] whether or not the section appears in an ooze instance
47
+ def attached?
48
+ !ooze.stages? || stages.any?
49
+ end
50
+
51
+ # @return [Boolean] whether or not this section is a split section
37
52
  def split?
38
53
  doc && doc["type"] == "split"
39
54
  end
40
55
 
56
+ # @return [Array<String>] all the `ids` of the components/fields in this section
41
57
  def all_component_ids
42
58
  return component_ids.to_a unless split?
43
59
  left_component_ids.to_a | right_component_ids.to_a
44
60
  end
45
61
 
46
- def component?(id)
47
- all_component_ids.include?(id)
62
+ # @raise [ArgumentError] if `com_or_id` is not of any of the allowed types
63
+ # @param com_or_id [Ecoportal::API::V2::Page::Component, String] Component or `id` thereof
64
+ # @return [Boolean] whether or not a component/field belongs to this section.
65
+ def component?(com_or_id)
66
+ case
67
+ when Ecoportal::API::V2::Page::Component
68
+ component?(com_or_id.id)
69
+ when String
70
+ all_component_ids.include?(id)
71
+ else
72
+ msg = "Missuse: com_or_id should be a Component or a String. Given: #{com_or_id.class}"
73
+ raise ArgumentError.new(msg)
74
+ end
48
75
  end
49
76
 
77
+ # It looks up the components that belong to this section.
78
+ # @return [Array<Ecoportal::API::V2::Page::Component>]
50
79
  def components(side: nil)
51
80
  case side
52
81
  when :right
@@ -60,11 +89,17 @@ module Ecoportal
60
89
  end
61
90
  end
62
91
 
92
+ # It looks up the components that belong to the `left` side of this section.
93
+ # @raise [Exception] if this is not a `split` section
94
+ # @return [Array<Ecoportal::API::V2::Page::Component>]
63
95
  def components_left
64
96
  raise "You are trying to retrieve side components in a Split Section" unless split?
65
97
  components_by_id(*left_component_ids)
66
98
  end
67
99
 
100
+ # It looks up the components that belong to the `right` side of this section.
101
+ # @raise [Exception] if this is not a `split` section
102
+ # @return [Array<Ecoportal::API::V2::Page::Component>]
68
103
  def components_right
69
104
  raise "You are trying to retrieve side components in a Split Section" unless split?
70
105
  components_by_id(*right_component_ids)
@@ -77,9 +112,20 @@ module Ecoportal
77
112
  # - If `after` is specified, it searches field
78
113
  # - On the specific `side`, if specified (and split section)
79
114
  # - And adds the `field` after it, when found, or at the end if `after` is not found
115
+ # @raise [ArgumentError] if `field` is not a Component
116
+ # @raise [Exception] if the field does not belong to ooze.components
117
+ # @raise [Exception] if the field belongs to another section
80
118
  # @param field [Ecoportal::API::V2::Page::Component] the field to be added.
81
- def add_component(field, after: nil, side: nil)
82
- raise "field should be a Ecoportal::API::V2::Page::Component. Given: #{field.class}" unless field.is_a?(Ecoportal::API::V2::Page::Component)
119
+ def add_component(field, before: nil, after: nil, side: nil)
120
+ unless field.is_a?(Ecoportal::API::V2::Page::Component)
121
+ msg = "Expected Ecoportal::API::V2::Page::Component. Given: #{field.class}"
122
+ raise ArgumentError.new(msg)
123
+ end
124
+ unless ooze.components.include?(field)
125
+ msg = "The field '#{field.label}' (#{field.id}) is not present in ooze.components.\n"
126
+ msg += "Review your script (i.e. @var where you store previous ooze runs)."
127
+ raise msg
128
+ end
83
129
  # IMPORTANT NOTE: The code below creates objects, because field.section does a search on section.component_ids
84
130
  if field.section == self
85
131
  puts "Field with id '#{field.id}' already belongs to this section"
@@ -88,24 +134,19 @@ module Ecoportal
88
134
  raise "Field with id '#{field.id}' belongs to section '#{sec.heading || "Unnamed"}' (id: '#{sec.id}')"
89
135
  end
90
136
 
137
+ if before
138
+ before_fld = to_component(before, side: side)
139
+ elsif after
140
+ after_fld = to_component(afterm, side: side)
141
+ end
142
+
91
143
  if split?
92
144
  ids_ary = side == :right ? right_component_ids : left_component_ids
93
- fields = components(side: side || :left)
94
145
  else
95
146
  ids_ary = component_ids
96
- fields = components
97
- end
98
-
99
- if after
100
- after_fld = fields.find do |fld|
101
- found = nil
102
- found ||= !!after if after.is_a?(Ecoportal::API::V2::Page::Component)
103
- found ||= fld.id == after
104
- found ||= same_string?(fld.label, after)
105
- end
106
147
  end
107
148
 
108
- ids_ary.insert_one(field.id, after: after_fld&.id)
149
+ ids_ary.insert_one(field.id, before: before_fld&.id, after: after_fld&.id)
109
150
  self
110
151
  end
111
152
 
@@ -118,6 +159,20 @@ module Ecoportal
118
159
  end
119
160
  end
120
161
 
162
+ def to_component(value, side: nil)
163
+ if split?
164
+ fields = components(side: side || :left)
165
+ else
166
+ fields = components
167
+ end
168
+
169
+ fields.find do |fld|
170
+ found = nil
171
+ found ||= !!value if value.is_a?(Ecoportal::API::V2::Page::Component)
172
+ found ||= fld.id == value
173
+ found ||= same_string?(fld.label, value)
174
+ end
175
+ end
121
176
  end
122
177
  end
123
178
  end
@@ -7,6 +7,10 @@ module Ecoportal
7
7
 
8
8
  self.klass = :section_class
9
9
 
10
+ def ooze
11
+ self._parent.ooze
12
+ end
13
+
10
14
  # Creates a new `section`
11
15
  # @note
12
16
  # - It won't fix weights unless all the sections of the ooze are present
@@ -24,12 +28,21 @@ module Ecoportal
24
28
  end
25
29
  end
26
30
 
31
+ # @return [Ecoportal::API::V2::Page::Section]
32
+ def get_by_id(id)
33
+ self.find do |sec|
34
+ sec.id == id
35
+ end
36
+ end
37
+
38
+ # @return [Array<Ecoportal::API::V2::Page::Section>]
27
39
  def get_by_type(type)
28
40
  ordered.select do |sec|
29
41
  sec.type == type
30
42
  end
31
43
  end
32
44
 
45
+ # @return [Array<Ecoportal::API::V2::Page::Section>]
33
46
  def get_by_heading(heading)
34
47
  ordered.select do |sec|
35
48
  value = heading == :unnamed ? nil : heading
@@ -38,6 +51,7 @@ module Ecoportal
38
51
  end
39
52
 
40
53
  # Gets all the sections between `sec1` and `sec2`
54
+ # @return [Array<Ecoportal::API::V2::Page::Section>]
41
55
  def between(sec1, sec2, included: false)
42
56
  sorted_secs = ordered
43
57
  pos1 = (sec1 = to_section(sec1)) && sorted_secs.index(sec1)
@@ -53,12 +67,18 @@ module Ecoportal
53
67
  end
54
68
 
55
69
  # Gets the sections ordered by `weight` (as they appear in the page)
70
+ # @return [Array<Ecoportal::API::V2::Page::Section>] section sorted by `weight`
56
71
  def ordered
57
72
  self.sort_by.with_index do |section, index|
58
73
  [section.weight, index]
59
74
  end
60
75
  end
61
76
 
77
+ # @return [Array<Ecoportal::API::V2::Page::Section>] sections not attached to any stage
78
+ def unattached
79
+ select {|sec| !sec.attached?}
80
+ end
81
+
62
82
  private
63
83
 
64
84
  def scope_weight(section, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
@@ -108,7 +128,6 @@ module Ecoportal
108
128
  return if pos < 0
109
129
  secs[pos]
110
130
  end
111
-
112
131
  end
113
132
  end
114
133
  end
@@ -17,6 +17,13 @@ module Ecoportal
17
17
  embeds_one :creator_flags, klass: "Ecoportal::API::V2::Page::PermissionFlags"
18
18
  passthrough :can
19
19
 
20
+ def ooze
21
+ self._parent.ooze
22
+ end
23
+
24
+ # @yield [section] do some stuff with section.
25
+ # @yieldparam section [Ecoportal::API::V2::Page::Section] one of the sections of this stage
26
+ # @return [Array<Ecoportal::API::V2::Page::Section>] sections attached to this stage.
20
27
  def sections
21
28
  sec_ids = section_ids.to_a
22
29
  root.sections.values_at(*sec_ids).select.with_index do |sec, i|
@@ -25,11 +32,40 @@ module Ecoportal
25
32
  end.sort_by.with_index {|sec, index| [sec.weight, index]}
26
33
  end
27
34
 
35
+ # Check if a section belongs to a stage
36
+ # @raise [ArgumentError] if none of the valid types of `sec_or_id` are used
37
+ # @param sec_or_id [String, Ecoportal::API::V2::Page::Section]
38
+ # @return [Boolean] whether or not the section belongs to the stage
39
+ def section?(sec_or_id)
40
+ case sec_or_id
41
+ when Ecoportal::API::V2::Page::Section
42
+ section?(sec_or_id.id)
43
+ when String
44
+ self.section_ids.include?(id)
45
+ else
46
+ raise ArgumentError.new("sec_or_id must be either a Section or a String. Given: #{sec_or_id.class}")
47
+ end
48
+ end
49
+
50
+ # Adds one or more sections to this stage
51
+ # @raise [ArgumentError] if any of the elements is not a Section
52
+ # @raise [Exception] if any of the sections does not belong to ooze.sections
53
+ # @param secs [Array<Ecoportal::API::V2::Page::Section>] sections to be added to this stage.
28
54
  def add_section(*secs)
29
- secs.each {|sec| section_ids.insert_one(sec.id)}
55
+ secs.each do |sec|
56
+ unless sec.is_a?(Ecoportal::API::V2::Page::Section)
57
+ msg = "Expected Ecoportal::API::V2::Page::Section. Given: #{sec.class}"
58
+ raise ArgumentError.new(msg)
59
+ end
60
+ unless ooze.sections.include?(sec)
61
+ msg = "The section '#{sec.heading}' (#{sec.id}) is not present in ooze.sections.\n"
62
+ msg += "Review your script (i.e. @var where you store previous ooze runs)."
63
+ raise msg
64
+ end
65
+ section_ids.insert_one(sec.id)
66
+ end
30
67
  self
31
68
  end
32
-
33
69
  end
34
70
  end
35
71
  end
@@ -7,6 +7,12 @@ module Ecoportal
7
7
 
8
8
  self.klass = :stage_class
9
9
 
10
+ def ooze
11
+ self._parent.ooze
12
+ end
13
+
14
+ # @param name [String, Regexp] the `name` of the stage to find.
15
+ # @return [Ecoportal::API::V2::Page::Stage, nil]
10
16
  def get_by_name(name)
11
17
  self.find do |stage|
12
18
  same_string?(stage.name, name)
@@ -10,7 +10,7 @@ module Ecoportal
10
10
  "permits", "mould_counter", "mould",
11
11
  "state", "task_priority",
12
12
  "votes_enabled", "upvotes", "downvotes",
13
- "force_errors", "subtags"
13
+ "forces", "force_errors", "subtags"
14
14
  ]
15
15
 
16
16
  passkey :id
@@ -24,15 +24,21 @@ module Ecoportal
24
24
  class_resolver :components_class, "Ecoportal::API::V2::Page::Components"
25
25
  class_resolver :sections_class, "Ecoportal::API::V2::Page::Sections"
26
26
  class_resolver :stages_class, "Ecoportal::API::V2::Page::Stages"
27
+ class_resolver :forces_class, "Ecoportal::API::V2::Page::Forces"
27
28
 
28
29
  embeds_many :components, enum_class: :components_class
29
30
  embeds_many :sections, enum_class: :sections_class
30
31
  embeds_many :stages, enum_class: :stages_class
32
+ embeds_many :forces, enum_class: :forces_class
31
33
 
32
34
  def initialize(doc = [], parent: self, key: nil)
33
35
  super(_doc_bug_fix(doc), parent: parent, key: key)
34
36
  end
35
37
 
38
+ def ooze
39
+ self
40
+ end
41
+
36
42
  def as_update
37
43
  super.tap do |hash|
38
44
  unless !hash
@@ -90,3 +96,5 @@ require 'ecoportal/api/v2/page/section'
90
96
  require 'ecoportal/api/v2/page/sections'
91
97
  require 'ecoportal/api/v2/page/stage'
92
98
  require 'ecoportal/api/v2/page/stages'
99
+ require 'ecoportal/api/v2/page/force'
100
+ require 'ecoportal/api/v2/page/forces'
@@ -10,7 +10,7 @@ module Ecoportal
10
10
  embeds_many :permits, klass: "Ecoportal::API::V2::Page::Permit"
11
11
  passarray :force_errors, :subtags, order_matters: false
12
12
 
13
- # `id` of the stage we got the data of.
13
+ # @return [String] `id` of the stage we got the data of.
14
14
  def current_stage_id
15
15
  doc.dig("active_stage", "id") || doc["current_stage_id"]
16
16
  end
@@ -22,6 +22,44 @@ module Ecoportal
22
22
  end
23
23
  end
24
24
 
25
+ # @raise [Exception] if for this page instance, there are any of:
26
+ # 1. orphaned components (fields not belonging to any section)
27
+ # 2. components multi-section (fields belonging to more than one section)
28
+ # 3. orphaned sections (sections not belonging to any stage)
29
+ def as_update(*args, **kargs)
30
+ validate.tap do |validation|
31
+ raise validation if validation.is_a?(String)
32
+ end
33
+ super(*args, **kargs)
34
+ end
35
+
36
+ private
37
+
38
+ def validate
39
+ msg = ""
40
+ if (orphans = components.unattached).length > 0
41
+ msg += "There are fields not attached to any sections:"
42
+ msg += orphans.map do |fld|
43
+ fld.label
44
+ end.join("\n • ") + "\n"
45
+ end
46
+
47
+ if (multi = components.multi_section).length > 0
48
+ msg += "There are fields attached to more than one section:"
49
+ msg += orphans.map do |fld|
50
+ fld.label
51
+ end.join("\n • ") + "\n"
52
+ end
53
+
54
+ if (orphans = sections.unattached).length > 0
55
+ msg += "There are sections not attached to any stage:"
56
+ msg += orphans.map do |sec|
57
+ "'#{sec.header}' (#{sec.id})"
58
+ end.join("\n • ") + "\n"
59
+ end
60
+ msg.empty?? true : msg
61
+ end
62
+
25
63
  end
26
64
  end
27
65
  end
@@ -1,5 +1,5 @@
1
1
  module Ecoportal
2
2
  module API
3
- GEM2_VERSION = "0.8.13"
3
+ GEM2_VERSION = "0.8.14"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ecoportal-api-v2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.13
4
+ version: 0.8.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oscar Segura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-02 00:00:00.000000000 Z
11
+ date: 2021-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -218,6 +218,10 @@ files:
218
218
  - lib/ecoportal/api/v2/page/component/signature_field.rb
219
219
  - lib/ecoportal/api/v2/page/component/tag_field.rb
220
220
  - lib/ecoportal/api/v2/page/components.rb
221
+ - lib/ecoportal/api/v2/page/force.rb
222
+ - lib/ecoportal/api/v2/page/force/binding.rb
223
+ - lib/ecoportal/api/v2/page/force/bindings.rb
224
+ - lib/ecoportal/api/v2/page/forces.rb
221
225
  - lib/ecoportal/api/v2/page/permission_flags.rb
222
226
  - lib/ecoportal/api/v2/page/permit.rb
223
227
  - lib/ecoportal/api/v2/page/section.rb