ree_lib 1.0.105 → 1.0.106
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/Gemfile.lock +1 -1
- data/lib/ree_lib/packages/ree_dao/package/ree_dao/dataset_extensions.rb +40 -22
- data/lib/ree_lib/packages/ree_dao/spec/ree_dao/functions/one_to_one_spec.rb +5 -9
- data/lib/ree_lib/packages/ree_dto/Package.schema.json +17 -2
- data/lib/ree_lib/packages/ree_dto/package/ree_dto/dsl.rb +8 -0
- data/lib/ree_lib/packages/ree_dto/package/ree_dto/dto/collection_filter.rb +49 -0
- data/lib/ree_lib/packages/ree_dto/package/ree_dto/dto/collection_meta.rb +13 -0
- data/lib/ree_lib/packages/ree_dto/package/ree_dto/dto/dto_builder.rb +42 -0
- data/lib/ree_lib/packages/ree_dto/package/ree_dto/dto/dto_class_methods.rb +27 -0
- data/lib/ree_lib/packages/ree_dto/package/ree_dto/dto/dto_collection.rb +45 -0
- data/lib/ree_lib/packages/ree_dto/package/ree_dto/dto/dto_instance_methods.rb +111 -0
- data/lib/ree_lib/packages/ree_dto/package/ree_dto/dto/field_meta.rb +21 -0
- data/lib/ree_lib/packages/ree_dto/package/ree_dto/functions/build_dto.rb +62 -0
- data/lib/ree_lib/packages/ree_dto/package/ree_dto/functions/build_dto_collection_class.rb +27 -0
- data/lib/ree_lib/packages/ree_dto/package/ree_dto.rb +16 -3
- data/lib/ree_lib/packages/ree_dto/spec/ree_dto/dsl_spec.rb +117 -0
- data/lib/ree_lib/packages/ree_mapper/Package.schema.json +3 -0
- data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/mapper.rb +1 -1
- data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/mapper_strategy.rb +2 -0
- data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/strategy_outputs/ree_dto_output.rb +14 -0
- data/lib/ree_lib/packages/ree_mapper/package/ree_mapper.rb +2 -0
- data/lib/ree_lib/packages/ree_mapper/spec/ree_mapper/mapper_spec.rb +28 -0
- data/lib/ree_lib/packages/ree_mapper/spec/ree_mapper/types/hash_spec.rb +1 -1
- data/lib/ree_lib/packages/ree_mapper/spec/ree_mapper/types/type_options_spec.rb +1 -1
- data/lib/ree_lib/version.rb +1 -1
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 558dbd22038f40db477b968aa0595ca664484c05606827276b2d872ad6eb5dab
|
4
|
+
data.tar.gz: f190528d1cf21d60f1403252fd16d6e9e62beb291b4cddcca2af03f77bddce85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b5f0cf27786a919265f1c01982c7f8640e5b27edf837ed9a207243e6c513f3a97ab2487ad4c16ebc01feb7d7641509d38b336ef77024fb325b059aa32a61d96e
|
7
|
+
data.tar.gz: 6ddd53ea7f83cded5374da832c06ef845062c2574e832ccfec241602722a63a32390f78f02fc50f2d030f94b7e1cf404e976c00fa3237c11607cd2fa96504ee7
|
data/Gemfile.lock
CHANGED
@@ -44,7 +44,8 @@ module ReeDao
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def put(entity)
|
47
|
-
raw =
|
47
|
+
raw = dump_entity(entity)
|
48
|
+
|
48
49
|
remove_null_primary_key(raw)
|
49
50
|
key = insert(raw)
|
50
51
|
|
@@ -71,7 +72,6 @@ module ReeDao
|
|
71
72
|
end
|
72
73
|
|
73
74
|
key = insert_conflict(conflict_opts).insert(raw)
|
74
|
-
return if key.nil?
|
75
75
|
|
76
76
|
set_entity_primary_key(entity, raw, key)
|
77
77
|
set_persistence_state(entity, raw)
|
@@ -82,7 +82,6 @@ module ReeDao
|
|
82
82
|
def import_all(entities, batch_size: IMPORT_BATCH_SIZE)
|
83
83
|
return if entities.empty?
|
84
84
|
|
85
|
-
mapper = opts[:schema_mapper]
|
86
85
|
columns = self.columns
|
87
86
|
raw = {}
|
88
87
|
|
@@ -90,7 +89,7 @@ module ReeDao
|
|
90
89
|
columns.delete(:row)
|
91
90
|
|
92
91
|
data = entities.map do |entity|
|
93
|
-
hash =
|
92
|
+
hash = dump_entity(entity)
|
94
93
|
raw[entity] = hash
|
95
94
|
|
96
95
|
columns.map { hash[_1] }
|
@@ -116,16 +115,22 @@ module ReeDao
|
|
116
115
|
end
|
117
116
|
|
118
117
|
def update(hash_or_entity)
|
119
|
-
|
120
|
-
|
118
|
+
if !opts[:schema_mapper] || hash_or_entity.is_a?(Hash)
|
119
|
+
return __original_update(hash_or_entity)
|
120
|
+
end
|
121
121
|
|
122
|
-
|
123
|
-
raw = extract_changes(hash_or_entity,
|
122
|
+
dump = dump_entity(hash_or_entity)
|
123
|
+
raw = extract_changes(hash_or_entity, dump)
|
124
124
|
|
125
|
-
|
126
|
-
update_persistence_state(hash_or_entity, raw)
|
125
|
+
if !raw.empty?
|
127
126
|
key_condition = prepare_key_condition_from_entity(hash_or_entity)
|
128
127
|
where(key_condition).__original_update(raw)
|
128
|
+
|
129
|
+
if is_ree_dto?(hash_or_entity)
|
130
|
+
hash_or_entity.reset_changes
|
131
|
+
else
|
132
|
+
update_persistence_state(hash_or_entity, raw)
|
133
|
+
end
|
129
134
|
end
|
130
135
|
|
131
136
|
hash_or_entity
|
@@ -160,9 +165,7 @@ module ReeDao
|
|
160
165
|
|
161
166
|
if m
|
162
167
|
entity = m.db_load(hash)
|
163
|
-
|
164
168
|
self.set_persistence_state(entity, hash)
|
165
|
-
|
166
169
|
entity
|
167
170
|
else
|
168
171
|
hash
|
@@ -206,6 +209,8 @@ module ReeDao
|
|
206
209
|
end
|
207
210
|
|
208
211
|
def set_persistence_state(entity, raw)
|
212
|
+
return if is_ree_dto?(entity)
|
213
|
+
|
209
214
|
if !entity.is_a?(Integer) && !entity.is_a?(Symbol)
|
210
215
|
entity.instance_variable_set(PERSISTENCE_STATE_VARIABLE, raw)
|
211
216
|
end
|
@@ -220,22 +225,26 @@ module ReeDao
|
|
220
225
|
end
|
221
226
|
|
222
227
|
def extract_changes(entity, hash)
|
223
|
-
|
224
|
-
|
228
|
+
if is_ree_dto?(entity)
|
229
|
+
hash.slice(*entity.changed_fields)
|
230
|
+
else
|
231
|
+
return hash unless entity.instance_variable_defined?(PERSISTENCE_STATE_VARIABLE)
|
232
|
+
changes = {}
|
225
233
|
|
226
|
-
|
234
|
+
persistence_state = entity.instance_variable_get(PERSISTENCE_STATE_VARIABLE)
|
227
235
|
|
228
|
-
|
229
|
-
|
236
|
+
hash.each do |column, value|
|
237
|
+
previous_column_value = persistence_state[column]
|
230
238
|
|
231
|
-
|
232
|
-
|
233
|
-
|
239
|
+
if persistence_state.has_key?(column)
|
240
|
+
if previous_column_value != value || value.respond_to?(:each)
|
241
|
+
changes[column] = value
|
242
|
+
end
|
234
243
|
end
|
235
244
|
end
|
236
|
-
end
|
237
245
|
|
238
|
-
|
246
|
+
changes
|
247
|
+
end
|
239
248
|
end
|
240
249
|
|
241
250
|
def set_entity_primary_key(entity, raw, key)
|
@@ -247,10 +256,19 @@ module ReeDao
|
|
247
256
|
else
|
248
257
|
entity.instance_variable_set("@#{primary_key}", key)
|
249
258
|
end
|
259
|
+
|
250
260
|
raw[primary_key] = key
|
251
261
|
end
|
252
262
|
end
|
253
263
|
|
264
|
+
def dump_entity(entity_or_hash)
|
265
|
+
opts[:schema_mapper].db_dump(entity_or_hash)
|
266
|
+
end
|
267
|
+
|
268
|
+
def is_ree_dto?(entity)
|
269
|
+
entity.class.include?(ReeDto::DSL)
|
270
|
+
end
|
271
|
+
|
254
272
|
def prepare_key_condition_from_entity(entity)
|
255
273
|
key_condition = {}
|
256
274
|
|
@@ -45,6 +45,7 @@ RSpec.describe :one_to_one do
|
|
45
45
|
|
46
46
|
package do
|
47
47
|
depends_on :ree_dao
|
48
|
+
depends_on :ree_dto
|
48
49
|
end
|
49
50
|
|
50
51
|
class Db
|
@@ -89,16 +90,11 @@ RSpec.describe :one_to_one do
|
|
89
90
|
end
|
90
91
|
|
91
92
|
class ProjectUser
|
92
|
-
include ReeDto::
|
93
|
-
|
94
|
-
properties(
|
95
|
-
id: Nilor[Integer],
|
96
|
-
project_id: Nilor[Integer]
|
97
|
-
)
|
93
|
+
include ReeDto::DSL
|
98
94
|
|
99
|
-
|
100
|
-
|
101
|
-
|
95
|
+
build_dto do
|
96
|
+
field :id, Nilor[Integer], default: nil
|
97
|
+
field :project_id, Nilor[Integer]
|
102
98
|
end
|
103
99
|
end
|
104
100
|
|
@@ -7,12 +7,27 @@
|
|
7
7
|
"ree_dto"
|
8
8
|
],
|
9
9
|
"depends_on": [
|
10
|
-
|
10
|
+
{
|
11
|
+
"name": "ree_object"
|
12
|
+
}
|
11
13
|
],
|
12
14
|
"env_vars": [
|
13
15
|
|
14
16
|
],
|
15
17
|
"objects": [
|
16
|
-
|
18
|
+
{
|
19
|
+
"name": "build_dto",
|
20
|
+
"schema": "packages/ree_dto/schemas/ree_dto/functions/build_dto.schema.json",
|
21
|
+
"tags": [
|
22
|
+
"fn"
|
23
|
+
]
|
24
|
+
},
|
25
|
+
{
|
26
|
+
"name": "build_dto_collection_class",
|
27
|
+
"schema": "packages/ree_dto/schemas/ree_dto/functions/build_dto_collection_class.schema.json",
|
28
|
+
"tags": [
|
29
|
+
"fn"
|
30
|
+
]
|
31
|
+
}
|
17
32
|
]
|
18
33
|
}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class ReeDto::CollectionFilter
|
2
|
+
include Ree::Contracts::Core
|
3
|
+
include Ree::Contracts::ArgContracts
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
InvalidFilterItemErr = Class.new(ArgumentError)
|
7
|
+
|
8
|
+
contract Any, Symbol, Proc => Any
|
9
|
+
def initialize(collection, name, filter_proc)
|
10
|
+
@collection = collection
|
11
|
+
@name = name
|
12
|
+
@filter_proc = filter_proc
|
13
|
+
end
|
14
|
+
|
15
|
+
contract Optblock => Any
|
16
|
+
def each(&block)
|
17
|
+
@collection.select(&@filter_proc).each(&block)
|
18
|
+
end
|
19
|
+
|
20
|
+
contract Any => Any
|
21
|
+
def add(item)
|
22
|
+
check_item(item)
|
23
|
+
@collection.add(item)
|
24
|
+
end
|
25
|
+
|
26
|
+
contract None => Integer
|
27
|
+
def size
|
28
|
+
count
|
29
|
+
end
|
30
|
+
|
31
|
+
contract Any => Any
|
32
|
+
def remove(item)
|
33
|
+
check_item(item)
|
34
|
+
@collection.remove(item)
|
35
|
+
end
|
36
|
+
|
37
|
+
alias :<< :add
|
38
|
+
alias :push :add
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def check_item(item)
|
43
|
+
if !@filter_proc.call(item)
|
44
|
+
raise InvalidFilterItemErr.new(
|
45
|
+
"invalid item for #{@collection.parent_class}##{@name} filter"
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class ReeDto::CollectionMeta
|
2
|
+
include Ree::Contracts::Core
|
3
|
+
include Ree::Contracts::ArgContracts
|
4
|
+
|
5
|
+
attr_reader :name, :contract, :filter_proc
|
6
|
+
|
7
|
+
contract Symbol, Any, Proc => Any
|
8
|
+
def initialize(name, contract, filter_proc)
|
9
|
+
@name = name
|
10
|
+
@contract = contract
|
11
|
+
@filter_proc = filter_proc
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class ReeDto::DtoBuilder
|
2
|
+
include Ree::Contracts::Core
|
3
|
+
include Ree::Contracts::ArgContracts
|
4
|
+
include Ree::LinkDSL
|
5
|
+
|
6
|
+
link "ree_dto/dto/field_meta", -> { FieldMeta }
|
7
|
+
link "ree_dto/dto/collection_meta", -> { CollectionMeta }
|
8
|
+
|
9
|
+
attr_reader :fields, :collections
|
10
|
+
|
11
|
+
def initialize(klass)
|
12
|
+
@klass = klass
|
13
|
+
@fields = []
|
14
|
+
@collections = []
|
15
|
+
end
|
16
|
+
|
17
|
+
contract(Symbol, Any, Kwargs[setter: Bool, default: Any] => FieldMeta)
|
18
|
+
def field(name, contract, setter: true, default: FieldMeta::NONE)
|
19
|
+
existing = @fields.find { _1.name == name }
|
20
|
+
|
21
|
+
if existing
|
22
|
+
raise ArgumentError.new("field :#{name} already defined for #{@klass}")
|
23
|
+
end
|
24
|
+
|
25
|
+
field = FieldMeta.new(name, contract, setter, default)
|
26
|
+
@fields << field
|
27
|
+
field
|
28
|
+
end
|
29
|
+
|
30
|
+
contract Symbol, Any, Optblock => CollectionMeta
|
31
|
+
def collection(name, contract, &proc)
|
32
|
+
existing = @collections.find { _1.name == name }
|
33
|
+
|
34
|
+
if existing
|
35
|
+
raise ArgumentError.new("collection :#{name} already defined for #{@klass}")
|
36
|
+
end
|
37
|
+
|
38
|
+
collection = CollectionMeta.new(name, contract, proc)
|
39
|
+
@collections.push(collection)
|
40
|
+
collection
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative "./field_meta"
|
2
|
+
require_relative "./collection_meta"
|
3
|
+
|
4
|
+
module ReeDto::DtoClassMethods
|
5
|
+
include Ree::Contracts::Core
|
6
|
+
include Ree::Contracts::ArgContracts
|
7
|
+
|
8
|
+
contract None => ArrayOf[ReeDto::FieldMeta]
|
9
|
+
def fields
|
10
|
+
@fields ||= []
|
11
|
+
end
|
12
|
+
|
13
|
+
contract None => ArrayOf[ReeDto::CollectionMeta]
|
14
|
+
def collections
|
15
|
+
@collections ||= []
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def set_fields(v)
|
21
|
+
@fields = v
|
22
|
+
end
|
23
|
+
|
24
|
+
def set_collections(v)
|
25
|
+
@collections = v
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative "./collection_filter"
|
2
|
+
|
3
|
+
class ReeDto::DtoCollection
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
LoadError = Class.new(ArgumentError)
|
7
|
+
|
8
|
+
attr_reader :name, :contract, :parent_class
|
9
|
+
|
10
|
+
contract Symbol, Any, Any => Any
|
11
|
+
def initialize(name, contract, parent_class)
|
12
|
+
@parent_class = parent_class
|
13
|
+
@contract = contract
|
14
|
+
@name = name
|
15
|
+
@list = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
contract None => nil
|
19
|
+
def reset
|
20
|
+
@list = []
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
contract Optblock => Any
|
25
|
+
def each(&block)
|
26
|
+
if @list.nil?
|
27
|
+
raise LoadError.new("collection :#{@name} for #{@parent_class} is not loaded")
|
28
|
+
end
|
29
|
+
|
30
|
+
@list.each(&block)
|
31
|
+
end
|
32
|
+
|
33
|
+
contract None => Integer
|
34
|
+
def size
|
35
|
+
@list.size
|
36
|
+
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
def filter(name, filter_proc)
|
40
|
+
define_method name do
|
41
|
+
ReeDto::CollectionFilter.new(self, name, filter_proc)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require_relative "./field_meta"
|
2
|
+
|
3
|
+
module ReeDto::DtoInstanceMethods
|
4
|
+
include Ree::Contracts::Core
|
5
|
+
include Ree::Contracts::ArgContracts
|
6
|
+
|
7
|
+
FieldNotSetError = Class.new(ArgumentError)
|
8
|
+
|
9
|
+
if ReeDto.debug_mode?
|
10
|
+
contract Hash, Ksplat[RestKeys => Any] => Any
|
11
|
+
def initialize(attrs = nil, **kwargs)
|
12
|
+
@_attrs = attrs || kwargs
|
13
|
+
list = self.class.fields.map(&:name)
|
14
|
+
extra = attrs.keys - list
|
15
|
+
|
16
|
+
if !extra.empty?
|
17
|
+
puts("WARNING: #{self.class}.new does not have definition for #{extra.inspect} fields")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
else
|
21
|
+
contract Hash, Ksplat[RestKeys => Any] => Any
|
22
|
+
def initialize(attrs = nil, **kwargs)
|
23
|
+
@_attrs = attrs || kwargs
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
contract None => nil
|
28
|
+
def reset_changes
|
29
|
+
@changed_fields = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
contract Symbol => ReeDto::FieldMeta
|
33
|
+
def get_meta(name)
|
34
|
+
self
|
35
|
+
.class
|
36
|
+
.fields
|
37
|
+
.find { _1.name == name} || (raise ArgumentError.new("field :#{name} not defined for :#{self.class}"))
|
38
|
+
end
|
39
|
+
|
40
|
+
contract Symbol => Any
|
41
|
+
def get_value(name)
|
42
|
+
@_attrs.fetch(name) do
|
43
|
+
meta = get_meta(name)
|
44
|
+
|
45
|
+
if !meta.has_default?
|
46
|
+
raise FieldNotSetError.new("field :#{name} not set for:#{self}")
|
47
|
+
else
|
48
|
+
@_attrs[name] = meta.default
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
contract None => Hash
|
54
|
+
def attrs
|
55
|
+
@_attrs
|
56
|
+
end
|
57
|
+
|
58
|
+
contract Symbol, Any => Any
|
59
|
+
def set_attr(name, val)
|
60
|
+
@_attrs[name] = val
|
61
|
+
end
|
62
|
+
|
63
|
+
contract Symbol, Any => Any
|
64
|
+
def set_value(name, val)
|
65
|
+
if has_value?(name)
|
66
|
+
old = get_value(name)
|
67
|
+
return if old == val
|
68
|
+
end
|
69
|
+
|
70
|
+
@changed_fields ||= Set.new
|
71
|
+
@changed_fields << name
|
72
|
+
@_attrs[name] = val
|
73
|
+
end
|
74
|
+
|
75
|
+
contract Symbol => Bool
|
76
|
+
def has_value?(name)
|
77
|
+
@_attrs.key?(name) || get_meta(name).has_default?
|
78
|
+
end
|
79
|
+
|
80
|
+
contract None => ArrayOf[Symbol]
|
81
|
+
def changed_fields
|
82
|
+
@changed_fields.to_a
|
83
|
+
end
|
84
|
+
|
85
|
+
contract Block => Any
|
86
|
+
def each_field(&proc)
|
87
|
+
self.class.fields.select { has_value?(_1.name) }.each do |field|
|
88
|
+
proc.call(field.name, get_value(field.name))
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
contract None => String
|
93
|
+
def to_s
|
94
|
+
fields = self.class.fields
|
95
|
+
max_length = fields.map(&:name).sort_by(&:size).last.size
|
96
|
+
result = "\n#{self.class}\n"
|
97
|
+
|
98
|
+
data = fields.select { has_value?(_1.name) }.map do |field|
|
99
|
+
name = field.name.to_s
|
100
|
+
extra_spaces = ' ' * (max_length - name.size)
|
101
|
+
%Q( #{name}#{extra_spaces} = #{get_value(field.name).inspect})
|
102
|
+
end
|
103
|
+
|
104
|
+
result << data.join("\n")
|
105
|
+
end
|
106
|
+
|
107
|
+
contract None => String
|
108
|
+
def inspect
|
109
|
+
to_s
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class ReeDto::FieldMeta
|
2
|
+
include Ree::Contracts::Core
|
3
|
+
include Ree::Contracts::ArgContracts
|
4
|
+
|
5
|
+
NONE = Object.new.freeze
|
6
|
+
|
7
|
+
attr_reader :name, :contract, :setter, :default
|
8
|
+
|
9
|
+
contract Symbol, Any, Bool, Any => Any
|
10
|
+
def initialize(name, contract, setter, default)
|
11
|
+
@name = name
|
12
|
+
@contract = contract
|
13
|
+
@setter = setter
|
14
|
+
@default = default
|
15
|
+
end
|
16
|
+
|
17
|
+
contract None => Bool
|
18
|
+
def has_default?
|
19
|
+
@default != NONE
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ReeDto::BuildDto
|
4
|
+
include Ree::FnDSL
|
5
|
+
|
6
|
+
fn :build_dto do
|
7
|
+
target :class
|
8
|
+
with_caller
|
9
|
+
link :build_dto_collection_class
|
10
|
+
link "ree_dto/dto/dto_instance_methods", -> { DtoInstanceMethods }
|
11
|
+
link "ree_dto/dto/dto_class_methods", -> { DtoClassMethods }
|
12
|
+
link "ree_dto/dto/dto_builder", -> { DtoBuilder }
|
13
|
+
end
|
14
|
+
|
15
|
+
contract(Block => nil)
|
16
|
+
def call(&proc)
|
17
|
+
klass = get_caller
|
18
|
+
klass.include DtoInstanceMethods
|
19
|
+
klass.extend DtoClassMethods
|
20
|
+
|
21
|
+
builder = DtoBuilder.new(self)
|
22
|
+
builder.instance_exec(&proc)
|
23
|
+
|
24
|
+
klass.send(:set_fields, builder.fields)
|
25
|
+
klass.send(:set_collections, builder.collections)
|
26
|
+
|
27
|
+
builder.fields.each do |field|
|
28
|
+
klass.instance_exec do
|
29
|
+
contract None => field.contract
|
30
|
+
end
|
31
|
+
|
32
|
+
klass.define_method field.name do
|
33
|
+
get_value(field.name)
|
34
|
+
end
|
35
|
+
|
36
|
+
if field.setter
|
37
|
+
klass.instance_exec do
|
38
|
+
contract field.contract => field.contract
|
39
|
+
end
|
40
|
+
|
41
|
+
klass.define_method :"#{field.name}=" do |val|
|
42
|
+
set_value(field.name, val)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
builder.collections.each do |collection|
|
48
|
+
col_class = build_dto_collection_class(collection.contract)
|
49
|
+
col_class.class_exec(&collection.filter_proc)
|
50
|
+
|
51
|
+
klass.define_method collection.name do
|
52
|
+
@collections ||= {}
|
53
|
+
|
54
|
+
@collections[collection.name] ||= col_class.new(
|
55
|
+
collection.name, collection.contract, klass
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class ReeDto::BuildDtoCollectionClass
|
2
|
+
include Ree::FnDSL
|
3
|
+
|
4
|
+
fn :build_dto_collection_class do
|
5
|
+
link "ree_dto/dto/dto_collection", -> { DtoCollection }
|
6
|
+
end
|
7
|
+
|
8
|
+
contract Any => Class
|
9
|
+
def call(entity_contract)
|
10
|
+
Class.new(DtoCollection) do
|
11
|
+
contract entity_contract => nil
|
12
|
+
def add(item)
|
13
|
+
@list ||= []
|
14
|
+
@list.push(item)
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
contract entity_contract => Nilor[entity_contract]
|
19
|
+
def remove(item)
|
20
|
+
@list.delete(item)
|
21
|
+
end
|
22
|
+
|
23
|
+
alias :<< :add
|
24
|
+
alias :push :add
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,7 +1,20 @@
|
|
1
1
|
module ReeDto
|
2
2
|
include Ree::PackageDSL
|
3
|
-
|
4
|
-
package
|
3
|
+
|
4
|
+
package do
|
5
|
+
depends_on :ree_object
|
6
|
+
end
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def set_debug_mode
|
10
|
+
@debug_mode = true
|
11
|
+
end
|
12
|
+
|
13
|
+
def debug_mode?
|
14
|
+
!!@debug_mode
|
15
|
+
end
|
16
|
+
end
|
5
17
|
end
|
6
18
|
|
7
|
-
require_relative 'ree_dto/entity_dsl'
|
19
|
+
require_relative 'ree_dto/entity_dsl'
|
20
|
+
require_relative 'ree_dto/dsl'
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
package_require("ree_dto/dsl")
|
3
|
+
|
4
|
+
RSpec.describe ReeDto::DSL do
|
5
|
+
class ReeDto::DtoClass
|
6
|
+
include ReeDto::DSL
|
7
|
+
|
8
|
+
User = Struct.new(:id, :name, :status)
|
9
|
+
|
10
|
+
build_dto do
|
11
|
+
field :with_default, Nilor[Integer], default: 1
|
12
|
+
field :string, String
|
13
|
+
field :without_setter, Integer, setter: false
|
14
|
+
|
15
|
+
collection :numbers, Integer do
|
16
|
+
filter :odd, -> { _1.odd? }
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"odd_collection"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
collection :users, User do
|
24
|
+
filter :active, -> { _1.status == "active" }
|
25
|
+
filter :inactive, -> { _1.status != "active" }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "fields" do
|
31
|
+
it {
|
32
|
+
dto = ReeDto::DtoClass.new
|
33
|
+
expect(dto.with_default).to eq(1)
|
34
|
+
|
35
|
+
dto = ReeDto::DtoClass.new({})
|
36
|
+
expect(dto.with_default).to eq(1)
|
37
|
+
expect(dto.get_value(:with_default)).to eq(1)
|
38
|
+
}
|
39
|
+
|
40
|
+
it {
|
41
|
+
dto = ReeDto::DtoClass.new
|
42
|
+
expect(dto.has_value?(:with_default)).to eq(true)
|
43
|
+
expect(dto.has_value?(:string)).to eq(false)
|
44
|
+
}
|
45
|
+
|
46
|
+
it {
|
47
|
+
dto = ReeDto::DtoClass.new({with_default: 1, string: "string", without_setter: 22})
|
48
|
+
expect(dto.to_s).to include("ReeDto::DtoClass")
|
49
|
+
}
|
50
|
+
|
51
|
+
it {
|
52
|
+
dto = ReeDto::DtoClass.new
|
53
|
+
|
54
|
+
expect {
|
55
|
+
dto.string
|
56
|
+
}.to raise_error do |e|
|
57
|
+
expect(e.message).to eq("field :string not set for:\nReeDto::DtoClass\n with_default = 1")
|
58
|
+
end
|
59
|
+
}
|
60
|
+
|
61
|
+
it {
|
62
|
+
dto = ReeDto::DtoClass.new(string: "string")
|
63
|
+
expect(dto.string).to eq("string")
|
64
|
+
|
65
|
+
dto.string = "changed"
|
66
|
+
expect(dto.string).to eq("changed")
|
67
|
+
expect(dto.changed_fields).to eq([:string])
|
68
|
+
}
|
69
|
+
|
70
|
+
it {
|
71
|
+
dto = ReeDto::DtoClass.new
|
72
|
+
fields = []
|
73
|
+
values = []
|
74
|
+
|
75
|
+
dto.each_field do |name, value|
|
76
|
+
fields << name
|
77
|
+
values << value
|
78
|
+
end
|
79
|
+
|
80
|
+
expect(fields).to eq([:with_default])
|
81
|
+
expect(values).to eq([1])
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
context "collections" do
|
86
|
+
it {
|
87
|
+
dto = ReeDto::DtoClass.new
|
88
|
+
|
89
|
+
dto.numbers << 1
|
90
|
+
dto.numbers << 2
|
91
|
+
expect(dto.numbers.sum).to eq(3)
|
92
|
+
expect(dto.numbers.to_s).to eq("odd_collection")
|
93
|
+
}
|
94
|
+
|
95
|
+
it {
|
96
|
+
dto = ReeDto::DtoClass.new
|
97
|
+
|
98
|
+
dto.users.push(ReeDto::DtoClass::User.new(1, "John", "active"))
|
99
|
+
dto.users.push(ReeDto::DtoClass::User.new(1, "Adam", "inactive"))
|
100
|
+
|
101
|
+
expect(dto.users.active.size).to eq(1)
|
102
|
+
|
103
|
+
peter = ReeDto::DtoClass::User.new(1, "Peter", "active")
|
104
|
+
dto.users.active << peter
|
105
|
+
|
106
|
+
expect(dto.users.size).to eq(3)
|
107
|
+
|
108
|
+
expect {
|
109
|
+
dto.users.active << ReeDto::DtoClass::User.new(1, "John", "inactive")
|
110
|
+
}.to raise_error(ReeDto::CollectionFilter::InvalidFilterItemErr)
|
111
|
+
|
112
|
+
dto.users.active.remove(peter)
|
113
|
+
expect(dto.users.size).to eq(2)
|
114
|
+
expect(dto.users.active.size).to eq(1)
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ReeMapper::ReeDtoOutput < ReeMapper::StrategyOutput
|
4
|
+
contract(None => Any)
|
5
|
+
def build_object
|
6
|
+
dto.new
|
7
|
+
end
|
8
|
+
|
9
|
+
contract(Object, ReeMapper::Field, Any => nil)
|
10
|
+
def assign_value(object, field, value)
|
11
|
+
object.set_attr(field.name, value)
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
end
|
@@ -6,6 +6,7 @@ module ReeMapper
|
|
6
6
|
package do
|
7
7
|
depends_on :ree_string
|
8
8
|
depends_on :ree_datetime
|
9
|
+
depends_on :ree_dto
|
9
10
|
end
|
10
11
|
|
11
12
|
package_require('ree_string/functions/underscore')
|
@@ -40,6 +41,7 @@ module ReeMapper
|
|
40
41
|
|
41
42
|
require_relative 'ree_mapper/strategy_outputs/strategy_output'
|
42
43
|
require_relative 'ree_mapper/strategy_outputs/object_output'
|
44
|
+
require_relative 'ree_mapper/strategy_outputs/ree_dto_output'
|
43
45
|
require_relative 'ree_mapper/strategy_outputs/hash_output'
|
44
46
|
require_relative 'ree_mapper/strategy_outputs/struct_output'
|
45
47
|
|
@@ -57,6 +57,34 @@ RSpec.describe ReeMapper::Mapper do
|
|
57
57
|
}
|
58
58
|
end
|
59
59
|
|
60
|
+
describe 'ree_dto dto' do
|
61
|
+
class ReeMapper::TestDto
|
62
|
+
include ReeDto::DSL
|
63
|
+
|
64
|
+
build_dto do
|
65
|
+
field :my_field, Integer
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
let(:mapper) {
|
70
|
+
build_mapper_factory(
|
71
|
+
strategies: [
|
72
|
+
build_mapper_strategy(method: :cast, dto: ReeMapper::TestDto),
|
73
|
+
]
|
74
|
+
).call.use(:cast) do
|
75
|
+
integer :my_field
|
76
|
+
end
|
77
|
+
}
|
78
|
+
|
79
|
+
it {
|
80
|
+
expect(mapper.cast({ my_field: 1 }).my_field).to eq(1)
|
81
|
+
}
|
82
|
+
|
83
|
+
it {
|
84
|
+
expect(mapper.cast({ my_field: 1 })).to be_a(ReeMapper::TestDto)
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
60
88
|
describe 'ostruct dto' do
|
61
89
|
let(:mapper) {
|
62
90
|
build_mapper_factory(
|
@@ -27,7 +27,7 @@ RSpec.describe 'Mapper Hash' do
|
|
27
27
|
}
|
28
28
|
|
29
29
|
it {
|
30
|
-
expect { mapper.cast({ point: 1 }) }.to raise_error(ReeMapper::TypeError, /`point\[x\]` is missing required field/)
|
30
|
+
expect { mapper.cast({ point: 1 }) }.to raise_error(ReeMapper::TypeError, /`point\[x\]` is missing \(required field\)/)
|
31
31
|
}
|
32
32
|
|
33
33
|
it {
|
@@ -174,7 +174,7 @@ RSpec.describe 'ReeMapper::MapperFactory type options' do
|
|
174
174
|
}
|
175
175
|
|
176
176
|
it {
|
177
|
-
expect { mapper.cast({}) }.to raise_error(ReeMapper::TypeError, /`number` is missing required field/)
|
177
|
+
expect { mapper.cast({}) }.to raise_error(ReeMapper::TypeError, /`number` is missing \(required field\)/)
|
178
178
|
}
|
179
179
|
|
180
180
|
it {
|
data/lib/ree_lib/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ree_lib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.106
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ruslan Gatiyatov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-08-
|
11
|
+
date: 2024-08-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ree
|
@@ -651,8 +651,19 @@ files:
|
|
651
651
|
- lib/ree_lib/packages/ree_dto/Package.schema.json
|
652
652
|
- lib/ree_lib/packages/ree_dto/bin/console
|
653
653
|
- lib/ree_lib/packages/ree_dto/package/ree_dto.rb
|
654
|
+
- lib/ree_lib/packages/ree_dto/package/ree_dto/dsl.rb
|
655
|
+
- lib/ree_lib/packages/ree_dto/package/ree_dto/dto/collection_filter.rb
|
656
|
+
- lib/ree_lib/packages/ree_dto/package/ree_dto/dto/collection_meta.rb
|
657
|
+
- lib/ree_lib/packages/ree_dto/package/ree_dto/dto/dto_builder.rb
|
658
|
+
- lib/ree_lib/packages/ree_dto/package/ree_dto/dto/dto_class_methods.rb
|
659
|
+
- lib/ree_lib/packages/ree_dto/package/ree_dto/dto/dto_collection.rb
|
660
|
+
- lib/ree_lib/packages/ree_dto/package/ree_dto/dto/dto_instance_methods.rb
|
661
|
+
- lib/ree_lib/packages/ree_dto/package/ree_dto/dto/field_meta.rb
|
654
662
|
- lib/ree_lib/packages/ree_dto/package/ree_dto/entity_dsl.rb
|
663
|
+
- lib/ree_lib/packages/ree_dto/package/ree_dto/functions/build_dto.rb
|
664
|
+
- lib/ree_lib/packages/ree_dto/package/ree_dto/functions/build_dto_collection_class.rb
|
655
665
|
- lib/ree_lib/packages/ree_dto/spec/package_schema_spec.rb
|
666
|
+
- lib/ree_lib/packages/ree_dto/spec/ree_dto/dsl_spec.rb
|
656
667
|
- lib/ree_lib/packages/ree_dto/spec/ree_dto/entity_dsl_spec.rb
|
657
668
|
- lib/ree_lib/packages/ree_dto/spec/spec_helper.rb
|
658
669
|
- lib/ree_lib/packages/ree_enum/.gitignore
|
@@ -828,6 +839,7 @@ files:
|
|
828
839
|
- lib/ree_lib/packages/ree_mapper/package/ree_mapper/mapper_strategy.rb
|
829
840
|
- lib/ree_lib/packages/ree_mapper/package/ree_mapper/strategy_outputs/hash_output.rb
|
830
841
|
- lib/ree_lib/packages/ree_mapper/package/ree_mapper/strategy_outputs/object_output.rb
|
842
|
+
- lib/ree_lib/packages/ree_mapper/package/ree_mapper/strategy_outputs/ree_dto_output.rb
|
831
843
|
- lib/ree_lib/packages/ree_mapper/package/ree_mapper/strategy_outputs/strategy_output.rb
|
832
844
|
- lib/ree_lib/packages/ree_mapper/package/ree_mapper/strategy_outputs/struct_output.rb
|
833
845
|
- lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/abstract_type.rb
|