serega 0.21.0 → 0.31.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 +113 -229
- data/VERSION +1 -1
- data/lib/serega/attribute.rb +31 -3
- data/lib/serega/attribute_normalizer.rb +143 -42
- data/lib/serega/batch/attribute_loader.rb +70 -0
- data/lib/serega/batch/attribute_loaders.rb +59 -0
- data/lib/serega/batch/auto_resolver.rb +24 -0
- data/lib/serega/batch/auto_resolver_factory.rb +48 -0
- data/lib/serega/batch/loader.rb +67 -0
- data/lib/serega/config.rb +59 -1
- data/lib/serega/object_serializer.rb +11 -9
- data/lib/serega/plan.rb +16 -0
- data/lib/serega/plan_point.rb +31 -5
- data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +25 -13
- data/lib/serega/plugins/activerecord_preloads/lib/active_record_objects.rb +31 -0
- data/lib/serega/plugins/camel_case/camel_case.rb +1 -1
- data/lib/serega/plugins/formatters/formatters.rb +79 -22
- data/lib/serega/plugins/if/if.rb +41 -22
- data/lib/serega/plugins/if/validations/check_opt_if.rb +27 -6
- data/lib/serega/plugins/if/validations/check_opt_if_value.rb +27 -6
- data/lib/serega/plugins/if/validations/check_opt_unless.rb +27 -6
- data/lib/serega/plugins/if/validations/check_opt_unless_value.rb +27 -6
- data/lib/serega/plugins/metadata/meta_attribute.rb +9 -10
- data/lib/serega/plugins/metadata/validations/check_block.rb +2 -4
- data/lib/serega/plugins/metadata/validations/check_opt_value.rb +2 -3
- data/lib/serega/plugins/presenter/presenter.rb +1 -1
- data/lib/serega/utils/format_user_preloads.rb +58 -0
- data/lib/serega/utils/method_signature.rb +89 -0
- data/lib/serega/utils/preload_paths.rb +53 -0
- data/lib/serega/utils/preloads_constructor.rb +77 -0
- data/lib/serega/validations/attribute/check_block.rb +38 -6
- data/lib/serega/validations/attribute/check_opt_batch.rb +101 -0
- data/lib/serega/validations/attribute/check_opt_const.rb +1 -0
- data/lib/serega/validations/attribute/check_opt_delegate.rb +1 -0
- data/lib/serega/validations/attribute/check_opt_method.rb +1 -0
- data/lib/serega/{plugins/preloads/validations → validations/attribute}/check_opt_preload.rb +2 -2
- data/lib/serega/{plugins/preloads/validations → validations/attribute}/check_opt_preload_path.rb +3 -3
- data/lib/serega/validations/attribute/check_opt_value.rb +35 -9
- data/lib/serega/validations/check_attribute_params.rb +3 -2
- data/lib/serega/validations/check_batch_loader_params.rb +80 -0
- data/lib/serega/validations/initiate/check_modifiers.rb +1 -1
- data/lib/serega.rb +98 -7
- metadata +17 -37
- data/lib/serega/plugins/batch/batch.rb +0 -168
- data/lib/serega/plugins/batch/lib/batch_config.rb +0 -77
- data/lib/serega/plugins/batch/lib/loader.rb +0 -113
- data/lib/serega/plugins/batch/lib/loaders.rb +0 -45
- data/lib/serega/plugins/batch/lib/modules/attribute.rb +0 -26
- data/lib/serega/plugins/batch/lib/modules/attribute_normalizer.rb +0 -78
- data/lib/serega/plugins/batch/lib/modules/check_attribute_params.rb +0 -22
- data/lib/serega/plugins/batch/lib/modules/config.rb +0 -23
- data/lib/serega/plugins/batch/lib/modules/object_serializer.rb +0 -46
- data/lib/serega/plugins/batch/lib/modules/plan_point.rb +0 -22
- data/lib/serega/plugins/batch/lib/plugins_extensions/activerecord_preloads.rb +0 -43
- data/lib/serega/plugins/batch/lib/plugins_extensions/formatters.rb +0 -54
- data/lib/serega/plugins/batch/lib/plugins_extensions/if.rb +0 -31
- data/lib/serega/plugins/batch/lib/plugins_extensions/preloads.rb +0 -34
- data/lib/serega/plugins/batch/lib/validations/check_batch_opt_id_method.rb +0 -43
- data/lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb +0 -59
- data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +0 -62
- data/lib/serega/plugins/preloads/lib/format_user_preloads.rb +0 -60
- data/lib/serega/plugins/preloads/lib/modules/attribute.rb +0 -28
- data/lib/serega/plugins/preloads/lib/modules/attribute_normalizer.rb +0 -99
- data/lib/serega/plugins/preloads/lib/modules/check_attribute_params.rb +0 -22
- data/lib/serega/plugins/preloads/lib/modules/config.rb +0 -19
- data/lib/serega/plugins/preloads/lib/modules/plan_point.rb +0 -41
- data/lib/serega/plugins/preloads/lib/preload_paths.rb +0 -53
- data/lib/serega/plugins/preloads/lib/preloads_config.rb +0 -62
- data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +0 -79
- data/lib/serega/plugins/preloads/preloads.rb +0 -162
- data/lib/serega/utils/params_count.rb +0 -50
- data/lib/serega/validations/utils/check_extra_keyword_arg.rb +0 -33
@@ -1,99 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Serega
|
4
|
-
module SeregaPlugins
|
5
|
-
module Preloads
|
6
|
-
#
|
7
|
-
# Serega::SeregaAttributeNormalizer additional/patched instance methods
|
8
|
-
#
|
9
|
-
# @see SeregaAttributeNormalizer::AttributeNormalizerInstanceMethods
|
10
|
-
#
|
11
|
-
module AttributeNormalizerInstanceMethods
|
12
|
-
# @return [Hash,nil] normalized attribute preloads
|
13
|
-
def preloads
|
14
|
-
return @preloads if instance_variable_defined?(:@preloads)
|
15
|
-
|
16
|
-
@preloads = prepare_preloads
|
17
|
-
end
|
18
|
-
|
19
|
-
# @return [Array, nil] normalized attribute preloads path
|
20
|
-
def preloads_path
|
21
|
-
return @preloads_path if instance_variable_defined?(:@preloads_path)
|
22
|
-
|
23
|
-
@preloads_path = prepare_preloads_path
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
#
|
29
|
-
# Patched in:
|
30
|
-
# - plugin :batch (extension :preloads - skips auto preloads when batch option provided)
|
31
|
-
#
|
32
|
-
def prepare_preloads
|
33
|
-
opts = init_opts
|
34
|
-
preloads_provided = opts.key?(:preload)
|
35
|
-
preloads =
|
36
|
-
if preloads_provided
|
37
|
-
opts[:preload]
|
38
|
-
elsif opts.key?(:serializer) && self.class.serializer_class.config.preloads.auto_preload_attributes_with_serializer
|
39
|
-
method_name
|
40
|
-
elsif opts.key?(:delegate) && self.class.serializer_class.config.preloads.auto_preload_attributes_with_delegate
|
41
|
-
opts[:delegate].fetch(:to)
|
42
|
-
end
|
43
|
-
|
44
|
-
# Nil and empty hash differs as we can preload nested results to
|
45
|
-
# empty hash, but we will skip nested preloading if nil or false provided
|
46
|
-
return if preloads_provided && !preloads
|
47
|
-
|
48
|
-
FormatUserPreloads.call(preloads)
|
49
|
-
end
|
50
|
-
|
51
|
-
def prepare_preloads_path
|
52
|
-
path = init_opts.fetch(:preload_path) { default_preload_path(preloads) }
|
53
|
-
|
54
|
-
if path && path[0].is_a?(Array)
|
55
|
-
prepare_many_preload_paths(path)
|
56
|
-
else
|
57
|
-
prepare_one_preload_path(path)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def prepare_one_preload_path(path)
|
62
|
-
return unless path
|
63
|
-
|
64
|
-
case path
|
65
|
-
when Array
|
66
|
-
path.map(&:to_sym).freeze
|
67
|
-
else
|
68
|
-
[path.to_sym].freeze
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def prepare_many_preload_paths(paths)
|
73
|
-
paths.map { |path| prepare_one_preload_path(path) }.freeze
|
74
|
-
end
|
75
|
-
|
76
|
-
def default_preload_path(preloads)
|
77
|
-
return FROZEN_EMPTY_ARRAY if !preloads || preloads.empty?
|
78
|
-
|
79
|
-
[preloads.keys.first]
|
80
|
-
end
|
81
|
-
|
82
|
-
#
|
83
|
-
# Patch for original `prepare_hide` method
|
84
|
-
# @see
|
85
|
-
#
|
86
|
-
# Marks attribute hidden if auto_hide_attribute_with_preloads option was set and attribute has preloads
|
87
|
-
#
|
88
|
-
def prepare_hide
|
89
|
-
res = super
|
90
|
-
return res unless res.nil?
|
91
|
-
|
92
|
-
if preloads && !preloads.empty?
|
93
|
-
self.class.serializer_class.config.preloads.auto_hide_attributes_with_preload || nil
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Serega
|
4
|
-
module SeregaPlugins
|
5
|
-
module Preloads
|
6
|
-
#
|
7
|
-
# Serega::SeregaValidations::CheckAttributeParams additional/patched class methods
|
8
|
-
#
|
9
|
-
# @see Serega::SeregaValidations::CheckAttributeParams
|
10
|
-
#
|
11
|
-
module CheckAttributeParamsInstanceMethods
|
12
|
-
private
|
13
|
-
|
14
|
-
def check_opts
|
15
|
-
super
|
16
|
-
CheckOptPreload.call(opts)
|
17
|
-
CheckOptPreloadPath.call(opts)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Serega
|
4
|
-
module SeregaPlugins
|
5
|
-
module Preloads
|
6
|
-
#
|
7
|
-
# Config class additional/patched instance methods
|
8
|
-
#
|
9
|
-
# @see Serega::SeregaConfig
|
10
|
-
#
|
11
|
-
module ConfigInstanceMethods
|
12
|
-
# @return [Serega::SeregaPlugins::Preloads::PreloadsConfig] `preloads` plugin config
|
13
|
-
def preloads
|
14
|
-
@preloads ||= PreloadsConfig.new(opts.fetch(:preloads))
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Serega
|
4
|
-
module SeregaPlugins
|
5
|
-
module Preloads
|
6
|
-
#
|
7
|
-
# Serega::SeregaPlanPoint additional/patched instance methods
|
8
|
-
#
|
9
|
-
# @see Serega::SeregaPlanPoint::InstanceMethods
|
10
|
-
#
|
11
|
-
module PlanPointInstanceMethods
|
12
|
-
#
|
13
|
-
# @return [Hash] preloads for nested attributes
|
14
|
-
#
|
15
|
-
attr_reader :preloads
|
16
|
-
|
17
|
-
#
|
18
|
-
# @return [Array<Symbol>] preloads path for current attribute
|
19
|
-
#
|
20
|
-
attr_reader :preloads_path
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def set_normalized_vars
|
25
|
-
super
|
26
|
-
|
27
|
-
@preloads = prepare_preloads
|
28
|
-
@preloads_path = prepare_preloads_path
|
29
|
-
end
|
30
|
-
|
31
|
-
def prepare_preloads
|
32
|
-
PreloadsConstructor.call(child_plan)
|
33
|
-
end
|
34
|
-
|
35
|
-
def prepare_preloads_path
|
36
|
-
attribute.preloads_path
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Serega
|
4
|
-
module SeregaPlugins
|
5
|
-
module Preloads
|
6
|
-
#
|
7
|
-
# Utility that helps to transform preloads to array of paths
|
8
|
-
#
|
9
|
-
# Example:
|
10
|
-
#
|
11
|
-
# call({ a: { b: { c: {}, d: {} } }, e: {} })
|
12
|
-
#
|
13
|
-
# => [
|
14
|
-
# [:a],
|
15
|
-
# [:a, :b],
|
16
|
-
# [:a, :b, :c],
|
17
|
-
# [:a, :b, :d],
|
18
|
-
# [:e]
|
19
|
-
# ]
|
20
|
-
class PreloadPaths
|
21
|
-
class << self
|
22
|
-
#
|
23
|
-
# Transforms user provided preloads to array of paths
|
24
|
-
#
|
25
|
-
# @param preloads [Array,Hash,String,Symbol,nil,false] association(s) to preload
|
26
|
-
#
|
27
|
-
# @return [Hash] preloads transformed to hash
|
28
|
-
#
|
29
|
-
def call(preloads)
|
30
|
-
formatted_preloads = FormatUserPreloads.call(preloads)
|
31
|
-
return FROZEN_EMPTY_ARRAY if formatted_preloads.empty?
|
32
|
-
|
33
|
-
paths(formatted_preloads, [], [])
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def paths(formatted_preloads, path, result)
|
39
|
-
formatted_preloads.each do |key, nested_preloads|
|
40
|
-
path << key
|
41
|
-
result << path.dup
|
42
|
-
|
43
|
-
paths(nested_preloads, path, result)
|
44
|
-
path.pop
|
45
|
-
end
|
46
|
-
|
47
|
-
result
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
@@ -1,62 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Serega
|
4
|
-
module SeregaPlugins
|
5
|
-
module Preloads
|
6
|
-
#
|
7
|
-
# Config for `preloads` plugin
|
8
|
-
#
|
9
|
-
class PreloadsConfig
|
10
|
-
# @return [Hash] preloads plugin options
|
11
|
-
attr_reader :opts
|
12
|
-
|
13
|
-
#
|
14
|
-
# Initializes PreloadsConfig object
|
15
|
-
#
|
16
|
-
# @param opts [Hash] options
|
17
|
-
#
|
18
|
-
# @return [Serega::SeregaPlugins::Preloads::PreloadsConfig]
|
19
|
-
#
|
20
|
-
def initialize(opts)
|
21
|
-
@opts = opts
|
22
|
-
end
|
23
|
-
|
24
|
-
# @!method auto_preload_attributes_with_delegate
|
25
|
-
# @return [Boolean, nil] option value
|
26
|
-
#
|
27
|
-
# @!method auto_preload_attributes_with_delegate=(value)
|
28
|
-
# @param value [Boolean] New option value
|
29
|
-
# @return [Boolean] New option value
|
30
|
-
#
|
31
|
-
# @!method auto_preload_attributes_with_serializer
|
32
|
-
# @return [Boolean, nil] option value
|
33
|
-
#
|
34
|
-
# @!method auto_preload_attributes_with_serializer=(value)
|
35
|
-
# @param value [Boolean] New option value
|
36
|
-
# @return [Boolean] New option value
|
37
|
-
#
|
38
|
-
# @!method auto_hide_attributes_with_preload
|
39
|
-
# @return [Boolean, nil] option value
|
40
|
-
#
|
41
|
-
# @!method auto_hide_attributes_with_preload=(value)
|
42
|
-
# @param value [Boolean] New option value
|
43
|
-
# @return [Boolean] New option value
|
44
|
-
#
|
45
|
-
%i[
|
46
|
-
auto_preload_attributes_with_delegate
|
47
|
-
auto_preload_attributes_with_serializer
|
48
|
-
auto_hide_attributes_with_preload
|
49
|
-
].each do |method_name|
|
50
|
-
define_method(method_name) do
|
51
|
-
opts.fetch(method_name)
|
52
|
-
end
|
53
|
-
|
54
|
-
define_method(:"#{method_name}=") do |value|
|
55
|
-
raise SeregaError, "Must have boolean value, #{value.inspect} provided" if (value != true) && (value != false)
|
56
|
-
opts[method_name] = value
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
@@ -1,79 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Serega
|
4
|
-
module SeregaPlugins
|
5
|
-
module Preloads
|
6
|
-
#
|
7
|
-
# Finds preloads for provided attributes plan
|
8
|
-
#
|
9
|
-
class PreloadsConstructor
|
10
|
-
class << self
|
11
|
-
#
|
12
|
-
# Constructs preloads hash for given attributes plan
|
13
|
-
#
|
14
|
-
# @param plan [Array<SeregaPlanPoint>] Serialization plan
|
15
|
-
#
|
16
|
-
# @return [Hash]
|
17
|
-
#
|
18
|
-
def call(plan)
|
19
|
-
return FROZEN_EMPTY_HASH unless plan
|
20
|
-
|
21
|
-
preloads = {}
|
22
|
-
append_many(preloads, plan)
|
23
|
-
preloads
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def append_many(preloads, plan)
|
29
|
-
plan.points.each do |point|
|
30
|
-
current_preloads = point.attribute.preloads
|
31
|
-
next unless current_preloads
|
32
|
-
|
33
|
-
child_plan = point.child_plan
|
34
|
-
current_preloads = SeregaUtils::EnumDeepDup.call(current_preloads) if child_plan
|
35
|
-
append_current(preloads, current_preloads)
|
36
|
-
next unless child_plan
|
37
|
-
|
38
|
-
each_child_preloads(preloads, point.preloads_path) do |child_preloads|
|
39
|
-
append_many(child_preloads, child_plan)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def append_current(preloads, current_preloads)
|
45
|
-
merge(preloads, current_preloads) unless current_preloads.empty?
|
46
|
-
end
|
47
|
-
|
48
|
-
def merge(preloads, current_preloads)
|
49
|
-
preloads.merge!(current_preloads) do |_key, value_one, value_two|
|
50
|
-
merge(value_one, value_two)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def each_child_preloads(preloads, preloads_path)
|
55
|
-
return yield(preloads) if preloads_path.nil?
|
56
|
-
|
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)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def dig_fetch(preloads, preloads_path)
|
67
|
-
return preloads if !preloads_path || preloads_path.empty?
|
68
|
-
|
69
|
-
preloads_path.each do |path|
|
70
|
-
preloads = preloads.fetch(path)
|
71
|
-
end
|
72
|
-
|
73
|
-
preloads
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
@@ -1,162 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Serega
|
4
|
-
module SeregaPlugins
|
5
|
-
#
|
6
|
-
# Plugin `:preloads`
|
7
|
-
#
|
8
|
-
# Allows to define `:preloads` to attributes and then allows to merge preloads
|
9
|
-
# from serialized attributes and return single associations hash.
|
10
|
-
#
|
11
|
-
# Plugin accepts options:
|
12
|
-
# - `auto_preload_attributes_with_delegate` - default false
|
13
|
-
# - `auto_preload_attributes_with_serializer` - default false
|
14
|
-
# - `auto_hide_attributes_with_preload` - default false
|
15
|
-
#
|
16
|
-
# This options are very handy if you want to forget about finding preloads manually.
|
17
|
-
#
|
18
|
-
# Preloads can be disabled with `preload: false` attribute option.
|
19
|
-
# Also automatically added preloads can be overwritten with manually specified `preload: :another_value`.
|
20
|
-
#
|
21
|
-
# Some examples, **please read comments in the code below**
|
22
|
-
#
|
23
|
-
# @example
|
24
|
-
# class AppSerializer < Serega
|
25
|
-
# plugin :preloads,
|
26
|
-
# auto_preload_attributes_with_delegate: true,
|
27
|
-
# auto_preload_attributes_with_serializer: true,
|
28
|
-
# auto_hide_attributes_with_preload: true
|
29
|
-
# end
|
30
|
-
#
|
31
|
-
# class UserSerializer < AppSerializer
|
32
|
-
# # No preloads
|
33
|
-
# attribute :username
|
34
|
-
#
|
35
|
-
# # Specify `preload: :user_stats` manually
|
36
|
-
# attribute :followers_count, preload: :user_stats, value: proc { |user| user.user_stats.followers_count }
|
37
|
-
#
|
38
|
-
# # Automatically `preloads: :user_stats` as `auto_preload_attributes_with_delegate` option is true
|
39
|
-
# attribute :comments_count, delegate: { to: :user_stats }
|
40
|
-
#
|
41
|
-
# # Automatically `preloads: :albums` as `auto_preload_attributes_with_serializer` option is true
|
42
|
-
# attribute :albums, serializer: 'AlbumSerializer'
|
43
|
-
# end
|
44
|
-
#
|
45
|
-
# class AlbumSerializer < AppSerializer
|
46
|
-
# attribute :images_count, delegate: { to: :album_stats }
|
47
|
-
# end
|
48
|
-
#
|
49
|
-
# # By default preloads are empty, as we specify `auto_hide_attributes_with_preload = true`,
|
50
|
-
# # and attributes with preloads will be not serialized
|
51
|
-
# UserSerializer.new.preloads # => {}
|
52
|
-
# UserSerializer.new.to_h(OpenStruct.new(username: 'foo')) # => {:username=>"foo"}
|
53
|
-
#
|
54
|
-
# UserSerializer.new(with: :followers_count).preloads # => {:user_stats=>{}}
|
55
|
-
# UserSerializer.new(with: %i[followers_count comments_count]).preloads # => {:user_stats=>{}}
|
56
|
-
# UserSerializer.new(with: [:followers_count, :comments_count, { albums: :images_count }]).preloads # => {:user_stats=>{}, :albums=>{:album_stats=>{}}}
|
57
|
-
#
|
58
|
-
|
59
|
-
module Preloads
|
60
|
-
DEFAULT_CONFIG = {
|
61
|
-
auto_preload_attributes_with_delegate: false,
|
62
|
-
auto_preload_attributes_with_serializer: false,
|
63
|
-
auto_hide_attributes_with_preload: false
|
64
|
-
}.freeze
|
65
|
-
|
66
|
-
private_constant :DEFAULT_CONFIG
|
67
|
-
|
68
|
-
# @return [Symbol] Plugin name
|
69
|
-
def self.plugin_name
|
70
|
-
:preloads
|
71
|
-
end
|
72
|
-
|
73
|
-
# Checks requirements to load plugin
|
74
|
-
#
|
75
|
-
# @param serializer_class [Class<Serega>] Current serializer class
|
76
|
-
# @param opts [Hash] plugin options
|
77
|
-
#
|
78
|
-
# @return [void]
|
79
|
-
#
|
80
|
-
def self.before_load_plugin(serializer_class, **opts)
|
81
|
-
allowed_keys = DEFAULT_CONFIG.keys
|
82
|
-
opts.each_key do |key|
|
83
|
-
next if allowed_keys.include?(key)
|
84
|
-
|
85
|
-
raise SeregaError,
|
86
|
-
"Plugin #{plugin_name.inspect} does not accept the #{key.inspect} option. Allowed options:\n" \
|
87
|
-
" - :auto_preload_attributes_with_delegate [Boolean] - Automatically adds `preload: <delegate_to>` option to attributes with :delegate option specified\n" \
|
88
|
-
" - :auto_preload_attributes_with_serializer [Boolean] - Automatically adds `preload: <attribute_name>` option to attributes with :serializer option specified\n" \
|
89
|
-
" - :auto_hide_attributes_with_preload [Boolean] - Automatically adds `hide: true` option to attributes with :preload option (specified manually or added automatically)"
|
90
|
-
end
|
91
|
-
|
92
|
-
if serializer_class.plugin_used?(:batch)
|
93
|
-
raise SeregaError, "Plugin #{plugin_name.inspect} must be loaded before the :batch plugin"
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
#
|
98
|
-
# Applies plugin code to specific serializer
|
99
|
-
#
|
100
|
-
# @param serializer_class [Class<Serega>] Current serializer class
|
101
|
-
# @param _opts [Hash] Plugin options
|
102
|
-
#
|
103
|
-
# @return [void]
|
104
|
-
#
|
105
|
-
def self.load_plugin(serializer_class, **_opts)
|
106
|
-
require_relative "lib/format_user_preloads"
|
107
|
-
require_relative "lib/modules/attribute"
|
108
|
-
require_relative "lib/modules/attribute_normalizer"
|
109
|
-
require_relative "lib/modules/check_attribute_params"
|
110
|
-
require_relative "lib/modules/config"
|
111
|
-
require_relative "lib/modules/plan_point"
|
112
|
-
require_relative "lib/preload_paths"
|
113
|
-
require_relative "lib/preloads_config"
|
114
|
-
require_relative "lib/preloads_constructor"
|
115
|
-
require_relative "validations/check_opt_preload"
|
116
|
-
require_relative "validations/check_opt_preload_path"
|
117
|
-
|
118
|
-
serializer_class.include(InstanceMethods)
|
119
|
-
serializer_class::SeregaAttribute.include(AttributeInstanceMethods)
|
120
|
-
serializer_class::SeregaAttributeNormalizer.include(AttributeNormalizerInstanceMethods)
|
121
|
-
serializer_class::SeregaConfig.include(ConfigInstanceMethods)
|
122
|
-
serializer_class::SeregaPlanPoint.include(PlanPointInstanceMethods)
|
123
|
-
|
124
|
-
serializer_class::CheckAttributeParams.include(CheckAttributeParamsInstanceMethods)
|
125
|
-
end
|
126
|
-
|
127
|
-
#
|
128
|
-
# Adds config options and runs other callbacks after plugin was loaded
|
129
|
-
#
|
130
|
-
# @param serializer_class [Class<Serega>] Current serializer class
|
131
|
-
# @param opts [Hash] Plugin options
|
132
|
-
#
|
133
|
-
# @return [void]
|
134
|
-
#
|
135
|
-
def self.after_load_plugin(serializer_class, **opts)
|
136
|
-
config = serializer_class.config
|
137
|
-
config.attribute_keys << :preload << :preload_path
|
138
|
-
|
139
|
-
preloads_opts = DEFAULT_CONFIG.merge(opts.slice(*DEFAULT_CONFIG.keys))
|
140
|
-
config.opts[:preloads] = {}
|
141
|
-
preloads_config = config.preloads
|
142
|
-
preloads_config.auto_preload_attributes_with_delegate = preloads_opts[:auto_preload_attributes_with_delegate]
|
143
|
-
preloads_config.auto_preload_attributes_with_serializer = preloads_opts[:auto_preload_attributes_with_serializer]
|
144
|
-
preloads_config.auto_hide_attributes_with_preload = preloads_opts[:auto_hide_attributes_with_preload]
|
145
|
-
end
|
146
|
-
|
147
|
-
#
|
148
|
-
# Serega additional/patched instance methods
|
149
|
-
#
|
150
|
-
# @see Serega
|
151
|
-
#
|
152
|
-
module InstanceMethods
|
153
|
-
# @return [Hash] merged preloads of all serialized attributes
|
154
|
-
def preloads
|
155
|
-
@preloads ||= PreloadsConstructor.call(plan)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
register_plugin(Preloads.plugin_name, Preloads)
|
161
|
-
end
|
162
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Serega
|
4
|
-
#
|
5
|
-
# Utilities
|
6
|
-
#
|
7
|
-
module SeregaUtils
|
8
|
-
#
|
9
|
-
# Utility to count regular parameters of callable object
|
10
|
-
#
|
11
|
-
class ParamsCount
|
12
|
-
NO_NAMED_REST_PARAM = [:rest].freeze
|
13
|
-
private_constant :NO_NAMED_REST_PARAM
|
14
|
-
|
15
|
-
class << self
|
16
|
-
#
|
17
|
-
# Count parameters for callable object
|
18
|
-
#
|
19
|
-
# @param object [#call] callable object
|
20
|
-
#
|
21
|
-
# @return [Integer] count of regular parameters
|
22
|
-
#
|
23
|
-
def call(object, max_count:)
|
24
|
-
# Procs (but not lambdas) can accept all provided parameters
|
25
|
-
parameters = object.is_a?(Proc) ? object.parameters : object.method(:call).parameters
|
26
|
-
return 1 if parameters[0] == NO_NAMED_REST_PARAM
|
27
|
-
return max_count if object.is_a?(Proc) && !object.lambda?
|
28
|
-
|
29
|
-
count = 0
|
30
|
-
|
31
|
-
# If all we have is no-name *rest parameters, then we assume we need to provide
|
32
|
-
# 1 argument. It is now always correct, but in serialization context it's most common that
|
33
|
-
# only one argument is needed.
|
34
|
-
parameters.each do |parameter|
|
35
|
-
next if parameter == NO_NAMED_REST_PARAM # Workaround for procs like :odd?.to_proc
|
36
|
-
param_type = parameter[0]
|
37
|
-
|
38
|
-
case param_type
|
39
|
-
when :req then count += 1
|
40
|
-
when :opt then count += 1 if count < max_count
|
41
|
-
when :rest then count += max_count - count if max_count > count
|
42
|
-
end # else :opt, :keyreq, :key, :keyrest, :block - do nothing
|
43
|
-
end
|
44
|
-
|
45
|
-
count
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Serega
|
4
|
-
module SeregaValidations
|
5
|
-
#
|
6
|
-
# Validations Utilities
|
7
|
-
#
|
8
|
-
module Utils
|
9
|
-
#
|
10
|
-
# Utility to check that callable object has no required keyword arguments
|
11
|
-
#
|
12
|
-
class CheckExtraKeywordArg
|
13
|
-
# Checks hash keys are allowed
|
14
|
-
#
|
15
|
-
# @param callable [#call] Callable object
|
16
|
-
# @param callable_description [Symbol] Callable object description
|
17
|
-
#
|
18
|
-
# @raise [Serega::SeregaError] error if callable accepts required keyword argument
|
19
|
-
#
|
20
|
-
# @return [void]
|
21
|
-
def self.call(callable, callable_description)
|
22
|
-
parameters = callable.is_a?(Proc) ? callable.parameters : callable.method(:call).parameters
|
23
|
-
|
24
|
-
parameters.each do |parameter|
|
25
|
-
next unless parameter[0] == :keyreq
|
26
|
-
|
27
|
-
raise Serega::SeregaError, "Invalid #{callable_description}. It should not have any required keyword arguments"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|