ecoportal-api-v2 2.0.12 → 2.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/CHANGELOG.md +29 -3
- data/ecoportal-api-v2.gemspec +1 -1
- data/lib/ecoportal/api/common/content/array_model.rb +20 -3
- data/lib/ecoportal/api/common/content/class_helpers.rb +4 -2
- data/lib/ecoportal/api/common/content/client.rb +12 -6
- data/lib/ecoportal/api/common/content/collection_model/doc/items.rb +33 -0
- data/lib/ecoportal/api/common/content/collection_model/doc/rooted_key.rb +68 -0
- data/lib/ecoportal/api/common/content/collection_model/doc.rb +25 -0
- data/lib/ecoportal/api/common/content/collection_model/doc_mutation/delete.rb +33 -0
- data/lib/ecoportal/api/common/content/collection_model/doc_mutation/position.rb +42 -0
- data/lib/ecoportal/api/common/content/collection_model/doc_mutation/upsert.rb +45 -0
- data/lib/ecoportal/api/common/content/collection_model/doc_mutation.rb +27 -0
- data/lib/ecoportal/api/common/content/collection_model/model/cache.rb +44 -0
- data/lib/ecoportal/api/common/content/collection_model/model/items.rb +43 -0
- data/lib/ecoportal/api/common/content/collection_model/model/iterable.rb +44 -0
- data/lib/ecoportal/api/common/content/collection_model/model/lookup.rb +45 -0
- data/lib/ecoportal/api/common/content/collection_model/model/numeric_key.rb +40 -0
- data/lib/ecoportal/api/common/content/collection_model/model/var_tracking.rb +50 -0
- data/lib/ecoportal/api/common/content/collection_model/model.rb +33 -0
- data/lib/ecoportal/api/common/content/collection_model/modifiers/items_key.rb +57 -0
- data/lib/ecoportal/api/common/content/collection_model/modifiers/items_klass.rb +144 -0
- data/lib/ecoportal/api/common/content/collection_model/modifiers/items_order.rb +38 -0
- data/lib/ecoportal/api/common/content/collection_model/modifiers.rb +27 -0
- data/lib/ecoportal/api/common/content/collection_model/mutation/clear.rb +30 -0
- data/lib/ecoportal/api/common/content/collection_model/mutation/delete.rb +42 -0
- data/lib/ecoportal/api/common/content/collection_model/mutation/upsert.rb +56 -0
- data/lib/ecoportal/api/common/content/collection_model/mutation.rb +27 -0
- data/lib/ecoportal/api/common/content/collection_model.rb +19 -351
- data/lib/ecoportal/api/common/content/doc_helpers.rb +18 -16
- data/lib/ecoportal/api/common/content/double_model/attributable/base.rb +23 -0
- data/lib/ecoportal/api/common/content/double_model/attributable/enforce.rb +62 -0
- data/lib/ecoportal/api/common/content/double_model/attributable/nesting/cascaded_callback.rb +104 -0
- data/lib/ecoportal/api/common/content/double_model/attributable/nesting/embeddable.rb +76 -0
- data/lib/ecoportal/api/common/content/double_model/attributable/nesting/keyable.rb +119 -0
- data/lib/ecoportal/api/common/content/double_model/attributable/nesting.rb +136 -0
- data/lib/ecoportal/api/common/content/double_model/attributable/passthrough.rb +70 -0
- data/lib/ecoportal/api/common/content/double_model/attributable/simple.rb +48 -0
- data/lib/ecoportal/api/common/content/double_model/attributable.rb +30 -0
- data/lib/ecoportal/api/common/content/double_model/base.rb +29 -0
- data/lib/ecoportal/api/common/content/double_model/diffable_model.rb +43 -0
- data/lib/ecoportal/api/common/content/double_model/double_doc/base.rb +23 -0
- data/lib/ecoportal/api/common/content/double_model/double_doc/linkable_doc.rb +87 -0
- data/lib/ecoportal/api/common/content/double_model/double_doc/replaceable_doc.rb +61 -0
- data/lib/ecoportal/api/common/content/double_model/double_doc/reset_consolidate.rb +54 -0
- data/lib/ecoportal/api/common/content/double_model/double_doc/rooted_key.rb +49 -0
- data/lib/ecoportal/api/common/content/double_model/double_doc.rb +31 -0
- data/lib/ecoportal/api/common/content/double_model/hash_helpers.rb +40 -0
- data/lib/ecoportal/api/common/content/double_model/modifiers/read_only_able.rb +73 -0
- data/lib/ecoportal/api/common/content/double_model/modifiers/rootable.rb +64 -0
- data/lib/ecoportal/api/common/content/double_model/modifiers.rb +24 -0
- data/lib/ecoportal/api/common/content/double_model/parented.rb +22 -0
- data/lib/ecoportal/api/common/content/double_model/var_tracking.rb +45 -0
- data/lib/ecoportal/api/common/content/double_model.rb +28 -486
- data/lib/ecoportal/api/common/content/hash_diff_patch.rb +2 -1
- data/lib/ecoportal/api/common/content/includer.rb +16 -0
- data/lib/ecoportal/api/common/content/model_helpers.rb +14 -16
- data/lib/ecoportal/api/common/content.rb +1 -0
- data/lib/ecoportal/api/v2/page/component.rb +46 -46
- data/lib/ecoportal/api/v2/page/components.rb +2 -2
- data/lib/ecoportal/api/v2/page.rb +14 -14
- data/lib/ecoportal/api/v2/pages/page_stage/task.rb +2 -2
- data/lib/ecoportal/api/v2/pages/page_stage/tasks.rb +3 -3
- data/lib/ecoportal/api/v2/pages/page_stage.rb +8 -8
- data/lib/ecoportal/api/v2/pages/stages.rb +2 -2
- data/lib/ecoportal/api/v2/pages.rb +15 -15
- data/lib/ecoportal/api/v2/registers.rb +20 -19
- data/lib/ecoportal/api/v2/s3/files/batch_upload.rb +1 -0
- data/lib/ecoportal/api/v2/s3/files/poll.rb +5 -5
- data/lib/ecoportal/api/v2/s3/files/poll_status.rb +3 -3
- data/lib/ecoportal/api/v2/s3/files.rb +6 -6
- data/lib/ecoportal/api/v2/s3.rb +5 -5
- data/lib/ecoportal/api/v2.rb +18 -8
- data/lib/ecoportal/api/v2_version.rb +1 -1
- metadata +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
|