ecoportal-api-v2 0.8.4

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.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +55 -0
  5. data/.travis.yml +5 -0
  6. data/.yardopts +10 -0
  7. data/CHANGELOG.md +171 -0
  8. data/Gemfile +6 -0
  9. data/LICENSE +21 -0
  10. data/README.md +22 -0
  11. data/Rakefile +27 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/ecoportal-api-v2.gemspec +34 -0
  15. data/lib/ecoportal/api-v2.rb +10 -0
  16. data/lib/ecoportal/api/common.rb +18 -0
  17. data/lib/ecoportal/api/common/content.rb +18 -0
  18. data/lib/ecoportal/api/common/content/array_model.rb +286 -0
  19. data/lib/ecoportal/api/common/content/class_helpers.rb +146 -0
  20. data/lib/ecoportal/api/common/content/client.rb +40 -0
  21. data/lib/ecoportal/api/common/content/collection_model.rb +279 -0
  22. data/lib/ecoportal/api/common/content/doc_helpers.rb +67 -0
  23. data/lib/ecoportal/api/common/content/double_model.rb +356 -0
  24. data/lib/ecoportal/api/common/content/hash_diff_patch.rb +183 -0
  25. data/lib/ecoportal/api/common/content/string_digest.rb +27 -0
  26. data/lib/ecoportal/api/common/content/wrapped_response.rb +42 -0
  27. data/lib/ecoportal/api/v2.rb +82 -0
  28. data/lib/ecoportal/api/v2/page.rb +42 -0
  29. data/lib/ecoportal/api/v2/page/component.rb +133 -0
  30. data/lib/ecoportal/api/v2/page/component/action.rb +28 -0
  31. data/lib/ecoportal/api/v2/page/component/action_field.rb +54 -0
  32. data/lib/ecoportal/api/v2/page/component/chart_field.rb +54 -0
  33. data/lib/ecoportal/api/v2/page/component/chart_field/frequency.rb +29 -0
  34. data/lib/ecoportal/api/v2/page/component/chart_field/heatmap.rb +27 -0
  35. data/lib/ecoportal/api/v2/page/component/chart_field/indicator.rb +26 -0
  36. data/lib/ecoportal/api/v2/page/component/chart_field/multiseries.rb +31 -0
  37. data/lib/ecoportal/api/v2/page/component/chart_field/sankey.rb +27 -0
  38. data/lib/ecoportal/api/v2/page/component/chart_field/serie.rb +26 -0
  39. data/lib/ecoportal/api/v2/page/component/chart_field/series_config.rb +23 -0
  40. data/lib/ecoportal/api/v2/page/component/chart_fr_field.rb +32 -0
  41. data/lib/ecoportal/api/v2/page/component/checklist_field.rb +49 -0
  42. data/lib/ecoportal/api/v2/page/component/checklist_item.rb +25 -0
  43. data/lib/ecoportal/api/v2/page/component/date_field.rb +34 -0
  44. data/lib/ecoportal/api/v2/page/component/file.rb +16 -0
  45. data/lib/ecoportal/api/v2/page/component/files_field.rb +13 -0
  46. data/lib/ecoportal/api/v2/page/component/gauge_field.rb +36 -0
  47. data/lib/ecoportal/api/v2/page/component/gauge_stop.rb +88 -0
  48. data/lib/ecoportal/api/v2/page/component/geo_field.rb +13 -0
  49. data/lib/ecoportal/api/v2/page/component/image.rb +16 -0
  50. data/lib/ecoportal/api/v2/page/component/images_field.rb +23 -0
  51. data/lib/ecoportal/api/v2/page/component/law_field.rb +12 -0
  52. data/lib/ecoportal/api/v2/page/component/number_field.rb +12 -0
  53. data/lib/ecoportal/api/v2/page/component/people_field.rb +25 -0
  54. data/lib/ecoportal/api/v2/page/component/plain_text_field.rb +15 -0
  55. data/lib/ecoportal/api/v2/page/component/reference_field.rb +16 -0
  56. data/lib/ecoportal/api/v2/page/component/rich_text_field.rb +13 -0
  57. data/lib/ecoportal/api/v2/page/component/selection_field.rb +78 -0
  58. data/lib/ecoportal/api/v2/page/component/selection_option.rb +25 -0
  59. data/lib/ecoportal/api/v2/page/component/signature_field.rb +25 -0
  60. data/lib/ecoportal/api/v2/page/component/tag_field.rb +14 -0
  61. data/lib/ecoportal/api/v2/page/components.rb +42 -0
  62. data/lib/ecoportal/api/v2/page/section.rb +59 -0
  63. data/lib/ecoportal/api/v2/page/sections.rb +47 -0
  64. data/lib/ecoportal/api/v2/page/stage.rb +29 -0
  65. data/lib/ecoportal/api/v2/page/stages.rb +26 -0
  66. data/lib/ecoportal/api/v2/pages.rb +92 -0
  67. data/lib/ecoportal/api/v2/pages/page_stage.rb +16 -0
  68. data/lib/ecoportal/api/v2/pages/stages.rb +55 -0
  69. data/lib/ecoportal/api/v2/people.rb +31 -0
  70. data/lib/ecoportal/api/v2/registers.rb +89 -0
  71. data/lib/ecoportal/api/v2/registers/page_result.rb +21 -0
  72. data/lib/ecoportal/api/v2/registers/register.rb +37 -0
  73. data/lib/ecoportal/api/v2/registers/stage_result.rb +14 -0
  74. data/lib/ecoportal/api/v2/registers/stages_result.rb +13 -0
  75. data/lib/ecoportal/api/v2/registers/template.rb +12 -0
  76. data/lib/ecoportal/api/v2/version.rb +7 -0
  77. metadata +254 -0
@@ -0,0 +1,18 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module Content
5
+ end
6
+ end
7
+ end
8
+ end
9
+
10
+ require 'ecoportal/api/common/content/class_helpers'
11
+ require 'ecoportal/api/common/content/string_digest'
12
+ require 'ecoportal/api/common/content/double_model'
13
+ require 'ecoportal/api/common/content/array_model'
14
+ require 'ecoportal/api/common/content/collection_model'
15
+ require 'ecoportal/api/common/content/doc_helpers'
16
+ require 'ecoportal/api/common/content/hash_diff_patch'
17
+ require 'ecoportal/api/common/content/client'
18
+ require 'ecoportal/api/common/content/wrapped_response'
@@ -0,0 +1,286 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module Content
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`)
7
+ class ArrayModel < Content::DoubleModel
8
+ class TypeMismatchedComparison < Exception
9
+ def initialize (this: nil, that: msg = "Trying to compare objects with different behavior.")
10
+ if this
11
+ msg += " From object with 'order_matters: #{this.order_matters?}' and 'uniq: #{this.uniq?}'."
12
+ end
13
+ if that
14
+ msg += " To object where 'order_matters: #{that.order_matters?}' and 'uniq: #{that.uniq?}'."
15
+ end
16
+ super(msg)
17
+ end
18
+ end
19
+
20
+ include Enumerable
21
+
22
+ class << self
23
+ attr_accessor :order_matters, :uniq
24
+
25
+ # @param a [ArrayModel]
26
+ # @param b [ArrayModel]
27
+ # @return [Boolean] `true` if both elements have same behaviour
28
+ def same_type?(a, b)
29
+ raise "To use this comparison both objects should be `ArrayModel`" unless a.is_a?(ArrayModel) && b.is_a?(ArrayModel)
30
+ (a.order_matters? == b.order_matters?) && (a.uniq? == b.uniq?)
31
+ end
32
+
33
+ end
34
+
35
+ def initialize(doc = {}, parent: self, key: nil)
36
+ super(doc, parent: parent, key: key)
37
+ end
38
+
39
+ def order_matters?; self.class.order_matters; end
40
+ def uniq?; self.class.uniq; end
41
+
42
+ def length; count; end
43
+ def empty?; count == 0; end
44
+ def present?; count > 0; end
45
+
46
+ def each(&block)
47
+ return to_enum(:each) unless block
48
+ _items.each(&block)
49
+ end
50
+
51
+ # @return [Array] the array element represented by this object
52
+ def _items
53
+ replace_doc([]) unless doc.is_a?(Array)
54
+ doc.tap {|d| d.uniq! if uniq?}
55
+ end
56
+
57
+ # @see #_items
58
+ # @return [Array] a **copy** of the `Array` elements
59
+ def to_a
60
+ _items.slice(0..-1)
61
+ end
62
+
63
+ # @param value [Object, Array<Object>, ArrayModel] the value(s) of the new object
64
+ # @return [ArrayModel] a new object with the current class
65
+ def new_from(value)
66
+ self.class.new(into_a(value))
67
+ end
68
+
69
+ # @return [ArrayModel] a copy of the current object
70
+ def dup
71
+ new_from(to_a)
72
+ end
73
+
74
+ # @return [Integer] the position of the element in the `Array`
75
+ def index(value)
76
+ _items.index(value)
77
+ end
78
+
79
+ # Retrieves the element of a certain position
80
+ # @param pos [Integer] the position of the element
81
+ # @return [Date, String, Number]
82
+ def [](pos)
83
+ _items[pos]
84
+ end
85
+
86
+ # Sets the element of a certain position
87
+ # @param pos [Integer] the position of the element
88
+ # @param value [String, Date, Number] the element
89
+ # @return [Date, String, Number]
90
+ def []=(post, value)
91
+ _items[pos] = value
92
+ on_change
93
+ self[pos]
94
+ end
95
+
96
+ # Compares with an `Array` or another `ArrayModel`
97
+ # @param a [ArrayModel, Array]
98
+ def ==(a)
99
+ return true if self.equal?(a)
100
+ return false unless (a.class == self.class) || a.is_a?(Array)
101
+ case a
102
+ when Array
103
+ self == new_from(a)
104
+ when ArrayModel
105
+ return true if
106
+ raise TypeMismatchedComparison.new(this: self, that: a) unless self.class.same_type?(self, a)
107
+
108
+ if self.order_matters?
109
+ _items == a.to_a
110
+ else
111
+ (_items - a.to_a).empty? && (a.to_a - _items).empty?
112
+ end
113
+ end
114
+ end
115
+
116
+ # @return [Boolean] `true` if `value` is present, `false` otherwise
117
+ def include?(value)
118
+ _items.include?(value)
119
+ end
120
+
121
+ def include_any?(*value)
122
+ value.any? {|v| _items.include?(v)}
123
+ end
124
+
125
+ def include_all?(*value)
126
+ value.all? {|v| _items.include?(v)}
127
+ end
128
+
129
+ # Adds an element to the subjacent `Array`
130
+ # @note if the class variable `uniq` is `true`, it skips duplicates
131
+ def <<(value)
132
+ _items.concat(into_a(value)).tap do |doc|
133
+ doc.uniq! if uniq?
134
+ end
135
+ on_change
136
+ self
137
+ end
138
+
139
+ # @see #<<
140
+ def push!(value)
141
+ self << value
142
+ end
143
+
144
+ # @see #<<
145
+ # @note same as #push! but for multiple elements
146
+ def concat!(values)
147
+ self << values
148
+ end
149
+
150
+ # Resets the `Array` by keeping its reference and adds the value(s)
151
+ # @param value [Object, Array<Object>, ArrayModel] the value(s) to be added
152
+ # @param values [Array]
153
+ def <(values)
154
+ _items.clear
155
+ self << values
156
+ end
157
+
158
+ # Clears the `Array` keeping its reference
159
+ def clear!
160
+ _items.clear
161
+ on_change
162
+ self
163
+ end
164
+
165
+ # Concat to new
166
+ def +(value)
167
+ new_from(self.to_a + into_a(value))
168
+ end
169
+
170
+ # Join
171
+ # @param value [Object, Array<Object>, ArrayModel] the value(s) to be joined
172
+ # @return [ArrayModel] a new object instance with the intersection done
173
+ def |(value)
174
+ new = new_from(value) - self
175
+ new_from(to_a + new.to_a)
176
+ end
177
+
178
+ # Intersect
179
+ # @param value [Object, Array<Object>, ArrayModel] the value(s) to be deleted
180
+ # @return [ArrayModel] a new object instance with the intersection done
181
+ def &(value)
182
+ self.dup.tap do |out|
183
+ self.dup.tap do |delta|
184
+ delta.delete!(*into_a(value))
185
+ out.delete!(*into_a(delta))
186
+ end
187
+ end
188
+ end
189
+
190
+ # Subtract
191
+ # @param value [Object, Array<Object>, ArrayModel] the value(s) to be deleted
192
+ # @return [ArrayModel] a **copy** of the object with the elements subtracted
193
+ def -(value)
194
+ self.dup.tap do |copy|
195
+ copy.delete!(*into_a(value))
196
+ end
197
+ end
198
+
199
+ # Deletes `values` from the `Array`
200
+ def delete!(*values)
201
+ values.map do |v|
202
+ deletion!(v)
203
+ end.tap do |r|
204
+ on_change
205
+ end
206
+ end
207
+
208
+ # Swaps two values' positions
209
+ # @note this will work with first instances when **not** `uniq?`
210
+ # @param val1 [Object] the first value to swap
211
+ # @param val2 [Object] the second value to swap
212
+ # @return [Integer] the new of `value1`, `nil` if it wasn't moved
213
+ def swap(value1, value2)
214
+ index(value2).tap do |dest|
215
+ if dest && pos = index(value1)
216
+ _items[dest] = value1
217
+ _items[pos] = value2
218
+ end
219
+ end
220
+ end
221
+
222
+ def insert_one(value, pos: NOT_USED, before: NOT_USED, after: NOT_USED)
223
+ i = index(value)
224
+ return i if (i && uniq?)
225
+
226
+ pos = case
227
+ when used_param?(pos)
228
+ pos
229
+ when used_param?(before)
230
+ index(before)
231
+ when used_param?(after)
232
+ if i = index(after)
233
+ i + 1
234
+ end
235
+ else
236
+ length
237
+ end
238
+
239
+ pos.tap do |i|
240
+ unless !i
241
+ _items.insert(pos, value)
242
+ on_change
243
+ end
244
+ end
245
+ end
246
+
247
+ # 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
256
+ end
257
+
258
+ protected
259
+
260
+ def on_change
261
+ # to be overriden by child classes
262
+ end
263
+
264
+ private
265
+
266
+ def into_a(value)
267
+ raise "Can't convert to 'Array' a 'Hash', as is a key_value pair Enumerable" if value.is_a?(Hash)
268
+ return value.to_a.slice(0..-1) if value.is_a?(Enumerable)
269
+ [].push(value).compact
270
+ end
271
+
272
+ 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
+ end
281
+
282
+ end
283
+ end
284
+ end
285
+ end
286
+ end
@@ -0,0 +1,146 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module Content
5
+ module ClassHelpers
6
+ include Common::BaseClass
7
+ NOT_USED = "no_used!"
8
+
9
+ # Class resolver
10
+ # @note it caches the resolved `klass`es
11
+ # @raise [Exception] when could not resolve if `exception` is `true`
12
+ # @param klass [Class, String, Symbol] the class to resolve
13
+ # @param exception [Boolean] if it should raise exception when could not resolve
14
+ # @return [Class] the `Class` constant
15
+ def resolve_class(klass, exception: true)
16
+ @resolved ||= {}
17
+ @resolved[klass] ||=
18
+ case klass
19
+ when Class
20
+ klass
21
+ when String
22
+ begin
23
+ Kernel.const_get(klass)
24
+ rescue NameError => e
25
+ raise if exception
26
+ end
27
+ when Symbol
28
+ resolve_class(self.send(klass))
29
+ else
30
+ raise "Unknown class: #{klass}" if exception
31
+ end
32
+ end
33
+
34
+ # Helper to normalize `key` into a correct `ruby` **constant name**
35
+ # @param key [String, Symbol] to be normalized
36
+ # @return [String] a correct constant name
37
+ def to_constant(key)
38
+ str_name = key.to_s.strip.split(/[\-\_ ]/i).compact.map do |str|
39
+ str.slice(0).upcase + str.slice(1..-1).downcase
40
+ end.join("")
41
+ end
42
+
43
+ # Helper to create an instance variable `name`
44
+ # @param [String, Symbol] the name of the variable
45
+ # @reutrn [String] the name of the created instance variable
46
+ def instance_variable_name(name)
47
+ str = name.to_s
48
+ str = "@#{str}" unless str.start_with?("@")
49
+ str
50
+ end
51
+
52
+ # If the class for `name` exists, it returns it. Otherwise it generates it.
53
+ # @param name [String, Symbol] the name of the new class
54
+ # @param inherits [Class] the parent class to _inherit_ from
55
+ # @yield [child_class] configure the new class
56
+ # @yieldparam child_class [Class] the new class
57
+ # @return [Class] the new generated class
58
+ def new_class(name, inherits:)
59
+ name = name.to_sym.freeze
60
+ class_name = to_constant(name)
61
+ full_class_name = "#{inherits}::#{class_name}"
62
+
63
+ unless target_class = resolve_class(full_class_name, exception: false)
64
+ target_class = Class.new(inherits)
65
+ self.const_set class_name, target_class
66
+ end
67
+
68
+ target_class.tap do |klass|
69
+ yield(klass) if block_given?
70
+ end
71
+ end
72
+
73
+ # Helper to parse a value into a `Time` object.
74
+ # @raise [Exception] if `exception` is `true` and could not convert
75
+ # @param value [String, Date] the value to convert to `Time`
76
+ # @param exception [Boolean] if should raise `Exception` when could not convert
77
+ # @return
78
+ def to_time(value, exception: true)
79
+ case value
80
+ when NilClass
81
+ value
82
+ when String
83
+ begin
84
+ Time.parse(value)
85
+ rescue ArgumentArgument => e
86
+ raise if exception
87
+ nil
88
+ end
89
+ when Date
90
+ Time.parse(value.to_s)
91
+ when Time
92
+ value
93
+ else
94
+ to_time(value.to_s) if value.respond_to?(:to_s)
95
+ end
96
+ end
97
+
98
+ # Helper to determine if a paramter has been used
99
+ # @note to effectivelly use this helper, you should initialize your target
100
+ # paramters with the constant `NOT_USED`
101
+ # @param val [] the value of the paramter
102
+ # @return [Boolean] `true` if value other than `NOT_USED`, `false` otherwise
103
+ def used_param?(val)
104
+ val != NOT_USED
105
+ end
106
+
107
+ # Keeps track on class instance variables that should be inherited by child classes.
108
+ # @note
109
+ # - subclasses will inherit the value as is at that moment
110
+ # - any change afterwards will be only on the specific class (in line with class instance variables)
111
+ # - adapted from https://stackoverflow.com/a/10729812/4352306
112
+ # TODO: this separates the logic of the method to the instance var. Think if would be possible to join them somehow.
113
+ def inheritable_class_vars(*vars)
114
+ @inheritable_class_vars ||= [:inheritable_class_vars]
115
+ @inheritable_class_vars += vars
116
+ end
117
+
118
+ # Builds the attr_reader and attr_writer of `attrs` and registers the associated instance variable as inheritable.
119
+ def inheritable_attrs(*attrs)
120
+ attrs.each do |attr|
121
+ class_eval %(
122
+ class << self; attr_accessor :#{attr} end
123
+ )
124
+ end
125
+ inheritable_class_vars(*attrs)
126
+ end
127
+
128
+ # This callback method is called whenever a subclass of the current class is created.
129
+ # @note
130
+ # - values of the instance variables are copied as they are (no dups or clones)
131
+ # - the above means: avoid methods that change the state of the mutable object on it
132
+ # - mutating methods would reflect the changes on other classes as well
133
+ # - therefore, `freeze` will be called on the values that are inherited.
134
+ def inherited(subclass)
135
+ inheritable_class_vars.each do |var|
136
+ instance_var = instance_variable_name(var)
137
+ value = instance_variable_get(instance_var)
138
+ subclass.instance_variable_set(instance_var, value.freeze)
139
+ end
140
+ end
141
+
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end