ecoportal-api-v2 2.0.12 → 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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/CHANGELOG.md +29 -3
  4. data/ecoportal-api-v2.gemspec +1 -1
  5. data/lib/ecoportal/api/common/content/array_model.rb +20 -3
  6. data/lib/ecoportal/api/common/content/class_helpers.rb +4 -2
  7. data/lib/ecoportal/api/common/content/client.rb +12 -6
  8. data/lib/ecoportal/api/common/content/collection_model/doc/items.rb +33 -0
  9. data/lib/ecoportal/api/common/content/collection_model/doc/rooted_key.rb +68 -0
  10. data/lib/ecoportal/api/common/content/collection_model/doc.rb +25 -0
  11. data/lib/ecoportal/api/common/content/collection_model/doc_mutation/delete.rb +33 -0
  12. data/lib/ecoportal/api/common/content/collection_model/doc_mutation/position.rb +42 -0
  13. data/lib/ecoportal/api/common/content/collection_model/doc_mutation/upsert.rb +45 -0
  14. data/lib/ecoportal/api/common/content/collection_model/doc_mutation.rb +27 -0
  15. data/lib/ecoportal/api/common/content/collection_model/model/cache.rb +44 -0
  16. data/lib/ecoportal/api/common/content/collection_model/model/items.rb +43 -0
  17. data/lib/ecoportal/api/common/content/collection_model/model/iterable.rb +44 -0
  18. data/lib/ecoportal/api/common/content/collection_model/model/lookup.rb +45 -0
  19. data/lib/ecoportal/api/common/content/collection_model/model/numeric_key.rb +40 -0
  20. data/lib/ecoportal/api/common/content/collection_model/model/var_tracking.rb +50 -0
  21. data/lib/ecoportal/api/common/content/collection_model/model.rb +33 -0
  22. data/lib/ecoportal/api/common/content/collection_model/modifiers/items_key.rb +57 -0
  23. data/lib/ecoportal/api/common/content/collection_model/modifiers/items_klass.rb +144 -0
  24. data/lib/ecoportal/api/common/content/collection_model/modifiers/items_order.rb +38 -0
  25. data/lib/ecoportal/api/common/content/collection_model/modifiers.rb +27 -0
  26. data/lib/ecoportal/api/common/content/collection_model/mutation/clear.rb +30 -0
  27. data/lib/ecoportal/api/common/content/collection_model/mutation/delete.rb +42 -0
  28. data/lib/ecoportal/api/common/content/collection_model/mutation/upsert.rb +56 -0
  29. data/lib/ecoportal/api/common/content/collection_model/mutation.rb +27 -0
  30. data/lib/ecoportal/api/common/content/collection_model.rb +19 -351
  31. data/lib/ecoportal/api/common/content/doc_helpers.rb +18 -16
  32. data/lib/ecoportal/api/common/content/double_model/attributable/base.rb +23 -0
  33. data/lib/ecoportal/api/common/content/double_model/attributable/enforce.rb +62 -0
  34. data/lib/ecoportal/api/common/content/double_model/attributable/nesting/cascaded_callback.rb +104 -0
  35. data/lib/ecoportal/api/common/content/double_model/attributable/nesting/embeddable.rb +76 -0
  36. data/lib/ecoportal/api/common/content/double_model/attributable/nesting/keyable.rb +119 -0
  37. data/lib/ecoportal/api/common/content/double_model/attributable/nesting.rb +136 -0
  38. data/lib/ecoportal/api/common/content/double_model/attributable/passthrough.rb +70 -0
  39. data/lib/ecoportal/api/common/content/double_model/attributable/simple.rb +48 -0
  40. data/lib/ecoportal/api/common/content/double_model/attributable.rb +30 -0
  41. data/lib/ecoportal/api/common/content/double_model/base.rb +29 -0
  42. data/lib/ecoportal/api/common/content/double_model/diffable_model.rb +43 -0
  43. data/lib/ecoportal/api/common/content/double_model/double_doc/base.rb +23 -0
  44. data/lib/ecoportal/api/common/content/double_model/double_doc/linkable_doc.rb +87 -0
  45. data/lib/ecoportal/api/common/content/double_model/double_doc/replaceable_doc.rb +61 -0
  46. data/lib/ecoportal/api/common/content/double_model/double_doc/reset_consolidate.rb +54 -0
  47. data/lib/ecoportal/api/common/content/double_model/double_doc/rooted_key.rb +49 -0
  48. data/lib/ecoportal/api/common/content/double_model/double_doc.rb +31 -0
  49. data/lib/ecoportal/api/common/content/double_model/hash_helpers.rb +40 -0
  50. data/lib/ecoportal/api/common/content/double_model/modifiers/read_only_able.rb +73 -0
  51. data/lib/ecoportal/api/common/content/double_model/modifiers/rootable.rb +64 -0
  52. data/lib/ecoportal/api/common/content/double_model/modifiers.rb +24 -0
  53. data/lib/ecoportal/api/common/content/double_model/parented.rb +22 -0
  54. data/lib/ecoportal/api/common/content/double_model/var_tracking.rb +45 -0
  55. data/lib/ecoportal/api/common/content/double_model.rb +28 -486
  56. data/lib/ecoportal/api/common/content/hash_diff_patch.rb +2 -1
  57. data/lib/ecoportal/api/common/content/includer.rb +16 -0
  58. data/lib/ecoportal/api/common/content/model_helpers.rb +14 -16
  59. data/lib/ecoportal/api/common/content.rb +1 -0
  60. data/lib/ecoportal/api/v2/page/component.rb +46 -46
  61. data/lib/ecoportal/api/v2/page/components.rb +2 -2
  62. data/lib/ecoportal/api/v2/page.rb +14 -14
  63. data/lib/ecoportal/api/v2/pages/page_stage/task.rb +2 -2
  64. data/lib/ecoportal/api/v2/pages/page_stage/tasks.rb +3 -3
  65. data/lib/ecoportal/api/v2/pages/page_stage.rb +8 -8
  66. data/lib/ecoportal/api/v2/pages/stages.rb +2 -2
  67. data/lib/ecoportal/api/v2/pages.rb +15 -15
  68. data/lib/ecoportal/api/v2/registers.rb +20 -19
  69. data/lib/ecoportal/api/v2/s3/files/batch_upload.rb +1 -0
  70. data/lib/ecoportal/api/v2/s3/files/poll.rb +5 -5
  71. data/lib/ecoportal/api/v2/s3/files/poll_status.rb +3 -3
  72. data/lib/ecoportal/api/v2/s3/files.rb +6 -6
  73. data/lib/ecoportal/api/v2/s3.rb +5 -5
  74. data/lib/ecoportal/api/v2.rb +18 -8
  75. data/lib/ecoportal/api/v2_version.rb +1 -1
  76. metadata +50 -4
@@ -0,0 +1,76 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module Content
5
+ class DoubleModel
6
+ module Attributable
7
+ module Nesting
8
+ module Embeddable
9
+ class << self
10
+ include Content::Includer
11
+
12
+ def included(base)
13
+ super
14
+ base.extend Content::ClassHelpers
15
+
16
+ include_missing(base, DoubleModel::VarTracking)
17
+ include_missing(base, DoubleModel::Modifiers)
18
+ include_missing(base, Attributable::Base)
19
+
20
+ include_missing(base, Nesting::Keyable)
21
+ include_missing(base, Nesting::CascadedCallback)
22
+
23
+ base.extend ClassMethods
24
+ end
25
+ end
26
+
27
+ module ClassMethods
28
+ private
29
+
30
+ def embed(
31
+ method,
32
+ klass:,
33
+ key: method,
34
+ nullable: false,
35
+ multiple: false,
36
+ read_only: read_only?,
37
+ &embed_block
38
+ )
39
+ method = method.to_s.freeze
40
+ var = instance_variable_name(method).freeze
41
+ obj_k = key.to_s.freeze
42
+
43
+ _cascaded_attribute!(method, obj_k)
44
+
45
+ # retrieving method (getter)
46
+ define_method(method) do
47
+ # set item klass as referrer to klass (to allow resolve symbol)
48
+ embed_block&.call(self)
49
+ return instance_variable_get(var) if instance_variable_defined?(var)
50
+
51
+ doc[obj_k] ||= (multiple ? [] : {}) unless nullable
52
+ return variable_set(var, nil) if (doc_value = doc[obj_k]).nil?
53
+
54
+ embedded_class = self.class.resolve_class(klass)
55
+ items_key_setup!(embedded_class, doc_value) if multiple && read_only
56
+
57
+ embedded_class.new(
58
+ doc_value,
59
+ parent: self,
60
+ key: obj_k,
61
+ read_only: read_only? || read_only
62
+ ).tap do |collection|
63
+ variable_set(var, collection)
64
+ _cascaded_attribute!(method, obj_k)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,119 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module Content
5
+ class DoubleModel
6
+ module Attributable
7
+ module Nesting
8
+ module Keyable
9
+ class NoKeyMethod < StandardError
10
+ end
11
+
12
+ class << self
13
+ include Content::Includer
14
+
15
+ def included(base)
16
+ super
17
+ base.extend Content::ClassHelpers
18
+
19
+ include_missing(base, DoubleModel::VarTracking)
20
+
21
+ base.extend ClassMethods
22
+ base.inheritable_class_vars :key
23
+ end
24
+ end
25
+
26
+ module ClassMethods
27
+ attr_reader :key
28
+
29
+ def key?
30
+ !!key
31
+ end
32
+
33
+ # key property (and method) of this model
34
+ # @note this is auto-set when `passkey` is used
35
+ def key=(value)
36
+ @key = value.to_s.freeze
37
+ end
38
+
39
+ # This method is essential to give stability to the model
40
+ # @note `Content::CollectionModel` needs to find elements in the doc `Array`.
41
+ # The only way to do it is via the access key (i.e. `id`). However, there is
42
+ # no chance you can avoid infinite loop for `get_key` (CollectionModel)
43
+ # without setting an instance variable key at the moment of the object creation,
44
+ # when the `doc` is firstly received
45
+ # @param method [Symbol] the method that exposes the value
46
+ # as well as its `key` in the underlying `Hash` model.
47
+ def passkey(method)
48
+ method = method.to_s.freeze
49
+ var = instance_variable_name(method)
50
+ self.key = method
51
+
52
+ define_method method do
53
+ return instance_variable_get(var) if instance_variable_defined?(var)
54
+
55
+ value = send(:doc)[method]
56
+ value = yield(value) if block_given?
57
+ value
58
+ end
59
+
60
+ define_method "#{method}=" do |value|
61
+ variable_set(var, value)
62
+ value = yield(value) if block_given?
63
+ send(:doc)[method] = value
64
+ end
65
+
66
+ self
67
+ end
68
+ end
69
+
70
+ # INSTANCE METHODS
71
+
72
+ # @return [String] the `value` of the `key` method (i.e. `id` value)
73
+ def key
74
+ msg = "No key_method defined for #{self.class}"
75
+ raise NoKeyMethod, msg unless key_method?
76
+
77
+ method(key_method).call
78
+ end
79
+
80
+ # @param [String] the `value` of the `key` method (i.e. `id` value)
81
+ def key=(value)
82
+ msg = "No key_method defined for #{self.class}"
83
+ raise NoKeyMethod, msg unless key_method?
84
+
85
+ method("#{key_method}=").call(value)
86
+ end
87
+
88
+ private
89
+
90
+ def key_method?
91
+ self.class.key?
92
+ end
93
+
94
+ def key_method
95
+ self.class.key
96
+ end
97
+
98
+ # It allows to work-around missing item_key
99
+ def items_key_setup!(embedded_class, obj_doc)
100
+ # only if is going to be a collection
101
+ return unless obj_doc.is_a?(Array) && embedded_class < CollectionModel
102
+ return unless (item_class = embedded_class.klass)
103
+ # if already has key don't need to work around
104
+ return if item_class&.key
105
+
106
+ # apply work around
107
+ item_class.passkey :id
108
+ obj_doc.each_with_index do |item_doc, idx|
109
+ item_doc['id'] = idx.to_s unless item_doc.key?('id')
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,136 @@
1
+ require 'ecoportal/api/common/content/double_model/attributable/nesting/keyable'
2
+ require 'ecoportal/api/common/content/double_model/attributable/nesting/cascaded_callback'
3
+ require 'ecoportal/api/common/content/double_model/attributable/nesting/embeddable'
4
+ module Ecoportal
5
+ module API
6
+ module Common
7
+ module Content
8
+ class DoubleModel
9
+ module Attributable
10
+ module Nesting
11
+ class << self
12
+ include Content::Includer
13
+
14
+ def included(base)
15
+ super
16
+ base.extend Content::ClassHelpers
17
+
18
+ include_missing(base, DoubleModel::VarTracking)
19
+ include_missing(base, DoubleModel::Modifiers)
20
+
21
+ include_missing(base, Attributable::Base)
22
+ include_missing(base, Keyable)
23
+ include_missing(base, CascadedCallback)
24
+ include_missing(base, Embeddable)
25
+
26
+ base.extend ClassMethods
27
+ end
28
+ end
29
+
30
+ module ClassMethods
31
+ # To link as plain `Array` to a subjacent `Hash` model property
32
+ # @param methods [Array<Symbol>] the method that exposes the value
33
+ # as well as its `key` in the underlying `Hash` model.
34
+ # @param order_matters [Boolean] does the order matter
35
+ # @param uniq [Boolean] should it contain unique elements
36
+ def passarray(*methods, order_matters: true, uniq: true)
37
+ methods.each do |method|
38
+ method = method.to_s.freeze
39
+ var = instance_variable_name(method)
40
+
41
+ _cascaded_attribute!(method)
42
+
43
+ dim_class = new_class(method, inherits: ArrayModel) do |klass|
44
+ klass.order_matters = order_matters
45
+ klass.uniq = uniq
46
+ end
47
+
48
+ define_method method do
49
+ return instance_variable_get(var) if instance_variable_defined?(var)
50
+
51
+ dim_class.new(
52
+ parent: self,
53
+ key: method,
54
+ read_only: read_only?
55
+ ).tap do |new_obj|
56
+ variable_set(var, new_obj)
57
+ _cascaded_attribute!(method)
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ # Helper to embed one nested object under one property
64
+ # @param method [Symbol] the method that exposes the embeded object
65
+ # @param key [Symbol] the `key` that embeds it to the underlying `Hash` model
66
+ # @param nullable [Boolean] to specify if this object can be `nil`
67
+ # @param read_only [Boolean] if the subjacent model should be read_only
68
+ # @param klass [Class, String] the class of the embedded object
69
+ def embeds_one(method, klass:, key: method, read_only: read_only?, nullable: false)
70
+ embed(
71
+ method,
72
+ key: key,
73
+ nullable: nullable,
74
+ read_only: read_only,
75
+ multiple: false,
76
+ klass: klass
77
+ )
78
+ end
79
+
80
+ # @note
81
+ # - if you have a dedicated `Enumerable` class to manage `many`, you should use `:enum_class`
82
+ # - otherwise, just indicate the child class in `:klass` and it will auto generate the class
83
+ # @param method [Symbol] the method that exposes the embeded object
84
+ # @param key [Symbol] the `key` that embeds it to the underlying `Hash` model
85
+ # @param order_matters [Boolean] to state if the order will matter
86
+ # @param klass [Class, String] the class of the individual elements it embeds
87
+ # @param enum_class [Class, String] the class of the collection that will hold the individual elements
88
+ # @param read_only [Boolean] whether or not should try to **work around** items `klass` missing a `key`
89
+ # - If set to `true` this is meant only for read purposes (won't be able to successufully insert)
90
+ def embeds_many(
91
+ method,
92
+ key: method,
93
+ klass: nil,
94
+ enum_class: nil,
95
+ order_matters: false,
96
+ order_key: nil,
97
+ read_only: read_only?
98
+ )
99
+ if enum_class
100
+ eclass = enum_class
101
+ elsif klass
102
+ eclass = new_class("#{method}::#{klass}", inherits: CollectionModel) do |dim_class|
103
+ # NOTE: new_class may resolve the namespace of the class to an already existing class
104
+ dim_class.klass ||= klass
105
+ dim_class.order_matters = order_matters
106
+ dim_class.order_key = order_key
107
+ dim_class.read_only! if read_only
108
+ end
109
+ else
110
+ raise "You should either specify the 'klass' of the elements or the 'enum_class'"
111
+ end
112
+
113
+ embed(
114
+ method,
115
+ key: key,
116
+ multiple: true,
117
+ klass: eclass,
118
+ read_only: read_only
119
+ ) do |instance_of_called_method|
120
+ # keep reference to the original class to resolve the `klass` dependency
121
+ # See stackoverflow: https://stackoverflow.com/a/73709529/4352306
122
+ referrer_class = instance_of_called_method.class
123
+ # eclass is of type CollectionModel, it must have `::klass` class method.
124
+ eclass.klass = {referrer_class => klass} if klass
125
+ # This helps `resolve_class` to correctly resolve a symbol
126
+ # by using referrer_class as a base module to resolve it
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,70 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module Content
5
+ class DoubleModel
6
+ module Attributable
7
+ module Passthrough
8
+ class << self
9
+ include Content::Includer
10
+
11
+ def included(base)
12
+ super
13
+
14
+ include_missing(base, Attributable::Base)
15
+
16
+ base.extend ClassMethods
17
+ end
18
+ end
19
+
20
+ module ClassMethods
21
+ # Same as `attr_reader` but links to a subjacent `Hash` model property
22
+ # @note it does **not** create an _instance variable_
23
+ # @param methods [Array<Symbol>] the method that exposes the value
24
+ # as well as its `key` in the underlying `Hash` model.
25
+ def pass_reader(*methods)
26
+ methods.each do |method|
27
+ method = method.to_s.freeze
28
+
29
+ define_method method do
30
+ value = send(:doc)[method]
31
+ value = yield(value) if block_given?
32
+ value
33
+ end
34
+ end
35
+ self
36
+ end
37
+
38
+ # Same as `attr_writer` but links to a subjacent `Hash` model property
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.
42
+ def pass_writer(*methods)
43
+ methods.each do |method|
44
+ method = method.to_s.freeze
45
+
46
+ define_method "#{method}=" do |value|
47
+ value = yield(value) if block_given?
48
+ send(:doc)[method] = value
49
+ end
50
+ end
51
+ self
52
+ end
53
+
54
+ # Same as `attr_accessor` but links to a subjacent `Hash` model property
55
+ # @param methods [Array<Symbol>] the method that exposes the value
56
+ # as well as its `key` in the underlying `Hash` model.
57
+ # @param read_only [Boolean] should it only define the reader?
58
+ def passthrough(*methods, read_only: false)
59
+ pass_reader(*methods)
60
+ pass_writer(*methods) unless read_only
61
+ self
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,48 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module Content
5
+ class DoubleModel
6
+ module Attributable
7
+ module Simple
8
+ class << self
9
+ include Content::Includer
10
+
11
+ def included(base)
12
+ super
13
+
14
+ include_missing(base, Attributable::Passthrough)
15
+
16
+ base.extend ClassMethods
17
+ end
18
+ end
19
+
20
+ module ClassMethods
21
+ # To link as a `Time` date to a subjacent `Hash` model property
22
+ # @see Ecoportal::API::Common::Content::DoubleModel#passthrough
23
+ # @param methods [Array<Symbol>] the method that exposes the value
24
+ # as well as its `key` in the underlying `Hash` model.
25
+ # @param read_only [Boolean] should it only define the reader?
26
+ def passdate(*methods, read_only: false)
27
+ pass_reader(*methods) {|value| to_time(value)}
28
+ pass_writer(*methods) {|value| to_time(value)&.iso8601} unless read_only
29
+ self
30
+ end
31
+
32
+ # To link as a `Boolean` to a subjacent `Hash` model property
33
+ # @param methods [Array<Symbol>] the method that exposes the value
34
+ # as well as its `key` in the underlying `Hash` model.
35
+ # @param read_only [Boolean] should it only define the reader?
36
+ def passboolean(*methods, read_only: false)
37
+ pass_reader(*methods) {|value| value}
38
+ pass_writer(*methods) {|value| !!value} unless read_only
39
+ self
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,30 @@
1
+ require 'ecoportal/api/common/content/double_model/attributable/base'
2
+ require 'ecoportal/api/common/content/double_model/attributable/passthrough'
3
+ require 'ecoportal/api/common/content/double_model/attributable/enforce'
4
+ require 'ecoportal/api/common/content/double_model/attributable/simple'
5
+ require 'ecoportal/api/common/content/double_model/attributable/nesting'
6
+ module Ecoportal
7
+ module API
8
+ module Common
9
+ module Content
10
+ class DoubleModel
11
+ module Attributable
12
+ class << self
13
+ include Content::Includer
14
+
15
+ def included(base)
16
+ super
17
+
18
+ include_missing(base, Base)
19
+ include_missing(base, Passthrough)
20
+ include_missing(base, Enforce)
21
+ include_missing(base, Simple)
22
+ include_missing(base, Nesting)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module Content
5
+ class DoubleModel
6
+ module Base
7
+ class << self
8
+ include Content::Includer
9
+
10
+ def included(base)
11
+ super
12
+ base.extend(Content::ClassHelpers)
13
+ include_missing(base, Content::ModelHelpers)
14
+ end
15
+
16
+ NOT_USED = ClassHelpers::NOT_USED
17
+
18
+ private
19
+
20
+ def used_param?(val)
21
+ self.class.used_param?(val)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,43 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module Content
5
+ class DoubleModel
6
+ module DiffableModel
7
+ class << self
8
+ include Content::Includer
9
+
10
+ def included(base)
11
+ super
12
+
13
+ include_missing(base, DoubleModel::HashHelpers)
14
+ include_missing(base, DoubleModel::DoubleDoc::LinkableDoc)
15
+ include_missing(base, DoubleModel::Attributable::Nesting::CascadedCallback)
16
+ end
17
+ end
18
+
19
+ # INSTANCE METHODS
20
+
21
+ # @return [nil, Hash] the patch `Hash` model including only
22
+ # the changes between `original_doc` and `doc`.
23
+ def as_update
24
+ HashDiffPatch.patch_diff(
25
+ as_json,
26
+ original_doc
27
+ )
28
+ end
29
+
30
+ # @return [Boolean] stating if there are changes.
31
+ def dirty?
32
+ au = as_update
33
+ return false if au.nil?
34
+ return false if au == {}
35
+
36
+ true
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,23 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module Content
5
+ class DoubleModel
6
+ module DoubleDoc
7
+ module Base
8
+ class << self
9
+ include Content::Includer
10
+
11
+ def included(base)
12
+ super
13
+
14
+ include_missing(base, DoubleModel::Modifiers)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,87 @@
1
+ module Ecoportal
2
+ module API
3
+ module Common
4
+ module Content
5
+ class DoubleModel
6
+ module DoubleDoc
7
+ module LinkableDoc
8
+ class UnlinkedModel < StandardError
9
+ def initialize(msg = "Something went wrong when linking the document.", from: nil, key: nil)
10
+ msg += " From: #{from}." if from
11
+ msg += " key: #{key}." if key
12
+ super(msg)
13
+ end
14
+ end
15
+
16
+ class << self
17
+ include Content::Includer
18
+
19
+ def included(base)
20
+ super
21
+
22
+ include_missing(base, DoubleDoc::Base)
23
+ include_missing(base, DoubleDoc::RootedKey)
24
+ end
25
+ end
26
+
27
+ # INSTANCE METHODS
28
+
29
+ def to_json(*args)
30
+ doc.to_json(*args)
31
+ end
32
+
33
+ def as_json
34
+ doc
35
+ end
36
+
37
+ def print_pretty
38
+ puts JSON.pretty_generate(as_json)
39
+ self
40
+ end
41
+
42
+ # @return [nil, Hash] the underlying `Hash` model as is (carrying current changes)
43
+ def doc
44
+ return @doc if doc_var?
45
+ return @doc if root?
46
+
47
+ unless linked?
48
+ raise UnlinkedModel.new(
49
+ from: "#{self.class}##{__method__}",
50
+ key: _parent_key
51
+ )
52
+ end
53
+
54
+ _parent.doc.dig(*resolved_rooted_doc_key)
55
+ end
56
+
57
+ # The `original_doc` holds the model as is now on server-side.
58
+ # @return [nil, Hash] the underlying `Hash` model as after last `consolidate!` changes
59
+ def original_doc
60
+ return @original_doc if root?
61
+
62
+ unless linked?
63
+ raise UnlinkedModel.new(
64
+ from: "#{self.class}##{__method__}",
65
+ key: _parent_key
66
+ )
67
+ end
68
+
69
+ _parent.original_doc.dig(*resolved_rooted_doc_key)
70
+ end
71
+
72
+ protected
73
+
74
+ # @return [Boolean] can the subjecent model be fetched?
75
+ def linked?
76
+ return true if root?
77
+ return true if _parent.doc
78
+
79
+ false
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end