serega 0.37.2 → 0.39.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.
- checksums.yaml +4 -4
- data/README.md +124 -26
- data/VERSION +1 -1
- data/lib/serega/attribute.rb +3 -3
- data/lib/serega/attribute_normalizer.rb +87 -14
- data/lib/serega/attribute_value_resolvers/batch.rb +4 -0
- data/lib/serega/config.rb +48 -1
- data/lib/serega/engine/level.rb +57 -0
- data/lib/serega/engine/level_queue.rb +54 -0
- data/lib/serega/{batch → engine}/loader.rb +1 -1
- data/lib/serega/object_serializer.rb +61 -56
- data/lib/serega/plan.rb +0 -9
- data/lib/serega/plan_point.rb +56 -5
- data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +21 -1
- data/lib/serega/plugins/explicit_many_option/explicit_many_option.rb +3 -2
- data/lib/serega/plugins/explicit_many_option/validations/check_opt_many.rb +6 -4
- data/lib/serega/plugins/if/if.rb +9 -10
- data/lib/serega/plugins/presenter/presenter.rb +101 -5
- data/lib/serega/plugins/root/root.rb +1 -1
- data/lib/serega/utils/collection_detector.rb +26 -0
- data/lib/serega/validations/attribute/check_block.rb +20 -55
- data/lib/serega/validations/attribute/check_opt_base_serializer.rb +37 -0
- data/lib/serega/validations/attribute/check_opt_batch.rb +6 -6
- data/lib/serega/validations/attribute/check_opt_const.rb +3 -4
- data/lib/serega/validations/attribute/check_opt_delegate.rb +3 -4
- data/lib/serega/validations/attribute/check_opt_many.rb +8 -7
- data/lib/serega/validations/attribute/check_opt_method.rb +3 -4
- data/lib/serega/validations/attribute/check_opt_serializer.rb +4 -1
- data/lib/serega/validations/attribute/check_opt_value.rb +3 -4
- data/lib/serega/validations/check_attribute_params.rb +8 -7
- data/lib/serega.rb +33 -29
- metadata +6 -5
- data/lib/serega/batch/attribute_loader.rb +0 -82
- data/lib/serega/batch/attribute_loaders.rb +0 -117
- data/lib/serega/batch/level.rb +0 -48
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Serega
|
|
4
|
+
module SeregaValidations
|
|
5
|
+
module Attribute
|
|
6
|
+
#
|
|
7
|
+
# Attribute `:base_serializer` option validator
|
|
8
|
+
#
|
|
9
|
+
class CheckOptBaseSerializer
|
|
10
|
+
class << self
|
|
11
|
+
#
|
|
12
|
+
# Checks attribute :base_serializer option. It specifies the parent
|
|
13
|
+
# class for the nested serializer defined with the attribute block,
|
|
14
|
+
# so it makes sense only when a block is provided.
|
|
15
|
+
#
|
|
16
|
+
# @param opts [Hash] Attribute options
|
|
17
|
+
# @param block [nil, Proc] Attribute block (defines a nested serializer)
|
|
18
|
+
#
|
|
19
|
+
# @raise [SeregaError] SeregaError that option has invalid value
|
|
20
|
+
#
|
|
21
|
+
# @return [void]
|
|
22
|
+
#
|
|
23
|
+
def call(opts, block = nil)
|
|
24
|
+
return unless opts.key?(:base_serializer)
|
|
25
|
+
|
|
26
|
+
raise SeregaError, "Option :base_serializer can be used only with a block" unless block
|
|
27
|
+
|
|
28
|
+
value = opts[:base_serializer]
|
|
29
|
+
return if value.is_a?(Class) && (value <= Serega)
|
|
30
|
+
|
|
31
|
+
raise SeregaError, "Invalid option :base_serializer => #{value.inspect}. Must be a Serega subclass"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -17,11 +17,11 @@ class Serega
|
|
|
17
17
|
#
|
|
18
18
|
# @return [void]
|
|
19
19
|
#
|
|
20
|
-
def call(serializer_class, opts
|
|
20
|
+
def call(serializer_class, opts)
|
|
21
21
|
return unless opts.key?(:batch)
|
|
22
22
|
|
|
23
23
|
check_opt_batch(opts, serializer_class)
|
|
24
|
-
check_usage_with_other_params(opts
|
|
24
|
+
check_usage_with_other_params(opts)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
private
|
|
@@ -72,22 +72,22 @@ class Serega
|
|
|
72
72
|
Utils::CheckAllowedKeys.call(batch_opts, %i[use id], :batch)
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
def check_usage_with_other_params(opts
|
|
75
|
+
def check_usage_with_other_params(opts)
|
|
76
76
|
batch = opts[:batch]
|
|
77
77
|
use_id = batch.is_a?(Hash) && batch.key?(:id)
|
|
78
78
|
use_multiple = batch.is_a?(Hash) && (Array(batch[:use]).size > 1)
|
|
79
|
-
value_added = opts.key?(:value)
|
|
79
|
+
value_added = opts.key?(:value)
|
|
80
80
|
|
|
81
81
|
if use_multiple && use_id
|
|
82
82
|
raise SeregaError, "Option `batch.id` should not be used with multiple loaders provided in `batch.use`"
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
if use_multiple && !value_added
|
|
86
|
-
raise SeregaError, "Attribute :value option
|
|
86
|
+
raise SeregaError, "Attribute :value option should be provided when selecting multiple batch loaders"
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
if use_id && value_added
|
|
90
|
-
raise SeregaError, "Option `batch.id` should not be used when :value
|
|
90
|
+
raise SeregaError, "Option `batch.id` should not be used when :value option provided directly"
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
raise SeregaError, "Option :batch can not be used together with option :method" if opts.key?(:method)
|
|
@@ -17,19 +17,18 @@ class Serega
|
|
|
17
17
|
#
|
|
18
18
|
# @return [void]
|
|
19
19
|
#
|
|
20
|
-
def call(opts
|
|
20
|
+
def call(opts)
|
|
21
21
|
return unless opts.key?(:const)
|
|
22
22
|
|
|
23
|
-
check_usage_with_other_params(opts
|
|
23
|
+
check_usage_with_other_params(opts)
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
private
|
|
27
27
|
|
|
28
|
-
def check_usage_with_other_params(opts
|
|
28
|
+
def check_usage_with_other_params(opts)
|
|
29
29
|
raise SeregaError, "Option :const can not be used together with option :method" if opts.key?(:method)
|
|
30
30
|
raise SeregaError, "Option :const can not be used together with option :value" if opts.key?(:value)
|
|
31
31
|
raise SeregaError, "Option :const can not be used together with option :batch" if opts.key?(:batch)
|
|
32
|
-
raise SeregaError, "Option :const can not be used together with block" if block
|
|
33
32
|
end
|
|
34
33
|
end
|
|
35
34
|
end
|
|
@@ -18,11 +18,11 @@ class Serega
|
|
|
18
18
|
#
|
|
19
19
|
# @return [void]
|
|
20
20
|
#
|
|
21
|
-
def call(opts
|
|
21
|
+
def call(opts)
|
|
22
22
|
return unless opts.key?(:delegate)
|
|
23
23
|
|
|
24
24
|
check_opt_delegate(opts)
|
|
25
|
-
check_usage_with_other_params(opts
|
|
25
|
+
check_usage_with_other_params(opts)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
private
|
|
@@ -56,12 +56,11 @@ class Serega
|
|
|
56
56
|
Utils::CheckAllowedKeys.call(delegate_opts, %i[to method allow_nil], :delegate)
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
-
def check_usage_with_other_params(opts
|
|
59
|
+
def check_usage_with_other_params(opts)
|
|
60
60
|
raise SeregaError, "Option :delegate can not be used together with option :method" if opts.key?(:method)
|
|
61
61
|
raise SeregaError, "Option :delegate can not be used together with option :const" if opts.key?(:const)
|
|
62
62
|
raise SeregaError, "Option :delegate can not be used together with option :value" if opts.key?(:value)
|
|
63
63
|
raise SeregaError, "Option :delegate can not be used together with option :batch" if opts.key?(:batch)
|
|
64
|
-
raise SeregaError, "Option :delegate can not be used together with block" if block
|
|
65
64
|
end
|
|
66
65
|
end
|
|
67
66
|
end
|
|
@@ -12,28 +12,29 @@ class Serega
|
|
|
12
12
|
# Checks attribute :many option
|
|
13
13
|
#
|
|
14
14
|
# @param opts [Hash] Attribute options
|
|
15
|
+
# @param block [nil, Proc] Attribute block
|
|
15
16
|
#
|
|
16
17
|
# @raise [SeregaError] SeregaError that option has invalid value
|
|
17
18
|
#
|
|
18
19
|
# @return [void]
|
|
19
20
|
#
|
|
20
|
-
def call(opts)
|
|
21
|
+
def call(opts, block = nil)
|
|
21
22
|
return unless opts.key?(:many)
|
|
22
23
|
|
|
23
|
-
check_many_option_makes_sence(opts)
|
|
24
|
+
check_many_option_makes_sence(opts, block)
|
|
24
25
|
Utils::CheckOptIsBool.call(opts, :many)
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
private
|
|
28
29
|
|
|
29
|
-
def check_many_option_makes_sence(opts)
|
|
30
|
-
return if many_option_makes_sence?(opts)
|
|
30
|
+
def check_many_option_makes_sence(opts, block)
|
|
31
|
+
return if many_option_makes_sence?(opts, block)
|
|
31
32
|
|
|
32
|
-
raise SeregaError, "Option :many can be provided only together with :serializer
|
|
33
|
+
raise SeregaError, "Option :many can be provided only together with :serializer, :batch option or a block"
|
|
33
34
|
end
|
|
34
35
|
|
|
35
|
-
def many_option_makes_sence?(opts)
|
|
36
|
-
opts[:serializer] || opts[:batch]
|
|
36
|
+
def many_option_makes_sence?(opts, block)
|
|
37
|
+
opts[:serializer] || opts[:batch] || block
|
|
37
38
|
end
|
|
38
39
|
end
|
|
39
40
|
end
|
|
@@ -17,20 +17,19 @@ class Serega
|
|
|
17
17
|
#
|
|
18
18
|
# @return [void]
|
|
19
19
|
#
|
|
20
|
-
def call(opts
|
|
20
|
+
def call(opts)
|
|
21
21
|
return unless opts.key?(:method)
|
|
22
22
|
|
|
23
|
-
check_usage_with_other_params(opts
|
|
23
|
+
check_usage_with_other_params(opts)
|
|
24
24
|
Utils::CheckOptIsStringOrSymbol.call(opts, :method)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
private
|
|
28
28
|
|
|
29
|
-
def check_usage_with_other_params(opts
|
|
29
|
+
def check_usage_with_other_params(opts)
|
|
30
30
|
raise SeregaError, "Option :method can not be used together with option :const" if opts.key?(:const)
|
|
31
31
|
raise SeregaError, "Option :method can not be used together with option :value" if opts.key?(:value)
|
|
32
32
|
raise SeregaError, "Option :method can not be used together with option :batch" if opts.key?(:batch)
|
|
33
|
-
raise SeregaError, "Option :method can not be used together with block" if block
|
|
34
33
|
end
|
|
35
34
|
end
|
|
36
35
|
end
|
|
@@ -12,14 +12,17 @@ class Serega
|
|
|
12
12
|
# Checks attribute :serializer option
|
|
13
13
|
#
|
|
14
14
|
# @param opts [Hash] Attribute options
|
|
15
|
+
# @param block [nil, Proc] Attribute block (defines a nested serializer)
|
|
15
16
|
#
|
|
16
17
|
# @raise [SeregaError] SeregaError that option has invalid value
|
|
17
18
|
#
|
|
18
19
|
# @return [void]
|
|
19
20
|
#
|
|
20
|
-
def call(opts)
|
|
21
|
+
def call(opts, block = nil)
|
|
21
22
|
return unless opts.key?(:serializer)
|
|
22
23
|
|
|
24
|
+
raise SeregaError, "Option :serializer can not be used together with block" if block
|
|
25
|
+
|
|
23
26
|
value = opts[:serializer]
|
|
24
27
|
return if valid_serializer?(value)
|
|
25
28
|
|
|
@@ -17,20 +17,19 @@ class Serega
|
|
|
17
17
|
#
|
|
18
18
|
# @return [void]
|
|
19
19
|
#
|
|
20
|
-
def call(opts
|
|
20
|
+
def call(opts)
|
|
21
21
|
return unless opts.key?(:value)
|
|
22
22
|
|
|
23
|
-
check_usage_with_other_params(opts
|
|
23
|
+
check_usage_with_other_params(opts)
|
|
24
24
|
|
|
25
25
|
check_value(opts[:value])
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
private
|
|
29
29
|
|
|
30
|
-
def check_usage_with_other_params(opts
|
|
30
|
+
def check_usage_with_other_params(opts)
|
|
31
31
|
raise SeregaError, "Option :value can not be used together with option :method" if opts.key?(:method)
|
|
32
32
|
raise SeregaError, "Option :value can not be used together with option :const" if opts.key?(:const)
|
|
33
|
-
raise SeregaError, "Option :value can not be used together with block" if block
|
|
34
33
|
end
|
|
35
34
|
|
|
36
35
|
def check_value(value)
|
|
@@ -59,15 +59,16 @@ class Serega
|
|
|
59
59
|
def check_opts
|
|
60
60
|
Utils::CheckAllowedKeys.call(opts, allowed_opts_keys, :attribute)
|
|
61
61
|
|
|
62
|
-
Attribute::
|
|
63
|
-
Attribute::
|
|
62
|
+
Attribute::CheckOptBaseSerializer.call(opts, block)
|
|
63
|
+
Attribute::CheckOptConst.call(opts)
|
|
64
|
+
Attribute::CheckOptDelegate.call(opts)
|
|
64
65
|
Attribute::CheckOptHide.call(opts)
|
|
65
|
-
Attribute::CheckOptMethod.call(opts
|
|
66
|
-
Attribute::CheckOptMany.call(opts)
|
|
66
|
+
Attribute::CheckOptMethod.call(opts)
|
|
67
|
+
Attribute::CheckOptMany.call(opts, block)
|
|
67
68
|
Attribute::CheckOptPreload.call(opts)
|
|
68
|
-
Attribute::CheckOptSerializer.call(opts)
|
|
69
|
-
Attribute::CheckOptValue.call(opts
|
|
70
|
-
Attribute::CheckOptBatch.call(self.class.serializer_class, opts
|
|
69
|
+
Attribute::CheckOptSerializer.call(opts, block)
|
|
70
|
+
Attribute::CheckOptValue.call(opts)
|
|
71
|
+
Attribute::CheckOptBatch.call(self.class.serializer_class, opts)
|
|
71
72
|
end
|
|
72
73
|
|
|
73
74
|
def check_block
|
data/lib/serega.rb
CHANGED
|
@@ -19,6 +19,7 @@ end
|
|
|
19
19
|
|
|
20
20
|
require_relative "serega/errors"
|
|
21
21
|
require_relative "serega/helpers/serializer_class_helper"
|
|
22
|
+
require_relative "serega/utils/collection_detector"
|
|
22
23
|
require_relative "serega/utils/enum_deep_dup"
|
|
23
24
|
require_relative "serega/utils/enum_deep_freeze"
|
|
24
25
|
require_relative "serega/utils/method_signature"
|
|
@@ -31,16 +32,16 @@ require_relative "serega/attribute_value_resolvers/delegate"
|
|
|
31
32
|
require_relative "serega/attribute_value_resolvers/keyword"
|
|
32
33
|
require_relative "serega/attribute"
|
|
33
34
|
require_relative "serega/attribute_normalizer"
|
|
34
|
-
require_relative "serega/
|
|
35
|
-
require_relative "serega/
|
|
36
|
-
require_relative "serega/
|
|
37
|
-
require_relative "serega/batch/loader"
|
|
35
|
+
require_relative "serega/engine/level_queue"
|
|
36
|
+
require_relative "serega/engine/level"
|
|
37
|
+
require_relative "serega/engine/loader"
|
|
38
38
|
require_relative "serega/validations/utils/check_allowed_keys"
|
|
39
39
|
require_relative "serega/validations/utils/check_opt_is_bool"
|
|
40
40
|
require_relative "serega/validations/utils/check_opt_is_hash"
|
|
41
41
|
require_relative "serega/validations/utils/check_opt_is_string_or_symbol"
|
|
42
42
|
require_relative "serega/validations/attribute/check_block"
|
|
43
43
|
require_relative "serega/validations/attribute/check_name"
|
|
44
|
+
require_relative "serega/validations/attribute/check_opt_base_serializer"
|
|
44
45
|
require_relative "serega/validations/attribute/check_opt_const"
|
|
45
46
|
require_relative "serega/validations/attribute/check_opt_hide"
|
|
46
47
|
require_relative "serega/validations/attribute/check_opt_delegate"
|
|
@@ -86,15 +87,10 @@ class Serega
|
|
|
86
87
|
check_batch_loader_params_class.serializer_class = self
|
|
87
88
|
const_set(:CheckBatchLoaderParams, check_batch_loader_params_class)
|
|
88
89
|
|
|
89
|
-
# Assigns `
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const_set(:
|
|
93
|
-
|
|
94
|
-
# Assigns `SeregaBatchAttributeLoader` constant to current class
|
|
95
|
-
batch_attribute_loader_class = Class.new(SeregaBatch::AttributeLoader)
|
|
96
|
-
batch_attribute_loader_class.serializer_class = self
|
|
97
|
-
const_set(:SeregaBatchAttributeLoader, batch_attribute_loader_class)
|
|
90
|
+
# Assigns `SeregaEngineLoader` constant to current class
|
|
91
|
+
engine_loader_class = Class.new(SeregaEngine::Loader)
|
|
92
|
+
engine_loader_class.serializer_class = self
|
|
93
|
+
const_set(:SeregaEngineLoader, engine_loader_class)
|
|
98
94
|
|
|
99
95
|
#
|
|
100
96
|
# Serializers class methods
|
|
@@ -174,7 +170,19 @@ class Serega
|
|
|
174
170
|
#
|
|
175
171
|
# @param name [Symbol] Attribute name. Attribute value will be found by executing `object.<name>`
|
|
176
172
|
# @param opts [Hash] Options to serialize attribute
|
|
177
|
-
# @param block [Proc]
|
|
173
|
+
# @param block [Proc] Defines attributes of a nested anonymous serializer.
|
|
174
|
+
# The block is executed in the context of that serializer, so everything
|
|
175
|
+
# available in a serializer class body can be used inside. The attribute
|
|
176
|
+
# value (found by name/:method/:value/:delegate/:const/:batch as usual)
|
|
177
|
+
# is serialized with this nested serializer. The nested serializer
|
|
178
|
+
# inherits from the `:base_serializer` attribute option or
|
|
179
|
+
# `config.base_serializer` (one of them is required).
|
|
180
|
+
#
|
|
181
|
+
# @example
|
|
182
|
+
# attribute :statistics, method: :itself do
|
|
183
|
+
# attribute :likes_count
|
|
184
|
+
# attribute :comments_count
|
|
185
|
+
# end
|
|
178
186
|
#
|
|
179
187
|
# @return [Serega::SeregaAttribute] Added attribute
|
|
180
188
|
#
|
|
@@ -214,7 +222,7 @@ class Serega
|
|
|
214
222
|
def batch(name, value = nil, &block)
|
|
215
223
|
raise SeregaError, "Batch loader must be defined with a callable value or block" if (value && block) || (!value && !block)
|
|
216
224
|
|
|
217
|
-
batch_loader = self::
|
|
225
|
+
batch_loader = self::SeregaEngineLoader.new(name: name, block: value || block)
|
|
218
226
|
batch_loaders[batch_loader.name] = batch_loader
|
|
219
227
|
end
|
|
220
228
|
|
|
@@ -260,7 +268,7 @@ class Serega
|
|
|
260
268
|
# @option opts [Array, Hash, String, Symbol] :with Attributes (usually hidden) to serialize additionally
|
|
261
269
|
# @option opts [Boolean] :validate Validates provided modifiers (Default is true)
|
|
262
270
|
# @option opts [Hash] :context Serialization context
|
|
263
|
-
# @option opts [Boolean] :many Set true if provided multiple objects (Default `object.is_a?(Enumerable)`)
|
|
271
|
+
# @option opts [Boolean] :many Set true if provided multiple objects (Default `object.is_a?(Enumerable) && !object.is_a?(Hash) && !object.is_a?(Struct)`)
|
|
264
272
|
#
|
|
265
273
|
# @return [Hash] Serialization result
|
|
266
274
|
#
|
|
@@ -281,7 +289,7 @@ class Serega
|
|
|
281
289
|
# @option opts [Array, Hash, String, Symbol] :with Attributes (usually hidden) to serialize additionally
|
|
282
290
|
# @option opts [Boolean] :validate Validates provided modifiers (Default is true)
|
|
283
291
|
# @option opts [Hash] :context Serialization context
|
|
284
|
-
# @option opts [Boolean] :many Set true if provided multiple objects (Default `object.is_a?(Enumerable)`)
|
|
292
|
+
# @option opts [Boolean] :many Set true if provided multiple objects (Default `object.is_a?(Enumerable) && !object.is_a?(Hash) && !object.is_a?(Struct)`)
|
|
285
293
|
#
|
|
286
294
|
# @return [Data, Array<Data>, nil] Serialization result as Data object(s)
|
|
287
295
|
#
|
|
@@ -333,13 +341,9 @@ class Serega
|
|
|
333
341
|
plan_point_class.serializer_class = subclass
|
|
334
342
|
subclass.const_set(:SeregaPlanPoint, plan_point_class)
|
|
335
343
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
subclass.const_set(:
|
|
339
|
-
|
|
340
|
-
batch_attribute_loader_class = Class.new(self::SeregaBatchAttributeLoader)
|
|
341
|
-
batch_attribute_loader_class.serializer_class = subclass
|
|
342
|
-
subclass.const_set(:SeregaBatchAttributeLoader, batch_attribute_loader_class)
|
|
344
|
+
engine_loader_class = Class.new(self::SeregaEngineLoader)
|
|
345
|
+
engine_loader_class.serializer_class = subclass
|
|
346
|
+
subclass.const_set(:SeregaEngineLoader, engine_loader_class)
|
|
343
347
|
|
|
344
348
|
object_serializer_class = Class.new(self::SeregaObjectSerializer)
|
|
345
349
|
object_serializer_class.serializer_class = subclass
|
|
@@ -419,7 +423,7 @@ class Serega
|
|
|
419
423
|
# @param object [Object] Serialized object
|
|
420
424
|
# @param opts [Hash, nil] Serializing options
|
|
421
425
|
# @option opts [Hash] :context Serialization context
|
|
422
|
-
# @option opts [Boolean] :many Set true if provided multiple objects (Default `object.is_a?(Enumerable)`)
|
|
426
|
+
# @option opts [Boolean] :many Set true if provided multiple objects (Default `object.is_a?(Enumerable) && !object.is_a?(Hash) && !object.is_a?(Struct)`)
|
|
423
427
|
#
|
|
424
428
|
# @return [Hash] Serialization result
|
|
425
429
|
#
|
|
@@ -441,7 +445,7 @@ class Serega
|
|
|
441
445
|
# @param object [Object] Serialized object
|
|
442
446
|
# @param opts [Hash, nil] Serializing options
|
|
443
447
|
# @option opts [Hash] :context Serialization context
|
|
444
|
-
# @option opts [Boolean] :many Set true if provided multiple objects (Default `object.is_a?(Enumerable)`)
|
|
448
|
+
# @option opts [Boolean] :many Set true if provided multiple objects (Default `object.is_a?(Enumerable) && !object.is_a?(Hash) && !object.is_a?(Struct)`)
|
|
445
449
|
#
|
|
446
450
|
# @return [Data] Serialization result
|
|
447
451
|
#
|
|
@@ -481,8 +485,8 @@ class Serega
|
|
|
481
485
|
self.class::CheckSerializeParams.new(opts).validate unless opts.empty?
|
|
482
486
|
|
|
483
487
|
opts[:context] ||= {}
|
|
484
|
-
opts[:
|
|
485
|
-
opts[:many] =
|
|
488
|
+
opts[:level_queue] = SeregaEngine::LevelQueue.new
|
|
489
|
+
opts[:many] = SeregaUtils::CollectionDetector.call(object) unless opts.key?(:many)
|
|
486
490
|
opts[:plan] = plan
|
|
487
491
|
opts
|
|
488
492
|
end
|
|
@@ -493,7 +497,7 @@ class Serega
|
|
|
493
497
|
# - plugin :metadata (adds metadata to final result)
|
|
494
498
|
def serialize(object, opts)
|
|
495
499
|
result = self.class::SeregaObjectSerializer.new(**opts).serialize(object)
|
|
496
|
-
opts[:
|
|
500
|
+
opts[:level_queue].run
|
|
497
501
|
result
|
|
498
502
|
end
|
|
499
503
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: serega
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.39.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrey Glushkov
|
|
@@ -33,12 +33,11 @@ files:
|
|
|
33
33
|
- lib/serega/attribute_value_resolvers/const.rb
|
|
34
34
|
- lib/serega/attribute_value_resolvers/delegate.rb
|
|
35
35
|
- lib/serega/attribute_value_resolvers/keyword.rb
|
|
36
|
-
- lib/serega/batch/attribute_loader.rb
|
|
37
|
-
- lib/serega/batch/attribute_loaders.rb
|
|
38
|
-
- lib/serega/batch/level.rb
|
|
39
|
-
- lib/serega/batch/loader.rb
|
|
40
36
|
- lib/serega/config.rb
|
|
41
37
|
- lib/serega/data_builder.rb
|
|
38
|
+
- lib/serega/engine/level.rb
|
|
39
|
+
- lib/serega/engine/level_queue.rb
|
|
40
|
+
- lib/serega/engine/loader.rb
|
|
42
41
|
- lib/serega/errors.rb
|
|
43
42
|
- lib/serega/helpers/serializer_class_helper.rb
|
|
44
43
|
- lib/serega/object_serializer.rb
|
|
@@ -71,6 +70,7 @@ files:
|
|
|
71
70
|
- lib/serega/plugins/root/root.rb
|
|
72
71
|
- lib/serega/plugins/string_modifiers/parse_string_modifiers.rb
|
|
73
72
|
- lib/serega/plugins/string_modifiers/string_modifiers.rb
|
|
73
|
+
- lib/serega/utils/collection_detector.rb
|
|
74
74
|
- lib/serega/utils/enum_deep_dup.rb
|
|
75
75
|
- lib/serega/utils/enum_deep_freeze.rb
|
|
76
76
|
- lib/serega/utils/method_signature.rb
|
|
@@ -79,6 +79,7 @@ files:
|
|
|
79
79
|
- lib/serega/utils/to_hash.rb
|
|
80
80
|
- lib/serega/validations/attribute/check_block.rb
|
|
81
81
|
- lib/serega/validations/attribute/check_name.rb
|
|
82
|
+
- lib/serega/validations/attribute/check_opt_base_serializer.rb
|
|
82
83
|
- lib/serega/validations/attribute/check_opt_batch.rb
|
|
83
84
|
- lib/serega/validations/attribute/check_opt_const.rb
|
|
84
85
|
- lib/serega/validations/attribute/check_opt_delegate.rb
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class Serega
|
|
4
|
-
#
|
|
5
|
-
# Batch feature main module
|
|
6
|
-
#
|
|
7
|
-
module SeregaBatch
|
|
8
|
-
# Remembers data required to batch load single attribute
|
|
9
|
-
class AttributeLoader
|
|
10
|
-
# Instance methods for AttributeLoader
|
|
11
|
-
module InstanceMethods
|
|
12
|
-
# @param point [SeregaPlanPoint]
|
|
13
|
-
# @param level [SeregaBatch::Level] Shared objects and results for this point's plan level
|
|
14
|
-
def initialize(point, level)
|
|
15
|
-
@point = point
|
|
16
|
-
@level = level
|
|
17
|
-
@serialized_object_attachers = []
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Stores object with attacher to find and attach attribute values in batch later.
|
|
21
|
-
# @param object [Object] Serialized object
|
|
22
|
-
# @param attacher [Proc] Proc that attaches found values to originated attributes
|
|
23
|
-
# @return [void]
|
|
24
|
-
def store(object, attacher)
|
|
25
|
-
serialized_object_attachers << [object, attacher]
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Preloads this attribute's associations onto the level's objects, once for
|
|
29
|
-
# the attribute (not per named batch loader), via the serializer's registered
|
|
30
|
-
# `preload_with` handler. Raises when `:preload` is declared but no handler
|
|
31
|
-
# exists, so a declared preload never silently does nothing.
|
|
32
|
-
# @return [void]
|
|
33
|
-
def preload_associations
|
|
34
|
-
preloads = point.attribute.preloads
|
|
35
|
-
return unless preloads
|
|
36
|
-
|
|
37
|
-
handler = point.class.serializer_class.preload_with
|
|
38
|
-
unless handler
|
|
39
|
-
raise SeregaError, "The :preload option requires a preload handler. Register one with `preload_with` (the :activerecord_preloads plugin does this for you)."
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
handler.call(level.objects, preloads)
|
|
43
|
-
rescue => error
|
|
44
|
-
SeregaUtils::SerializedAttributeError.call(error, point)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Loads a single named batch for this level's objects. Caching across
|
|
48
|
-
# attributes that reuse the loader is handled by the Level.
|
|
49
|
-
# @param loader [SeregaBatch::Loader] Named batch loader
|
|
50
|
-
# @return [Object] Loaded batch values
|
|
51
|
-
def load_batch(loader)
|
|
52
|
-
level.load(loader)
|
|
53
|
-
rescue => error
|
|
54
|
-
SeregaUtils::SerializedAttributeError.call(error, point)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Attaches already loaded batch values to every stored object.
|
|
58
|
-
# Value resolution happens here (inside the attacher), so errors are
|
|
59
|
-
# annotated with the attribute, matching the synchronous walk.
|
|
60
|
-
# @param batches [Hash] Loaded values grouped by batch loader name
|
|
61
|
-
# @return [void]
|
|
62
|
-
def attach(batches)
|
|
63
|
-
serialized_object_attachers.each do |object, attacher|
|
|
64
|
-
attacher.call(object, batches)
|
|
65
|
-
end
|
|
66
|
-
rescue => error
|
|
67
|
-
SeregaUtils::SerializedAttributeError.call(error, point)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
attr_reader :point
|
|
71
|
-
attr_reader :level
|
|
72
|
-
|
|
73
|
-
private
|
|
74
|
-
|
|
75
|
-
attr_reader :serialized_object_attachers
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
extend SeregaHelpers::SerializerClassHelper
|
|
79
|
-
include InstanceMethods
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class Serega
|
|
4
|
-
#
|
|
5
|
-
# Batch feature main module
|
|
6
|
-
#
|
|
7
|
-
module SeregaBatch
|
|
8
|
-
# Reserved batch-loader name that marks relation/preload attributes as
|
|
9
|
-
# batch-processed. It has no registered loader — the attribute's value comes
|
|
10
|
-
# from its own resolver — so `load_batches` skips it (nothing to load).
|
|
11
|
-
AUTO_BATCH_LOADER_NAME = :__auto_batch__
|
|
12
|
-
|
|
13
|
-
#
|
|
14
|
-
# Batch loaders
|
|
15
|
-
#
|
|
16
|
-
# Remembers data required to batch load all attributes
|
|
17
|
-
class AttributeLoaders
|
|
18
|
-
#
|
|
19
|
-
# Initializes new batch loaders
|
|
20
|
-
#
|
|
21
|
-
# @param context [Hash] Serialization context, constant for the whole run
|
|
22
|
-
#
|
|
23
|
-
def initialize(context)
|
|
24
|
-
@context = context
|
|
25
|
-
@attribute_loaders = []
|
|
26
|
-
@point_index = {}.compare_by_identity
|
|
27
|
-
@levels = {}.compare_by_identity
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Gathers a chunk of objects serialized at a plan level, so every attribute at
|
|
31
|
-
# that level shares one set of objects to batch load against.
|
|
32
|
-
#
|
|
33
|
-
# @param plan [SeregaPlan] Plan serializing these objects
|
|
34
|
-
# @param objects [Array] Objects being serialized at this level
|
|
35
|
-
#
|
|
36
|
-
# @return [void]
|
|
37
|
-
def add_objects(plan, objects)
|
|
38
|
-
level_for(plan).add_objects(objects)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Remembers data for batch serialization:
|
|
42
|
-
#
|
|
43
|
-
# @param point [SeregaPlanPoint] Serialization plan point
|
|
44
|
-
# @param object [Object] Serialized object
|
|
45
|
-
# @param attacher [Proc] Serialized value attacher
|
|
46
|
-
#
|
|
47
|
-
# @return [void]
|
|
48
|
-
def remember(point, object, attacher)
|
|
49
|
-
attribute_loader = point_index[point]
|
|
50
|
-
|
|
51
|
-
unless attribute_loader
|
|
52
|
-
serializer_class = point.class.serializer_class
|
|
53
|
-
attribute_loader = serializer_class::SeregaBatchAttributeLoader.new(point, level_for(point.plan))
|
|
54
|
-
attribute_loaders << attribute_loader
|
|
55
|
-
point_index[point] = attribute_loader
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
attribute_loader.store(object, attacher)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
#
|
|
62
|
-
# Loads all registered batches and attaches loaded values.
|
|
63
|
-
#
|
|
64
|
-
# Iterates over each loader including loaders added during iteration
|
|
65
|
-
# (child serializers discovered inside a batch flush add new loaders).
|
|
66
|
-
def load_all
|
|
67
|
-
i = 0
|
|
68
|
-
while i < attribute_loaders.size
|
|
69
|
-
attribute_loader = attribute_loaders[i]
|
|
70
|
-
attribute_loader.attach(load_batches(attribute_loader))
|
|
71
|
-
i += 1
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
private
|
|
76
|
-
|
|
77
|
-
# Serialization context shared by every level
|
|
78
|
-
attr_reader :context
|
|
79
|
-
|
|
80
|
-
# keeps all AttributeLoader instances
|
|
81
|
-
attr_reader :attribute_loaders
|
|
82
|
-
|
|
83
|
-
# keeps tracking of already added points
|
|
84
|
-
attr_reader :point_index
|
|
85
|
-
|
|
86
|
-
# keeps one Level per plan (compare_by_identity: plan instance identifies a level)
|
|
87
|
-
attr_reader :levels
|
|
88
|
-
|
|
89
|
-
def level_for(plan)
|
|
90
|
-
levels[plan] ||= Level.new(context)
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# Builds the { batch_loader_name => loaded_values } hash for one attribute.
|
|
94
|
-
#
|
|
95
|
-
# Loading is delegated to the attribute's Level, which loads each named batch
|
|
96
|
-
# once for the whole level: attributes sharing a loader over the same objects
|
|
97
|
-
# reuse a single result, deeper levels load separately.
|
|
98
|
-
def load_batches(attribute_loader)
|
|
99
|
-
attribute_loader.preload_associations
|
|
100
|
-
|
|
101
|
-
point = attribute_loader.point
|
|
102
|
-
serializer_class = point.class.serializer_class
|
|
103
|
-
|
|
104
|
-
batches = {}
|
|
105
|
-
point.batch_loaders.each do |batch_loader_name|
|
|
106
|
-
# Auto-batched relations/preloads carry the marker, not a real loader:
|
|
107
|
-
# their value comes from their own resolver, so there is nothing to load.
|
|
108
|
-
next if batch_loader_name == AUTO_BATCH_LOADER_NAME
|
|
109
|
-
|
|
110
|
-
loader = serializer_class.batch_loaders[batch_loader_name]
|
|
111
|
-
batches[batch_loader_name] = attribute_loader.load_batch(loader)
|
|
112
|
-
end
|
|
113
|
-
batches
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
end
|