serega 0.21.0 → 0.30.0

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +113 -229
  3. data/VERSION +1 -1
  4. data/lib/serega/attribute.rb +31 -3
  5. data/lib/serega/attribute_normalizer.rb +143 -42
  6. data/lib/serega/batch/attribute_loader.rb +70 -0
  7. data/lib/serega/batch/attribute_loaders.rb +59 -0
  8. data/lib/serega/batch/auto_resolver.rb +24 -0
  9. data/lib/serega/batch/auto_resolver_factory.rb +48 -0
  10. data/lib/serega/batch/loader.rb +67 -0
  11. data/lib/serega/config.rb +59 -1
  12. data/lib/serega/object_serializer.rb +11 -9
  13. data/lib/serega/plan.rb +16 -0
  14. data/lib/serega/plan_point.rb +31 -5
  15. data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +25 -13
  16. data/lib/serega/plugins/activerecord_preloads/lib/active_record_objects.rb +31 -0
  17. data/lib/serega/plugins/camel_case/camel_case.rb +1 -1
  18. data/lib/serega/plugins/formatters/formatters.rb +79 -22
  19. data/lib/serega/plugins/if/if.rb +41 -22
  20. data/lib/serega/plugins/if/validations/check_opt_if.rb +27 -6
  21. data/lib/serega/plugins/if/validations/check_opt_if_value.rb +27 -6
  22. data/lib/serega/plugins/if/validations/check_opt_unless.rb +27 -6
  23. data/lib/serega/plugins/if/validations/check_opt_unless_value.rb +27 -6
  24. data/lib/serega/plugins/metadata/meta_attribute.rb +9 -10
  25. data/lib/serega/plugins/metadata/validations/check_block.rb +2 -4
  26. data/lib/serega/plugins/metadata/validations/check_opt_value.rb +2 -3
  27. data/lib/serega/plugins/presenter/presenter.rb +1 -1
  28. data/lib/serega/utils/format_user_preloads.rb +58 -0
  29. data/lib/serega/utils/method_signature.rb +89 -0
  30. data/lib/serega/utils/preload_paths.rb +53 -0
  31. data/lib/serega/utils/preloads_constructor.rb +77 -0
  32. data/lib/serega/validations/attribute/check_block.rb +38 -6
  33. data/lib/serega/validations/attribute/check_opt_batch.rb +101 -0
  34. data/lib/serega/validations/attribute/check_opt_const.rb +1 -0
  35. data/lib/serega/validations/attribute/check_opt_delegate.rb +1 -0
  36. data/lib/serega/validations/attribute/check_opt_method.rb +1 -0
  37. data/lib/serega/{plugins/preloads/validations → validations/attribute}/check_opt_preload.rb +2 -2
  38. data/lib/serega/{plugins/preloads/validations → validations/attribute}/check_opt_preload_path.rb +3 -3
  39. data/lib/serega/validations/attribute/check_opt_value.rb +35 -9
  40. data/lib/serega/validations/check_attribute_params.rb +3 -2
  41. data/lib/serega/validations/check_batch_loader_params.rb +80 -0
  42. data/lib/serega/validations/initiate/check_modifiers.rb +1 -1
  43. data/lib/serega.rb +98 -7
  44. metadata +17 -37
  45. data/lib/serega/plugins/batch/batch.rb +0 -168
  46. data/lib/serega/plugins/batch/lib/batch_config.rb +0 -77
  47. data/lib/serega/plugins/batch/lib/loader.rb +0 -113
  48. data/lib/serega/plugins/batch/lib/loaders.rb +0 -45
  49. data/lib/serega/plugins/batch/lib/modules/attribute.rb +0 -26
  50. data/lib/serega/plugins/batch/lib/modules/attribute_normalizer.rb +0 -78
  51. data/lib/serega/plugins/batch/lib/modules/check_attribute_params.rb +0 -22
  52. data/lib/serega/plugins/batch/lib/modules/config.rb +0 -23
  53. data/lib/serega/plugins/batch/lib/modules/object_serializer.rb +0 -46
  54. data/lib/serega/plugins/batch/lib/modules/plan_point.rb +0 -22
  55. data/lib/serega/plugins/batch/lib/plugins_extensions/activerecord_preloads.rb +0 -43
  56. data/lib/serega/plugins/batch/lib/plugins_extensions/formatters.rb +0 -54
  57. data/lib/serega/plugins/batch/lib/plugins_extensions/if.rb +0 -31
  58. data/lib/serega/plugins/batch/lib/plugins_extensions/preloads.rb +0 -34
  59. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_id_method.rb +0 -43
  60. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb +0 -59
  61. data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +0 -62
  62. data/lib/serega/plugins/preloads/lib/format_user_preloads.rb +0 -60
  63. data/lib/serega/plugins/preloads/lib/modules/attribute.rb +0 -28
  64. data/lib/serega/plugins/preloads/lib/modules/attribute_normalizer.rb +0 -99
  65. data/lib/serega/plugins/preloads/lib/modules/check_attribute_params.rb +0 -22
  66. data/lib/serega/plugins/preloads/lib/modules/config.rb +0 -19
  67. data/lib/serega/plugins/preloads/lib/modules/plan_point.rb +0 -41
  68. data/lib/serega/plugins/preloads/lib/preload_paths.rb +0 -53
  69. data/lib/serega/plugins/preloads/lib/preloads_config.rb +0 -62
  70. data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +0 -79
  71. data/lib/serega/plugins/preloads/preloads.rb +0 -162
  72. data/lib/serega/utils/params_count.rb +0 -50
  73. data/lib/serega/validations/utils/check_extra_keyword_arg.rb +0 -33
@@ -71,7 +71,7 @@ class Serega
71
71
 
72
72
  def build_full_attribute_name(*names)
73
73
  head, *nested = *names
74
- result = head.to_s # names are symbols, we need not frozen string
74
+ result = +head.to_s # names are symbols, we need not frozen string
75
75
  nested.each { |nested_name| result << "(" << nested_name.to_s }
76
76
  nested.each { result << ")" }
77
77
  result
data/lib/serega.rb CHANGED
@@ -17,15 +17,22 @@ require_relative "serega/errors"
17
17
  require_relative "serega/helpers/serializer_class_helper"
18
18
  require_relative "serega/utils/enum_deep_dup"
19
19
  require_relative "serega/utils/enum_deep_freeze"
20
- require_relative "serega/utils/params_count"
20
+ require_relative "serega/utils/format_user_preloads"
21
+ require_relative "serega/utils/method_signature"
22
+ require_relative "serega/utils/preload_paths"
23
+ require_relative "serega/utils/preloads_constructor"
21
24
  require_relative "serega/utils/symbol_name"
22
25
  require_relative "serega/utils/to_hash"
23
26
  require_relative "serega/json/adapter"
24
27
 
25
28
  require_relative "serega/attribute"
26
29
  require_relative "serega/attribute_normalizer"
30
+ require_relative "serega/batch/attribute_loader"
31
+ require_relative "serega/batch/attribute_loaders"
32
+ require_relative "serega/batch/auto_resolver"
33
+ require_relative "serega/batch/auto_resolver_factory"
34
+ require_relative "serega/batch/loader"
27
35
  require_relative "serega/validations/utils/check_allowed_keys"
28
- require_relative "serega/validations/utils/check_extra_keyword_arg"
29
36
  require_relative "serega/validations/utils/check_opt_is_bool"
30
37
  require_relative "serega/validations/utils/check_opt_is_hash"
31
38
  require_relative "serega/validations/utils/check_opt_is_string_or_symbol"
@@ -34,13 +41,17 @@ require_relative "serega/validations/attribute/check_name"
34
41
  require_relative "serega/validations/attribute/check_opt_const"
35
42
  require_relative "serega/validations/attribute/check_opt_hide"
36
43
  require_relative "serega/validations/attribute/check_opt_delegate"
44
+ require_relative "serega/validations/attribute/check_opt_batch"
37
45
  require_relative "serega/validations/attribute/check_opt_many"
38
46
  require_relative "serega/validations/attribute/check_opt_method"
47
+ require_relative "serega/validations/attribute/check_opt_preload"
48
+ require_relative "serega/validations/attribute/check_opt_preload_path"
39
49
  require_relative "serega/validations/attribute/check_opt_serializer"
40
50
  require_relative "serega/validations/attribute/check_opt_value"
41
51
  require_relative "serega/validations/initiate/check_modifiers"
42
52
  require_relative "serega/validations/check_attribute_params"
43
53
  require_relative "serega/validations/check_initiate_params"
54
+ require_relative "serega/validations/check_batch_loader_params"
44
55
  require_relative "serega/validations/check_serialize_params"
45
56
 
46
57
  require_relative "serega/config"
@@ -67,6 +78,21 @@ class Serega
67
78
  check_serialize_params_class.serializer_class = self
68
79
  const_set(:CheckSerializeParams, check_serialize_params_class)
69
80
 
81
+ # Validates `Serializer.batch_loader` params
82
+ check_batch_loader_params_class = Class.new(SeregaValidations::CheckBatchLoaderParams)
83
+ check_batch_loader_params_class.serializer_class = self
84
+ const_set(:CheckBatchLoaderParams, check_batch_loader_params_class)
85
+
86
+ # Assigns `SeregaBatchLoader` constant to current class
87
+ batch_loader_class = Class.new(SeregaBatch::Loader)
88
+ batch_loader_class.serializer_class = self
89
+ const_set(:SeregaBatchLoader, batch_loader_class)
90
+
91
+ # Assigns `SeregaBatchAttributeLoader` constant to current class
92
+ batch_attribute_loader_class = Class.new(SeregaBatch::AttributeLoader)
93
+ batch_attribute_loader_class.serializer_class = self
94
+ const_set(:SeregaBatchAttributeLoader, batch_attribute_loader_class)
95
+
70
96
  #
71
97
  # Serializers class methods
72
98
  #
@@ -128,6 +154,15 @@ class Serega
128
154
  @attributes ||= {}
129
155
  end
130
156
 
157
+ #
158
+ # Lists batch loaders
159
+ #
160
+ # @return [Hash] batch loaders list
161
+ #
162
+ def batch_loaders
163
+ @batch_loaders ||= {}
164
+ end
165
+
131
166
  #
132
167
  # Adds attribute
133
168
  #
@@ -145,6 +180,41 @@ class Serega
145
180
  attributes[attribute.name] = attribute
146
181
  end
147
182
 
183
+ #
184
+ # Defines a batch loader
185
+ #
186
+ # @example
187
+ # batch_loader :tags, PostTagsLoader
188
+ #
189
+ # @example with block
190
+ # batch_loader(:tags) do |posts|
191
+ # Tags.where(post: posts).group(:post_id).pluck(:post_id, Arel.sql('ARRAY_AGG(tags.tag ORDER BY tag)')).to_h
192
+ # end
193
+ #
194
+ # attribute :tags, batch: :tags, value: { |post, batch:| batch[:tags][post.id] }
195
+ #
196
+ # @example with context
197
+ # batch_loader(:tags) do |posts, ctx:|
198
+ # next {} if ctx[:bot]
199
+ #
200
+ # Tags.where(post: posts).group(:post_id).pluck(:post_id, Arel.sql('ARRAY_AGG(tags.tag ORDER BY tag)')).to_h
201
+ # end
202
+ #
203
+ # attribute :tags, batch: :tags, value: { |post, batch:| batch[:tags][post.id] }
204
+ #
205
+ # @param name [Symbol] A batch loader name
206
+ # @param value [#call] Batch loader
207
+ # @param block [Proc] Batch loader
208
+ #
209
+ # @return [#call] Batch loader
210
+ #
211
+ def batch_loader(name, value = nil, &block)
212
+ raise SeregaError, "Batch loader must be defined with a callable value or block" if (value && block) || (!value && !block)
213
+
214
+ batch_loader = self::SeregaBatchLoader.new(name: name, block: value || block)
215
+ batch_loaders[batch_loader.name] = batch_loader
216
+ end
217
+
148
218
  #
149
219
  # Serializes provided object to Hash
150
220
  #
@@ -232,7 +302,6 @@ class Serega
232
302
  private
233
303
 
234
304
  # Patched in:
235
- # - plugin :batch (defines SeregaBatchLoaders, SeregaBatchLoader)
236
305
  # - plugin :metadata (defines MetaAttribute and copies meta_attributes to subclasses)
237
306
  # - plugin :presenter (defines Presenter)
238
307
  def inherited(subclass)
@@ -257,6 +326,14 @@ class Serega
257
326
  plan_point_class.serializer_class = subclass
258
327
  subclass.const_set(:SeregaPlanPoint, plan_point_class)
259
328
 
329
+ batch_loader_class = Class.new(self::SeregaBatchLoader)
330
+ batch_loader_class.serializer_class = subclass
331
+ subclass.const_set(:SeregaBatchLoader, batch_loader_class)
332
+
333
+ batch_attribute_loader_class = Class.new(self::SeregaBatchAttributeLoader)
334
+ batch_attribute_loader_class.serializer_class = subclass
335
+ subclass.const_set(:SeregaBatchAttributeLoader, batch_attribute_loader_class)
336
+
260
337
  object_serializer_class = Class.new(self::SeregaObjectSerializer)
261
338
  object_serializer_class.serializer_class = subclass
262
339
  subclass.const_set(:SeregaObjectSerializer, object_serializer_class)
@@ -273,12 +350,21 @@ class Serega
273
350
  check_serialize_params_class.serializer_class = subclass
274
351
  subclass.const_set(:CheckSerializeParams, check_serialize_params_class)
275
352
 
353
+ check_batch_loader_params_class = Class.new(self::CheckBatchLoaderParams)
354
+ check_batch_loader_params_class.serializer_class = self
355
+ subclass.const_set(:CheckBatchLoaderParams, check_batch_loader_params_class)
356
+
276
357
  # Assign same attributes
277
358
  attributes.each_value do |attr|
278
359
  params = attr.initials
279
360
  subclass.attribute(params[:name], **params[:opts], &params[:block])
280
361
  end
281
362
 
363
+ # Assign same batch loaders
364
+ batch_loaders.each_value do |loader|
365
+ subclass.batch_loader(loader.name, loader.block)
366
+ end
367
+
282
368
  super
283
369
  end
284
370
  end
@@ -340,6 +426,11 @@ class Serega
340
426
  call(object, opts)
341
427
  end
342
428
 
429
+ # @return [Hash] merged preloads of all serialized attributes
430
+ def preloads
431
+ @preloads ||= SeregaUtils::PreloadsConstructor.call(plan)
432
+ end
433
+
343
434
  #
344
435
  # Serializes provided object to JSON string
345
436
  #
@@ -397,14 +488,14 @@ class Serega
397
488
 
398
489
  # Patched in:
399
490
  # - plugin :activerecord_preloads (loads defined :preloads to object)
400
- # - plugin :batch (runs serialization of collected batches)
401
491
  # - plugin :root (wraps result `{ root => result }`)
402
492
  # - plugin :context_metadata (adds context metadata to final result)
403
493
  # - plugin :metadata (adds metadata to final result)
404
494
  def serialize(object, opts)
405
- self.class::SeregaObjectSerializer
406
- .new(**opts, plan: plan)
407
- .serialize(object)
495
+ batch_loaders = plan.has_batch_points ? SeregaBatch::AttributeLoaders.new : nil
496
+ result = self.class::SeregaObjectSerializer.new(**opts, batch_loaders: batch_loaders, plan: plan).serialize(object)
497
+ batch_loaders&.load_all(opts[:context])
498
+ result
408
499
  end
409
500
  end
410
501
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: serega
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.21.0
4
+ version: 0.30.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Glushkov
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-11-18 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
12
  description: |
14
13
  JSON Serializer
@@ -30,6 +29,11 @@ files:
30
29
  - lib/serega.rb
31
30
  - lib/serega/attribute.rb
32
31
  - lib/serega/attribute_normalizer.rb
32
+ - lib/serega/batch/attribute_loader.rb
33
+ - lib/serega/batch/attribute_loaders.rb
34
+ - lib/serega/batch/auto_resolver.rb
35
+ - lib/serega/batch/auto_resolver_factory.rb
36
+ - lib/serega/batch/loader.rb
33
37
  - lib/serega/config.rb
34
38
  - lib/serega/errors.rb
35
39
  - lib/serega/helpers/serializer_class_helper.rb
@@ -41,24 +45,8 @@ files:
41
45
  - lib/serega/plan_point.rb
42
46
  - lib/serega/plugins.rb
43
47
  - lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb
48
+ - lib/serega/plugins/activerecord_preloads/lib/active_record_objects.rb
44
49
  - lib/serega/plugins/activerecord_preloads/lib/preloader.rb
45
- - lib/serega/plugins/batch/batch.rb
46
- - lib/serega/plugins/batch/lib/batch_config.rb
47
- - lib/serega/plugins/batch/lib/loader.rb
48
- - lib/serega/plugins/batch/lib/loaders.rb
49
- - lib/serega/plugins/batch/lib/modules/attribute.rb
50
- - lib/serega/plugins/batch/lib/modules/attribute_normalizer.rb
51
- - lib/serega/plugins/batch/lib/modules/check_attribute_params.rb
52
- - lib/serega/plugins/batch/lib/modules/config.rb
53
- - lib/serega/plugins/batch/lib/modules/object_serializer.rb
54
- - lib/serega/plugins/batch/lib/modules/plan_point.rb
55
- - lib/serega/plugins/batch/lib/plugins_extensions/activerecord_preloads.rb
56
- - lib/serega/plugins/batch/lib/plugins_extensions/formatters.rb
57
- - lib/serega/plugins/batch/lib/plugins_extensions/if.rb
58
- - lib/serega/plugins/batch/lib/plugins_extensions/preloads.rb
59
- - lib/serega/plugins/batch/lib/validations/check_batch_opt_id_method.rb
60
- - lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb
61
- - lib/serega/plugins/batch/lib/validations/check_opt_batch.rb
62
50
  - lib/serega/plugins/camel_case/camel_case.rb
63
51
  - lib/serega/plugins/context_metadata/context_metadata.rb
64
52
  - lib/serega/plugins/depth_limit/depth_limit.rb
@@ -79,42 +67,36 @@ files:
79
67
  - lib/serega/plugins/metadata/validations/check_opt_value.rb
80
68
  - lib/serega/plugins/metadata/validations/check_opts.rb
81
69
  - lib/serega/plugins/metadata/validations/check_path.rb
82
- - lib/serega/plugins/preloads/lib/format_user_preloads.rb
83
- - lib/serega/plugins/preloads/lib/modules/attribute.rb
84
- - lib/serega/plugins/preloads/lib/modules/attribute_normalizer.rb
85
- - lib/serega/plugins/preloads/lib/modules/check_attribute_params.rb
86
- - lib/serega/plugins/preloads/lib/modules/config.rb
87
- - lib/serega/plugins/preloads/lib/modules/plan_point.rb
88
- - lib/serega/plugins/preloads/lib/preload_paths.rb
89
- - lib/serega/plugins/preloads/lib/preloads_config.rb
90
- - lib/serega/plugins/preloads/lib/preloads_constructor.rb
91
- - lib/serega/plugins/preloads/preloads.rb
92
- - lib/serega/plugins/preloads/validations/check_opt_preload.rb
93
- - lib/serega/plugins/preloads/validations/check_opt_preload_path.rb
94
70
  - lib/serega/plugins/presenter/presenter.rb
95
71
  - lib/serega/plugins/root/root.rb
96
72
  - lib/serega/plugins/string_modifiers/parse_string_modifiers.rb
97
73
  - lib/serega/plugins/string_modifiers/string_modifiers.rb
98
74
  - lib/serega/utils/enum_deep_dup.rb
99
75
  - lib/serega/utils/enum_deep_freeze.rb
100
- - lib/serega/utils/params_count.rb
76
+ - lib/serega/utils/format_user_preloads.rb
77
+ - lib/serega/utils/method_signature.rb
78
+ - lib/serega/utils/preload_paths.rb
79
+ - lib/serega/utils/preloads_constructor.rb
101
80
  - lib/serega/utils/symbol_name.rb
102
81
  - lib/serega/utils/to_hash.rb
103
82
  - lib/serega/validations/attribute/check_block.rb
104
83
  - lib/serega/validations/attribute/check_name.rb
84
+ - lib/serega/validations/attribute/check_opt_batch.rb
105
85
  - lib/serega/validations/attribute/check_opt_const.rb
106
86
  - lib/serega/validations/attribute/check_opt_delegate.rb
107
87
  - lib/serega/validations/attribute/check_opt_hide.rb
108
88
  - lib/serega/validations/attribute/check_opt_many.rb
109
89
  - lib/serega/validations/attribute/check_opt_method.rb
90
+ - lib/serega/validations/attribute/check_opt_preload.rb
91
+ - lib/serega/validations/attribute/check_opt_preload_path.rb
110
92
  - lib/serega/validations/attribute/check_opt_serializer.rb
111
93
  - lib/serega/validations/attribute/check_opt_value.rb
112
94
  - lib/serega/validations/check_attribute_params.rb
95
+ - lib/serega/validations/check_batch_loader_params.rb
113
96
  - lib/serega/validations/check_initiate_params.rb
114
97
  - lib/serega/validations/check_serialize_params.rb
115
98
  - lib/serega/validations/initiate/check_modifiers.rb
116
99
  - lib/serega/validations/utils/check_allowed_keys.rb
117
- - lib/serega/validations/utils/check_extra_keyword_arg.rb
118
100
  - lib/serega/validations/utils/check_opt_is_bool.rb
119
101
  - lib/serega/validations/utils/check_opt_is_hash.rb
120
102
  - lib/serega/validations/utils/check_opt_is_string_or_symbol.rb
@@ -126,7 +108,6 @@ metadata:
126
108
  source_code_uri: https://github.com/aglushkov/serega
127
109
  documentation_uri: https://www.rubydoc.info/gems/serega
128
110
  changelog_uri: https://github.com/aglushkov/serega/blob/master/CHANGELOG.md
129
- post_install_message:
130
111
  rdoc_options: []
131
112
  require_paths:
132
113
  - lib
@@ -141,8 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
122
  - !ruby/object:Gem::Version
142
123
  version: '0'
143
124
  requirements: []
144
- rubygems_version: 3.5.6
145
- signing_key:
125
+ rubygems_version: 3.7.1
146
126
  specification_version: 4
147
127
  summary: JSON Serializer
148
128
  test_files: []
@@ -1,168 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Serega
4
- module SeregaPlugins
5
- #
6
- # Plugin `:batch`
7
- #
8
- # Must be used to omit N+1 when loading attributes values.
9
- #
10
- # @example Quick example
11
- #
12
- # class AppSerializer
13
- # plugin :batch, id_method: :id
14
- # end
15
- #
16
- # class UserSerializer < AppSerializer
17
- # attribute :comments_count, batch: { loader: CommentsCountBatchLoader }, default: 0
18
- # attribute :company, serializer: CompanySerializer, batch: { loader: UserCompanyBatchLoader }
19
- # end
20
- #
21
- module Batch
22
- # Returns plugin name
23
- # @return [Symbol] Plugin name
24
- def self.plugin_name
25
- :batch
26
- end
27
-
28
- # Checks requirements to load plugin
29
- #
30
- # @param serializer_class [Class<Serega>] Current serializer class
31
- # @param opts [Hash] plugin options
32
- #
33
- # @return [void]
34
- #
35
- def self.before_load_plugin(serializer_class, **opts)
36
- allowed_keys = %i[auto_hide id_method]
37
- opts.each_key do |key|
38
- next if allowed_keys.include?(key)
39
-
40
- raise SeregaError,
41
- "Plugin #{plugin_name.inspect} does not accept the #{key.inspect} option. Allowed options:\n" \
42
- " - :auto_hide [Boolean] - Marks attribute as hidden when it has :batch loader specified\n" \
43
- " - :id_method [Symbol, #call] - Specified the default method to use to find object identifier"
44
- end
45
- end
46
-
47
- #
48
- # Applies plugin code to specific serializer
49
- #
50
- # @param serializer_class [Class<Serega>] Current serializer class
51
- # @param _opts [Hash] Plugin options
52
- #
53
- # @return [void]
54
- #
55
- def self.load_plugin(serializer_class, **_opts)
56
- require_relative "lib/batch_config"
57
- require_relative "lib/loader"
58
- require_relative "lib/loaders"
59
- require_relative "lib/modules/attribute"
60
- require_relative "lib/modules/attribute_normalizer"
61
- require_relative "lib/modules/check_attribute_params"
62
- require_relative "lib/modules/config"
63
- require_relative "lib/modules/object_serializer"
64
- require_relative "lib/modules/plan_point"
65
- require_relative "lib/validations/check_batch_opt_id_method"
66
- require_relative "lib/validations/check_batch_opt_loader"
67
- require_relative "lib/validations/check_opt_batch"
68
-
69
- serializer_class.extend(ClassMethods)
70
- serializer_class.include(InstanceMethods)
71
- serializer_class::CheckAttributeParams.include(CheckAttributeParamsInstanceMethods)
72
- serializer_class::SeregaAttribute.include(AttributeInstanceMethods)
73
- serializer_class::SeregaAttributeNormalizer.include(AttributeNormalizerInstanceMethods)
74
- serializer_class::SeregaPlanPoint.include(PlanPointInstanceMethods)
75
- serializer_class::SeregaObjectSerializer.include(SeregaObjectSerializerInstanceMethods)
76
- end
77
-
78
- #
79
- # Runs callbacks after plugin was attached
80
- #
81
- # @param serializer_class [Class<Serega>] Current serializer class
82
- # @param opts [Hash] Plugin options
83
- #
84
- # @return [void]
85
- #
86
- def self.after_load_plugin(serializer_class, **opts)
87
- serializer_class::SeregaConfig.include(ConfigInstanceMethods)
88
-
89
- batch_loaders_class = Class.new(SeregaBatchLoaders)
90
- batch_loaders_class.serializer_class = serializer_class
91
- serializer_class.const_set(:SeregaBatchLoaders, batch_loaders_class)
92
-
93
- batch_loader_class = Class.new(SeregaBatchLoader)
94
- batch_loader_class.serializer_class = serializer_class
95
- serializer_class.const_set(:SeregaBatchLoader, batch_loader_class)
96
-
97
- if serializer_class.plugin_used?(:activerecord_preloads)
98
- require_relative "lib/plugins_extensions/activerecord_preloads"
99
- serializer_class::SeregaBatchLoader.include(PluginsExtensions::ActiveRecordPreloads::BatchLoaderInstanceMethods)
100
- end
101
-
102
- if serializer_class.plugin_used?(:formatters)
103
- require_relative "lib/plugins_extensions/formatters"
104
- serializer_class::SeregaBatchLoader.include(PluginsExtensions::Formatters::BatchLoaderInstanceMethods)
105
- serializer_class::SeregaAttribute.include(PluginsExtensions::Formatters::SeregaAttributeInstanceMethods)
106
- end
107
-
108
- if serializer_class.plugin_used?(:if)
109
- require_relative "lib/plugins_extensions/if"
110
- serializer_class::SeregaObjectSerializer.include(PluginsExtensions::If::ObjectSerializerInstanceMethods123)
111
- end
112
-
113
- if serializer_class.plugin_used?(:preloads)
114
- require_relative "lib/plugins_extensions/preloads"
115
- serializer_class::SeregaAttributeNormalizer.include(PluginsExtensions::Preloads::AttributeNormalizerInstanceMethods)
116
- end
117
-
118
- config = serializer_class.config
119
- config.attribute_keys << :batch
120
- config.opts[:batch] = {loaders: {}, id_method: nil, auto_hide: false}
121
- config.batch.auto_hide = opts[:auto_hide] if opts.key?(:auto_hide)
122
- config.batch.id_method = opts[:id_method] if opts.key?(:id_method)
123
- end
124
-
125
- #
126
- # Serega class additional/patched class methods
127
- #
128
- # @see Serega::SeregaConfig
129
- #
130
- module ClassMethods
131
- private
132
-
133
- def inherited(subclass)
134
- super
135
-
136
- batch_loaders_class = Class.new(self::SeregaBatchLoaders)
137
- batch_loaders_class.serializer_class = subclass
138
- subclass.const_set(:SeregaBatchLoaders, batch_loaders_class)
139
-
140
- batch_loader_class = Class.new(self::SeregaBatchLoader)
141
- batch_loader_class.serializer_class = subclass
142
- subclass.const_set(:SeregaBatchLoader, batch_loader_class)
143
- end
144
- end
145
-
146
- #
147
- # Serega additional/patched instance methods
148
- #
149
- # @see Serega::InstanceMethods
150
- #
151
- module InstanceMethods
152
- private
153
-
154
- #
155
- # Loads batch loaded attributes after serialization
156
- #
157
- def serialize(object, opts)
158
- batch_loaders = opts[:batch_loaders] = self.class::SeregaBatchLoaders.new
159
- result = super
160
- batch_loaders.load_all
161
- result
162
- end
163
- end
164
- end
165
-
166
- register_plugin(Batch.plugin_name, Batch)
167
- end
168
- end
@@ -1,77 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Serega
4
- module SeregaPlugins
5
- module Batch
6
- #
7
- # Batch plugin config
8
- #
9
- class BatchConfig
10
- attr_reader :opts
11
-
12
- def initialize(opts)
13
- @opts = opts
14
- end
15
-
16
- #
17
- # Defines batch loader
18
- #
19
- # @param loader_name [Symbol] Batch loader name, that is used when defining attribute with batch loader.
20
- # @param block [Proc] Block that can accept 3 parameters - ids, context, plan_point
21
- # and returns hash with ids as keys and values are batch loaded objects
22
- #
23
- # @return [void]
24
- #
25
- def define(loader_name, callable = nil, &block)
26
- if (!callable && !block) || (callable && block)
27
- raise SeregaError, "Batch loader can be specified with one of arguments - callable value or &block"
28
- end
29
-
30
- callable ||= block
31
- SeregaValidations::Utils::CheckExtraKeywordArg.call(callable, "batch loader `#{loader_name}`")
32
- params_count = SeregaUtils::ParamsCount.call(callable, max_count: 3)
33
-
34
- if params_count > 3
35
- raise SeregaError, "Batch loader can have maximum 3 parameters (ids, context, plan)"
36
- end
37
-
38
- loaders[loader_name] = callable
39
- end
40
-
41
- # Shows defined loaders
42
- # @return [Hash] defined loaders
43
- def loaders
44
- opts[:loaders]
45
- end
46
-
47
- # Shows option to auto hide attributes with :batch specified
48
- # @return [Boolean, nil] option value
49
- def auto_hide
50
- opts[:auto_hide]
51
- end
52
-
53
- # @param value [Boolean] New :auto_hide option value
54
- # @return [Boolean] New option value
55
- def auto_hide=(value)
56
- raise SeregaError, "Must have boolean value, #{value.inspect} provided" if (value != true) && (value != false)
57
- opts[:auto_hide] = value
58
- end
59
-
60
- # Shows method name or callable object needed to get object identifier for batch load
61
- # @return [Symbol, #call, nil] Default method name or callable object to get identifier
62
- def id_method
63
- opts[:id_method]
64
- end
65
-
66
- # Sets new identifier method name or callable value needed for batch loading
67
- #
68
- # @param value [Symbol, #call] New :id_method value
69
- # @return [Boolean] New option value
70
- def id_method=(value)
71
- CheckBatchOptIdMethod.call(value)
72
- opts[:id_method] = value
73
- end
74
- end
75
- end
76
- end
77
- end
@@ -1,113 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Serega
4
- module SeregaPlugins
5
- module Batch
6
- #
7
- # Encapsulates point and according object_serializer
8
- # so we can put batch loaded values to this serializer response
9
- #
10
- class SeregaBatchLoader
11
- #
12
- # Batch Loader instance methods
13
- #
14
- module InstanceMethods
15
- # @return [Serega::SeregaPlanPoint]
16
- attr_reader :point
17
-
18
- # @return [Serega::SeregaObjectSerializer]
19
- attr_reader :object_serializer
20
-
21
- #
22
- # Initializes new SeregaBatchLoader
23
- #
24
- # @param object_serializer [Serega::SeregaObjectSerializer]
25
- # @param point [Serega::SeregaPlanPoint]
26
- #
27
- # @return [Serega::SeregaPlugins::Batch::SeregaBatchLoader]
28
- #
29
- def initialize(object_serializer, point)
30
- @object_serializer = object_serializer
31
- @point = point
32
- end
33
-
34
- #
35
- # Remembers key and hash container where value for this key must be inserted
36
- #
37
- # @param key [Object] key that identifies batch loaded objects
38
- # @param container [Hash] container where batch loaded objects must be attached
39
- #
40
- # @return [void]
41
- #
42
- def remember(key, container)
43
- (keys[key] ||= []) << container
44
- end
45
-
46
- #
47
- # Loads this batch and assigns values to remembered containers
48
- #
49
- # @return [void]
50
- #
51
- def load
52
- keys_values = keys_values()
53
-
54
- each_key do |key, container|
55
- value = keys_values.fetch(key) { point.batch[:default] }
56
- final_value = object_serializer.__send__(:final_value, value, point)
57
- object_serializer.__send__(:attach_final_value, final_value, point, container)
58
- end
59
- end
60
-
61
- private
62
-
63
- def keys
64
- @keys ||= {}
65
- end
66
-
67
- def each_key
68
- keys.each do |key, containers|
69
- containers.each do |container|
70
- yield(key, container)
71
- end
72
- end
73
- end
74
-
75
- # Patched in:
76
- # - plugin batch (extension :activerecord_preloads - preloads data to found values)
77
- # - plugin batch (extension :formatters - formats values)
78
- def keys_values
79
- ids = keys.keys
80
-
81
- keys_values = load_keys_values(ids)
82
- validate(keys_values)
83
-
84
- keys_values
85
- end
86
-
87
- def load_keys_values(ids)
88
- point.batch[:loader].call(ids, object_serializer.context, point)
89
- rescue => error
90
- raise reraise_with_serialized_attribute_details(error, point)
91
- end
92
-
93
- def validate(keys_values)
94
- return if keys_values.is_a?(Hash)
95
-
96
- attribute_name = "#{point.class.serializer_class}.#{point.name}"
97
- raise SeregaError, "Batch loader for `#{attribute_name}` must return Hash, but #{keys_values.inspect} was returned"
98
- end
99
-
100
- def reraise_with_serialized_attribute_details(error, point)
101
- raise error.exception(<<~MESSAGE.strip)
102
- #{error.message}
103
- (when serializing '#{point.name}' attribute in #{self.class.serializer_class})
104
- MESSAGE
105
- end
106
- end
107
-
108
- include InstanceMethods
109
- extend Serega::SeregaHelpers::SerializerClassHelper
110
- end
111
- end
112
- end
113
- end