ree_lib 1.0.105 → 1.0.106

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/lib/ree_lib/packages/ree_dao/package/ree_dao/dataset_extensions.rb +40 -22
  4. data/lib/ree_lib/packages/ree_dao/spec/ree_dao/functions/one_to_one_spec.rb +5 -9
  5. data/lib/ree_lib/packages/ree_dto/Package.schema.json +17 -2
  6. data/lib/ree_lib/packages/ree_dto/package/ree_dto/dsl.rb +8 -0
  7. data/lib/ree_lib/packages/ree_dto/package/ree_dto/dto/collection_filter.rb +49 -0
  8. data/lib/ree_lib/packages/ree_dto/package/ree_dto/dto/collection_meta.rb +13 -0
  9. data/lib/ree_lib/packages/ree_dto/package/ree_dto/dto/dto_builder.rb +42 -0
  10. data/lib/ree_lib/packages/ree_dto/package/ree_dto/dto/dto_class_methods.rb +27 -0
  11. data/lib/ree_lib/packages/ree_dto/package/ree_dto/dto/dto_collection.rb +45 -0
  12. data/lib/ree_lib/packages/ree_dto/package/ree_dto/dto/dto_instance_methods.rb +111 -0
  13. data/lib/ree_lib/packages/ree_dto/package/ree_dto/dto/field_meta.rb +21 -0
  14. data/lib/ree_lib/packages/ree_dto/package/ree_dto/functions/build_dto.rb +62 -0
  15. data/lib/ree_lib/packages/ree_dto/package/ree_dto/functions/build_dto_collection_class.rb +27 -0
  16. data/lib/ree_lib/packages/ree_dto/package/ree_dto.rb +16 -3
  17. data/lib/ree_lib/packages/ree_dto/spec/ree_dto/dsl_spec.rb +117 -0
  18. data/lib/ree_lib/packages/ree_mapper/Package.schema.json +3 -0
  19. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/mapper.rb +1 -1
  20. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/mapper_strategy.rb +2 -0
  21. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper/strategy_outputs/ree_dto_output.rb +14 -0
  22. data/lib/ree_lib/packages/ree_mapper/package/ree_mapper.rb +2 -0
  23. data/lib/ree_lib/packages/ree_mapper/spec/ree_mapper/mapper_spec.rb +28 -0
  24. data/lib/ree_lib/packages/ree_mapper/spec/ree_mapper/types/hash_spec.rb +1 -1
  25. data/lib/ree_lib/packages/ree_mapper/spec/ree_mapper/types/type_options_spec.rb +1 -1
  26. data/lib/ree_lib/version.rb +1 -1
  27. metadata +14 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 442200670979210f1a6c213d404dc6f7739055cf22bf5b11705af43aea6eee35
4
- data.tar.gz: d310e3ca905adf0dca68296732d5050f24cb82e1bcef6f0405afef3024f8ba7e
3
+ metadata.gz: 558dbd22038f40db477b968aa0595ca664484c05606827276b2d872ad6eb5dab
4
+ data.tar.gz: f190528d1cf21d60f1403252fd16d6e9e62beb291b4cddcca2af03f77bddce85
5
5
  SHA512:
6
- metadata.gz: cd6611723dcb21b52ec19bf3d095417261eebfdc1ac9a7b063ba2ef87a223593227a8e5a57e815108be6fc92529b5c1c5c422bef987bceee5330f5e5e6ea0b64
7
- data.tar.gz: db2b6aae15c2c18531ffe160c903acc3c5f559853c66da2acbdb53ae8239ab828c63af4d8a2bd6c535ef50a6b4ea51a97d4ea6595118592e42bc93ee91197985
6
+ metadata.gz: b5f0cf27786a919265f1c01982c7f8640e5b27edf837ed9a207243e6c513f3a97ab2487ad4c16ebc01feb7d7641509d38b336ef77024fb325b059aa32a61d96e
7
+ data.tar.gz: 6ddd53ea7f83cded5374da832c06ef845062c2574e832ccfec241602722a63a32390f78f02fc50f2d030f94b7e1cf404e976c00fa3237c11607cd2fa96504ee7
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ree_lib (1.0.105)
4
+ ree_lib (1.0.106)
5
5
  bigdecimal (~> 3.1.6)
6
6
  binding_of_caller (~> 1.0.0)
7
7
  i18n (~> 1.14.1)
@@ -44,7 +44,8 @@ module ReeDao
44
44
  end
45
45
 
46
46
  def put(entity)
47
- raw = opts[:schema_mapper].db_dump(entity)
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 = mapper.db_dump(entity)
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
- return __original_update(hash_or_entity) if !opts[:schema_mapper]
120
- return __original_update(hash_or_entity) if hash_or_entity.is_a?(Hash)
118
+ if !opts[:schema_mapper] || hash_or_entity.is_a?(Hash)
119
+ return __original_update(hash_or_entity)
120
+ end
121
121
 
122
- raw = opts[:schema_mapper].db_dump(hash_or_entity)
123
- raw = extract_changes(hash_or_entity, raw)
122
+ dump = dump_entity(hash_or_entity)
123
+ raw = extract_changes(hash_or_entity, dump)
124
124
 
125
- unless raw.empty?
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
- return hash unless entity.instance_variable_defined?(PERSISTENCE_STATE_VARIABLE)
224
- changes = {}
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
- persistence_state = entity.instance_variable_get(PERSISTENCE_STATE_VARIABLE)
234
+ persistence_state = entity.instance_variable_get(PERSISTENCE_STATE_VARIABLE)
227
235
 
228
- hash.each do |column, value|
229
- previous_column_value = persistence_state[column]
236
+ hash.each do |column, value|
237
+ previous_column_value = persistence_state[column]
230
238
 
231
- if persistence_state.has_key?(column)
232
- if previous_column_value != value || value.respond_to?(:each)
233
- changes[column] = value
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
- changes
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::EntityDSL
93
-
94
- properties(
95
- id: Nilor[Integer],
96
- project_id: Nilor[Integer]
97
- )
93
+ include ReeDto::DSL
98
94
 
99
- contract Integer => Integer
100
- def project_id=(id)
101
- @project_id = id
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,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ReeDto::DSL
4
+ def self.included(base)
5
+ base.include Ree::LinkDSL
6
+ base.link :build_dto, from: :ree_dto
7
+ end
8
+ end
@@ -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
@@ -10,6 +10,9 @@
10
10
  {
11
11
  "name": "ree_datetime"
12
12
  },
13
+ {
14
+ "name": "ree_dto"
15
+ },
13
16
  {
14
17
  "name": "ree_string"
15
18
  }
@@ -55,7 +55,7 @@ class ReeMapper::Mapper
55
55
  else
56
56
  if !field.optional && !@#{method}_strategy.always_optional
57
57
  raise ReeMapper::TypeError.new(
58
- "is missing required field",
58
+ "is missing (required field)",
59
59
  field.location,
60
60
  [field.from_as_str]
61
61
  )
@@ -68,6 +68,8 @@ class ReeMapper::MapperStrategy
68
68
  ReeMapper::HashOutput.new(dto)
69
69
  elsif dto == Struct
70
70
  ReeMapper::StructOutput.new
71
+ elsif dto.include?(ReeDto::DSL)
72
+ ReeMapper::ReeDtoOutput.new(dto)
71
73
  else
72
74
  ReeMapper::ObjectOutput.new(dto)
73
75
  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 {
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ReeLib
4
- VERSION = "1.0.105"
4
+ VERSION = "1.0.106"
5
5
  end
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.105
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-06 00:00:00.000000000 Z
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