ree_lib 1.0.54 → 1.0.56

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c1de406ab816861e6a42c542db02cddbc0f68f6aeca80bff9dba9e8c76ef81a
4
- data.tar.gz: 05dba8342aa073f8198c42d051ca9783396b40bc8a939edd64b3ddc42d35a868
3
+ metadata.gz: 23c4603ab3ab820869ca4478b42b3d350c5484c8da7b21c99b469449f45764a1
4
+ data.tar.gz: 4293f4d083185f09f6098e11698b5f35981a748b00ff84e486389bab19445751
5
5
  SHA512:
6
- metadata.gz: cf9b082920297cca214ab5a21ce389d77833936d12c5b6583f2edcc34b3e794b1ac7565a1ae9c66745f0cd43010658f1bf9fbdee9af23cf82b7d7537529bc58e
7
- data.tar.gz: ded7b6e245997cad79fbd20cfa8ad33f4b89b402985f9a133a0d55b6daee823764d5e0888cfed8eb0761f1d4108edeb176aaf61595864820d5562d516b907295
6
+ metadata.gz: fe885988996c1f0b9aeba40c9f61e119ab4ffc02aa471afe3774b470192fc5601093c1b13673febebad76bf348baa0be61cb2e19a5abf81b1970b518d86ddc9d
7
+ data.tar.gz: acbdf84eacfa6c46c6997999af1e0eb5dc03eedc95b95d954927a3907048c9c28d924328041203d02db1bd5c9dc78310c2cc234db826c74e585a8701b7982ed9
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ree_lib (1.0.54)
4
+ ree_lib (1.0.56)
5
5
  binding_of_caller (~> 1.0.0)
6
6
  i18n (~> 1.12.0)
7
7
  loofah (~> 2.18.0)
@@ -38,6 +38,8 @@ GEM
38
38
  crass (~> 1.0.2)
39
39
  nokogiri (>= 1.5.9)
40
40
  msgpack (1.6.0)
41
+ nokogiri (1.15.3-x86_64-darwin)
42
+ racc (~> 1.4)
41
43
  nokogiri (1.15.3-x86_64-linux)
42
44
  racc (~> 1.4)
43
45
  oj (3.13.23)
@@ -26,8 +26,12 @@ module ReeDao
26
26
  load_association(assoc_type, assoc_name, **opts, &block)
27
27
  end
28
28
 
29
+ def handle_field(assoc_name, proc)
30
+ proc.call
31
+ end
32
+
29
33
  contract(
30
- Or[:belongs_to, :has_one, :has_many, :field],
34
+ Or[:belongs_to, :has_one, :has_many],
31
35
  Symbol,
32
36
  Ksplat[RestKeys => Any],
33
37
  Optblock => Nilor[Array]
@@ -56,6 +60,7 @@ module ReeDao
56
60
  assoc_name,
57
61
  list,
58
62
  scope: opts[:scope],
63
+ primary_key: opts[:primary_key],
59
64
  foreign_key: opts[:foreign_key],
60
65
  setter: opts[:setter],
61
66
  reverse: false
@@ -65,6 +70,7 @@ module ReeDao
65
70
  assoc_name,
66
71
  list,
67
72
  scope: opts[:scope],
73
+ primary_key: opts[:primary_key],
68
74
  foreign_key: opts[:foreign_key],
69
75
  setter: opts[:setter],
70
76
  reverse: true
@@ -74,6 +80,7 @@ module ReeDao
74
80
  assoc_name,
75
81
  list,
76
82
  scope: opts[:scope],
83
+ primary_key: opts[:primary_key],
77
84
  foreign_key: opts[:foreign_key],
78
85
  setter: opts[:setter]
79
86
  )
@@ -82,16 +89,16 @@ module ReeDao
82
89
  assoc_name,
83
90
  list,
84
91
  scope: opts[:scope],
92
+ primary_key: opts[:primary_key],
85
93
  foreign_key: opts[:foreign_key],
86
- setter: opts[:setter],
87
- skip_dao: true
94
+ setter: opts[:setter]
88
95
  )
89
96
  end
90
97
  end
91
98
 
92
- contract(Hash, Bool, Block => Any)
99
+ contract(Or[Hash, Array], Bool, Block => Any)
93
100
  def process_block(assoc, autoload_children, &block)
94
- assoc_list = assoc.values.flatten
101
+ assoc_list = assoc.is_a?(Array) ? assoc : assoc.values.flatten
95
102
 
96
103
  if ReeDao::Associations.sync_mode?
97
104
  ReeDao::Associations.new(
@@ -108,7 +115,7 @@ module ReeDao
108
115
  parent.local_vars,
109
116
  autoload_children,
110
117
  **global_opts
111
- ).instance_exec(assoc_list, &block).map(&:join)
118
+ ).instance_exec(assoc_list, &block)[:association_threads].map(&:join)
112
119
  end
113
120
  end
114
121
 
@@ -116,59 +123,66 @@ module ReeDao
116
123
  Symbol,
117
124
  Array,
118
125
  Kwargs[
126
+ primary_key: Nilor[Symbol],
119
127
  foreign_key: Nilor[Symbol],
120
- scope: Nilor[Sequel::Dataset],
128
+ scope: Nilor[Sequel::Dataset, Array],
121
129
  setter: Nilor[Or[Symbol, Proc]],
122
130
  reverse: Bool
123
- ] => Hash
124
- )
125
- def one_to_one(
126
- assoc_name,
127
- list,
128
- scope: nil,
129
- foreign_key: nil,
130
- setter: nil,
131
- reverse: true
131
+ ] => Or[Hash, Array]
132
132
  )
133
+ def one_to_one(assoc_name, list, scope: nil, primary_key: :id, foreign_key: nil, setter: nil, reverse: true)
133
134
  return {} if list.empty?
134
135
 
135
- assoc_dao = find_dao(assoc_name, parent, scope)
136
-
137
- if reverse
138
- if !foreign_key
139
- name = underscore(demodulize(list.first.class.name))
140
- foreign_key = "#{name}_id".to_sym
141
- end
136
+ primary_key ||= :id
142
137
 
143
- root_ids = list.map(&:id).uniq
138
+ if scope.is_a?(Array)
139
+ items = scope
144
140
  else
145
- if !foreign_key
146
- dto_class = assoc_dao
147
- .opts[:schema_mapper]
148
- .dto(:db_load)
141
+ assoc_dao = find_dao(assoc_name, parent, scope)
142
+
143
+ if reverse
144
+ if !foreign_key
145
+ name = underscore(demodulize(list.first.class.name))
146
+ foreign_key = "#{name}_id".to_sym
147
+ end
149
148
 
150
- name = underscore(demodulize(dto_class.name))
151
-
152
- root_ids = list.map(&:"#{"#{name}_id".to_sym}").uniq
153
- foreign_key = :id
149
+ root_ids = list.map(&:id).uniq
154
150
  else
155
- root_ids = list.map(&:"#{foreign_key}")
156
- foreign_key = :id
151
+ if !foreign_key
152
+ dto_class = assoc_dao
153
+ .opts[:schema_mapper]
154
+ .dto(:db_load)
155
+
156
+ name = underscore(demodulize(dto_class.name))
157
+
158
+ root_ids = list.map(&:"#{"#{name}_id".to_sym}").uniq
159
+ foreign_key = :id
160
+ else
161
+ root_ids = list.map(&:"#{foreign_key}")
162
+ foreign_key = :id
163
+ end
164
+ end
165
+
166
+ default_scope = if !scope
167
+ assoc_dao&.where(foreign_key => root_ids)
157
168
  end
169
+
170
+ items = add_scopes(default_scope, scope, global_opts[assoc_name])
158
171
  end
159
172
 
160
- default_scope = assoc_dao&.where(foreign_key => root_ids)
161
-
162
- items = add_scopes(default_scope, scope, global_opts[assoc_name])
163
-
164
- assoc = index_by(items) { _1.send(foreign_key) }
173
+ assoc = if foreign_key
174
+ index_by(items) { _1.send(foreign_key) }
175
+ else
176
+ items
177
+ end
165
178
 
166
179
  populate_association(
167
180
  list,
168
181
  assoc,
169
182
  assoc_name,
170
183
  setter: setter,
171
- reverse: reverse
184
+ reverse: reverse,
185
+ primary_key: primary_key
172
186
  )
173
187
 
174
188
  assoc
@@ -179,39 +193,45 @@ module ReeDao
179
193
  Array,
180
194
  Kwargs[
181
195
  foreign_key: Nilor[Symbol],
182
- scope: Nilor[Sequel::Dataset],
183
- setter: Nilor[Or[Symbol, Proc]],
184
- skip_dao: Bool
185
- ] => Hash
186
- )
187
- def one_to_many(
188
- assoc_name,
189
- list,
190
- foreign_key: nil,
191
- scope: nil,
192
- setter: nil,
193
- skip_dao: false
196
+ primary_key: Nilor[Symbol],
197
+ scope: Nilor[Sequel::Dataset, Array],
198
+ setter: Nilor[Or[Symbol, Proc]]
199
+ ] => Or[Hash, Array]
194
200
  )
201
+ def one_to_many(assoc_name, list, primary_key: nil, foreign_key: nil, scope: nil, setter: nil)
195
202
  return {} if list.empty?
196
203
 
197
- assoc_dao = nil
198
- assoc_dao = find_dao(assoc_name, parent, scope) if !skip_dao
204
+ primary_key ||= :id
199
205
 
200
- foreign_key ||= "#{underscore(demodulize(list.first.class.name))}_id".to_sym
201
-
202
- root_ids = list.map(&:id)
203
-
204
- default_scope = assoc_dao&.where(foreign_key => root_ids)
205
-
206
- items = add_scopes(default_scope, scope, global_opts[assoc_name])
206
+ if scope.is_a?(Array)
207
+ items = scope
208
+ else
209
+ assoc_dao = nil
210
+ assoc_dao = find_dao(assoc_name, parent, scope)
211
+
212
+ foreign_key ||= "#{underscore(demodulize(list.first.class.name))}_id".to_sym
213
+
214
+ root_ids = list.map(&:"#{primary_key}")
215
+
216
+ default_scope = if !scope
217
+ assoc_dao&.where(foreign_key => root_ids)
218
+ end
219
+
220
+ items = add_scopes(default_scope, scope, global_opts[assoc_name])
221
+ end
207
222
 
208
- assoc = group_by(items) { _1.send(foreign_key) }
223
+ assoc = if foreign_key
224
+ group_by(items) { _1.send(foreign_key) }
225
+ else
226
+ items
227
+ end
209
228
 
210
229
  populate_association(
211
230
  list,
212
231
  assoc,
213
232
  assoc_name,
214
- setter: setter
233
+ setter: setter,
234
+ primary_key: primary_key
215
235
  )
216
236
 
217
237
  assoc
@@ -219,20 +239,15 @@ module ReeDao
219
239
 
220
240
  contract(
221
241
  Array,
222
- Hash,
242
+ Or[Hash, Array],
223
243
  Symbol,
224
244
  Kwargs[
245
+ primary_key: Nilor[Symbol],
225
246
  reverse: Nilor[Bool],
226
247
  setter: Nilor[Or[Symbol, Proc]]
227
248
  ] => Any
228
249
  )
229
- def populate_association(
230
- list,
231
- association_index,
232
- assoc_name,
233
- reverse: nil,
234
- setter: nil
235
- )
250
+ def populate_association(list, association_index, assoc_name, primary_key: nil, reverse: nil, setter: nil)
236
251
  assoc_setter = if setter
237
252
  setter
238
253
  else
@@ -244,37 +259,25 @@ module ReeDao
244
259
  self.instance_exec(item, association_index, &assoc_setter)
245
260
  else
246
261
  key = if reverse.nil?
247
- :id
262
+ primary_key
248
263
  else
249
- reverse ? :id : "#{assoc_name}_id"
264
+ reverse ? primary_key : "#{assoc_name}_id"
250
265
  end
251
266
  value = association_index[item.send(key)]
252
267
  next if value.nil?
253
268
 
254
- item.send(assoc_setter, value)
269
+ begin
270
+ item.send(assoc_setter, value)
271
+ rescue NoMethodError
272
+ item.send("#{assoc_name}=", value)
273
+ end
255
274
  end
256
275
  end
257
276
  end
258
277
 
259
278
  contract(Nilor[Sequel::Dataset], Nilor[Sequel::Dataset], Nilor[Proc] => Array)
260
279
  def add_scopes(default_scope, scope, named_scope)
261
- if default_scope && !scope
262
- res = default_scope
263
- end
264
-
265
- if default_scope && scope
266
- if scope == []
267
- res = default_scope
268
- else
269
- res = merge_scopes(default_scope, scope)
270
- end
271
- end
272
-
273
- if !default_scope && scope
274
- return [] if scope.empty?
275
-
276
- res = scope
277
- end
280
+ res = scope || default_scope
278
281
 
279
282
  if named_scope
280
283
  res = named_scope.call(res)
@@ -283,18 +286,6 @@ module ReeDao
283
286
  res.all
284
287
  end
285
288
 
286
- def merge_scopes(s1, s2)
287
- if s2.opts[:where]
288
- s1 = s1.where(s2.opts[:where])
289
- end
290
-
291
- if s2.opts[:order]
292
- s1 = s1.order(*s2.opts[:order])
293
- end
294
-
295
- s1
296
- end
297
-
298
289
  def find_dao(assoc_name, parent, scope)
299
290
  dao_from_name = parent.instance_variable_get("@#{assoc_name}") || parent.instance_variable_get("@#{assoc_name}s")
300
291
  return dao_from_name if dao_from_name
@@ -307,5 +298,11 @@ module ReeDao
307
298
 
308
299
  raise ArgumentError, "can't find DAO for :#{assoc_name}, provide correct scope or association name"
309
300
  end
301
+
302
+ def method_missing(method, *args, &block)
303
+ return super if !parent.agg_caller.private_methods(false).include?(method)
304
+
305
+ parent.agg_caller.send(method, *args, &block)
306
+ end
310
307
  end
311
308
  end
@@ -8,14 +8,19 @@ module ReeDao
8
8
  @agg_caller = agg_caller
9
9
  @list = list
10
10
  @local_vars = local_vars
11
- @threads = [] if !self.class.sync_mode?
12
- @global_opts = opts
11
+ @global_opts = opts || {}
13
12
  @only = opts[:only] if opts[:only]
14
13
  @except = opts[:except] if opts[:except]
15
14
  @autoload_children = autoload_children
16
15
 
17
16
  raise ArgumentError.new("you can't use both :only and :except arguments at the same time") if @only && @except
18
17
 
18
+
19
+ if !self.class.sync_mode?
20
+ @assoc_threads = []
21
+ @field_threads = []
22
+ end
23
+
19
24
  local_vars.each do |k, v|
20
25
  instance_variable_set(k, v)
21
26
 
@@ -31,58 +36,34 @@ module ReeDao
31
36
 
32
37
  contract(
33
38
  Symbol,
34
- Ksplat[
35
- scope?: Sequel::Dataset,
36
- setter?: Or[Symbol, Proc],
37
- foreign_key?: Symbol,
38
- autoload_children?: Bool
39
- ],
39
+ Nilor[Proc, Sequel::Dataset],
40
40
  Optblock => Any
41
41
  )
42
- def belongs_to(assoc_name, **opts, &block)
43
- association(__method__, assoc_name, **opts, &block)
42
+ def belongs_to(assoc_name, opts = nil, &block)
43
+ association(__method__, assoc_name, opts, &block)
44
44
  end
45
45
 
46
46
  contract(
47
47
  Symbol,
48
- Ksplat[
49
- scope?: Sequel::Dataset,
50
- setter?: Or[Symbol, Proc],
51
- foreign_key?: Symbol,
52
- autoload_children?: Bool
53
- ],
48
+ Nilor[Proc, Sequel::Dataset],
54
49
  Optblock => Any
55
50
  )
56
- def has_one(assoc_name, **opts, &block)
57
- association(__method__, assoc_name, **opts, &block)
51
+ def has_one(assoc_name, opts = nil, &block)
52
+ association(__method__, assoc_name, opts, &block)
58
53
  end
59
54
 
60
55
  contract(
61
56
  Symbol,
62
- Ksplat[
63
- scope?: Sequel::Dataset,
64
- setter?: Or[Symbol, Proc],
65
- foreign_key?: Symbol,
66
- autoload_children?: Bool
67
- ],
57
+ Nilor[Proc, Sequel::Dataset],
68
58
  Optblock => Any
69
59
  )
70
- def has_many(assoc_name, **opts, &block)
71
- association(__method__, assoc_name, **opts, &block)
60
+ def has_many(assoc_name, opts = nil, &block)
61
+ association(__method__, assoc_name, opts, &block)
72
62
  end
73
63
 
74
- contract(
75
- Symbol,
76
- Ksplat[
77
- scope?: Sequel::Dataset,
78
- setter?: Or[Symbol, Proc],
79
- foreign_key?: Symbol,
80
- autoload_children?: Bool
81
- ],
82
- Optblock => Any
83
- )
84
- def field(assoc_name, **opts, &block)
85
- association(__method__, assoc_name, **opts, &block)
64
+ contract(Symbol, Proc => Any)
65
+ def field(assoc_name, proc)
66
+ association(__method__, assoc_name, proc)
86
67
  end
87
68
 
88
69
  private
@@ -95,26 +76,40 @@ module ReeDao
95
76
  :field
96
77
  ],
97
78
  Symbol,
98
- Ksplat[
99
- scope?: Sequel::Dataset,
100
- setter?: Or[Symbol, Proc],
101
- foreign_key?: Symbol,
102
- autoload_children?: Bool
103
- ],
79
+ Nilor[Proc, Sequel::Dataset],
104
80
  Optblock => Any
105
81
  )
106
- def association(assoc_type, assoc_name, **assoc_opts, &block)
82
+ def association(assoc_type, assoc_name, opts, &block)
107
83
  if self.class.sync_mode?
108
84
  return if association_is_not_included?(assoc_name) || list.empty?
109
-
85
+
110
86
  association = Association.new(self, list, **global_opts)
111
- association.load(assoc_type, assoc_name, **assoc_opts, &block)
87
+ if assoc_type == :field
88
+ association.handle_field(assoc_name, opts)
89
+ else
90
+ association.load(assoc_type, assoc_name, **get_assoc_opts(opts), &block)
91
+ end
112
92
  else
113
- return @threads if association_is_not_included?(assoc_name) || list.empty?
93
+ if association_is_not_included?(assoc_name) || list.empty?
94
+ return { association_threads: @assoc_threads, field_threads: @field_threads }
95
+ end
96
+
97
+ association = Association.new(self, list, **global_opts)
114
98
 
115
- @threads << Thread.new do
116
- association = Association.new(self, list, **global_opts)
117
- association.load(assoc_type, assoc_name, **assoc_opts, &block)
99
+ if assoc_type == :field
100
+ {
101
+ association_threads: @assoc_threads,
102
+ field_threads: @field_threads << Thread.new do
103
+ association.handle_field(assoc_name, opts)
104
+ end
105
+ }
106
+ else
107
+ {
108
+ association_threads: @assoc_threads << Thread.new do
109
+ association.load(assoc_type, assoc_name, **get_assoc_opts(opts), &block)
110
+ end,
111
+ field_threads: @field_threads
112
+ }
118
113
  end
119
114
  end
120
115
  end
@@ -144,5 +139,13 @@ module ReeDao
144
139
 
145
140
  agg_caller.send(method, *args, &block)
146
141
  end
142
+
143
+ def get_assoc_opts(opts)
144
+ if opts.is_a?(Proc)
145
+ opts.call
146
+ else
147
+ {}
148
+ end
149
+ end
147
150
  end
148
151
  end
@@ -151,6 +151,26 @@ module ReeDao
151
151
  for_update
152
152
  end
153
153
 
154
+ def with_mapper(mapper)
155
+ clone(
156
+ schema_mapper: mapper || opts[:schema_mapper],
157
+ ).with_row_proc(
158
+ Proc.new { |hash|
159
+ m = mapper || opts[:schema_mapper]
160
+
161
+ if m
162
+ entity = m.db_load(hash)
163
+
164
+ self.set_entity_cache(entity, hash)
165
+
166
+ entity
167
+ else
168
+ hash
169
+ end
170
+ }
171
+ )
172
+ end
173
+
154
174
  private
155
175
 
156
176
  def __ree_dao_cache
@@ -179,26 +199,6 @@ module ReeDao
179
199
  end
180
200
  end
181
201
 
182
- def with_mapper(mapper)
183
- clone(
184
- schema_mapper: mapper || opts[:schema_mapper],
185
- ).with_row_proc(
186
- Proc.new { |hash|
187
- m = mapper || opts[:schema_mapper]
188
-
189
- if m
190
- entity = m.db_load(hash)
191
-
192
- self.set_entity_cache(entity, hash)
193
-
194
- entity
195
- else
196
- hash
197
- end
198
- }
199
- )
200
- end
201
-
202
202
  def extract_primary_key(entity)
203
203
  if primary_key.is_a?(Array)
204
204
  primary_key.map do |key|
@@ -10,16 +10,17 @@ class ReeDao::LoadAgg
10
10
  end
11
11
 
12
12
  contract(
13
- Or[Sequel::Dataset, ArrayOf[Integer], ArrayOf[EntityContract], Integer],
14
13
  Nilor[DaoDatasetContract],
14
+ Or[Sequel::Dataset, ArrayOf[Integer], ArrayOf[EntityContract], Integer],
15
15
  Ksplat[
16
16
  only?: ArrayOf[Symbol],
17
17
  except?: ArrayOf[Symbol],
18
+ to_dto?: Proc,
18
19
  RestKeys => Any
19
20
  ],
20
21
  Optblock => ArrayOf[Any]
21
22
  )
22
- def call(ids_or_scope, dao = nil, **opts, &block)
23
+ def call(dao = nil, ids_or_scope, **opts, &block)
23
24
  scope = if ids_or_scope.is_a?(Array) && ids_or_scope.any? { _1.is_a?(Integer) }
24
25
  raise ArgumentError.new("Dao should be provided") if dao.nil?
25
26
  return [] if ids_or_scope.empty?
@@ -35,6 +36,10 @@ class ReeDao::LoadAgg
35
36
 
36
37
  list = scope.is_a?(Sequel::Dataset) ? scope.all : scope
37
38
 
39
+ if opts[:to_dto]
40
+ list = opts[:to_dto].call(list)
41
+ end
42
+
38
43
  load_associations(list, **opts, &block) if block_given?
39
44
 
40
45
  if ids_or_scope.is_a?(Array)
@@ -61,7 +66,8 @@ class ReeDao::LoadAgg
61
66
  if ReeDao.load_sync_associations_enabled?
62
67
  associations
63
68
  else
64
- associations.map(&:join)
69
+ associations[:association_threads].map(&:join)
70
+ associations[:field_threads].map(&:join)
65
71
  end
66
72
  end
67
73
  end
@@ -14,20 +14,20 @@
14
14
  ],
15
15
  "return": "ArrayOf[Any]",
16
16
  "args": [
17
- {
18
- "arg": "ids_or_scope",
19
- "arg_type": "req",
20
- "type": "Or[Sequel::Dataset, ArrayOf[Integer], ArrayOf[PackageName::Entity], Integer]"
21
- },
22
17
  {
23
18
  "arg": "dao",
24
19
  "arg_type": "opt",
25
20
  "type": "Nilor[PackageName::DaoName::Dao: \"SELECT * FROM `table`\"]"
26
21
  },
22
+ {
23
+ "arg": "ids_or_scope",
24
+ "arg_type": "req",
25
+ "type": "Or[Sequel::Dataset, ArrayOf[Integer], ArrayOf[PackageName::Entity], Integer]"
26
+ },
27
27
  {
28
28
  "arg": "opts",
29
29
  "arg_type": "keyrest",
30
- "type": "Ksplat[:only? => ArrayOf[Symbol], :except? => ArrayOf[Symbol], \"RestKeys\" => Any]"
30
+ "type": "Ksplat[:only? => ArrayOf[Symbol], :except? => ArrayOf[Symbol], :to_dto? => Proc, \"RestKeys\" => Any]"
31
31
  },
32
32
  {
33
33
  "arg": "block",
@@ -99,19 +99,67 @@ RSpec.describe :load_agg do
99
99
  end
100
100
 
101
101
  def call(ids_or_scope, **opts)
102
- load_agg(ids_or_scope, users, **opts) do
102
+ load_agg(users, ids_or_scope, **opts) do |users_list|
103
103
  belongs_to :organization
104
- has_many :books do
104
+ has_many :books do |books_list|
105
105
  has_one :author
106
106
  has_many :chapters
107
107
 
108
- has_many :reviews do
108
+ has_many :reviews do |reviews_list|
109
109
  has_one :review_author
110
+
111
+ field :review_calculatetable_field, -> { some_method(reviews_list) }
110
112
  end
113
+
114
+ field :book_calculatetable_field, -> { change_book_titles(books_list) }
111
115
  end
112
116
 
113
- has_one :passport, foreign_key: :user_id, scope: user_passports
114
- field :custom_field, scope: books.where(title: "1984")
117
+ has_one :passport, -> { passport_opts }
118
+ has_one :custom_field, -> { custom_field_opts }
119
+
120
+ field :user_calculatetable_field, -> { some_method(users_list) }
121
+ end
122
+ end
123
+
124
+ private
125
+
126
+ def change_book_titles(books_list)
127
+ books_list.each do |book|
128
+ book.title = "#{book.title.upcase} changed"
129
+ end
130
+ end
131
+
132
+ def some_method(list)
133
+ puts list.map(&:id)
134
+ puts list.map { _1.class.name }
135
+ end
136
+
137
+ def passport_opts
138
+ {
139
+ foreign_key: :user_id,
140
+ scope: user_passports
141
+ }
142
+ end
143
+
144
+ def custom_field_opts
145
+ {
146
+ scope: books.where(title: "1984")
147
+ }
148
+ end
149
+ end
150
+
151
+ class ReeDaoLoadAggTest::UsersAggWithDto
152
+ include ReeDao::AggregateDSL
153
+
154
+ aggregate :users_agg_with_dto do
155
+ link :users, from: :ree_dao_load_agg_test
156
+ link :organizations, from: :ree_dao_load_agg_test
157
+ link :load_agg, from: :ree_dao
158
+ end
159
+
160
+ def call(ids_or_scope, **opts)
161
+ load_agg(users, ids_or_scope, **opts) do
162
+ belongs_to :organization
115
163
  end
116
164
  end
117
165
  end
@@ -130,9 +178,9 @@ RSpec.describe :load_agg do
130
178
  end
131
179
 
132
180
  def call(ids_or_scope, **opts)
133
- load_agg(ids_or_scope, users, **opts) do
181
+ load_agg(users, ids_or_scope, **opts) do
134
182
  belongs_to :organization
135
- has_many :books, autoload_children: true do
183
+ has_many :books, -> { books_opts } do
136
184
  has_one :author
137
185
  has_many :chapters
138
186
 
@@ -142,6 +190,12 @@ RSpec.describe :load_agg do
142
190
  end
143
191
  end
144
192
  end
193
+
194
+ private
195
+
196
+ def books_opts
197
+ { autoload_children: true }
198
+ end
145
199
  end
146
200
 
147
201
  class ReeDaoLoadAggTest::UsersAggAutoloadReviewsChildren
@@ -158,18 +212,24 @@ RSpec.describe :load_agg do
158
212
  end
159
213
 
160
214
  def call(ids_or_scope, **opts)
161
- load_agg(ids_or_scope, users, **opts) do
215
+ load_agg(users, ids_or_scope, **opts) do
162
216
  belongs_to :organization
163
217
  has_many :books do
164
218
  has_one :author
165
219
  has_many :chapters
166
220
 
167
- has_many :reviews, autoload_children: true do
221
+ has_many :reviews, -> { { autoload_children: true } } do
168
222
  has_one :review_author
169
223
  end
170
224
  end
171
225
  end
172
226
  end
227
+
228
+ private
229
+
230
+ def reviews_opts
231
+ { autoload_children: true }
232
+ end
173
233
  end
174
234
 
175
235
  class ReeDaoLoadAggTest::UsersAggBlockTest
@@ -183,13 +243,22 @@ RSpec.describe :load_agg do
183
243
  end
184
244
 
185
245
  def call(ids_or_scope, **opts)
186
- load_agg(ids_or_scope, users, **opts) do
246
+ load_agg(users, ids_or_scope, **opts) do
187
247
  belongs_to :organization
188
- has_many :books, setter: -> (item, items_index) {
189
- item.set_books([1337, 1337])
190
- }
248
+ has_many :books, -> { books_opts }
191
249
  end
192
250
  end
251
+
252
+ private
253
+
254
+ def books_opts
255
+ {
256
+ setter: -> (item, items_index) {
257
+ b = items_index[item.id].each { |b| b.title = "Changed" }
258
+ item.set_books(b)
259
+ }
260
+ }
261
+ end
193
262
  end
194
263
 
195
264
  class ReeDaoLoadAggTest::UsersAggScopeMethodTest
@@ -203,17 +272,21 @@ RSpec.describe :load_agg do
203
272
  end
204
273
 
205
274
  def call(ids_or_scope, **opts)
206
- load_agg(ids_or_scope, users, **opts) do |agg_list|
275
+ load_agg(users, ids_or_scope, **opts) do |agg_list|
207
276
  some_id = agg_list.first.id
208
277
  title = "1984"
209
278
  belongs_to :organization
210
279
 
211
- has_many :books, scope: books_scope(title)
280
+ has_many :books, -> { books_opts(title) }
212
281
  end
213
282
  end
214
283
 
215
284
  private
216
285
 
286
+ def books_opts(title)
287
+ { scope: books_scope(title) }
288
+ end
289
+
217
290
  def books_scope(title)
218
291
  books.where(title: title)
219
292
  end
@@ -230,7 +303,7 @@ RSpec.describe :load_agg do
230
303
  end
231
304
 
232
305
  def call(ids_or_scope, **opts)
233
- load_agg(ids_or_scope, users, **opts) do
306
+ load_agg(users, ids_or_scope, **opts) do
234
307
  has_many :something
235
308
  end
236
309
  end
@@ -242,6 +315,7 @@ RSpec.describe :load_agg do
242
315
  let(:users_agg_autoload_books_children) { ReeDaoLoadAggTest::UsersAggAutoloadBooksChildren.new }
243
316
  let(:users_agg_autoload_reviews_children) { ReeDaoLoadAggTest::UsersAggAutoloadReviewsChildren.new }
244
317
  let(:users_agg_without_dao) { ReeDaoLoadAggTest::UsersAggWithoutDao.new }
318
+ let(:users_agg_with_dto) { ReeDaoLoadAggTest::UsersAggWithDto.new }
245
319
  let(:organizations) { ReeDaoLoadAggTest::Organizations.new }
246
320
  let(:users) { ReeDaoLoadAggTest::Users.new }
247
321
  let(:user_passports) { ReeDaoLoadAggTest::UserPassports.new }
@@ -266,6 +340,37 @@ RSpec.describe :load_agg do
266
340
  }.to raise_error(ArgumentError)
267
341
  }
268
342
 
343
+ it {
344
+ organizations.delete_all
345
+ users.delete_all
346
+
347
+ organization = ReeDaoLoadAggTest::Organization.new(name: "Test Org")
348
+ organizations.put(organization)
349
+
350
+ user_1 = ReeDaoLoadAggTest::User.new(name: "John", age: 33, organization_id: organization.id)
351
+ user_2 = ReeDaoLoadAggTest::User.new(name: "Sam", age: 21, organization_id: organization.id)
352
+ users.put(user_1)
353
+ users.put(user_2)
354
+
355
+ res = users_agg_with_dto.call(
356
+ users.all,
357
+ to_dto: -> (users) {
358
+ users.map do |user|
359
+ ReeDaoLoadAggTest::UserDto.new(
360
+ id: user.id,
361
+ name: user.name,
362
+ organization_id: user.organization_id,
363
+ full_name: user.name
364
+ )
365
+ end
366
+ }
367
+ )
368
+
369
+ res_user = res[0]
370
+
371
+ expect(res_user.class).to eq(ReeDaoLoadAggTest::UserDto)
372
+ }
373
+
269
374
  it {
270
375
  organizations.delete_all
271
376
  users.delete_all
@@ -316,6 +421,7 @@ RSpec.describe :load_agg do
316
421
  expect(res_user.passport).to eq(passport_1)
317
422
  expect(res_user.passport.info).to eq("some info")
318
423
  expect(res_user.books.count).to eq(2)
424
+ expect(res_user.books.map(&:title)).to eq(["1984 changed", "1408 changed"])
319
425
  expect(res_user.books[0].author.name).to eq("George Orwell")
320
426
  expect(res_user.books[0].chapters.map(&:title)).to eq(["beginning"])
321
427
  expect(res_user.books[0].reviews[0].review_author.name).to eq("John Review")
@@ -540,7 +646,7 @@ RSpec.describe :load_agg do
540
646
  res = users_agg_block.call(user_1.id)
541
647
 
542
648
  u = res[0]
543
- expect(u.books).to eq([1337, 1337])
649
+ expect(u.books.map(&:title)).to eq(["Changed"])
544
650
  }
545
651
 
546
652
  it {
@@ -594,7 +700,7 @@ RSpec.describe :load_agg do
594
700
 
595
701
  ids = [user_1, user_2].map(&:id)
596
702
 
597
- res = load_agg(ids, users)
703
+ res = load_agg(users, ids)
598
704
  expect(res.count).to eq(2)
599
705
  }
600
706
 
@@ -608,7 +714,7 @@ RSpec.describe :load_agg do
608
714
  user_1 = ReeDaoLoadAggTest::User.new(name: "John", age: 33, organization_id: organization.id)
609
715
  users.put(user_1)
610
716
 
611
- res = load_agg(user_1.id, users)
717
+ res = load_agg(users, user_1.id)
612
718
  expect(res.count).to eq(1)
613
719
  }
614
720
 
@@ -624,7 +730,7 @@ RSpec.describe :load_agg do
624
730
  users.put(user_1)
625
731
  users.put(user_2)
626
732
 
627
- res = load_agg(users.where(organization_id: organization.id), users)
733
+ res = load_agg(users, users.where(organization_id: organization.id))
628
734
  expect(res.count).to eq(2)
629
735
  }
630
736
  end
@@ -35,25 +35,45 @@ class ReeDaoLoadAggTest::Db
35
35
  end
36
36
  end
37
37
 
38
- class ReeDaoLoadAggTest::Organization
38
+ class ReeDaoLoadAggTest::Book
39
39
  include ReeDto::EntityDSL
40
40
 
41
41
  properties(
42
42
  id: Nilor[Integer],
43
- name: String
43
+ user_id: Integer,
44
+ title: Nilor[String]
44
45
  )
45
46
 
46
- def set_users(users)
47
- @users = users
47
+ def set_chapters(chapters)
48
+ @chapters = chapters; nil
48
49
  end
49
50
 
50
- def users
51
- @users ||= []
51
+ def chapters
52
+ @chapters
52
53
  end
53
54
 
54
- attr_accessor :name
55
- end
55
+ def set_author(author)
56
+ @author = author
57
+ end
58
+
59
+ def author
60
+ @author
61
+ end
62
+
63
+ def set_reviews(reviews)
64
+ @reviews = reviews; nil
65
+ end
66
+
67
+ def reviews
68
+ @reviews
69
+ end
56
70
 
71
+ def title=(t)
72
+ @title = t
73
+ end
74
+
75
+ attr_accessor :title, :user_id
76
+ end
57
77
 
58
78
  class ReeDaoLoadAggTest::User
59
79
  include ReeDto::EntityDSL
@@ -81,10 +101,18 @@ class ReeDaoLoadAggTest::User
81
101
  @passport
82
102
  end
83
103
 
104
+ def books
105
+ @books
106
+ end
107
+
108
+ contract(ArrayOf[ReeDaoLoadAggTest::Book] => nil)
109
+ def set_books(books)
110
+ @books = books; nil
111
+ end
112
+
84
113
  [
85
114
  :organization,
86
115
  :passport,
87
- :books,
88
116
  :movies,
89
117
  :videogames,
90
118
  :hobbies,
@@ -106,6 +134,45 @@ class ReeDaoLoadAggTest::User
106
134
  attr_accessor :name, :age, :organization_id
107
135
  end
108
136
 
137
+ class ReeDaoLoadAggTest::UserDto
138
+ include ReeDto::EntityDSL
139
+
140
+ properties(
141
+ id: Integer,
142
+ organization_id: Integer,
143
+ name: String,
144
+ full_name: String,
145
+ )
146
+
147
+ def set_organization(org)
148
+ @organization = org; nil
149
+ end
150
+
151
+ def organization
152
+ @organization
153
+ end
154
+ end
155
+
156
+ class ReeDaoLoadAggTest::Organization
157
+ include ReeDto::EntityDSL
158
+
159
+ properties(
160
+ id: Nilor[Integer],
161
+ name: String
162
+ )
163
+
164
+ contract(Array[ReeDaoLoadAggTest::User] => nil)
165
+ def set_users(users)
166
+ @users = users; nil
167
+ end
168
+
169
+ def users
170
+ @users ||= []
171
+ end
172
+
173
+ attr_accessor :name
174
+ end
175
+
109
176
 
110
177
  class ReeDaoLoadAggTest::UserPassport
111
178
  include ReeDto::EntityDSL
@@ -189,42 +256,6 @@ class ReeDaoLoadAggTest::Dream
189
256
  )
190
257
  end
191
258
 
192
- class ReeDaoLoadAggTest::Book
193
- include ReeDto::EntityDSL
194
-
195
- properties(
196
- id: Nilor[Integer],
197
- user_id: Integer,
198
- title: Nilor[String]
199
- )
200
-
201
- def set_chapters(chapters)
202
- @chapters = chapters; nil
203
- end
204
-
205
- def chapters
206
- @chapters
207
- end
208
-
209
- def set_author(author)
210
- @author = author
211
- end
212
-
213
- def author
214
- @author
215
- end
216
-
217
- def set_reviews(reviews)
218
- @reviews = reviews; nil
219
- end
220
-
221
- def reviews
222
- @reviews
223
- end
224
-
225
- attr_accessor :title, :user_id
226
- end
227
-
228
259
  class ReeDaoLoadAggTest::Chapter
229
260
  include ReeDto::EntityDSL
230
261
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ReeLib
4
- VERSION = "1.0.54"
4
+ VERSION = "1.0.56"
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.54
4
+ version: 1.0.56
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ruslan Gatiyatov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-07-28 00:00:00.000000000 Z
11
+ date: 2023-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ree