serega 0.16.0 → 0.17.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 +22 -10
- data/VERSION +1 -1
- data/lib/serega/attribute.rb +3 -3
- data/lib/serega/attribute_normalizer.rb +21 -2
- data/lib/serega/plugins/batch/lib/batch_config.rb +11 -8
- data/lib/serega/plugins/batch/lib/modules/attribute_normalizer.rb +26 -11
- data/lib/serega/plugins/batch/lib/validations/check_batch_opt_key.rb +5 -35
- data/lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb +5 -35
- data/lib/serega/plugins/formatters/formatters.rb +88 -14
- data/lib/serega/plugins/if/if.rb +43 -19
- data/lib/serega/plugins/if/validations/check_opt_if.rb +4 -36
- data/lib/serega/plugins/if/validations/check_opt_if_value.rb +7 -39
- data/lib/serega/plugins/if/validations/check_opt_unless.rb +4 -45
- data/lib/serega/plugins/if/validations/check_opt_unless_value.rb +7 -39
- data/lib/serega/plugins/metadata/meta_attribute.rb +21 -5
- data/lib/serega/plugins/metadata/metadata.rb +16 -7
- data/lib/serega/plugins/metadata/validations/check_block.rb +10 -10
- data/lib/serega/plugins/metadata/validations/check_opt_const.rb +38 -0
- data/lib/serega/plugins/metadata/validations/check_opt_value.rb +61 -0
- data/lib/serega/plugins/metadata/validations/check_opts.rb +24 -10
- data/lib/serega/utils/params_count.rb +50 -0
- data/lib/serega/utils/to_hash.rb +1 -1
- data/lib/serega/validations/attribute/check_block.rb +4 -5
- data/lib/serega/validations/attribute/check_opt_value.rb +16 -12
- data/lib/serega/validations/check_attribute_params.rb +1 -0
- data/lib/serega/validations/utils/check_extra_keyword_arg.rb +33 -0
- data/lib/serega.rb +2 -0
- metadata +7 -3
@@ -26,54 +26,22 @@ class Serega
|
|
26
26
|
|
27
27
|
private
|
28
28
|
|
29
|
-
def check_type(value)
|
30
|
-
return if value.is_a?(Symbol)
|
31
|
-
|
32
|
-
raise SeregaError, must_be_callable unless value.respond_to?(:call)
|
33
|
-
|
34
|
-
if value.is_a?(Proc)
|
35
|
-
check_block(value)
|
36
|
-
else
|
37
|
-
check_callable(value)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
29
|
def check_usage_with_other_params(opts)
|
42
30
|
raise SeregaError, "Option :if_value can not be used together with option :serializer" if opts.key?(:serializer)
|
43
31
|
end
|
44
32
|
|
45
|
-
def
|
46
|
-
return if
|
47
|
-
|
48
|
-
raise SeregaError, block_parameters_error
|
49
|
-
end
|
50
|
-
|
51
|
-
def check_callable(callable)
|
52
|
-
return if valid_parameters?(callable.method(:call), accepted_count: 2..2)
|
53
|
-
|
54
|
-
raise SeregaError, callable_parameters_error
|
55
|
-
end
|
33
|
+
def check_type(value)
|
34
|
+
return if value.is_a?(Symbol)
|
35
|
+
raise SeregaError, must_be_callable unless value.respond_to?(:call)
|
56
36
|
|
57
|
-
|
58
|
-
|
59
|
-
accepted_count.include?(params.count) && valid_parameters_types?(params)
|
60
|
-
end
|
37
|
+
SeregaValidations::Utils::CheckExtraKeywordArg.call(value, ":if_value option")
|
38
|
+
params_count = SeregaUtils::ParamsCount.call(value, max_count: 2)
|
61
39
|
|
62
|
-
|
63
|
-
|
64
|
-
type = param[0]
|
65
|
-
(type == :req) || (type == :opt)
|
40
|
+
if params_count > 2
|
41
|
+
raise SeregaError, "Option :if_value value should have up to 2 parameters (value, context)"
|
66
42
|
end
|
67
43
|
end
|
68
44
|
|
69
|
-
def block_parameters_error
|
70
|
-
"Invalid attribute option :if_value. When it is a Proc it can have maximum two regular parameters (object, context)"
|
71
|
-
end
|
72
|
-
|
73
|
-
def callable_parameters_error
|
74
|
-
"Invalid attribute option :if_value. When it is a callable object it must have two regular parameters (object, context)"
|
75
|
-
end
|
76
|
-
|
77
45
|
def must_be_callable
|
78
46
|
"Invalid attribute option :if_value. It must be a Symbol, a Proc or respond to :call"
|
79
47
|
end
|
@@ -25,59 +25,18 @@ class Serega
|
|
25
25
|
|
26
26
|
private
|
27
27
|
|
28
|
-
#
|
29
|
-
# Checks attribute :unless option
|
30
|
-
#
|
31
|
-
# @param value [nil, Symbol, Proc, #call] Attribute :unless option
|
32
|
-
#
|
33
|
-
# @raise [SeregaError] validation error
|
34
|
-
#
|
35
|
-
# @return [void]
|
36
|
-
#
|
37
28
|
def check_type(value)
|
38
29
|
return if value.is_a?(Symbol)
|
39
|
-
|
40
30
|
raise SeregaError, must_be_callable unless value.respond_to?(:call)
|
41
31
|
|
42
|
-
|
43
|
-
|
44
|
-
else
|
45
|
-
check_callable(value)
|
46
|
-
end
|
47
|
-
end
|
32
|
+
SeregaValidations::Utils::CheckExtraKeywordArg.call(value, ":unless option")
|
33
|
+
params_count = SeregaUtils::ParamsCount.call(value, max_count: 2)
|
48
34
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
raise SeregaError, block_parameters_error
|
53
|
-
end
|
54
|
-
|
55
|
-
def check_callable(callable)
|
56
|
-
return if valid_parameters?(callable.method(:call), accepted_count: 2..2)
|
57
|
-
|
58
|
-
raise SeregaError, callable_parameters_error
|
59
|
-
end
|
60
|
-
|
61
|
-
def valid_parameters?(data, accepted_count:)
|
62
|
-
params = data.parameters
|
63
|
-
accepted_count.include?(params.count) && valid_parameters_types?(params)
|
64
|
-
end
|
65
|
-
|
66
|
-
def valid_parameters_types?(params)
|
67
|
-
params.all? do |param|
|
68
|
-
type = param[0]
|
69
|
-
(type == :req) || (type == :opt)
|
35
|
+
if params_count > 2
|
36
|
+
raise SeregaError, "Option :unless value should have up to 2 parameters (object, context)"
|
70
37
|
end
|
71
38
|
end
|
72
39
|
|
73
|
-
def block_parameters_error
|
74
|
-
"Invalid attribute option :unless. When it is a Proc it can have maximum two regular parameters (object, context)"
|
75
|
-
end
|
76
|
-
|
77
|
-
def callable_parameters_error
|
78
|
-
"Invalid attribute option :unless. When it is a callable object it must have two regular parameters (object, context)"
|
79
|
-
end
|
80
|
-
|
81
40
|
def must_be_callable
|
82
41
|
"Invalid attribute option :unless. It must be a Symbol, a Proc or respond to :call"
|
83
42
|
end
|
@@ -26,54 +26,22 @@ class Serega
|
|
26
26
|
|
27
27
|
private
|
28
28
|
|
29
|
-
def check_type(value)
|
30
|
-
return if value.is_a?(Symbol)
|
31
|
-
|
32
|
-
raise SeregaError, must_be_callable unless value.respond_to?(:call)
|
33
|
-
|
34
|
-
if value.is_a?(Proc)
|
35
|
-
check_block(value)
|
36
|
-
else
|
37
|
-
check_callable(value)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
29
|
def check_usage_with_other_params(opts)
|
42
30
|
raise SeregaError, "Option :unless_value can not be used together with option :serializer" if opts.key?(:serializer)
|
43
31
|
end
|
44
32
|
|
45
|
-
def
|
46
|
-
return if
|
47
|
-
|
48
|
-
raise SeregaError, block_parameters_error
|
49
|
-
end
|
50
|
-
|
51
|
-
def check_callable(callable)
|
52
|
-
return if valid_parameters?(callable.method(:call), accepted_count: 2..2)
|
53
|
-
|
54
|
-
raise SeregaError, callable_parameters_error
|
55
|
-
end
|
33
|
+
def check_type(value)
|
34
|
+
return if value.is_a?(Symbol)
|
35
|
+
raise SeregaError, must_be_callable unless value.respond_to?(:call)
|
56
36
|
|
57
|
-
|
58
|
-
|
59
|
-
accepted_count.include?(params.count) && valid_parameters_types?(params)
|
60
|
-
end
|
37
|
+
SeregaValidations::Utils::CheckExtraKeywordArg.call(value, ":unless_value option")
|
38
|
+
params_count = SeregaUtils::ParamsCount.call(value, max_count: 2)
|
61
39
|
|
62
|
-
|
63
|
-
|
64
|
-
type = param[0]
|
65
|
-
(type == :req) || (type == :opt)
|
40
|
+
if params_count > 2
|
41
|
+
raise SeregaError, "Option :unless_value value should have up to 2 parameters (value, context)"
|
66
42
|
end
|
67
43
|
end
|
68
44
|
|
69
|
-
def block_parameters_error
|
70
|
-
"Invalid attribute option :unless_value. When it is a Proc it can have maximum two regular parameters (object, context)"
|
71
|
-
end
|
72
|
-
|
73
|
-
def callable_parameters_error
|
74
|
-
"Invalid attribute option :unless_value. When it is a callable object it must have two regular parameters (object, context)"
|
75
|
-
end
|
76
|
-
|
77
45
|
def must_be_callable
|
78
46
|
"Invalid attribute option :unless_value. It must be a Symbol, a Proc or respond to :call"
|
79
47
|
end
|
@@ -20,7 +20,7 @@ class Serega
|
|
20
20
|
# @return [Proc] Meta attribute options
|
21
21
|
attr_reader :opts
|
22
22
|
|
23
|
-
# @return [Proc] Meta attribute originally added block
|
23
|
+
# @return [Proc,nil] Meta attribute originally added block
|
24
24
|
attr_reader :block
|
25
25
|
|
26
26
|
#
|
@@ -39,6 +39,7 @@ class Serega
|
|
39
39
|
@path = SeregaUtils::EnumDeepDup.call(path)
|
40
40
|
@opts = SeregaUtils::EnumDeepDup.call(opts)
|
41
41
|
@block = block
|
42
|
+
@normalized_block = normalize_block(opts[:value], opts[:const], block)
|
42
43
|
end
|
43
44
|
|
44
45
|
#
|
@@ -50,19 +51,34 @@ class Serega
|
|
50
51
|
# @return [Object] Serialized meta attribute value
|
51
52
|
#
|
52
53
|
def value(object, context)
|
53
|
-
|
54
|
+
normalized_block.call(object, context)
|
54
55
|
end
|
55
56
|
|
56
57
|
def hide?(value)
|
57
|
-
(opts[:hide_nil] && value.nil?) || (opts[:hide_empty] && value.empty?)
|
58
|
+
(!!opts[:hide_nil] && value.nil?) || (!!opts[:hide_empty] && (value.nil? || (value.respond_to?(:empty?) && value.empty?)))
|
58
59
|
end
|
59
60
|
|
60
61
|
private
|
61
62
|
|
63
|
+
attr_reader :normalized_block
|
64
|
+
|
65
|
+
def normalize_block(value, const, block)
|
66
|
+
return proc { const } if const
|
67
|
+
|
68
|
+
callable = (value || block)
|
69
|
+
params_count = SeregaUtils::ParamsCount.call(callable, max_count: 2)
|
70
|
+
|
71
|
+
case params_count
|
72
|
+
when 0 then proc { callable.call }
|
73
|
+
when 1 then proc { |obj| callable.call(obj) }
|
74
|
+
else callable
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
62
78
|
def check(path, opts, block)
|
63
79
|
CheckPath.call(path) if check_attribute_name
|
64
|
-
CheckOpts.call(opts, attribute_keys)
|
65
|
-
CheckBlock.call(block)
|
80
|
+
CheckOpts.call(opts, block, attribute_keys)
|
81
|
+
CheckBlock.call(block) if block
|
66
82
|
end
|
67
83
|
|
68
84
|
def attribute_keys
|
@@ -10,17 +10,24 @@ class Serega
|
|
10
10
|
# Adds ability to describe metadata that must be added to serialized response
|
11
11
|
#
|
12
12
|
# Added class-level method `:meta_attribute`, to define metadata, it accepts:
|
13
|
-
#
|
14
|
-
# -
|
15
|
-
# -
|
13
|
+
#
|
14
|
+
# - `*path` [Array of Symbols] - nested hash keys.
|
15
|
+
# - `**options` [Hash]
|
16
|
+
#
|
17
|
+
# - `:const` - describes metadata value (if it is constant)
|
18
|
+
# - `:value` - describes metadata value as any `#callable` instance
|
19
|
+
# - `:hide_nil` - does not show metadata key if value is nil, `false` by default
|
20
|
+
# - `:hide_empty`, does not show metadata key if value is nil or empty, `false` by default
|
21
|
+
#
|
22
|
+
# - `&block` [Proc] - describes value for current meta attribute
|
16
23
|
#
|
17
24
|
# @example
|
18
25
|
# class AppSerializer < Serega
|
19
26
|
# plugin :root
|
20
27
|
# plugin :metadata
|
21
28
|
#
|
22
|
-
# meta_attribute(:version
|
23
|
-
# meta_attribute(:ab_tests, :names
|
29
|
+
# meta_attribute(:version, const: '1.2.3')
|
30
|
+
# meta_attribute(:ab_tests, :names, value: ABTests.new.method(:names))
|
24
31
|
# meta_attribute(:meta, :paging, hide_nil: true) do |records, ctx|
|
25
32
|
# next unless records.respond_to?(:total_count)
|
26
33
|
#
|
@@ -28,7 +35,7 @@ class Serega
|
|
28
35
|
# end
|
29
36
|
# end
|
30
37
|
#
|
31
|
-
# AppSerializer.to_h(nil) # => {:data=>nil, :version=>"1.2.3", :ab_tests=>{:names=>
|
38
|
+
# AppSerializer.to_h(nil) # => {:data=>nil, :version=>"1.2.3", :ab_tests=>{:names=> ... }}
|
32
39
|
#
|
33
40
|
module Metadata
|
34
41
|
# @return [Symbol] Plugin name
|
@@ -64,8 +71,10 @@ class Serega
|
|
64
71
|
|
65
72
|
require_relative "meta_attribute"
|
66
73
|
require_relative "validations/check_block"
|
74
|
+
require_relative "validations/check_opt_const"
|
67
75
|
require_relative "validations/check_opt_hide_nil"
|
68
76
|
require_relative "validations/check_opt_hide_empty"
|
77
|
+
require_relative "validations/check_opt_value"
|
69
78
|
require_relative "validations/check_opts"
|
70
79
|
require_relative "validations/check_path"
|
71
80
|
|
@@ -83,7 +92,7 @@ class Serega
|
|
83
92
|
# @return [void]
|
84
93
|
#
|
85
94
|
def self.after_load_plugin(serializer_class, **_opts)
|
86
|
-
serializer_class.config.opts[:metadata] = {attribute_keys: %i[
|
95
|
+
serializer_class.config.opts[:metadata] = {attribute_keys: %i[const hide_nil hide_empty value]}
|
87
96
|
end
|
88
97
|
|
89
98
|
#
|
@@ -8,14 +8,10 @@ class Serega
|
|
8
8
|
# Validator for meta_attribute block parameter
|
9
9
|
#
|
10
10
|
class CheckBlock
|
11
|
-
ALLOWED_PARAM_TYPES = %i[opt req]
|
12
|
-
private_constant :ALLOWED_PARAM_TYPES
|
13
|
-
|
14
11
|
class << self
|
15
12
|
#
|
16
13
|
# Checks block provided with attribute
|
17
|
-
# Block must have up to two arguments - object and context.
|
18
|
-
# It should not have any *rest or **key arguments
|
14
|
+
# Block must have up to two arguments - object(s) and context.
|
19
15
|
#
|
20
16
|
# @example without arguments
|
21
17
|
# metadata(:version) { CONSTANT_VERSION }
|
@@ -24,7 +20,7 @@ class Serega
|
|
24
20
|
# metadata(:paging) { |scope| { { page: scope.page, per_page: scope.per_page, total_count: scope.total_count } }
|
25
21
|
#
|
26
22
|
# @example with two arguments
|
27
|
-
# metadata(:paging) { |scope, context| { { ... } if context[:
|
23
|
+
# metadata(:paging) { |scope, context| { { ... } if context[:pagy] }
|
28
24
|
#
|
29
25
|
# @param block [Proc] Block that returns serialized meta attribute value
|
30
26
|
#
|
@@ -33,12 +29,16 @@ class Serega
|
|
33
29
|
# @return [void]
|
34
30
|
#
|
35
31
|
def call(block)
|
36
|
-
|
32
|
+
SeregaValidations::Utils::CheckExtraKeywordArg.call(block, "block")
|
33
|
+
params_count = SeregaUtils::ParamsCount.call(block, max_count: 2)
|
34
|
+
|
35
|
+
raise SeregaError, block_error if params_count > 2
|
36
|
+
end
|
37
37
|
|
38
|
-
|
39
|
-
return if (params.count <= 2) && params.all? { |par| ALLOWED_PARAM_TYPES.include?(par[0]) }
|
38
|
+
private
|
40
39
|
|
41
|
-
|
40
|
+
def block_error
|
41
|
+
"Block can have maximum two parameters (object(s), context)"
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module Metadata
|
6
|
+
class MetaAttribute
|
7
|
+
#
|
8
|
+
# MetaAttribute `:const` option validator
|
9
|
+
#
|
10
|
+
class CheckOptConst
|
11
|
+
class << self
|
12
|
+
#
|
13
|
+
# Checks meta_attribute :const option
|
14
|
+
#
|
15
|
+
# @param opts [Hash] MetaAttribute options
|
16
|
+
#
|
17
|
+
# @raise [SeregaError] validation error
|
18
|
+
#
|
19
|
+
# @return [void]
|
20
|
+
#
|
21
|
+
def call(opts, block = nil)
|
22
|
+
return unless opts.key?(:const)
|
23
|
+
|
24
|
+
check_usage_with_other_params(opts, block)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def check_usage_with_other_params(opts, block)
|
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 block" if block
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module Metadata
|
6
|
+
class MetaAttribute
|
7
|
+
#
|
8
|
+
# Validator for meta_attribute :value option
|
9
|
+
#
|
10
|
+
class CheckOptValue
|
11
|
+
class << self
|
12
|
+
#
|
13
|
+
# Checks attribute :value option
|
14
|
+
#
|
15
|
+
# @param opts [Hash] Attribute options
|
16
|
+
#
|
17
|
+
# @raise [SeregaError] SeregaError that option has invalid value
|
18
|
+
#
|
19
|
+
# @return [void]
|
20
|
+
#
|
21
|
+
def call(opts, block = nil)
|
22
|
+
return unless opts.key?(:value)
|
23
|
+
|
24
|
+
check_usage_with_other_params(opts, block)
|
25
|
+
|
26
|
+
check_value(opts[:value])
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def check_usage_with_other_params(opts, block)
|
32
|
+
raise SeregaError, "Option :value can not be used together with option :const" if opts.key?(:const)
|
33
|
+
raise SeregaError, "Option :value can not be used together with block" if block
|
34
|
+
end
|
35
|
+
|
36
|
+
def check_value(value)
|
37
|
+
check_value_type(value)
|
38
|
+
|
39
|
+
SeregaValidations::Utils::CheckExtraKeywordArg.call(value, ":value option")
|
40
|
+
params_count = SeregaUtils::ParamsCount.call(value, max_count: 2)
|
41
|
+
|
42
|
+
raise SeregaError, params_count_error if params_count > 2
|
43
|
+
end
|
44
|
+
|
45
|
+
def check_value_type(value)
|
46
|
+
raise SeregaError, type_error if !value.is_a?(Proc) && !value.respond_to?(:call)
|
47
|
+
end
|
48
|
+
|
49
|
+
def type_error
|
50
|
+
"Option :value value must be a Proc or respond to #call"
|
51
|
+
end
|
52
|
+
|
53
|
+
def params_count_error
|
54
|
+
"Option :value value can have maximum 2 parameters (object(s), context)"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -10,31 +10,45 @@ class Serega
|
|
10
10
|
class CheckOpts
|
11
11
|
class << self
|
12
12
|
#
|
13
|
-
# Validates
|
13
|
+
# Validates meta_attribute options
|
14
14
|
# Checks used options are allowed and then checks options values.
|
15
15
|
#
|
16
16
|
# @param opts [Hash] Attribute options
|
17
|
-
# @param
|
17
|
+
# @param block [Proc] Attribute block
|
18
|
+
# @param allowed_keys [Array<Symbol>] Allowed options keys
|
18
19
|
#
|
19
20
|
# @raise [SeregaError] when attribute has invalid options
|
20
21
|
#
|
21
22
|
# @return [void]
|
22
23
|
#
|
23
|
-
def call(opts,
|
24
|
+
def call(opts, block, allowed_keys)
|
25
|
+
check_allowed_options_keys(opts, allowed_keys)
|
26
|
+
check_each_opt(opts, block)
|
27
|
+
check_any_value_provided(opts, block)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def check_allowed_options_keys(opts, allowed_keys)
|
24
33
|
opts.each_key do |key|
|
25
|
-
next if
|
34
|
+
next if allowed_keys.include?(key.to_sym)
|
26
35
|
|
27
|
-
|
36
|
+
allowed = allowed_keys.map(&:inspect).join(", ")
|
37
|
+
raise SeregaError, "Invalid option #{key.inspect}. Allowed options are: #{allowed}"
|
28
38
|
end
|
29
|
-
|
30
|
-
check_each_opt(opts)
|
31
39
|
end
|
32
40
|
|
33
|
-
|
34
|
-
|
35
|
-
def check_each_opt(opts)
|
41
|
+
def check_each_opt(opts, block)
|
36
42
|
CheckOptHideEmpty.call(opts)
|
37
43
|
CheckOptHideNil.call(opts)
|
44
|
+
CheckOptValue.call(opts, block)
|
45
|
+
CheckOptConst.call(opts, block)
|
46
|
+
end
|
47
|
+
|
48
|
+
def check_any_value_provided(opts, block)
|
49
|
+
return if opts.key?(:const) || opts.key?(:value) || block
|
50
|
+
|
51
|
+
raise SeregaError, "Please provide block argument or add :value or :const option"
|
38
52
|
end
|
39
53
|
end
|
40
54
|
end
|
@@ -0,0 +1,50 @@
|
|
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
|
data/lib/serega/utils/to_hash.rb
CHANGED
@@ -31,7 +31,7 @@ class Serega
|
|
31
31
|
when NilClass, FalseClass then nil_to_hash(value)
|
32
32
|
when String then string_to_hash(value)
|
33
33
|
when Symbol then symbol_to_hash(value)
|
34
|
-
else raise SeregaError, "
|
34
|
+
else raise SeregaError, "Can't convert #{value.class} class object to hash"
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -14,7 +14,6 @@ class Serega
|
|
14
14
|
#
|
15
15
|
# Checks block parameter provided with attribute.
|
16
16
|
# Must have up to two arguments - object and context.
|
17
|
-
# It should not have any *rest or **key arguments
|
18
17
|
#
|
19
18
|
# @example without arguments
|
20
19
|
# attribute(:email) { CONSTANT_EMAIL }
|
@@ -40,14 +39,14 @@ class Serega
|
|
40
39
|
private
|
41
40
|
|
42
41
|
def check_block(block)
|
43
|
-
|
44
|
-
|
42
|
+
SeregaValidations::Utils::CheckExtraKeywordArg.call(block, "block")
|
43
|
+
params_count = SeregaUtils::ParamsCount.call(block, max_count: 2)
|
45
44
|
|
46
|
-
raise SeregaError, block_error
|
45
|
+
raise SeregaError, block_error if params_count > 2
|
47
46
|
end
|
48
47
|
|
49
48
|
def block_error
|
50
|
-
"Block can have maximum two
|
49
|
+
"Block can have maximum two parameters (object, context)"
|
51
50
|
end
|
52
51
|
end
|
53
52
|
end
|
@@ -21,7 +21,8 @@ class Serega
|
|
21
21
|
return unless opts.key?(:value)
|
22
22
|
|
23
23
|
check_usage_with_other_params(opts, block)
|
24
|
-
|
24
|
+
|
25
|
+
check_value(opts[:value])
|
25
26
|
end
|
26
27
|
|
27
28
|
private
|
@@ -32,22 +33,25 @@ class Serega
|
|
32
33
|
raise SeregaError, "Option :value can not be used together with block" if block
|
33
34
|
end
|
34
35
|
|
35
|
-
def
|
36
|
-
|
36
|
+
def check_value(value)
|
37
|
+
check_value_type(value)
|
38
|
+
|
39
|
+
SeregaValidations::Utils::CheckExtraKeywordArg.call(value, ":value option")
|
40
|
+
params_count = SeregaUtils::ParamsCount.call(value, max_count: 2)
|
37
41
|
|
38
|
-
|
42
|
+
raise SeregaError, params_count_error if params_count > 2
|
43
|
+
end
|
39
44
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
return
|
44
|
-
end
|
45
|
+
def check_value_type(value)
|
46
|
+
raise SeregaError, type_error if !value.is_a?(Proc) && !value.respond_to?(:call)
|
47
|
+
end
|
45
48
|
|
46
|
-
|
49
|
+
def type_error
|
50
|
+
"Option :value value must be a Proc or respond to #call"
|
47
51
|
end
|
48
52
|
|
49
|
-
def
|
50
|
-
"Option :value
|
53
|
+
def params_count_error
|
54
|
+
"Option :value value can have maximum 2 parameters (object, context)"
|
51
55
|
end
|
52
56
|
end
|
53
57
|
end
|
@@ -55,6 +55,7 @@ class Serega
|
|
55
55
|
# Patched in:
|
56
56
|
# - plugin :batch (checks :batch option)
|
57
57
|
# - plugin :context_metadata (checks context metadata option which is :meta by default)
|
58
|
+
# - plugin :formatters (checks :format option)
|
58
59
|
# - plugin :if (checks :if, :if_value, :unless, :unless_value options)
|
59
60
|
# - plugin :preloads (checks :preload option)
|
60
61
|
def check_opts
|