serega 0.42.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +124 -1207
- data/VERSION +1 -1
- data/lib/serega/attribute.rb +186 -131
- data/lib/serega/attribute_normalizer.rb +164 -345
- data/lib/serega/attribute_normalizers/batch.rb +45 -0
- data/lib/serega/attribute_normalizers/block.rb +81 -0
- data/lib/serega/attribute_normalizers/delegate.rb +34 -0
- data/lib/serega/attribute_normalizers/hash_access.rb +37 -0
- data/lib/serega/attribute_normalizers/preloads.rb +46 -0
- data/lib/serega/attribute_value_resolvers/batch.rb +5 -54
- data/lib/serega/attribute_value_resolvers/const.rb +2 -24
- data/lib/serega/attribute_value_resolvers/delegate.rb +17 -28
- data/lib/serega/attribute_value_resolvers/hash_access.rb +34 -52
- data/lib/serega/attribute_value_resolvers/keyword.rb +10 -25
- data/lib/serega/attribute_values.rb +46 -0
- data/lib/serega/batch_loader.rb +37 -0
- data/lib/serega/conditions.rb +56 -0
- data/lib/serega/config.rb +105 -144
- data/lib/serega/engine/pulls.rb +64 -0
- data/lib/serega/engine/run.rb +106 -0
- data/lib/serega/errors.rb +36 -8
- data/lib/serega/formatter.rb +98 -0
- data/lib/serega/plan/cache.rb +96 -0
- data/lib/serega/plan/per_ractor_cache.rb +23 -0
- data/lib/serega/plan.rb +101 -128
- data/lib/serega/plan_point.rb +44 -151
- data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +13 -80
- data/lib/serega/plugins/activerecord_preloads/lib/preloader.rb +8 -75
- data/lib/serega/plugins/context_metadata/context_metadata.rb +40 -123
- data/lib/serega/plugins/metadata/check_params.rb +80 -0
- data/lib/serega/plugins/metadata/meta_attribute.rb +40 -79
- data/lib/serega/plugins/metadata/metadata.rb +40 -168
- data/lib/serega/plugins/root/root.rb +91 -235
- data/lib/serega/plugins.rb +16 -49
- data/lib/serega/presenter.rb +61 -19
- data/lib/serega/result_builder.rb +68 -0
- data/lib/serega/result_code.rb +203 -0
- data/lib/serega/shareable.rb +52 -0
- data/lib/serega/source_group.rb +187 -0
- data/lib/serega/utils/collection_detector.rb +19 -13
- data/lib/serega/utils/deep_merge.rb +26 -0
- data/lib/serega/utils/enum_deep_dup.rb +10 -16
- data/lib/serega/utils/enum_deep_freeze.rb +8 -14
- data/lib/serega/utils/method_signature.rb +6 -15
- data/lib/serega/utils/parse_string_modifiers.rb +63 -0
- data/lib/serega/utils/ractor_local.rb +36 -0
- data/lib/serega/utils/serialized_attribute_error.rb +60 -20
- data/lib/serega/utils/to_hash.rb +5 -26
- data/lib/serega/validations/attribute/check_block.rb +3 -15
- data/lib/serega/validations/attribute/check_name.rb +4 -15
- data/lib/serega/validations/attribute/check_opt_base_serializer.rb +3 -15
- data/lib/serega/validations/attribute/check_opt_batch.rb +26 -56
- data/lib/serega/validations/attribute/check_opt_condition.rb +55 -0
- data/lib/serega/validations/attribute/check_opt_delegate.rb +10 -63
- data/lib/serega/validations/attribute/check_opt_format.rb +36 -0
- data/lib/serega/validations/attribute/check_opt_hash_access.rb +11 -59
- data/lib/serega/validations/attribute/check_opt_hide.rb +3 -14
- data/lib/serega/validations/attribute/check_opt_many.rb +18 -20
- data/lib/serega/validations/attribute/check_opt_method.rb +3 -25
- data/lib/serega/validations/attribute/check_opt_preload.rb +3 -26
- data/lib/serega/validations/attribute/check_opt_serializer.rb +4 -18
- data/lib/serega/validations/attribute/check_opt_value.rb +3 -21
- data/lib/serega/validations/check_attribute_params.rb +92 -71
- data/lib/serega/validations/check_batch_loader_params.rb +43 -62
- data/lib/serega/validations/check_formatter.rb +80 -0
- data/lib/serega/validations/check_serialize_keywords.rb +23 -0
- data/lib/serega/validations/initiate/check_modifiers.rb +14 -20
- data/lib/serega/validations/utils/check_allowed_keys.rb +3 -13
- data/lib/serega/validations/utils/check_hash_access_value.rb +44 -0
- data/lib/serega/validations/utils/check_opt_is_bool.rb +3 -13
- data/lib/serega/validations/utils/check_opt_is_hash.rb +3 -13
- data/lib/serega/validations/utils/check_opt_is_string_or_symbol.rb +3 -13
- data/lib/serega/version.rb +1 -4
- data/lib/serega.rb +459 -388
- data/sig/serega/plugins.rbs +49 -0
- data/sig/serega.rbs +142 -0
- metadata +42 -38
- data/lib/serega/data_builder.rb +0 -54
- data/lib/serega/engine/level.rb +0 -59
- data/lib/serega/engine/level_queue.rb +0 -56
- data/lib/serega/engine/loader.rb +0 -70
- data/lib/serega/helpers/serializer_class_helper.rb +0 -19
- data/lib/serega/object_serializer.rb +0 -125
- data/lib/serega/plugins/camel_case/camel_case.rb +0 -213
- data/lib/serega/plugins/depth_limit/depth_limit.rb +0 -206
- data/lib/serega/plugins/explicit_many_option/explicit_many_option.rb +0 -69
- data/lib/serega/plugins/explicit_many_option/validations/check_opt_many.rb +0 -38
- data/lib/serega/plugins/formatters/formatters.rb +0 -322
- data/lib/serega/plugins/if/if.rb +0 -287
- data/lib/serega/plugins/if/validations/check_opt_if.rb +0 -72
- data/lib/serega/plugins/if/validations/check_opt_if_value.rb +0 -77
- data/lib/serega/plugins/if/validations/check_opt_unless.rb +0 -72
- data/lib/serega/plugins/if/validations/check_opt_unless_value.rb +0 -77
- data/lib/serega/plugins/metadata/validations/check_block.rb +0 -47
- data/lib/serega/plugins/metadata/validations/check_opt_const.rb +0 -39
- data/lib/serega/plugins/metadata/validations/check_opt_hide_empty.rb +0 -35
- data/lib/serega/plugins/metadata/validations/check_opt_hide_nil.rb +0 -35
- data/lib/serega/plugins/metadata/validations/check_opt_value.rb +0 -61
- data/lib/serega/plugins/metadata/validations/check_opts.rb +0 -59
- data/lib/serega/plugins/metadata/validations/check_path.rb +0 -50
- data/lib/serega/plugins/string_modifiers/parse_string_modifiers.rb +0 -111
- data/lib/serega/plugins/string_modifiers/string_modifiers.rb +0 -63
- data/lib/serega/utils/symbol_name.rb +0 -39
- data/lib/serega/validations/attribute/check_opt_const.rb +0 -40
- data/lib/serega/validations/check_initiate_params.rb +0 -57
- data/lib/serega/validations/check_serialize_params.rb +0 -54
|
@@ -1,31 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Serega
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
# @private
|
|
7
|
-
module Attribute
|
|
4
|
+
module SeregaValidations # :nodoc:
|
|
5
|
+
module Attribute # :nodoc:
|
|
8
6
|
#
|
|
9
7
|
# Attribute `:serializer` option validator
|
|
10
8
|
#
|
|
11
|
-
#
|
|
12
|
-
class CheckOptSerializer
|
|
9
|
+
class CheckOptSerializer # :nodoc:
|
|
13
10
|
class << self
|
|
14
|
-
#
|
|
15
11
|
# Checks attribute :serializer option
|
|
16
|
-
|
|
17
|
-
# @param opts [Hash] Attribute options
|
|
18
|
-
# @param block [nil, Proc] Attribute block (defines a nested serializer)
|
|
19
|
-
#
|
|
20
|
-
# @raise [SeregaError] SeregaError that option has invalid value
|
|
21
|
-
#
|
|
22
|
-
# @return [void]
|
|
23
|
-
#
|
|
24
|
-
def call(opts, block = nil)
|
|
12
|
+
def call(opts)
|
|
25
13
|
return unless opts.key?(:serializer)
|
|
26
14
|
|
|
27
|
-
raise SeregaError, "Option :serializer can not be used together with block" if block
|
|
28
|
-
|
|
29
15
|
value = opts[:serializer]
|
|
30
16
|
return if valid_serializer?(value)
|
|
31
17
|
|
|
@@ -1,40 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Serega
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
# @private
|
|
7
|
-
module Attribute
|
|
4
|
+
module SeregaValidations # :nodoc:
|
|
5
|
+
module Attribute # :nodoc:
|
|
8
6
|
#
|
|
9
7
|
# Attribute `:value` option validator
|
|
10
8
|
#
|
|
11
|
-
#
|
|
12
|
-
class CheckOptValue
|
|
9
|
+
class CheckOptValue # :nodoc:
|
|
13
10
|
class << self
|
|
14
|
-
#
|
|
15
11
|
# Checks attribute :value option
|
|
16
|
-
#
|
|
17
|
-
# @param opts [Hash] Attribute options
|
|
18
|
-
#
|
|
19
|
-
# @raise [SeregaError] SeregaError that option has invalid value
|
|
20
|
-
#
|
|
21
|
-
# @return [void]
|
|
22
|
-
#
|
|
23
12
|
def call(opts)
|
|
24
13
|
return unless opts.key?(:value)
|
|
25
14
|
|
|
26
|
-
check_usage_with_other_params(opts)
|
|
27
|
-
|
|
28
15
|
check_value(opts[:value])
|
|
29
16
|
end
|
|
30
17
|
|
|
31
18
|
private
|
|
32
19
|
|
|
33
|
-
def check_usage_with_other_params(opts)
|
|
34
|
-
raise SeregaError, "Option :value can not be used together with option :method" if opts.key?(:method)
|
|
35
|
-
raise SeregaError, "Option :value can not be used together with option :const" if opts.key?(:const)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
20
|
def check_value(value)
|
|
39
21
|
check_value_type(value)
|
|
40
22
|
signature = SeregaUtils::MethodSignature.call(value, pos_limit: 2, keyword_args: %i[ctx batches])
|
|
@@ -4,92 +4,113 @@ class Serega
|
|
|
4
4
|
#
|
|
5
5
|
# Validations
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
module SeregaValidations
|
|
7
|
+
module SeregaValidations # :nodoc:
|
|
9
8
|
#
|
|
10
9
|
# Validations for attribute params
|
|
11
10
|
#
|
|
12
|
-
#
|
|
13
|
-
|
|
11
|
+
class CheckAttributeParams # :nodoc:
|
|
12
|
+
# Options of `Serega.attribute`
|
|
13
|
+
ATTRIBUTE_KEYS = %i[
|
|
14
|
+
method value serializer many hide const delegate default format preload batch
|
|
15
|
+
base_serializer hash_access if unless if_value unless_value
|
|
16
|
+
].freeze
|
|
17
|
+
|
|
18
|
+
# Options that can't be given together, `block` for the attribute
|
|
19
|
+
# block. Each option lists every option it conflicts with, and the error
|
|
20
|
+
# names the option that comes first here. :hash_access with :delegate is
|
|
21
|
+
# left to CheckOptHashAccess, which points to the delegate sub-options.
|
|
22
|
+
CONFLICTS = {
|
|
23
|
+
method: %i[value const delegate batch],
|
|
24
|
+
value: %i[method const delegate hash_access],
|
|
25
|
+
const: %i[method value delegate batch hash_access preload],
|
|
26
|
+
delegate: %i[method value const batch],
|
|
27
|
+
batch: %i[method const delegate hash_access],
|
|
28
|
+
hash_access: %i[value const batch],
|
|
29
|
+
preload: %i[const],
|
|
30
|
+
serializer: %i[block if_value unless_value],
|
|
31
|
+
if_value: %i[serializer block],
|
|
32
|
+
unless_value: %i[serializer block]
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
35
|
+
# Validated attribute name
|
|
36
|
+
attr_reader :name
|
|
37
|
+
|
|
38
|
+
# Validated attribute options
|
|
39
|
+
attr_reader :opts
|
|
40
|
+
|
|
41
|
+
# Validated attribute block
|
|
42
|
+
attr_reader :block
|
|
43
|
+
|
|
44
|
+
# Initializes attribute params validator
|
|
45
|
+
def initialize(serializer_class, name, opts, block)
|
|
46
|
+
@serializer_class = serializer_class
|
|
47
|
+
@name = name
|
|
48
|
+
@opts = opts
|
|
49
|
+
@block = block
|
|
50
|
+
end
|
|
51
|
+
|
|
14
52
|
#
|
|
15
|
-
#
|
|
53
|
+
# Validates attribute params
|
|
16
54
|
#
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
# @return [Hash] validated attribute options
|
|
23
|
-
attr_reader :opts
|
|
24
|
-
|
|
25
|
-
# @return [nil, Proc] validated attribute block
|
|
26
|
-
attr_reader :block
|
|
27
|
-
|
|
28
|
-
#
|
|
29
|
-
# Initializes attribute params validator
|
|
30
|
-
#
|
|
31
|
-
# @param name [Symbol] attribute name
|
|
32
|
-
# @param opts [Hash] attribute options
|
|
33
|
-
# @param block [nil, Proc] block provided to attribute
|
|
34
|
-
#
|
|
35
|
-
# @return [void]
|
|
36
|
-
#
|
|
37
|
-
def initialize(name, opts, block)
|
|
38
|
-
@name = name
|
|
39
|
-
@opts = opts
|
|
40
|
-
@block = block
|
|
41
|
-
end
|
|
55
|
+
def validate
|
|
56
|
+
check_name if check_attribute_name
|
|
57
|
+
check_opts
|
|
58
|
+
check_block
|
|
59
|
+
end
|
|
42
60
|
|
|
43
|
-
|
|
44
|
-
# Validates attribute params
|
|
45
|
-
#
|
|
46
|
-
def validate
|
|
47
|
-
check_name if check_attribute_name
|
|
48
|
-
check_opts
|
|
49
|
-
check_block
|
|
50
|
-
end
|
|
61
|
+
private
|
|
51
62
|
|
|
52
|
-
|
|
63
|
+
def check_name
|
|
64
|
+
Attribute::CheckName.call(name)
|
|
65
|
+
end
|
|
53
66
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
67
|
+
def check_opts
|
|
68
|
+
Utils::CheckAllowedKeys.call(opts, ATTRIBUTE_KEYS, :attribute)
|
|
69
|
+
check_conflicts
|
|
57
70
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
Attribute::CheckOptValue.call(opts)
|
|
75
|
-
Attribute::CheckOptBatch.call(self.class.serializer_class, opts)
|
|
76
|
-
end
|
|
71
|
+
Attribute::CheckOptBaseSerializer.call(opts, block)
|
|
72
|
+
Attribute::CheckOptCondition.call(opts, :if)
|
|
73
|
+
Attribute::CheckOptCondition.call(opts, :unless)
|
|
74
|
+
Attribute::CheckOptCondition.call(opts, :if_value)
|
|
75
|
+
Attribute::CheckOptCondition.call(opts, :unless_value)
|
|
76
|
+
Attribute::CheckOptDelegate.call(opts)
|
|
77
|
+
Attribute::CheckOptFormat.call(opts, @serializer_class)
|
|
78
|
+
Attribute::CheckOptHashAccess.call(opts)
|
|
79
|
+
Attribute::CheckOptHide.call(opts)
|
|
80
|
+
Attribute::CheckOptMethod.call(opts)
|
|
81
|
+
Attribute::CheckOptMany.call(opts, block, require_many)
|
|
82
|
+
Attribute::CheckOptPreload.call(opts)
|
|
83
|
+
Attribute::CheckOptSerializer.call(opts)
|
|
84
|
+
Attribute::CheckOptValue.call(opts)
|
|
85
|
+
Attribute::CheckOptBatch.call(@serializer_class, name, opts)
|
|
86
|
+
end
|
|
77
87
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
def check_block
|
|
89
|
+
Attribute::CheckBlock.call(block)
|
|
90
|
+
end
|
|
81
91
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
92
|
+
def check_conflicts
|
|
93
|
+
CONFLICTS.each do |option, others|
|
|
94
|
+
next unless opts.key?(option)
|
|
85
95
|
|
|
86
|
-
|
|
87
|
-
|
|
96
|
+
other = others.find { |key| (key == :block) ? block : opts.key?(key) }
|
|
97
|
+
raise SeregaError, conflict_message(option, other) if other
|
|
88
98
|
end
|
|
89
99
|
end
|
|
90
100
|
|
|
91
|
-
|
|
92
|
-
|
|
101
|
+
def conflict_message(option, other)
|
|
102
|
+
return "Option #{option.inspect} can not be used together with block" if other == :block
|
|
103
|
+
|
|
104
|
+
"Option #{option.inspect} can not be used together with option #{other.inspect}"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def check_attribute_name
|
|
108
|
+
@serializer_class.config.check_attribute_name
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def require_many
|
|
112
|
+
@serializer_class.config.require_many
|
|
113
|
+
end
|
|
93
114
|
end
|
|
94
115
|
end
|
|
95
116
|
end
|
|
@@ -1,83 +1,64 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Serega
|
|
4
|
-
#
|
|
5
|
-
module SeregaValidations
|
|
4
|
+
module SeregaValidations # :nodoc:
|
|
6
5
|
#
|
|
7
6
|
# Batch loader parameters validators
|
|
8
7
|
#
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
# batch_loader parameters validation instance methods
|
|
13
|
-
#
|
|
14
|
-
# @private
|
|
15
|
-
module InstanceMethods
|
|
16
|
-
# @return [Symbol] validated batch_loader name
|
|
17
|
-
attr_reader :name
|
|
8
|
+
class CheckBatchLoaderParams # :nodoc:
|
|
9
|
+
# Validated batch_loader name
|
|
10
|
+
attr_reader :name
|
|
18
11
|
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
# Validated batch_loader value or block
|
|
13
|
+
attr_reader :batch_loader
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@batch_loader = batch_loader
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
-
# Checks batch loader parameters
|
|
32
|
-
#
|
|
33
|
-
# @raise [SeregaError] SeregaError that batch loader has invalid arguments
|
|
34
|
-
#
|
|
35
|
-
# @return [void]
|
|
36
|
-
#
|
|
37
|
-
def validate
|
|
38
|
-
check_name
|
|
39
|
-
check_loader
|
|
40
|
-
end
|
|
15
|
+
# Instantiates batch loader params checker
|
|
16
|
+
def initialize(name, batch_loader)
|
|
17
|
+
@name = name
|
|
18
|
+
@batch_loader = batch_loader
|
|
19
|
+
end
|
|
41
20
|
|
|
42
|
-
|
|
21
|
+
# Checks batch loader parameters
|
|
22
|
+
def validate
|
|
23
|
+
check_name
|
|
24
|
+
check_loader
|
|
25
|
+
end
|
|
43
26
|
|
|
44
|
-
|
|
45
|
-
raise SeregaError, name_type_error if !name.is_a?(Symbol) && !name.is_a?(String)
|
|
46
|
-
end
|
|
27
|
+
private
|
|
47
28
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
end
|
|
29
|
+
def check_name
|
|
30
|
+
raise SeregaError, name_type_error if !name.is_a?(Symbol) && !name.is_a?(String)
|
|
31
|
+
end
|
|
52
32
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
33
|
+
def check_loader
|
|
34
|
+
check_batch_loader_type
|
|
35
|
+
check_batch_loader_args
|
|
36
|
+
end
|
|
56
37
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
end
|
|
38
|
+
def check_batch_loader_type
|
|
39
|
+
raise SeregaError, type_error if !batch_loader.is_a?(Proc) && !batch_loader.respond_to?(:call)
|
|
40
|
+
end
|
|
61
41
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
42
|
+
def check_batch_loader_args
|
|
43
|
+
signature = SeregaUtils::MethodSignature.call(batch_loader, pos_limit: 2, keyword_args: [:ctx])
|
|
44
|
+
raise SeregaError, arguments_error unless %w[1 2 1_ctx].include?(signature)
|
|
45
|
+
end
|
|
65
46
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
47
|
+
def name_type_error
|
|
48
|
+
"Batch loader name must be a Symbol or String"
|
|
49
|
+
end
|
|
69
50
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
Batch loader arguments should have one of this signatures:
|
|
73
|
-
- (objects) # one argument
|
|
74
|
-
- (objects, :ctx) # one argument and one :ctx keyword argument
|
|
75
|
-
ERR
|
|
76
|
-
end
|
|
51
|
+
def type_error
|
|
52
|
+
"Batch loader value must be a Proc or respond to #call"
|
|
77
53
|
end
|
|
78
54
|
|
|
79
|
-
|
|
80
|
-
|
|
55
|
+
def arguments_error
|
|
56
|
+
<<~ERR.strip
|
|
57
|
+
Batch loader arguments should have one of this signatures:
|
|
58
|
+
- (objects) # one argument
|
|
59
|
+
- (objects, :ctx) # one argument and one :ctx keyword argument
|
|
60
|
+
ERR
|
|
61
|
+
end
|
|
81
62
|
end
|
|
82
63
|
end
|
|
83
64
|
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Serega
|
|
4
|
+
module SeregaValidations # :nodoc:
|
|
5
|
+
#
|
|
6
|
+
# Validator for formatters defined with `Serega.formatter` or given
|
|
7
|
+
# directly as the attribute :format option
|
|
8
|
+
#
|
|
9
|
+
class CheckFormatter # :nodoc:
|
|
10
|
+
class << self
|
|
11
|
+
# Checks the formatter is a callable with valid parameters, a method
|
|
12
|
+
# name, or an Array of a method name and its arguments. The attribute
|
|
13
|
+
# :format option also accepts a Hash with one of them as :use and the
|
|
14
|
+
# :allow_nil option.
|
|
15
|
+
def call(formatter_name, formatter)
|
|
16
|
+
if formatter.is_a?(Hash) && !formatter_name
|
|
17
|
+
Utils::CheckAllowedKeys.call(formatter, %i[use allow_nil], :formatter)
|
|
18
|
+
Utils::CheckOptIsBool.call(formatter, :allow_nil)
|
|
19
|
+
formatter = formatter[:use]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
check_value(formatter_name, formatter)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def check_value(formatter_name, formatter)
|
|
28
|
+
return if formatter.is_a?(Symbol)
|
|
29
|
+
return if formatter.is_a?(Array) && formatter.first.is_a?(Symbol)
|
|
30
|
+
raise SeregaError, invalid_formatter(formatter_name) unless formatter.respond_to?(:call)
|
|
31
|
+
|
|
32
|
+
signature = SeregaUtils::MethodSignature.call(formatter, pos_limit: 2, keyword_args: [:ctx])
|
|
33
|
+
raise SeregaError, signature_error unless valid_signature?(signature)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def valid_signature?(signature)
|
|
37
|
+
case signature
|
|
38
|
+
when "1" # (value)
|
|
39
|
+
true
|
|
40
|
+
when "2" # (value, context)
|
|
41
|
+
true
|
|
42
|
+
when "1_ctx" # (value, :ctx)
|
|
43
|
+
true
|
|
44
|
+
else
|
|
45
|
+
false
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def invalid_formatter(formatter_name)
|
|
50
|
+
return invalid_format_option unless formatter_name
|
|
51
|
+
|
|
52
|
+
<<~ERROR.strip
|
|
53
|
+
Invalid formatter #{formatter_name.inspect}. A formatter must be one of:
|
|
54
|
+
- a callable, for example ->(value) { value.round(2) }
|
|
55
|
+
- a method name, for example :to_s
|
|
56
|
+
- an Array of a method name and its arguments, for example [:round, 2]
|
|
57
|
+
ERROR
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def invalid_format_option
|
|
61
|
+
<<~ERROR.strip
|
|
62
|
+
Invalid attribute option :format. It must be a formatter name or one of:
|
|
63
|
+
- a callable, for example ->(value) { value.round(2) }
|
|
64
|
+
- an Array of a method name and its arguments, for example [:round, 2]
|
|
65
|
+
- a Hash with one of them or a method name as :use and the :allow_nil option, for example {use: :to_s, allow_nil: true}
|
|
66
|
+
ERROR
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def signature_error
|
|
70
|
+
<<~ERROR.strip
|
|
71
|
+
Invalid formatter parameters, valid parameters signatures:
|
|
72
|
+
- (value) # one positional parameter
|
|
73
|
+
- (value, context) # two positional parameters
|
|
74
|
+
- (value, :ctx) # one positional parameter and :ctx keyword
|
|
75
|
+
ERROR
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Serega
|
|
4
|
+
module SeregaValidations # :nodoc:
|
|
5
|
+
#
|
|
6
|
+
# Validator for the :context and :many serialization options
|
|
7
|
+
#
|
|
8
|
+
class CheckSerializeKeywords # :nodoc:
|
|
9
|
+
class << self
|
|
10
|
+
# Checks the context is a Hash and many is a boolean, when given
|
|
11
|
+
def call(context, many)
|
|
12
|
+
unless context.nil? || context.is_a?(Hash)
|
|
13
|
+
raise SeregaError, "Invalid option :context => #{context.inspect}. Must have a Hash value"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
return if many.nil? || many.equal?(true) || many.equal?(false)
|
|
17
|
+
|
|
18
|
+
raise SeregaError, "Invalid option :many => #{many.inspect}. Must have a boolean value"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -1,34 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Serega
|
|
4
|
-
#
|
|
5
|
-
module SeregaValidations
|
|
4
|
+
module SeregaValidations # :nodoc:
|
|
6
5
|
#
|
|
7
6
|
# Validations that take place when initializing serializer
|
|
8
7
|
#
|
|
9
|
-
#
|
|
10
|
-
module Initiate
|
|
8
|
+
module Initiate # :nodoc:
|
|
11
9
|
#
|
|
12
10
|
# Modifiers validation
|
|
13
11
|
#
|
|
14
|
-
#
|
|
15
|
-
class CheckModifiers
|
|
12
|
+
class CheckModifiers # :nodoc:
|
|
16
13
|
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# @param serializer_class [Serega]
|
|
20
|
-
# @param only [Hash, nil] `only` modifier
|
|
21
|
-
# @param with [Hash, nil] `with` modifier
|
|
22
|
-
# @param except [Hash, nil] `except` modifier
|
|
23
|
-
#
|
|
24
|
-
# @raise [Serega::AttributeNotExist] when some checked modifier has not existing attribute
|
|
25
|
-
#
|
|
26
|
-
# @return [void]
|
|
14
|
+
# Instantiates modifiers validator
|
|
27
15
|
#
|
|
16
|
+
def initialize
|
|
17
|
+
@parents_names = nil
|
|
18
|
+
@error_attributes = nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Validates provided fields names are existing attributes
|
|
28
22
|
def call(serializer_class, only, with, except)
|
|
29
|
-
validate(serializer_class, only)
|
|
30
|
-
validate(serializer_class, with)
|
|
31
|
-
validate(serializer_class, except)
|
|
23
|
+
validate(serializer_class, only)
|
|
24
|
+
validate(serializer_class, with)
|
|
25
|
+
validate(serializer_class, except)
|
|
32
26
|
|
|
33
27
|
raise_errors(serializer_class) if any_error?
|
|
34
28
|
end
|
|
@@ -85,7 +79,7 @@ class Serega
|
|
|
85
79
|
end
|
|
86
80
|
|
|
87
81
|
def any_error?
|
|
88
|
-
|
|
82
|
+
!@error_attributes.nil?
|
|
89
83
|
end
|
|
90
84
|
end
|
|
91
85
|
end
|
|
@@ -1,26 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Serega
|
|
4
|
-
#
|
|
5
|
-
module SeregaValidations
|
|
4
|
+
module SeregaValidations # :nodoc:
|
|
6
5
|
#
|
|
7
6
|
# Validations Utilities
|
|
8
7
|
#
|
|
9
|
-
#
|
|
10
|
-
module Utils
|
|
8
|
+
module Utils # :nodoc:
|
|
11
9
|
#
|
|
12
10
|
# Utility to check all hash keys are allowed
|
|
13
11
|
#
|
|
14
|
-
#
|
|
15
|
-
class CheckAllowedKeys
|
|
12
|
+
class CheckAllowedKeys # :nodoc:
|
|
16
13
|
# Checks hash keys are allowed
|
|
17
|
-
#
|
|
18
|
-
# @param opts [Hash] Some options Hash
|
|
19
|
-
# @param allowed_keys [Array] Allowed hash keys
|
|
20
|
-
#
|
|
21
|
-
# @raise [Serega::SeregaError] error when any hash key is not allowed
|
|
22
|
-
#
|
|
23
|
-
# @return [void]
|
|
24
14
|
def self.call(opts, allowed_keys, parameter_name)
|
|
25
15
|
opts.each_key do |key|
|
|
26
16
|
next if allowed_keys.include?(key)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Serega
|
|
4
|
+
module SeregaValidations # :nodoc:
|
|
5
|
+
module Utils # :nodoc:
|
|
6
|
+
#
|
|
7
|
+
# Validator of a hash access value: the attribute :hash_access option and
|
|
8
|
+
# the delegate :to_hash_access and :hash_access sub-options
|
|
9
|
+
#
|
|
10
|
+
class CheckHashAccessValue # :nodoc:
|
|
11
|
+
class << self
|
|
12
|
+
# Checks that the `key` value is a Boolean, a Symbol mode or a Hash
|
|
13
|
+
# with :mode and :allow_missing_key
|
|
14
|
+
def call(opts, key)
|
|
15
|
+
return unless opts.key?(key)
|
|
16
|
+
|
|
17
|
+
value = opts[key]
|
|
18
|
+
case value
|
|
19
|
+
when true, false then nil
|
|
20
|
+
when Symbol then check_mode(value)
|
|
21
|
+
when Hash
|
|
22
|
+
CheckAllowedKeys.call(value, %i[mode allow_missing_key], key)
|
|
23
|
+
check_mode(value[:mode]) if value.key?(:mode)
|
|
24
|
+
CheckOptIsBool.call(value, :allow_missing_key)
|
|
25
|
+
else
|
|
26
|
+
raise SeregaError,
|
|
27
|
+
"Invalid option #{key.inspect} => #{value.inspect}." \
|
|
28
|
+
" It must be a Boolean, a Symbol mode (:symbol, :string)" \
|
|
29
|
+
" or a Hash with :mode and :allow_missing_key keys"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def check_mode(mode)
|
|
36
|
+
return if AttributeValueResolvers::HashAccessResolver::MODES.include?(mode)
|
|
37
|
+
|
|
38
|
+
raise SeregaError, "Invalid :hash_access mode #{mode.inspect}. Allowed modes: :symbol, :string"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -1,23 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Serega
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
# @private
|
|
7
|
-
module Utils
|
|
4
|
+
module SeregaValidations # :nodoc:
|
|
5
|
+
module Utils # :nodoc:
|
|
8
6
|
#
|
|
9
7
|
# Utility to check hash key value is boolean
|
|
10
8
|
#
|
|
11
|
-
#
|
|
12
|
-
class CheckOptIsBool
|
|
9
|
+
class CheckOptIsBool # :nodoc:
|
|
13
10
|
# Checks hash key has boolean value
|
|
14
|
-
#
|
|
15
|
-
# @param opts [Hash] Some options Hash
|
|
16
|
-
# @param key [Object] Hash key
|
|
17
|
-
#
|
|
18
|
-
# @raise [Serega::SeregaError] error when provided key exists and value is not boolean
|
|
19
|
-
#
|
|
20
|
-
# @return [void]
|
|
21
11
|
def self.call(opts, key)
|
|
22
12
|
return unless opts.key?(key)
|
|
23
13
|
|