serega 0.21.0 → 0.30.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
@@ -62,7 +62,7 @@ class Serega
|
|
62
62
|
# Creates delegator method after first #method_missing hit to improve
|
63
63
|
# performance of following serializations.
|
64
64
|
#
|
65
|
-
def method_missing(name, *_args, &_block) # rubocop:disable Style/MissingRespondToMissing
|
65
|
+
def method_missing(name, *_args, &_block) # rubocop:disable Style/MissingRespondToMissing -- base SimpleDelegator class has this method
|
66
66
|
super.tap do
|
67
67
|
self.class.def_delegator :__getobj__, name
|
68
68
|
end
|
@@ -0,0 +1,58 @@
|
|
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
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
#
|
5
|
+
# Utilities
|
6
|
+
#
|
7
|
+
module SeregaUtils
|
8
|
+
#
|
9
|
+
# Utility to make method arguments signature
|
10
|
+
#
|
11
|
+
class MethodSignature
|
12
|
+
SYMBOL_TO_PROC_SIGNATURE_RUBY2 = [[:rest]]
|
13
|
+
SYMBOL_TO_PROC_SIGNATURE_RUBY3 = [[:req], [:rest]]
|
14
|
+
private_constant :SYMBOL_TO_PROC_SIGNATURE_RUBY2
|
15
|
+
private_constant :SYMBOL_TO_PROC_SIGNATURE_RUBY3
|
16
|
+
|
17
|
+
class << self
|
18
|
+
#
|
19
|
+
# Generates method arguments signature
|
20
|
+
#
|
21
|
+
# @param callable [#call] callable object
|
22
|
+
# @param pos_limit [Integer] Max count of positional parameters
|
23
|
+
# @param keyword_args [Array<Symbol>] List of accepted keyword argument names
|
24
|
+
#
|
25
|
+
# @return [String] Method signature which consist of number of positional parameters and
|
26
|
+
# keyword parameter names joined with undescrore.
|
27
|
+
#
|
28
|
+
def call(callable, pos_limit:, keyword_args: [])
|
29
|
+
params = callable.is_a?(Proc) ? callable.parameters : callable.method(:call).parameters
|
30
|
+
|
31
|
+
# Procs (but not lambdas) can accept all provided parameters
|
32
|
+
return full_signature(pos_limit, keyword_args) if params.empty? && callable.is_a?(Proc) && !callable.lambda?
|
33
|
+
|
34
|
+
# Return single positional argument for Symbol#to_proc
|
35
|
+
return "1" if (params == SYMBOL_TO_PROC_SIGNATURE_RUBY2) || (params == SYMBOL_TO_PROC_SIGNATURE_RUBY3)
|
36
|
+
|
37
|
+
keyword_args = keyword_args.dup
|
38
|
+
|
39
|
+
# signature parts
|
40
|
+
positional_parameters = 0
|
41
|
+
keyword_parameters = []
|
42
|
+
|
43
|
+
params.each do |type, name|
|
44
|
+
case type
|
45
|
+
when :req
|
46
|
+
positional_parameters += 1
|
47
|
+
pos_limit -= 1
|
48
|
+
when :opt
|
49
|
+
next if pos_limit <= 0
|
50
|
+
|
51
|
+
positional_parameters += 1
|
52
|
+
pos_limit -= 1
|
53
|
+
when :rest
|
54
|
+
next if pos_limit <= 0
|
55
|
+
|
56
|
+
positional_parameters += pos_limit
|
57
|
+
pos_limit = 0
|
58
|
+
when :keyreq
|
59
|
+
keyword_parameters << name
|
60
|
+
keyword_args.delete(name)
|
61
|
+
when :key
|
62
|
+
next unless keyword_args.include?(name)
|
63
|
+
|
64
|
+
keyword_parameters << name
|
65
|
+
keyword_args.delete(name)
|
66
|
+
when :keyrest
|
67
|
+
keyword_parameters.concat(keyword_args)
|
68
|
+
keyword_args.clear
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
build_signature_string(positional_parameters, keyword_parameters)
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def full_signature(pos_limit, keyword_args)
|
78
|
+
build_signature_string(pos_limit, keyword_args)
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_signature_string(positional_parameters, keyword_parameters)
|
82
|
+
sorted_signature_parts = keyword_parameters.sort
|
83
|
+
sorted_signature_parts.unshift(positional_parameters)
|
84
|
+
sorted_signature_parts.join("_")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,53 @@
|
|
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
|
@@ -0,0 +1,77 @@
|
|
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
|
@@ -13,7 +13,8 @@ class Serega
|
|
13
13
|
class << self
|
14
14
|
#
|
15
15
|
# Checks block parameter provided with attribute.
|
16
|
-
# Must have up to two arguments - object and context.
|
16
|
+
# Must have up to two arguments - object and context. Context can be
|
17
|
+
# also provided as keyword argument :ctx.
|
17
18
|
#
|
18
19
|
# @example without arguments
|
19
20
|
# attribute(:email) { CONSTANT_EMAIL }
|
@@ -24,6 +25,9 @@ class Serega
|
|
24
25
|
# @example with two arguments
|
25
26
|
# attribute(:email) { |obj, context| context['is_current'] ? obj.email : nil }
|
26
27
|
#
|
28
|
+
# @example with one argument and keyword context
|
29
|
+
# attribute(:email) { |obj, ctx:| obj.email if ctx[:show] }
|
30
|
+
#
|
27
31
|
# @param block [Proc] Block that returns serialized attribute value
|
28
32
|
#
|
29
33
|
# @raise [SeregaError] SeregaError that block has invalid arguments
|
@@ -39,14 +43,42 @@ class Serega
|
|
39
43
|
private
|
40
44
|
|
41
45
|
def check_block(block)
|
42
|
-
|
43
|
-
|
46
|
+
signature = SeregaUtils::MethodSignature.call(block, pos_limit: 2, keyword_args: [:ctx, :batches])
|
47
|
+
|
48
|
+
raise SeregaError, signature_error unless valid_signature?(signature)
|
49
|
+
end
|
44
50
|
|
45
|
-
|
51
|
+
def valid_signature?(signature)
|
52
|
+
case signature
|
53
|
+
when "0" # no parameters
|
54
|
+
true
|
55
|
+
when "1" # call(object)
|
56
|
+
true
|
57
|
+
when "1_ctx" # call(object, ctx:)
|
58
|
+
true
|
59
|
+
when "1_batches" # call(object, batches:)
|
60
|
+
true
|
61
|
+
when "1_batches_ctx" # call(object, batches:, ctx:)
|
62
|
+
true
|
63
|
+
when "2" # call(object, context)
|
64
|
+
true
|
65
|
+
when "2_batches_ctx" # call(object, context, batches:, ctx:) (proc with no params)
|
66
|
+
true
|
67
|
+
else
|
68
|
+
false
|
69
|
+
end
|
46
70
|
end
|
47
71
|
|
48
|
-
def
|
49
|
-
|
72
|
+
def signature_error
|
73
|
+
<<~ERROR.strip
|
74
|
+
Invalid attribute block parameters, valid parameters signatures:
|
75
|
+
- () # no parameters
|
76
|
+
- (object) # one positional parameter
|
77
|
+
- (object, ctx:) # one positional parameter and :ctx keyword
|
78
|
+
- (object, batches:) # one positional parameter and :batches keyword
|
79
|
+
- (object, ctx:, batches:) # one positional parameter, :ctx, and :batches keywords
|
80
|
+
- (object, context) # two positional parameters
|
81
|
+
ERROR
|
50
82
|
end
|
51
83
|
end
|
52
84
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaValidations
|
5
|
+
module Attribute
|
6
|
+
#
|
7
|
+
# Attribute `:batch` option validator
|
8
|
+
#
|
9
|
+
class CheckOptBatch
|
10
|
+
class << self
|
11
|
+
#
|
12
|
+
# Checks attribute :batch option
|
13
|
+
#
|
14
|
+
# @param opts [Hash] Attribute options
|
15
|
+
#
|
16
|
+
# @raise [SeregaError] Attribute validation error
|
17
|
+
#
|
18
|
+
# @return [void]
|
19
|
+
#
|
20
|
+
def call(serializer_class, opts, block)
|
21
|
+
return unless opts.key?(:batch)
|
22
|
+
|
23
|
+
check_opt_batch(opts, serializer_class)
|
24
|
+
check_usage_with_other_params(opts, block)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def check_opt_batch(opts, serializer_class)
|
30
|
+
batch_opts = opts[:batch]
|
31
|
+
return if batch_opts == true
|
32
|
+
return if batch_opts.respond_to?(:call)
|
33
|
+
|
34
|
+
if batch_opts.is_a?(Symbol) || batch_opts.is_a?(String)
|
35
|
+
check_loader_exists?(serializer_class, batch_opts)
|
36
|
+
else
|
37
|
+
Utils::CheckOptIsHash.call(opts, :batch)
|
38
|
+
check_opt_batch_use(serializer_class, batch_opts)
|
39
|
+
check_opt_batch_id(batch_opts)
|
40
|
+
check_opt_batch_extra_opts(batch_opts)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def check_opt_batch_use(serializer_class, batch_opts)
|
45
|
+
return unless batch_opts.key?(:use)
|
46
|
+
|
47
|
+
batch_loader_name = batch_opts[:use]
|
48
|
+
return if batch_loader_name.respond_to?(:call)
|
49
|
+
|
50
|
+
check_loader_exists?(serializer_class, batch_loader_name)
|
51
|
+
end
|
52
|
+
|
53
|
+
def check_opt_batch_id(batch_opts)
|
54
|
+
return unless batch_opts.key?(:id)
|
55
|
+
|
56
|
+
id_method_name = batch_opts[:id]
|
57
|
+
return if id_method_name.is_a?(Symbol) || id_method_name.is_a?(String)
|
58
|
+
|
59
|
+
raise SeregaError, "Invalid batch option `:id` value, it can be a Symbol or a String"
|
60
|
+
end
|
61
|
+
|
62
|
+
def check_loader_exists?(serializer_class, value)
|
63
|
+
values = Array(value)
|
64
|
+
values.each do |batch_loader_name|
|
65
|
+
next if serializer_class.batch_loaders.key?(batch_loader_name.to_sym)
|
66
|
+
|
67
|
+
raise SeregaError, "Batch loader with name `#{batch_loader_name.inspect}` is not defined"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def check_opt_batch_extra_opts(batch_opts)
|
72
|
+
Utils::CheckAllowedKeys.call(batch_opts, %i[use id], :batch)
|
73
|
+
end
|
74
|
+
|
75
|
+
def check_usage_with_other_params(opts, block)
|
76
|
+
batch = opts[:batch]
|
77
|
+
use_id = batch.is_a?(Hash) && batch.key?(:id)
|
78
|
+
use_multiple = batch.is_a?(Hash) && (Array(batch[:use]).size > 1)
|
79
|
+
value_added = opts.key?(:value) || block
|
80
|
+
|
81
|
+
if use_multiple && use_id
|
82
|
+
raise SeregaError, "Option `batch.id` should not be used with multiple loaders provided in `batch.use`"
|
83
|
+
end
|
84
|
+
|
85
|
+
if use_multiple && !value_added
|
86
|
+
raise SeregaError, "Attribute :value option or block should be provided when selecting multiple batch loaders"
|
87
|
+
end
|
88
|
+
|
89
|
+
if use_id && value_added
|
90
|
+
raise SeregaError, "Option `batch.id` should not be used when :value or block provided directly"
|
91
|
+
end
|
92
|
+
|
93
|
+
raise SeregaError, "Option :batch can not be used together with option :method" if opts.key?(:method)
|
94
|
+
raise SeregaError, "Option :batch can not be used together with option :const" if opts.key?(:const)
|
95
|
+
raise SeregaError, "Option :batch can not be used together with option :delegate" if opts.key?(:delegate)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -28,6 +28,7 @@ class Serega
|
|
28
28
|
def check_usage_with_other_params(opts, block)
|
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
|
+
raise SeregaError, "Option :const can not be used together with option :batch" if opts.key?(:batch)
|
31
32
|
raise SeregaError, "Option :const can not be used together with block" if block
|
32
33
|
end
|
33
34
|
end
|
@@ -60,6 +60,7 @@ class Serega
|
|
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
|
+
raise SeregaError, "Option :delegate can not be used together with option :batch" if opts.key?(:batch)
|
63
64
|
raise SeregaError, "Option :delegate can not be used together with block" if block
|
64
65
|
end
|
65
66
|
end
|
@@ -29,6 +29,7 @@ class Serega
|
|
29
29
|
def check_usage_with_other_params(opts, block)
|
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
|
+
raise SeregaError, "Option :method can not be used together with option :batch" if opts.key?(:batch)
|
32
33
|
raise SeregaError, "Option :method can not be used together with block" if block
|
33
34
|
end
|
34
35
|
end
|
data/lib/serega/{plugins/preloads/validations → validations/attribute}/check_opt_preload_path.rb
RENAMED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Serega
|
4
|
-
module
|
5
|
-
module
|
4
|
+
module SeregaValidations
|
5
|
+
module Attribute
|
6
6
|
#
|
7
7
|
# Validator for attribute :preload_path option
|
8
8
|
#
|
@@ -34,7 +34,7 @@ class Serega
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def check_allowed(path, opts)
|
37
|
-
allowed_paths = PreloadPaths.call(opts[:preload])
|
37
|
+
allowed_paths = SeregaUtils::PreloadPaths.call(opts[:preload])
|
38
38
|
check_required_when_many_allowed(path, allowed_paths)
|
39
39
|
check_in_allowed(path, allowed_paths)
|
40
40
|
end
|
@@ -35,23 +35,49 @@ class Serega
|
|
35
35
|
|
36
36
|
def check_value(value)
|
37
37
|
check_value_type(value)
|
38
|
-
|
39
|
-
|
40
|
-
params_count = SeregaUtils::ParamsCount.call(value, max_count: 2)
|
41
|
-
|
42
|
-
raise SeregaError, params_count_error if params_count > 2
|
38
|
+
signature = SeregaUtils::MethodSignature.call(value, pos_limit: 2, keyword_args: %i[ctx batches])
|
39
|
+
raise SeregaError, signature_error unless valid_signature?(signature)
|
43
40
|
end
|
44
41
|
|
45
42
|
def check_value_type(value)
|
46
43
|
raise SeregaError, type_error if !value.is_a?(Proc) && !value.respond_to?(:call)
|
47
44
|
end
|
48
45
|
|
49
|
-
def
|
50
|
-
|
46
|
+
def valid_signature?(signature)
|
47
|
+
case signature
|
48
|
+
when "0" # no parameters
|
49
|
+
true
|
50
|
+
when "1" # call(object)
|
51
|
+
true
|
52
|
+
when "1_ctx" # call(object, ctx:)
|
53
|
+
true
|
54
|
+
when "1_batches" # call(object, batches:)
|
55
|
+
true
|
56
|
+
when "1_batches_ctx" # call(object, ctx:, batches:)
|
57
|
+
true
|
58
|
+
when "2" # call(object, context)
|
59
|
+
true
|
60
|
+
when "2_batches_ctx" # call(object, context, ctx:, batches:) (proc with no params)
|
61
|
+
true
|
62
|
+
else
|
63
|
+
false
|
64
|
+
end
|
51
65
|
end
|
52
66
|
|
53
|
-
def
|
54
|
-
|
67
|
+
def signature_error
|
68
|
+
<<~ERROR.strip
|
69
|
+
Invalid attribute :value option parameters, valid parameters signatures:
|
70
|
+
- () # no parameters
|
71
|
+
- (object) # one positional parameter
|
72
|
+
- (object, ctx:) # one positional parameter and :ctx keyword
|
73
|
+
- (object, batches:) # one positional parameter and :batches keyword
|
74
|
+
- (object, ctx:, batches:) # one positional parameter, :ctx, and :batches keywords
|
75
|
+
- (object, context) # two positional parameters
|
76
|
+
ERROR
|
77
|
+
end
|
78
|
+
|
79
|
+
def type_error
|
80
|
+
"Option :value value must be a Proc or respond to #call"
|
55
81
|
end
|
56
82
|
end
|
57
83
|
end
|
@@ -53,11 +53,9 @@ class Serega
|
|
53
53
|
end
|
54
54
|
|
55
55
|
# Patched in:
|
56
|
-
# - plugin :batch (checks :batch option)
|
57
56
|
# - plugin :context_metadata (checks context metadata option which is :meta by default)
|
58
57
|
# - plugin :formatters (checks :format option)
|
59
58
|
# - plugin :if (checks :if, :if_value, :unless, :unless_value options)
|
60
|
-
# - plugin :preloads (checks :preload option)
|
61
59
|
def check_opts
|
62
60
|
Utils::CheckAllowedKeys.call(opts, allowed_opts_keys, :attribute)
|
63
61
|
|
@@ -66,8 +64,11 @@ class Serega
|
|
66
64
|
Attribute::CheckOptHide.call(opts)
|
67
65
|
Attribute::CheckOptMethod.call(opts, block)
|
68
66
|
Attribute::CheckOptMany.call(opts)
|
67
|
+
Attribute::CheckOptPreload.call(opts)
|
68
|
+
Attribute::CheckOptPreloadPath.call(opts)
|
69
69
|
Attribute::CheckOptSerializer.call(opts)
|
70
70
|
Attribute::CheckOptValue.call(opts, block)
|
71
|
+
Attribute::CheckOptBatch.call(self.class.serializer_class, opts, block)
|
71
72
|
end
|
72
73
|
|
73
74
|
def check_block
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaValidations
|
5
|
+
#
|
6
|
+
# Batch loader parameters validators
|
7
|
+
#
|
8
|
+
class CheckBatchLoaderParams
|
9
|
+
#
|
10
|
+
# batch_loader parameters validation instance methods
|
11
|
+
#
|
12
|
+
module InstanceMethods
|
13
|
+
# @return [Symbol] validated batch_loader name
|
14
|
+
attr_reader :name
|
15
|
+
|
16
|
+
# @return [nil, #call] validated batch_loader value or block
|
17
|
+
attr_reader :batch_loader
|
18
|
+
|
19
|
+
# Instantiates batch loader params checker
|
20
|
+
# @param name [Symbol, String] Batch loader name
|
21
|
+
# @param batch_loader [Proc, #call] Batch loader
|
22
|
+
def initialize(name, batch_loader)
|
23
|
+
@name = name
|
24
|
+
@batch_loader = batch_loader
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Checks batch loader parameters
|
29
|
+
#
|
30
|
+
# @raise [SeregaError] SeregaError that batch loader has invalid arguments
|
31
|
+
#
|
32
|
+
# @return [void]
|
33
|
+
#
|
34
|
+
def validate
|
35
|
+
check_name
|
36
|
+
check_loader
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def check_name
|
42
|
+
raise SeregaError, name_type_error if !name.is_a?(Symbol) && !name.is_a?(String)
|
43
|
+
end
|
44
|
+
|
45
|
+
def check_loader
|
46
|
+
check_batch_loader_type
|
47
|
+
check_batch_loader_args
|
48
|
+
end
|
49
|
+
|
50
|
+
def check_batch_loader_type
|
51
|
+
raise SeregaError, type_error if !batch_loader.is_a?(Proc) && !batch_loader.respond_to?(:call)
|
52
|
+
end
|
53
|
+
|
54
|
+
def check_batch_loader_args
|
55
|
+
signature = SeregaUtils::MethodSignature.call(batch_loader, pos_limit: 2, keyword_args: [:ctx])
|
56
|
+
raise SeregaError, arguments_error unless %w[1 2 1_ctx].include?(signature)
|
57
|
+
end
|
58
|
+
|
59
|
+
def name_type_error
|
60
|
+
"Batch loader name must be a Symbol or String"
|
61
|
+
end
|
62
|
+
|
63
|
+
def type_error
|
64
|
+
"Batch loader value must be a Proc or respond to #call"
|
65
|
+
end
|
66
|
+
|
67
|
+
def arguments_error
|
68
|
+
<<~ERR.strip
|
69
|
+
Batch loader arguments should have one of this signatures:
|
70
|
+
- (objects) # one argument
|
71
|
+
- (objects, :ctx) # one argument and one :ctx keyword argument
|
72
|
+
ERR
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
include InstanceMethods
|
77
|
+
extend Serega::SeregaHelpers::SerializerClassHelper
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|