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.
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