serega 0.11.1 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +62 -13
  3. data/VERSION +1 -1
  4. data/lib/serega/attribute.rb +9 -4
  5. data/lib/serega/attribute_normalizer.rb +4 -13
  6. data/lib/serega/object_serializer.rb +11 -0
  7. data/lib/serega/plan.rb +20 -25
  8. data/lib/serega/plan_point.rb +13 -16
  9. data/lib/serega/plugins/batch/batch.rb +7 -245
  10. data/lib/serega/plugins/batch/lib/batch_config.rb +82 -0
  11. data/lib/serega/plugins/batch/lib/loader.rb +25 -7
  12. data/lib/serega/plugins/batch/lib/modules/attribute.rb +26 -0
  13. data/lib/serega/plugins/batch/lib/modules/attribute_normalizer.rb +65 -0
  14. data/lib/serega/plugins/batch/lib/modules/check_attribute_params.rb +22 -0
  15. data/lib/serega/plugins/batch/lib/modules/config.rb +23 -0
  16. data/lib/serega/plugins/batch/lib/modules/object_serializer.rb +46 -0
  17. data/lib/serega/plugins/batch/lib/modules/plan_point.rb +39 -0
  18. data/lib/serega/plugins/metadata/metadata.rb +5 -0
  19. data/lib/serega/plugins/preloads/lib/modules/attribute.rb +28 -0
  20. data/lib/serega/plugins/preloads/lib/modules/attribute_normalizer.rb +99 -0
  21. data/lib/serega/plugins/preloads/lib/modules/check_attribute_params.rb +22 -0
  22. data/lib/serega/plugins/preloads/lib/modules/config.rb +19 -0
  23. data/lib/serega/plugins/preloads/lib/modules/plan_point.rb +41 -0
  24. data/lib/serega/plugins/preloads/lib/preload_paths.rb +46 -0
  25. data/lib/serega/plugins/preloads/lib/preloads_config.rb +62 -0
  26. data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +20 -7
  27. data/lib/serega/plugins/preloads/preloads.rb +12 -210
  28. data/lib/serega/plugins/preloads/validations/check_opt_preload_path.rb +54 -15
  29. metadata +16 -3
  30. data/lib/serega/plugins/preloads/lib/main_preload_path.rb +0 -53
@@ -35,8 +35,9 @@ class Serega
35
35
  append_current(preloads, current_preloads)
36
36
  next unless child_plan
37
37
 
38
- child_preloads = dig?(preloads, point.preloads_path)
39
- append_many(child_preloads, child_plan)
38
+ each_child_preloads(preloads, point.preloads_path) do |child_preloads|
39
+ append_many(child_preloads, child_plan)
40
+ end
40
41
  end
41
42
  end
42
43
 
@@ -50,14 +51,26 @@ class Serega
50
51
  end
51
52
  end
52
53
 
53
- def dig?(hash, path)
54
- return hash if !path || path.empty?
54
+ def each_child_preloads(preloads, preloads_path)
55
+ return yield(preloads) if preloads_path.nil?
55
56
 
56
- path.each do |point|
57
- hash = hash[point]
57
+ if preloads_path[0].is_a?(Array)
58
+ preloads_path.each do |path|
59
+ yield dig_fetch(preloads, path)
60
+ end
61
+ else
62
+ yield dig_fetch(preloads, preloads_path)
58
63
  end
64
+ end
65
+
66
+ def dig_fetch(preloads, preloads_path)
67
+ return preloads if !preloads_path || preloads_path.empty?
59
68
 
60
- hash
69
+ preloads_path.each do |path|
70
+ preloads = preloads.fetch(path)
71
+ end
72
+
73
+ preloads
61
74
  end
62
75
  end
63
76
  end
@@ -79,6 +79,18 @@ class Serega
79
79
  # @return [void]
80
80
  #
81
81
  def self.load_plugin(serializer_class, **_opts)
82
+ require_relative "./lib/format_user_preloads"
83
+ require_relative "./lib/modules/attribute"
84
+ require_relative "./lib/modules/attribute_normalizer"
85
+ require_relative "./lib/modules/check_attribute_params"
86
+ require_relative "./lib/modules/config"
87
+ require_relative "./lib/modules/plan_point"
88
+ require_relative "./lib/preload_paths"
89
+ require_relative "./lib/preloads_config"
90
+ require_relative "./lib/preloads_constructor"
91
+ require_relative "./validations/check_opt_preload"
92
+ require_relative "./validations/check_opt_preload_path"
93
+
82
94
  serializer_class.include(InstanceMethods)
83
95
  serializer_class::SeregaAttribute.include(AttributeInstanceMethods)
84
96
  serializer_class::SeregaAttributeNormalizer.include(AttributeNormalizerInstanceMethods)
@@ -86,12 +98,6 @@ class Serega
86
98
  serializer_class::SeregaPlanPoint.include(PlanPointInstanceMethods)
87
99
 
88
100
  serializer_class::CheckAttributeParams.include(CheckAttributeParamsInstanceMethods)
89
-
90
- require_relative "./lib/format_user_preloads"
91
- require_relative "./lib/main_preload_path"
92
- require_relative "./lib/preloads_constructor"
93
- require_relative "./validations/check_opt_preload"
94
- require_relative "./validations/check_opt_preload_path"
95
101
  end
96
102
 
97
103
  #
@@ -125,210 +131,6 @@ class Serega
125
131
  @preloads ||= PreloadsConstructor.call(plan)
126
132
  end
127
133
  end
128
-
129
- #
130
- # Config for `preloads` plugin
131
- #
132
- class PreloadsConfig
133
- # @return [Hash] preloads plugin options
134
- attr_reader :opts
135
-
136
- #
137
- # Initializes context_metadata config object
138
- #
139
- # @param opts [Hash] options
140
- #
141
- # @return [Serega::SeregaPlugins::Metadata::MetadataConfig]
142
- #
143
- def initialize(opts)
144
- @opts = opts
145
- end
146
-
147
- # @!method auto_preload_attributes_with_delegate
148
- # @return [Boolean, nil] option value
149
- #
150
- # @!method auto_preload_attributes_with_delegate=(value)
151
- # @param value [Boolean] New option value
152
- # @return [Boolean] New option value
153
- #
154
- # @!method auto_preload_attributes_with_serializer
155
- # @return [Boolean, nil] option value
156
- #
157
- # @!method auto_preload_attributes_with_serializer=(value)
158
- # @param value [Boolean] New option value
159
- # @return [Boolean] New option value
160
- #
161
- # @!method auto_hide_attributes_with_preload
162
- # @return [Boolean, nil] option value
163
- #
164
- # @!method auto_hide_attributes_with_preload=(value)
165
- # @param value [Boolean] New option value
166
- # @return [Boolean] New option value
167
- #
168
- %i[
169
- auto_preload_attributes_with_delegate
170
- auto_preload_attributes_with_serializer
171
- auto_hide_attributes_with_preload
172
- ].each do |method_name|
173
- define_method(method_name) do
174
- opts.fetch(method_name)
175
- end
176
-
177
- define_method("#{method_name}=") do |value|
178
- raise SeregaError, "Must have boolean value, #{value.inspect} provided" if (value != true) && (value != false)
179
- opts[method_name] = value
180
- end
181
- end
182
- end
183
-
184
- #
185
- # Config class additional/patched instance methods
186
- #
187
- # @see Serega::SeregaConfig
188
- #
189
- module ConfigInstanceMethods
190
- # @return [Serega::SeregaPlugins::Preloads::PreloadsConfig] `preloads` plugin config
191
- def preloads
192
- @preloads ||= PreloadsConfig.new(opts.fetch(:preloads))
193
- end
194
- end
195
-
196
- #
197
- # Serega::SeregaAttribute additional/patched instance methods
198
- #
199
- # @see Serega::SeregaAttribute::AttributeInstanceMethods
200
- #
201
- module AttributeInstanceMethods
202
- # @return [Hash, nil] normalized preloads of current attribute
203
- attr_reader :preloads
204
-
205
- # @return [Array] normalized preloads_path of current attribute
206
- attr_reader :preloads_path
207
-
208
- private
209
-
210
- def set_normalized_vars(normalizer)
211
- super
212
- @preloads = normalizer.preloads
213
- @preloads_path = normalizer.preloads_path
214
- end
215
- end
216
-
217
- #
218
- # SeregaAttributeNormalizer additional/patched instance methods
219
- #
220
- # @see SeregaAttributeNormalizer::AttributeInstanceMethods
221
- #
222
- module AttributeNormalizerInstanceMethods
223
- # @return [Hash,nil] normalized attribute preloads
224
- def preloads
225
- return @preloads if instance_variable_defined?(:@preloads)
226
-
227
- @preloads = prepare_preloads
228
- end
229
-
230
- # @return [Array, nil] normalized attribute preloads path
231
- def preloads_path
232
- return @preloads_path if instance_variable_defined?(:@preloads_path)
233
-
234
- @preloads_path = prepare_preloads_path
235
- end
236
-
237
- private
238
-
239
- #
240
- # Patched in:
241
- # - plugin :batch (extension :preloads - skips auto preloads when batch option provided)
242
- #
243
- def prepare_preloads
244
- opts = init_opts
245
- preloads_provided = opts.key?(:preload)
246
- preloads =
247
- if preloads_provided
248
- opts[:preload]
249
- elsif opts.key?(:serializer) && self.class.serializer_class.config.preloads.auto_preload_attributes_with_serializer
250
- key
251
- elsif opts.key?(:delegate) && self.class.serializer_class.config.preloads.auto_preload_attributes_with_delegate
252
- opts[:delegate].fetch(:to)
253
- end
254
-
255
- # Nil and empty hash differs as we can preload nested results to
256
- # empty hash, but we will skip nested preloading if nil or false provided
257
- return if preloads_provided && !preloads
258
-
259
- FormatUserPreloads.call(preloads)
260
- end
261
-
262
- def prepare_preloads_path
263
- opts = init_opts
264
- path = Array(opts[:preload_path]).map!(&:to_sym).freeze
265
- path = MainPreloadPath.call(preloads) if path.empty?
266
- path
267
- end
268
-
269
- #
270
- # Patch for original `prepare_hide` method
271
- #
272
- # Marks attribute hidden if auto_hide_attribute_with_preloads option was set and attribute has preloads
273
- #
274
- def prepare_hide
275
- res = super
276
- return res unless res.nil?
277
-
278
- if preloads && !preloads.empty?
279
- self.class.serializer_class.config.preloads.auto_hide_attributes_with_preload || nil
280
- end
281
- end
282
- end
283
-
284
- #
285
- # Serega::SeregaPlanPoint additional/patched instance methods
286
- #
287
- # @see Serega::SeregaPlanPoint::InstanceMethods
288
- #
289
- module PlanPointInstanceMethods
290
- #
291
- # @return [Hash] preloads for nested attributes
292
- #
293
- attr_reader :preloads
294
-
295
- #
296
- # @return [Array<Symbol>] preloads path for current attribute
297
- #
298
- attr_reader :preloads_path
299
-
300
- private
301
-
302
- def set_normalized_vars
303
- super
304
-
305
- @preloads = prepare_preloads
306
- @preloads_path = prepare_preloads_path
307
- end
308
-
309
- def prepare_preloads
310
- PreloadsConstructor.call(child_plan)
311
- end
312
-
313
- def prepare_preloads_path
314
- attribute.preloads_path
315
- end
316
- end
317
-
318
- #
319
- # Serega::SeregaValidations::CheckAttributeParams additional/patched class methods
320
- #
321
- # @see Serega::SeregaValidations::CheckAttributeParams
322
- #
323
- module CheckAttributeParamsInstanceMethods
324
- private
325
-
326
- def check_opts
327
- super
328
- CheckOptPreload.call(opts)
329
- CheckOptPreloadPath.call(opts)
330
- end
331
- end
332
134
  end
333
135
 
334
136
  register_plugin(Preloads.plugin_name, Preloads)
@@ -18,30 +18,69 @@ class Serega
18
18
  # @return [void]
19
19
  #
20
20
  def call(opts)
21
- return unless opts.key?(:preload_path)
21
+ return if exactly_nil?(opts, :preload_path) # allow to provide nil anyway
22
22
 
23
- value = opts[:preload_path]
24
- raise SeregaError, "Invalid option :preload_path => #{value.inspect}. Can be provided only when :preload option provided" unless opts[:preload]
25
- raise SeregaError, "Invalid option :preload_path => #{value.inspect}. Can be provided only when :serializer option provided" unless opts[:serializer]
23
+ path = opts[:preload_path]
24
+ check_usage_with_other_options(path, opts)
25
+ return unless opts[:serializer]
26
26
 
27
- path = Array(value).map!(&:to_sym)
28
- preloads = FormatUserPreloads.call(opts[:preload])
29
- allowed_paths = paths(preloads)
30
- raise SeregaError, "Invalid option :preload_path => #{value.inspect}. Can be one of #{allowed_paths.inspect[1..-2]}" unless allowed_paths.include?(path)
27
+ check_allowed(path, opts)
31
28
  end
32
29
 
33
30
  private
34
31
 
35
- def paths(preloads, path = [], result = [])
36
- preloads.each do |key, nested_preloads|
37
- path << key
38
- result << path.dup
32
+ def exactly_nil?(opts, opt_name)
33
+ opts.fetch(opt_name, false).nil?
34
+ end
35
+
36
+ def check_allowed(path, opts)
37
+ allowed_paths = PreloadPaths.call(opts[:preload])
38
+ check_required_when_many_allowed(path, allowed_paths)
39
+ check_in_allowed(path, allowed_paths)
40
+ end
41
+
42
+ def check_usage_with_other_options(path, opts)
43
+ return unless path
44
+
45
+ preload = opts[:preload]
46
+ raise SeregaError, "Invalid option preload_path: #{path.inspect}. Can be provided only when :preload option provided" unless preload
39
47
 
40
- paths(nested_preloads, path, result)
41
- path.pop
48
+ serializer = opts[:serializer]
49
+ raise SeregaError, "Invalid option preload_path: #{path.inspect}. Can be provided only when :serializer option provided" unless serializer
50
+ end
51
+
52
+ def check_required_when_many_allowed(path, allowed)
53
+ return if path || (allowed.size < 2)
54
+
55
+ raise SeregaError, "Option :preload_path must be provided. Possible values: #{allowed.inspect[1..-2]}"
56
+ end
57
+
58
+ def check_in_allowed(path, allowed)
59
+ return if !path && allowed.size <= 1
60
+
61
+ if multiple_preload_paths_provided?(path)
62
+ check_many(path, allowed)
63
+ else
64
+ check_one(path, allowed)
42
65
  end
66
+ end
67
+
68
+ def check_one(path, allowed)
69
+ formatted_path = Array(path).map(&:to_sym)
70
+ return if allowed.include?(formatted_path)
71
+
72
+ raise SeregaError,
73
+ "Invalid preload_path (#{path.inspect}). " \
74
+ "Can be one of #{allowed.inspect[1..-2]}"
75
+ end
76
+
77
+ def check_many(paths, allowed)
78
+ paths.each { |path| check_one(path, allowed) }
79
+ end
43
80
 
44
- result
81
+ # Check value is Array in Array
82
+ def multiple_preload_paths_provided?(value)
83
+ value.is_a?(Array) && value[0].is_a?(Array)
45
84
  end
46
85
  end
47
86
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: serega
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.1
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Glushkov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-25 00:00:00.000000000 Z
11
+ date: 2023-07-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  JSON Serializer
@@ -43,8 +43,15 @@ files:
43
43
  - lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb
44
44
  - lib/serega/plugins/activerecord_preloads/lib/preloader.rb
45
45
  - lib/serega/plugins/batch/batch.rb
46
+ - lib/serega/plugins/batch/lib/batch_config.rb
46
47
  - lib/serega/plugins/batch/lib/loader.rb
47
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
48
55
  - lib/serega/plugins/batch/lib/plugins_extensions/activerecord_preloads.rb
49
56
  - lib/serega/plugins/batch/lib/plugins_extensions/formatters.rb
50
57
  - lib/serega/plugins/batch/lib/plugins_extensions/preloads.rb
@@ -66,7 +73,13 @@ files:
66
73
  - lib/serega/plugins/metadata/validations/check_opts.rb
67
74
  - lib/serega/plugins/metadata/validations/check_path.rb
68
75
  - lib/serega/plugins/preloads/lib/format_user_preloads.rb
69
- - lib/serega/plugins/preloads/lib/main_preload_path.rb
76
+ - lib/serega/plugins/preloads/lib/modules/attribute.rb
77
+ - lib/serega/plugins/preloads/lib/modules/attribute_normalizer.rb
78
+ - lib/serega/plugins/preloads/lib/modules/check_attribute_params.rb
79
+ - lib/serega/plugins/preloads/lib/modules/config.rb
80
+ - lib/serega/plugins/preloads/lib/modules/plan_point.rb
81
+ - lib/serega/plugins/preloads/lib/preload_paths.rb
82
+ - lib/serega/plugins/preloads/lib/preloads_config.rb
70
83
  - lib/serega/plugins/preloads/lib/preloads_constructor.rb
71
84
  - lib/serega/plugins/preloads/preloads.rb
72
85
  - lib/serega/plugins/preloads/validations/check_opt_preload.rb
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Serega
4
- module SeregaPlugins
5
- module Preloads
6
- #
7
- # Class that constructs main preloads path.
8
- #
9
- # When we have nested preloads we will use this path to dig to `main` element and
10
- # assign nested preloads to it.
11
- #
12
- # By default its a path to latest provided preload
13
- #
14
- # @example
15
- # MainPreloadPath.(a: { b: { c: {} }, d: {} }) # => [:a, :d]
16
- #
17
- class MainPreloadPath
18
- class << self
19
- # Finds default preload path
20
- #
21
- # @param preloads [Hash] Formatted user provided preloads hash
22
- #
23
- # @return [Array<Symbol>] Preloads path to `main` element
24
- def call(preloads)
25
- return FROZEN_EMPTY_ARRAY if !preloads || preloads.empty?
26
-
27
- main_path(preloads).freeze
28
- end
29
-
30
- private
31
-
32
- # Generates path (Array) to the last included resource.
33
- # We need to know this path to include nested associations.
34
- #
35
- # main_path(a: { b: { c: {} }, d: {} }) # => [:a, :d]
36
- #
37
- def main_path(hash, path = [])
38
- current_level = path.size
39
-
40
- hash.each do |key, data|
41
- path.pop(path.size - current_level)
42
- path << key
43
-
44
- main_path(data, path)
45
- end
46
-
47
- path
48
- end
49
- end
50
- end
51
- end
52
- end
53
- end