serega 0.36.0 → 0.37.1

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.
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, Array<Symbol>] Current :hide_by_default config option
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, Array<Symbol>] false, true, or array of :preload/:batch symbols
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, Array<Symbol>] New :hide_by_default config option
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 an Array of [:preload, :batch], #{value.inspect} provided"
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) ? serialize_array(object) : serialize_object(object)
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)
@@ -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
- # It takes all defined preloads from serialized attributes (including attributes from serialized relations),
11
- # merges them into single associations hash and then uses ActiveRecord::Associations::Preloader
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) # => preloads {users_stats: {}, albums: { downloads: {} }}
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
- # Overrides SeregaBatch::AttributeLoader class instance methods
77
+ # Registers the ActiveRecord preload handler
83
78
  #
84
- module BatchAttributeLoaderInstanceMethods
85
- private
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
- # Overrides Serega class instance methods
82
+ # @return [void]
103
83
  #
104
- module InstanceMethods
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?) || preloads.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
@@ -275,8 +275,11 @@ class Serega
275
275
  super
276
276
  end
277
277
 
278
- def attach_final_value(value, point, _container)
279
- return KEY_SKIPPED unless point.satisfy_if_value_conditions?(value, context)
278
+ def attach_final_value(value, point, container)
279
+ unless point.satisfy_if_value_conditions?(value, context)
280
+ container.delete(point.name) # remove reserved placeholder
281
+ return KEY_SKIPPED
282
+ end
280
283
 
281
284
  super
282
285
  end
@@ -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 if plan.has_batch_points
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]&.load_all(opts[:context])
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.36.0
4
+ version: 0.37.1
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.11
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