serega 0.36.0 → 0.37.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 +61 -78
- data/VERSION +1 -1
- data/lib/serega/attribute.rb +0 -5
- data/lib/serega/attribute_normalizer.rb +28 -70
- data/lib/serega/batch/attribute_loader.rb +35 -20
- data/lib/serega/batch/attribute_loaders.rb +64 -15
- data/lib/serega/batch/level.rb +48 -0
- data/lib/serega/config.rb +8 -10
- data/lib/serega/object_serializer.rb +7 -1
- data/lib/serega/plan.rb +9 -16
- data/lib/serega/plan_point.rb +0 -19
- data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +9 -54
- data/lib/serega/plugins/activerecord_preloads/lib/preloader.rb +1 -1
- data/lib/serega/validations/attribute/check_opt_preload.rb +11 -0
- data/lib/serega/validations/check_attribute_params.rb +0 -1
- data/lib/serega.rb +38 -12
- metadata +3 -7
- data/lib/serega/plugins/activerecord_preloads/lib/active_record_objects.rb +0 -31
- data/lib/serega/utils/format_user_preloads.rb +0 -58
- data/lib/serega/utils/preload_paths.rb +0 -53
- data/lib/serega/utils/preloads_constructor.rb +0 -77
- data/lib/serega/validations/attribute/check_opt_preload_path.rb +0 -89
data/lib/serega/config.rb
CHANGED
|
@@ -23,7 +23,6 @@ class Serega
|
|
|
23
23
|
delegate
|
|
24
24
|
default
|
|
25
25
|
preload
|
|
26
|
-
preload_path
|
|
27
26
|
batch
|
|
28
27
|
].freeze,
|
|
29
28
|
serialize_keys: %i[context many].freeze,
|
|
@@ -116,27 +115,26 @@ class Serega
|
|
|
116
115
|
end
|
|
117
116
|
|
|
118
117
|
# Returns :hide_by_default config option
|
|
119
|
-
# @return [Boolean,
|
|
118
|
+
# @return [Boolean, Symbol] Current :hide_by_default config option
|
|
120
119
|
def hide_by_default
|
|
121
120
|
opts.fetch(:hide_by_default)
|
|
122
121
|
end
|
|
123
122
|
|
|
124
123
|
# Sets :hide_by_default config option
|
|
125
124
|
#
|
|
126
|
-
# @param value [Boolean,
|
|
125
|
+
# @param value [Boolean, Symbol] Accepted values:
|
|
126
|
+
# - false (default) — nothing is hidden by default
|
|
127
|
+
# - true — all attributes are hidden by default
|
|
128
|
+
# - :auto — hides attributes that declare :preload or :batch
|
|
127
129
|
#
|
|
128
|
-
# @return [Boolean,
|
|
130
|
+
# @return [Boolean, Symbol] New :hide_by_default config option
|
|
129
131
|
def hide_by_default=(value)
|
|
130
132
|
opts[:hide_by_default] =
|
|
131
133
|
case value
|
|
132
|
-
when true, false
|
|
133
|
-
value
|
|
134
|
-
when Array
|
|
135
|
-
invalid = value - %i[preload batch]
|
|
136
|
-
raise SeregaError, "Must have true, false, or an Array of [:preload, :batch], #{value.inspect} provided" if invalid.any?
|
|
134
|
+
when true, false, :auto
|
|
137
135
|
value
|
|
138
136
|
else
|
|
139
|
-
raise SeregaError, "Must have true, false, or
|
|
137
|
+
raise SeregaError, "Must have true, false, or :auto, #{value.inspect} provided"
|
|
140
138
|
end
|
|
141
139
|
end
|
|
142
140
|
|
|
@@ -34,7 +34,13 @@ class Serega
|
|
|
34
34
|
def serialize(object)
|
|
35
35
|
return if object.nil?
|
|
36
36
|
|
|
37
|
-
array?(object, many)
|
|
37
|
+
if array?(object, many)
|
|
38
|
+
batch_loaders.add_objects(plan, object.to_a) if plan.batch?
|
|
39
|
+
serialize_array(object)
|
|
40
|
+
else
|
|
41
|
+
batch_loaders.add_objects(plan, [object]) if plan.batch?
|
|
42
|
+
serialize_object(object)
|
|
43
|
+
end
|
|
38
44
|
end
|
|
39
45
|
|
|
40
46
|
private
|
data/lib/serega/plan.rb
CHANGED
|
@@ -69,10 +69,6 @@ class Serega
|
|
|
69
69
|
# SeregaPlan instance methods
|
|
70
70
|
#
|
|
71
71
|
module InstanceMethods
|
|
72
|
-
# Shows if plan includes batch points
|
|
73
|
-
# @return [Array<SeregaPlanPoint>] points to serialize
|
|
74
|
-
attr_reader :has_batch_points
|
|
75
|
-
|
|
76
72
|
# Parent plan point
|
|
77
73
|
# @return [SeregaPlanPoint, nil]
|
|
78
74
|
attr_reader :parent_plan_point
|
|
@@ -100,7 +96,6 @@ class Serega
|
|
|
100
96
|
# @return [SeregaPlan] Serialization plan
|
|
101
97
|
#
|
|
102
98
|
def initialize(parent_plan_point, modifiers)
|
|
103
|
-
@has_batch_points = false # should be before assigning points, generated points can change this attribute
|
|
104
99
|
@parent_plan_point = parent_plan_point
|
|
105
100
|
@points = attributes_points(modifiers)
|
|
106
101
|
@points_hash = points.to_h { |point| [point.name, point] }
|
|
@@ -113,6 +108,15 @@ class Serega
|
|
|
113
108
|
self.class.serializer_class
|
|
114
109
|
end
|
|
115
110
|
|
|
111
|
+
#
|
|
112
|
+
# @return [Boolean] whether any point of this plan is batch loaded
|
|
113
|
+
#
|
|
114
|
+
def batch?
|
|
115
|
+
return @batch if defined?(@batch)
|
|
116
|
+
|
|
117
|
+
@batch = points.any?(&:batch?)
|
|
118
|
+
end
|
|
119
|
+
|
|
116
120
|
# Returns the Data class whose members match this plan's serialized fields.
|
|
117
121
|
# Delegates to the class-level cache so identical field sets share one Data class.
|
|
118
122
|
#
|
|
@@ -122,17 +126,6 @@ class Serega
|
|
|
122
126
|
@data_class ||= self.class.data_class_for(point_names)
|
|
123
127
|
end
|
|
124
128
|
|
|
125
|
-
#
|
|
126
|
-
# Marks current plan and top-level plans as `has_batch_points`
|
|
127
|
-
# It is needed to initialize Batch Attribute Loaders when serialization starts
|
|
128
|
-
#
|
|
129
|
-
def mark_as_has_batch_points
|
|
130
|
-
return if has_batch_points
|
|
131
|
-
|
|
132
|
-
@has_batch_points = true
|
|
133
|
-
parent_plan_point.plan.mark_as_has_batch_points if parent_plan_point # rubocop:disable Style/SafeNavigation
|
|
134
|
-
end
|
|
135
|
-
|
|
136
129
|
private
|
|
137
130
|
|
|
138
131
|
def attributes_points(modifiers)
|
data/lib/serega/plan_point.rb
CHANGED
|
@@ -25,14 +25,6 @@ class Serega
|
|
|
25
25
|
# @return [Hash] Attributes to serialize
|
|
26
26
|
attr_reader :modifiers
|
|
27
27
|
|
|
28
|
-
# Shows preloads for nested attributes
|
|
29
|
-
# @return [Hash] preloads for nested attributes
|
|
30
|
-
attr_reader :preloads
|
|
31
|
-
|
|
32
|
-
# Shows preloads_path for current attribute
|
|
33
|
-
# @return [Array<Symbol>, nil] preloads path for current attribute
|
|
34
|
-
attr_reader :preloads_path
|
|
35
|
-
|
|
36
28
|
#
|
|
37
29
|
# Initializes plan point
|
|
38
30
|
#
|
|
@@ -97,9 +89,6 @@ class Serega
|
|
|
97
89
|
|
|
98
90
|
def set_normalized_vars
|
|
99
91
|
@child_plan = prepare_child_plan
|
|
100
|
-
@preloads = prepare_preloads
|
|
101
|
-
@preloads_path = prepare_preloads_path
|
|
102
|
-
plan.mark_as_has_batch_points if batch?
|
|
103
92
|
end
|
|
104
93
|
|
|
105
94
|
def prepare_child_plan
|
|
@@ -109,14 +98,6 @@ class Serega
|
|
|
109
98
|
|
|
110
99
|
serializer::SeregaPlan.new(self, fields)
|
|
111
100
|
end
|
|
112
|
-
|
|
113
|
-
def prepare_preloads
|
|
114
|
-
SeregaUtils::PreloadsConstructor.call(child_plan)
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def prepare_preloads_path
|
|
118
|
-
attribute.preloads_path
|
|
119
|
-
end
|
|
120
101
|
end
|
|
121
102
|
|
|
122
103
|
extend SeregaHelpers::SerializerClassHelper
|
|
@@ -7,9 +7,8 @@ class Serega
|
|
|
7
7
|
#
|
|
8
8
|
# Automatically preloads associations to serialized objects
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# to preload all associations.
|
|
10
|
+
# Every association declared with `:preload` is loaded once during serialization using
|
|
11
|
+
# ActiveRecord::Associations::Preloader, so there are no N+1 queries.
|
|
13
12
|
#
|
|
14
13
|
# @example
|
|
15
14
|
# class AppSerializer < Serega
|
|
@@ -39,7 +38,7 @@ class Serega
|
|
|
39
38
|
# attribute :downloads_count, preload: :downloads, value: proc { |album| album.downloads.count }
|
|
40
39
|
# end
|
|
41
40
|
#
|
|
42
|
-
# UserSerializer.to_h(user)
|
|
41
|
+
# UserSerializer.to_h(user)
|
|
43
42
|
#
|
|
44
43
|
module ActiverecordPreloads
|
|
45
44
|
#
|
|
@@ -72,62 +71,18 @@ class Serega
|
|
|
72
71
|
#
|
|
73
72
|
def self.load_plugin(serializer_class, **_opts)
|
|
74
73
|
require_relative "lib/preloader"
|
|
75
|
-
require_relative "lib/active_record_objects"
|
|
76
|
-
|
|
77
|
-
serializer_class.include(InstanceMethods)
|
|
78
|
-
serializer_class::SeregaBatchAttributeLoader.include(BatchAttributeLoaderInstanceMethods)
|
|
79
74
|
end
|
|
80
75
|
|
|
81
76
|
#
|
|
82
|
-
#
|
|
77
|
+
# Registers the ActiveRecord preload handler
|
|
83
78
|
#
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
# Preloads associations to batch-generated data
|
|
88
|
-
def load_one(serializer_class, batch_loader_name, context)
|
|
89
|
-
batch_loaded_data = super
|
|
90
|
-
|
|
91
|
-
preloads = point.preloads
|
|
92
|
-
return batch_loaded_data if preloads.empty?
|
|
93
|
-
|
|
94
|
-
ar_objects = ActiveRecordObjects.call(batch_loaded_data)
|
|
95
|
-
Preloader.preload(ar_objects, preloads)
|
|
96
|
-
|
|
97
|
-
batch_loaded_data
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
79
|
+
# @param serializer_class [Class<Serega>] Current serializer class
|
|
80
|
+
# @param _opts [Hash] Plugin options
|
|
101
81
|
#
|
|
102
|
-
#
|
|
82
|
+
# @return [void]
|
|
103
83
|
#
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
# Preloads associations to object
|
|
107
|
-
#
|
|
108
|
-
# @param object [Object] Any object
|
|
109
|
-
# @return provided object
|
|
110
|
-
#
|
|
111
|
-
def preload_associations_to(object)
|
|
112
|
-
return object if object.nil? || (object.is_a?(Array) && object.empty?)
|
|
113
|
-
|
|
114
|
-
preloads = preloads() # `preloads()` method comes from :preloads plugin
|
|
115
|
-
return object if preloads.empty?
|
|
116
|
-
|
|
117
|
-
Preloader.preload(object, preloads)
|
|
118
|
-
object
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
private
|
|
122
|
-
|
|
123
|
-
#
|
|
124
|
-
# Override original #serialize method
|
|
125
|
-
# Preloads associations to object before serialization
|
|
126
|
-
#
|
|
127
|
-
def serialize(object, _opts)
|
|
128
|
-
preload_associations_to(object)
|
|
129
|
-
super
|
|
130
|
-
end
|
|
84
|
+
def self.after_load_plugin(serializer_class, **_opts)
|
|
85
|
+
serializer_class.preload_with { |objects, preloads| Preloader.preload(objects, preloads) }
|
|
131
86
|
end
|
|
132
87
|
end
|
|
133
88
|
|
|
@@ -14,7 +14,7 @@ class Serega
|
|
|
14
14
|
# @return [Object] provided object with preloaded associations
|
|
15
15
|
#
|
|
16
16
|
def preload(object, preloads)
|
|
17
|
-
return object if object.nil? || (object.is_a?(Array) && object.empty?)
|
|
17
|
+
return object if object.nil? || (object.is_a?(Array) && object.empty?)
|
|
18
18
|
|
|
19
19
|
preload_handler = handlers.find { |handler| handler.fit?(object) }
|
|
20
20
|
raise SeregaError, "Can't preload #{preloads.inspect} to #{object.inspect}" unless preload_handler
|
|
@@ -19,6 +19,17 @@ class Serega
|
|
|
19
19
|
def call(opts)
|
|
20
20
|
return unless opts.key?(:preload)
|
|
21
21
|
|
|
22
|
+
check_opt_preload(opts)
|
|
23
|
+
check_usage_with_other_params(opts)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def check_opt_preload(opts)
|
|
29
|
+
raise SeregaError, "Option :preload value can not be `true`" if opts[:preload] == true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def check_usage_with_other_params(opts)
|
|
22
33
|
raise SeregaError, "Option :preload can not be used together with option :const" if opts.key?(:const)
|
|
23
34
|
end
|
|
24
35
|
end
|
|
@@ -65,7 +65,6 @@ class Serega
|
|
|
65
65
|
Attribute::CheckOptMethod.call(opts, block)
|
|
66
66
|
Attribute::CheckOptMany.call(opts)
|
|
67
67
|
Attribute::CheckOptPreload.call(opts)
|
|
68
|
-
Attribute::CheckOptPreloadPath.call(opts)
|
|
69
68
|
Attribute::CheckOptSerializer.call(opts)
|
|
70
69
|
Attribute::CheckOptValue.call(opts, block)
|
|
71
70
|
Attribute::CheckOptBatch.call(self.class.serializer_class, opts, block)
|
data/lib/serega.rb
CHANGED
|
@@ -21,10 +21,7 @@ require_relative "serega/errors"
|
|
|
21
21
|
require_relative "serega/helpers/serializer_class_helper"
|
|
22
22
|
require_relative "serega/utils/enum_deep_dup"
|
|
23
23
|
require_relative "serega/utils/enum_deep_freeze"
|
|
24
|
-
require_relative "serega/utils/format_user_preloads"
|
|
25
24
|
require_relative "serega/utils/method_signature"
|
|
26
|
-
require_relative "serega/utils/preload_paths"
|
|
27
|
-
require_relative "serega/utils/preloads_constructor"
|
|
28
25
|
require_relative "serega/utils/symbol_name"
|
|
29
26
|
require_relative "serega/utils/to_hash"
|
|
30
27
|
require_relative "serega/attribute_value_resolvers/batch"
|
|
@@ -35,6 +32,7 @@ require_relative "serega/attribute"
|
|
|
35
32
|
require_relative "serega/attribute_normalizer"
|
|
36
33
|
require_relative "serega/batch/attribute_loader"
|
|
37
34
|
require_relative "serega/batch/attribute_loaders"
|
|
35
|
+
require_relative "serega/batch/level"
|
|
38
36
|
require_relative "serega/batch/loader"
|
|
39
37
|
require_relative "serega/validations/utils/check_allowed_keys"
|
|
40
38
|
require_relative "serega/validations/utils/check_opt_is_bool"
|
|
@@ -49,7 +47,6 @@ require_relative "serega/validations/attribute/check_opt_batch"
|
|
|
49
47
|
require_relative "serega/validations/attribute/check_opt_many"
|
|
50
48
|
require_relative "serega/validations/attribute/check_opt_method"
|
|
51
49
|
require_relative "serega/validations/attribute/check_opt_preload"
|
|
52
|
-
require_relative "serega/validations/attribute/check_opt_preload_path"
|
|
53
50
|
require_relative "serega/validations/attribute/check_opt_serializer"
|
|
54
51
|
require_relative "serega/validations/attribute/check_opt_value"
|
|
55
52
|
require_relative "serega/validations/initiate/check_modifiers"
|
|
@@ -220,6 +217,38 @@ class Serega
|
|
|
220
217
|
batch_loaders[batch_loader.name] = batch_loader
|
|
221
218
|
end
|
|
222
219
|
|
|
220
|
+
#
|
|
221
|
+
# Registers (or returns) the handler used to preload an attribute's
|
|
222
|
+
# associations onto the records gathered during serialization.
|
|
223
|
+
#
|
|
224
|
+
# The handler is called once per preloaded attribute with the gathered
|
|
225
|
+
# objects and that attribute's preloads. ORM plugins register a handler
|
|
226
|
+
# that performs the actual eager loading.
|
|
227
|
+
#
|
|
228
|
+
# @example with a block
|
|
229
|
+
# preload_with { |objects, preloads| MyORM.eager_load(objects, preloads) }
|
|
230
|
+
#
|
|
231
|
+
# @example with a callable value
|
|
232
|
+
# preload_with MyPreloader
|
|
233
|
+
#
|
|
234
|
+
# @param value [#call, nil] Preload handler accepting two positional arguments
|
|
235
|
+
# @param block [Proc] Preload handler accepting two positional arguments
|
|
236
|
+
#
|
|
237
|
+
# @return [#call, nil] The registered preload handler
|
|
238
|
+
#
|
|
239
|
+
def preload_with(value = nil, &block)
|
|
240
|
+
return @preload_with if value.nil? && block.nil?
|
|
241
|
+
raise SeregaError, "preload_with accepts a single callable or a block, not both" if value && block
|
|
242
|
+
|
|
243
|
+
handler = value || block
|
|
244
|
+
raise SeregaError, "preload_with value must be a Proc or respond to #call" if !handler.is_a?(Proc) && !handler.respond_to?(:call)
|
|
245
|
+
|
|
246
|
+
signature = SeregaUtils::MethodSignature.call(handler, pos_limit: 2)
|
|
247
|
+
raise SeregaError, "preload_with handler must accept two positional arguments: (objects, preloads)" unless signature == "2"
|
|
248
|
+
|
|
249
|
+
@preload_with = handler
|
|
250
|
+
end
|
|
251
|
+
|
|
223
252
|
#
|
|
224
253
|
# Serializes provided object to Hash
|
|
225
254
|
#
|
|
@@ -342,6 +371,9 @@ class Serega
|
|
|
342
371
|
subclass.batch(loader.name, loader.block)
|
|
343
372
|
end
|
|
344
373
|
|
|
374
|
+
# Assign same preload handler
|
|
375
|
+
subclass.preload_with(preload_with) if preload_with
|
|
376
|
+
|
|
345
377
|
super
|
|
346
378
|
end
|
|
347
379
|
end
|
|
@@ -418,11 +450,6 @@ class Serega
|
|
|
418
450
|
self.class::SeregaDataBuilder.call(self, serialized_data)
|
|
419
451
|
end
|
|
420
452
|
|
|
421
|
-
# @return [Hash] merged preloads of all serialized attributes
|
|
422
|
-
def preloads
|
|
423
|
-
@preloads ||= SeregaUtils::PreloadsConstructor.call(plan)
|
|
424
|
-
end
|
|
425
|
-
|
|
426
453
|
private
|
|
427
454
|
|
|
428
455
|
attr_reader :opts
|
|
@@ -453,20 +480,19 @@ class Serega
|
|
|
453
480
|
self.class::CheckSerializeParams.new(opts).validate unless opts.empty?
|
|
454
481
|
|
|
455
482
|
opts[:context] ||= {}
|
|
456
|
-
opts[:batch_loaders] = SeregaBatch::AttributeLoaders.new
|
|
483
|
+
opts[:batch_loaders] = SeregaBatch::AttributeLoaders.new(opts[:context])
|
|
457
484
|
opts[:many] = object.is_a?(Enumerable) unless opts.key?(:many)
|
|
458
485
|
opts[:plan] = plan
|
|
459
486
|
opts
|
|
460
487
|
end
|
|
461
488
|
|
|
462
489
|
# Patched in:
|
|
463
|
-
# - plugin :activerecord_preloads (loads defined :preloads to object)
|
|
464
490
|
# - plugin :root (wraps result `{ root => result }`)
|
|
465
491
|
# - plugin :context_metadata (adds context metadata to final result)
|
|
466
492
|
# - plugin :metadata (adds metadata to final result)
|
|
467
493
|
def serialize(object, opts)
|
|
468
494
|
result = self.class::SeregaObjectSerializer.new(**opts).serialize(object)
|
|
469
|
-
opts[:batch_loaders]
|
|
495
|
+
opts[:batch_loaders].load_all
|
|
470
496
|
result
|
|
471
497
|
end
|
|
472
498
|
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.37.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrey Glushkov
|
|
@@ -35,6 +35,7 @@ files:
|
|
|
35
35
|
- lib/serega/attribute_value_resolvers/keyword.rb
|
|
36
36
|
- lib/serega/batch/attribute_loader.rb
|
|
37
37
|
- lib/serega/batch/attribute_loaders.rb
|
|
38
|
+
- lib/serega/batch/level.rb
|
|
38
39
|
- lib/serega/batch/loader.rb
|
|
39
40
|
- lib/serega/config.rb
|
|
40
41
|
- lib/serega/data_builder.rb
|
|
@@ -45,7 +46,6 @@ files:
|
|
|
45
46
|
- lib/serega/plan_point.rb
|
|
46
47
|
- lib/serega/plugins.rb
|
|
47
48
|
- lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb
|
|
48
|
-
- lib/serega/plugins/activerecord_preloads/lib/active_record_objects.rb
|
|
49
49
|
- lib/serega/plugins/activerecord_preloads/lib/preloader.rb
|
|
50
50
|
- lib/serega/plugins/camel_case/camel_case.rb
|
|
51
51
|
- lib/serega/plugins/context_metadata/context_metadata.rb
|
|
@@ -73,10 +73,7 @@ files:
|
|
|
73
73
|
- lib/serega/plugins/string_modifiers/string_modifiers.rb
|
|
74
74
|
- lib/serega/utils/enum_deep_dup.rb
|
|
75
75
|
- lib/serega/utils/enum_deep_freeze.rb
|
|
76
|
-
- lib/serega/utils/format_user_preloads.rb
|
|
77
76
|
- lib/serega/utils/method_signature.rb
|
|
78
|
-
- lib/serega/utils/preload_paths.rb
|
|
79
|
-
- lib/serega/utils/preloads_constructor.rb
|
|
80
77
|
- lib/serega/utils/symbol_name.rb
|
|
81
78
|
- lib/serega/utils/to_hash.rb
|
|
82
79
|
- lib/serega/validations/attribute/check_block.rb
|
|
@@ -88,7 +85,6 @@ files:
|
|
|
88
85
|
- lib/serega/validations/attribute/check_opt_many.rb
|
|
89
86
|
- lib/serega/validations/attribute/check_opt_method.rb
|
|
90
87
|
- lib/serega/validations/attribute/check_opt_preload.rb
|
|
91
|
-
- lib/serega/validations/attribute/check_opt_preload_path.rb
|
|
92
88
|
- lib/serega/validations/attribute/check_opt_serializer.rb
|
|
93
89
|
- lib/serega/validations/attribute/check_opt_value.rb
|
|
94
90
|
- lib/serega/validations/check_attribute_params.rb
|
|
@@ -122,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
122
118
|
- !ruby/object:Gem::Version
|
|
123
119
|
version: '0'
|
|
124
120
|
requirements: []
|
|
125
|
-
rubygems_version: 4.0.
|
|
121
|
+
rubygems_version: 4.0.13
|
|
126
122
|
specification_version: 4
|
|
127
123
|
summary: JSON Serializer
|
|
128
124
|
test_files: []
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class Serega
|
|
4
|
-
module SeregaPlugins
|
|
5
|
-
module ActiverecordPreloads
|
|
6
|
-
# Detects and returns found ActiveRecord objects
|
|
7
|
-
class ActiveRecordObjects
|
|
8
|
-
class << self
|
|
9
|
-
# Iterates over provided data and selects ActiveRecord objects
|
|
10
|
-
# @param data [Hash,Array,Object] any data
|
|
11
|
-
# @return [Array] Found ActiveRecord objects
|
|
12
|
-
def call(data)
|
|
13
|
-
res = []
|
|
14
|
-
extract(data, res)
|
|
15
|
-
res
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
private
|
|
19
|
-
|
|
20
|
-
def extract(data, res)
|
|
21
|
-
case data
|
|
22
|
-
when ActiveRecord::Base then res << data
|
|
23
|
-
when Array then data.each { |value| extract(value, res) }
|
|
24
|
-
when Hash then data.each_value { |value| extract(value, res) }
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class Serega
|
|
4
|
-
module SeregaUtils
|
|
5
|
-
#
|
|
6
|
-
# Utility that helps to transform user provided preloads to hash
|
|
7
|
-
#
|
|
8
|
-
class FormatUserPreloads
|
|
9
|
-
class << self
|
|
10
|
-
#
|
|
11
|
-
# Transforms user provided preloads to hash
|
|
12
|
-
#
|
|
13
|
-
# @param value [Array,Hash,String,Symbol,nil,false] preloads
|
|
14
|
-
#
|
|
15
|
-
# @return [Hash] preloads transformed to hash
|
|
16
|
-
#
|
|
17
|
-
def call(value)
|
|
18
|
-
case value
|
|
19
|
-
when Array then array_to_hash(value)
|
|
20
|
-
when FalseClass then nil_to_hash(value)
|
|
21
|
-
when Hash then hash_to_hash(value)
|
|
22
|
-
when NilClass then nil_to_hash(value)
|
|
23
|
-
when String then string_to_hash(value)
|
|
24
|
-
when Symbol then symbol_to_hash(value)
|
|
25
|
-
else raise Serega::SeregaError,
|
|
26
|
-
"Preload option value can consist from Symbols, Arrays, Hashes (#{value.class} #{value.inspect} was provided)"
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
def array_to_hash(values)
|
|
33
|
-
values.each_with_object({}) do |value, obj|
|
|
34
|
-
obj.merge!(call(value))
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def hash_to_hash(values)
|
|
39
|
-
values.each_with_object({}) do |(key, value), obj|
|
|
40
|
-
obj[key.to_sym] = call(value)
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def nil_to_hash(_value)
|
|
45
|
-
{}
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def string_to_hash(value)
|
|
49
|
-
{value.to_sym => {}}
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def symbol_to_hash(value)
|
|
53
|
-
{value => {}}
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class Serega
|
|
4
|
-
module SeregaUtils
|
|
5
|
-
#
|
|
6
|
-
# Utility that helps to transform preloads to array of paths
|
|
7
|
-
# It is used to validate manually set `:preload_path` attribute option has one of allowed values.
|
|
8
|
-
# `:preload_path` option can be used to specify where nested preloads must be attached.
|
|
9
|
-
#
|
|
10
|
-
# Example:
|
|
11
|
-
#
|
|
12
|
-
# call({ a: { b: { c: {}, d: {} } }, e: {} })
|
|
13
|
-
#
|
|
14
|
-
# => [
|
|
15
|
-
# [:a],
|
|
16
|
-
# [:a, :b],
|
|
17
|
-
# [:a, :b, :c],
|
|
18
|
-
# [:a, :b, :d],
|
|
19
|
-
# [:e]
|
|
20
|
-
# ]
|
|
21
|
-
class PreloadPaths
|
|
22
|
-
class << self
|
|
23
|
-
#
|
|
24
|
-
# Transforms user provided preloads to array of paths
|
|
25
|
-
#
|
|
26
|
-
# @param preloads [Array,Hash,String,Symbol,nil,false] association(s) to preload
|
|
27
|
-
#
|
|
28
|
-
# @return [Array] transformed preloads
|
|
29
|
-
#
|
|
30
|
-
def call(preloads)
|
|
31
|
-
formatted_preloads = FormatUserPreloads.call(preloads)
|
|
32
|
-
return FROZEN_EMPTY_ARRAY if formatted_preloads.empty?
|
|
33
|
-
|
|
34
|
-
paths(formatted_preloads, [], [])
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
private
|
|
38
|
-
|
|
39
|
-
def paths(formatted_preloads, path, result)
|
|
40
|
-
formatted_preloads.each do |key, nested_preloads|
|
|
41
|
-
path << key
|
|
42
|
-
result << path.dup
|
|
43
|
-
|
|
44
|
-
paths(nested_preloads, path, result)
|
|
45
|
-
path.pop
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
result
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class Serega
|
|
4
|
-
module SeregaUtils
|
|
5
|
-
#
|
|
6
|
-
# Finds preloads for provided attributes plan
|
|
7
|
-
#
|
|
8
|
-
class PreloadsConstructor
|
|
9
|
-
class << self
|
|
10
|
-
#
|
|
11
|
-
# Constructs preloads hash for given attributes plan
|
|
12
|
-
#
|
|
13
|
-
# @param plan [Array<SeregaPlanPoint>] Serialization plan
|
|
14
|
-
#
|
|
15
|
-
# @return [Hash]
|
|
16
|
-
#
|
|
17
|
-
def call(plan)
|
|
18
|
-
return FROZEN_EMPTY_HASH unless plan
|
|
19
|
-
|
|
20
|
-
preloads = {}
|
|
21
|
-
append_many(preloads, plan)
|
|
22
|
-
preloads
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
private
|
|
26
|
-
|
|
27
|
-
def append_many(preloads, plan)
|
|
28
|
-
plan.points.each do |point|
|
|
29
|
-
current_preloads = point.attribute.preloads
|
|
30
|
-
next unless current_preloads
|
|
31
|
-
|
|
32
|
-
child_plan = point.child_plan
|
|
33
|
-
current_preloads = SeregaUtils::EnumDeepDup.call(current_preloads) if child_plan
|
|
34
|
-
append_current(preloads, current_preloads)
|
|
35
|
-
next unless child_plan
|
|
36
|
-
|
|
37
|
-
each_child_preloads(preloads, point.preloads_path) do |child_preloads|
|
|
38
|
-
append_many(child_preloads, child_plan)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def append_current(preloads, current_preloads)
|
|
44
|
-
merge(preloads, current_preloads) unless current_preloads.empty?
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def merge(preloads, current_preloads)
|
|
48
|
-
preloads.merge!(current_preloads) do |_key, value_one, value_two|
|
|
49
|
-
merge(value_one, value_two)
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def each_child_preloads(preloads, preloads_path)
|
|
54
|
-
return yield(preloads) if preloads_path.nil?
|
|
55
|
-
|
|
56
|
-
if preloads_path[0].is_a?(Array)
|
|
57
|
-
preloads_path.each do |path|
|
|
58
|
-
yield dig_fetch(preloads, path)
|
|
59
|
-
end
|
|
60
|
-
else
|
|
61
|
-
yield dig_fetch(preloads, preloads_path)
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def dig_fetch(preloads, preloads_path)
|
|
66
|
-
return preloads if !preloads_path || preloads_path.empty?
|
|
67
|
-
|
|
68
|
-
preloads_path.each do |path|
|
|
69
|
-
preloads = preloads.fetch(path)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
preloads
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|