serega 0.16.0 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47eacc613b0a58af4dc552e09e0f55ed731f7e2a852e5c668794e7eb5128be69
|
4
|
+
data.tar.gz: bb9fec8432d12108e374bf15da74a230a4efda994835e9480e795b7222bb9ced
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d7536453cd03fd6e0f65faf22f85f5a12de61ef2bda50d674cd5b31ab1fba815c4b94d5118d82902f5a434d06112cfdc16757eefd334ba217946f9e920442e05
|
7
|
+
data.tar.gz: 9bcd5c46c1ffa32f95bc02e821e94acd46408e4e65a0861e4738454e24a57767105a5d3a9af48d52c4662a1030819963f11ee8c317c84ed179eb44b282a02c73
|
data/README.md
CHANGED
@@ -73,7 +73,8 @@ class UserSerializer < Serega
|
|
73
73
|
# Block is used to define attribute value
|
74
74
|
attribute(:first_name) { |user| user.profile&.first_name }
|
75
75
|
|
76
|
-
# Option :value can be used with callable object to define attribute value
|
76
|
+
# Option :value can be used with proc or callable object to define attribute value
|
77
|
+
attribute :first_name, value: UserProfile.new # must have #call method
|
77
78
|
attribute :first_name, value: proc { |user| user.profile&.first_name }
|
78
79
|
|
79
80
|
# Option :delegate can be used to define attribute value.
|
@@ -555,7 +556,7 @@ attribute :name, batch: { loader: :name_loader, key: :id, default: nil }
|
|
555
556
|
`:batch` option must be a hash with this keys:
|
556
557
|
|
557
558
|
- `loader` (required) [Symbol, Proc, callable] - Defines how to fetch values for
|
558
|
-
batch of keys. Receives 3 parameters: keys, context,
|
559
|
+
batch of keys. Receives 3 parameters: keys, context, plan.
|
559
560
|
- `key` (required) [Symbol, Proc, callable] - Defines current object identifier.
|
560
561
|
Key is optional if plugin was defined with `default_key` option.
|
561
562
|
- `default` (optional) - Default value for attribute.
|
@@ -715,17 +716,24 @@ Adds ability to describe metadata and adds it to serialized response
|
|
715
716
|
|
716
717
|
Added class-level method `:meta_attribute`, to define metadata, it accepts:
|
717
718
|
|
718
|
-
-
|
719
|
-
-
|
720
|
-
|
719
|
+
- `*path` [Array of Symbols] - nested hash keys.
|
720
|
+
- `**options` [Hash]
|
721
|
+
|
722
|
+
- `:const` - describes metadata value (if it is constant)
|
723
|
+
- `:value` - describes metadata value as any `#callable` instance
|
724
|
+
- `:hide_nil` - does not show metadata key if value is nil, `false` by default
|
725
|
+
- `:hide_empty`, does not show metadata key if value is nil or empty,
|
726
|
+
`false` by default
|
727
|
+
|
728
|
+
- `&block` [Proc] - describes value for current meta attribute
|
721
729
|
|
722
730
|
```ruby
|
723
731
|
class AppSerializer < Serega
|
724
732
|
plugin :root
|
725
733
|
plugin :metadata
|
726
734
|
|
727
|
-
meta_attribute(:version
|
728
|
-
meta_attribute(:ab_tests, :names
|
735
|
+
meta_attribute(:version, const: '1.2.3')
|
736
|
+
meta_attribute(:ab_tests, :names, value: ABTests.new.method(:names))
|
729
737
|
meta_attribute(:meta, :paging, hide_nil: true) do |records, ctx|
|
730
738
|
next unless records.respond_to?(:total_count)
|
731
739
|
|
@@ -738,7 +746,7 @@ class AppSerializer < Serega
|
|
738
746
|
end
|
739
747
|
|
740
748
|
AppSerializer.to_h(nil)
|
741
|
-
# => {:data=>nil, :version=>"1.2.3", :ab_tests=>{:names=>
|
749
|
+
# => {:data=>nil, :version=>"1.2.3", :ab_tests=>{:names=> ... }}
|
742
750
|
```
|
743
751
|
|
744
752
|
### Plugin :context_metadata
|
@@ -773,15 +781,18 @@ Allows to define `formatters` and apply them on attribute values.
|
|
773
781
|
|
774
782
|
Config option `config.formatters.add` can be used to add formatters.
|
775
783
|
|
776
|
-
Attribute option `:format`
|
784
|
+
Attribute option `:format` can be used with name of formatter or with
|
777
785
|
callable instance.
|
778
786
|
|
787
|
+
Formatters can accept up to 2 parameters (formatted object, context)
|
788
|
+
|
779
789
|
```ruby
|
780
790
|
class AppSerializer < Serega
|
781
791
|
plugin :formatters, formatters: {
|
782
792
|
iso8601: ->(value) { time.iso8601.round(6) },
|
783
793
|
on_off: ->(value) { value ? 'ON' : 'OFF' },
|
784
|
-
money: ->(value) { value
|
794
|
+
money: ->(value, ctx) { value / 10**ctx[:digits) }
|
795
|
+
date: DateTypeFormatter # callable
|
785
796
|
}
|
786
797
|
end
|
787
798
|
|
@@ -1029,6 +1040,7 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
1029
1040
|
[batch]: #plugin-batch
|
1030
1041
|
[camel_case]: #plugin-camel_case
|
1031
1042
|
[context_metadata]: #plugin-context_metadata
|
1043
|
+
[depth_limit]: #plugin-depth_limit
|
1032
1044
|
[formatters]: #plugin-formatters
|
1033
1045
|
[metadata]: #plugin-metadata
|
1034
1046
|
[preloads]: #plugin-preloads
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.17.0
|
data/lib/serega/attribute.rb
CHANGED
@@ -61,10 +61,10 @@ class Serega
|
|
61
61
|
# Shows specified serializer class
|
62
62
|
# @return [Serega, nil] Attribute serializer if exists
|
63
63
|
def serializer
|
64
|
-
|
65
|
-
return
|
64
|
+
serializer = @serializer
|
65
|
+
return serializer if (serializer.is_a?(Class) && (serializer < Serega)) || !serializer
|
66
66
|
|
67
|
-
@serializer =
|
67
|
+
@serializer = serializer.is_a?(String) ? Object.const_get(serializer, false) : serializer.call
|
68
68
|
end
|
69
69
|
|
70
70
|
#
|
@@ -97,8 +97,8 @@ class Serega
|
|
97
97
|
# - plugin :formatters (wraps resulted block in formatter block and formats :const values)
|
98
98
|
#
|
99
99
|
def prepare_value_block
|
100
|
-
|
101
|
-
|
100
|
+
prepare_init_block ||
|
101
|
+
prepare_value_option_block ||
|
102
102
|
prepare_const_block ||
|
103
103
|
prepare_delegate_block ||
|
104
104
|
prepare_keyword_block
|
@@ -139,6 +139,25 @@ class Serega
|
|
139
139
|
end
|
140
140
|
end
|
141
141
|
|
142
|
+
def prepare_init_block
|
143
|
+
prepare_callable_proc(init_block)
|
144
|
+
end
|
145
|
+
|
146
|
+
def prepare_value_option_block
|
147
|
+
prepare_callable_proc(init_opts[:value])
|
148
|
+
end
|
149
|
+
|
150
|
+
def prepare_callable_proc(callable)
|
151
|
+
return unless callable
|
152
|
+
|
153
|
+
params_count = SeregaUtils::ParamsCount.call(callable, max_count: 2)
|
154
|
+
case params_count
|
155
|
+
when 0 then proc { |obj, _ctx| callable.call }
|
156
|
+
when 1 then proc { |obj, _ctx| callable.call(obj) }
|
157
|
+
else callable
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
142
161
|
def prepare_delegate_block
|
143
162
|
delegate = init_opts[:delegate]
|
144
163
|
return unless delegate
|
@@ -22,17 +22,20 @@ class Serega
|
|
22
22
|
#
|
23
23
|
# @return [void]
|
24
24
|
#
|
25
|
-
def define(loader_name, &block)
|
26
|
-
|
27
|
-
raise SeregaError, "
|
25
|
+
def define(loader_name, callable = nil, &block)
|
26
|
+
if (!callable && !block) || (callable && block)
|
27
|
+
raise SeregaError, "Batch loader can be specified with one of arguments - callable value or &block"
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
callable ||= block
|
31
|
+
SeregaValidations::Utils::CheckExtraKeywordArg.call(callable, "batch loader `#{loader_name}`")
|
32
|
+
params_count = SeregaUtils::ParamsCount.call(callable, max_count: 3)
|
33
|
+
|
34
|
+
if params_count > 3
|
35
|
+
raise SeregaError, "Batch loader can have maximum 3 parameters (keys, context, plan)"
|
33
36
|
end
|
34
37
|
|
35
|
-
loaders[loader_name] =
|
38
|
+
loaders[loader_name] = callable
|
36
39
|
end
|
37
40
|
|
38
41
|
# Shows defined loaders
|
@@ -48,7 +51,7 @@ class Serega
|
|
48
51
|
#
|
49
52
|
# @return [Proc] batch loader block
|
50
53
|
def fetch_loader(loader_name)
|
51
|
-
loaders[loader_name] || (raise SeregaError, "Batch loader with name `#{loader_name.inspect}` was not defined. Define example: config.batch.define(:#{loader_name}) { |keys
|
54
|
+
loaders[loader_name] || (raise SeregaError, "Batch loader with name `#{loader_name.inspect}` was not defined. Define example: config.batch.define(:#{loader_name}) { |keys| ... }")
|
52
55
|
end
|
53
56
|
|
54
57
|
# Shows option to auto hide attributes with :batch specified
|
@@ -42,22 +42,37 @@ class Serega
|
|
42
42
|
batch = init_opts[:batch]
|
43
43
|
return unless batch
|
44
44
|
|
45
|
-
|
46
|
-
loader = batch[:loader]
|
45
|
+
loader = prepare_batch_loader(batch[:loader])
|
47
46
|
|
48
|
-
# take key
|
49
47
|
key = batch[:key] || self.class.serializer_class.config.batch.default_key
|
50
|
-
|
51
|
-
if key.is_a?(Symbol)
|
52
|
-
proc { |object| object.public_send(key) }
|
53
|
-
else
|
54
|
-
key
|
55
|
-
end
|
48
|
+
key = prepare_batch_key(key)
|
56
49
|
|
57
|
-
# take default value
|
58
50
|
default = batch.fetch(:default) { many ? FROZEN_EMPTY_ARRAY : nil }
|
59
51
|
|
60
|
-
{loader: loader, key:
|
52
|
+
{loader: loader, key: key, default: default}
|
53
|
+
end
|
54
|
+
|
55
|
+
def prepare_batch_key(key)
|
56
|
+
return proc { |object| object.public_send(key) } if key.is_a?(Symbol)
|
57
|
+
|
58
|
+
params_count = SeregaUtils::ParamsCount.call(key, max_count: 2)
|
59
|
+
case params_count
|
60
|
+
when 0 then proc { key.call }
|
61
|
+
when 1 then proc { |object| key.call(object) }
|
62
|
+
else key
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def prepare_batch_loader(loader)
|
67
|
+
return loader if loader.is_a?(Symbol)
|
68
|
+
|
69
|
+
params_count = SeregaUtils::ParamsCount.call(loader, max_count: 3)
|
70
|
+
case params_count
|
71
|
+
when 0 then proc { loader.call }
|
72
|
+
when 1 then proc { |object| loader.call(object) }
|
73
|
+
when 2 then proc { |object, context| loader.call(object, context) }
|
74
|
+
else loader
|
75
|
+
end
|
61
76
|
end
|
62
77
|
end
|
63
78
|
end
|
@@ -22,45 +22,15 @@ class Serega
|
|
22
22
|
|
23
23
|
raise SeregaError, must_be_callable unless key.respond_to?(:call)
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
check_callable(key)
|
29
|
-
end
|
25
|
+
SeregaValidations::Utils::CheckExtraKeywordArg.call(key, "batch option :key")
|
26
|
+
params_count = SeregaUtils::ParamsCount.call(key, max_count: 2)
|
27
|
+
raise SeregaError, params_count_error if params_count > 2
|
30
28
|
end
|
31
29
|
|
32
30
|
private
|
33
31
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
raise SeregaError, block_parameters_error
|
38
|
-
end
|
39
|
-
|
40
|
-
def check_callable(callable)
|
41
|
-
return if valid_parameters?(callable.method(:call), accepted_count: 2..2)
|
42
|
-
|
43
|
-
raise SeregaError, callable_parameters_error
|
44
|
-
end
|
45
|
-
|
46
|
-
def valid_parameters?(data, accepted_count:)
|
47
|
-
params = data.parameters
|
48
|
-
accepted_count.include?(params.count) && valid_parameters_types?(params)
|
49
|
-
end
|
50
|
-
|
51
|
-
def valid_parameters_types?(params)
|
52
|
-
params.all? do |param|
|
53
|
-
type = param[0]
|
54
|
-
(type == :req) || (type == :opt)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def block_parameters_error
|
59
|
-
"Invalid :batch option :key. When it is a Proc it can have maximum two regular parameters (object, context)"
|
60
|
-
end
|
61
|
-
|
62
|
-
def callable_parameters_error
|
63
|
-
"Invalid :batch option :key. When it is a callable object it must have two regular parameters (object, context)"
|
32
|
+
def params_count_error
|
33
|
+
"Invalid :batch option :key. It can accept maximum 2 parameters (object, context)"
|
64
34
|
end
|
65
35
|
|
66
36
|
def must_be_callable
|
@@ -22,45 +22,15 @@ class Serega
|
|
22
22
|
|
23
23
|
raise SeregaError, must_be_callable unless loader.respond_to?(:call)
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
check_callable(loader)
|
29
|
-
end
|
25
|
+
SeregaValidations::Utils::CheckExtraKeywordArg.call(loader, ":batch option :loader")
|
26
|
+
params_count = SeregaUtils::ParamsCount.call(loader, max_count: 3)
|
27
|
+
raise SeregaError, params_count_error if params_count > 3
|
30
28
|
end
|
31
29
|
|
32
30
|
private
|
33
31
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
raise SeregaError, block_parameters_error
|
38
|
-
end
|
39
|
-
|
40
|
-
def check_callable(callable)
|
41
|
-
return if valid_parameters?(callable.method(:call), accepted_count: 3..3)
|
42
|
-
|
43
|
-
raise SeregaError, callable_parameters_error
|
44
|
-
end
|
45
|
-
|
46
|
-
def valid_parameters?(data, accepted_count:)
|
47
|
-
params = data.parameters
|
48
|
-
accepted_count.include?(params.count) && valid_parameters_types?(params)
|
49
|
-
end
|
50
|
-
|
51
|
-
def valid_parameters_types?(params)
|
52
|
-
params.all? do |param|
|
53
|
-
type = param[0]
|
54
|
-
(type == :req) || (type == :opt)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def block_parameters_error
|
59
|
-
"Invalid :batch option :loader. When it is a Proc it can have maximum three regular parameters (keys, context, point)"
|
60
|
-
end
|
61
|
-
|
62
|
-
def callable_parameters_error
|
63
|
-
"Invalid :batch option :loader. When it is a callable object it must have three regular parameters (keys, context, point)"
|
32
|
+
def params_count_error
|
33
|
+
"Invalid :batch option :loader. It can accept maximum 3 parameters (keys, context, plan)"
|
64
34
|
end
|
65
35
|
|
66
36
|
def must_be_callable
|
@@ -11,12 +11,15 @@ class Serega
|
|
11
11
|
#
|
12
12
|
# Attribute option `:format` now can be used with name of formatter or with callable instance.
|
13
13
|
#
|
14
|
+
# Formatters can accept up to 2 parameters (formatted object, context)
|
15
|
+
#
|
14
16
|
# @example
|
15
17
|
# class AppSerializer < Serega
|
16
18
|
# plugin :formatters, formatters: {
|
17
19
|
# iso8601: ->(value) { time.iso8601.round(6) },
|
18
20
|
# on_off: ->(value) { value ? 'ON' : 'OFF' },
|
19
21
|
# money: ->(value) { value.round(2) }
|
22
|
+
# date: DateTypeFormatter # callable
|
20
23
|
# }
|
21
24
|
# end
|
22
25
|
#
|
@@ -35,6 +38,7 @@ class Serega
|
|
35
38
|
# attribute :updated_at, format: :iso8601
|
36
39
|
#
|
37
40
|
# # Using `callable` formatter
|
41
|
+
# attribute :score_percent, format: PercentFormmatter # callable class
|
38
42
|
# attribute :score_percent, format: proc { |percent| "#{percent.round(2)}%" }
|
39
43
|
# end
|
40
44
|
#
|
@@ -68,6 +72,7 @@ class Serega
|
|
68
72
|
def self.load_plugin(serializer_class, **_opts)
|
69
73
|
serializer_class::SeregaConfig.include(ConfigInstanceMethods)
|
70
74
|
serializer_class::SeregaAttributeNormalizer.include(AttributeNormalizerInstanceMethods)
|
75
|
+
serializer_class::CheckAttributeParams.include(CheckAttributeParamsInstanceMethods)
|
71
76
|
end
|
72
77
|
|
73
78
|
#
|
@@ -107,6 +112,7 @@ class Serega
|
|
107
112
|
# @return [void]
|
108
113
|
def add(formatters)
|
109
114
|
formatters.each_pair do |key, value|
|
115
|
+
CheckFormatter.call(key, value)
|
110
116
|
opts[key] = value
|
111
117
|
end
|
112
118
|
end
|
@@ -124,6 +130,21 @@ class Serega
|
|
124
130
|
end
|
125
131
|
end
|
126
132
|
|
133
|
+
#
|
134
|
+
# Serega::SeregaValidations::CheckAttributeParams additional/patched class methods
|
135
|
+
#
|
136
|
+
# @see Serega::SeregaValidations::CheckAttributeParams
|
137
|
+
#
|
138
|
+
module CheckAttributeParamsInstanceMethods
|
139
|
+
private
|
140
|
+
|
141
|
+
def check_opts
|
142
|
+
super
|
143
|
+
|
144
|
+
CheckOptFormat.call(opts, self.class.serializer_class)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
127
148
|
#
|
128
149
|
# Attribute class additional/patched instance methods
|
129
150
|
#
|
@@ -143,16 +164,10 @@ class Serega
|
|
143
164
|
def prepare_value_block
|
144
165
|
return super unless formatter
|
145
166
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
else
|
151
|
-
# Wrap original block into formatter block
|
152
|
-
proc do |object, context|
|
153
|
-
value = super.call(object, context)
|
154
|
-
formatter.call(value)
|
155
|
-
end
|
167
|
+
# Wrap original block into formatter block
|
168
|
+
proc do |object, context|
|
169
|
+
value = super.call(object, context)
|
170
|
+
formatter.call(value, context)
|
156
171
|
end
|
157
172
|
end
|
158
173
|
|
@@ -160,10 +175,69 @@ class Serega
|
|
160
175
|
formatter = init_opts[:format]
|
161
176
|
return unless formatter
|
162
177
|
|
163
|
-
if formatter.is_a?(Symbol)
|
164
|
-
|
165
|
-
|
166
|
-
|
178
|
+
formatter = self.class.serializer_class.config.formatters.opts.fetch(formatter) if formatter.is_a?(Symbol)
|
179
|
+
prepare_callable_proc(formatter)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
#
|
184
|
+
# Validator for attribute :format option
|
185
|
+
#
|
186
|
+
class CheckOptFormat
|
187
|
+
class << self
|
188
|
+
#
|
189
|
+
# Checks attribute :format option must be registered or valid callable with maximum 2 args
|
190
|
+
#
|
191
|
+
# @param opts [value] Attribute options
|
192
|
+
#
|
193
|
+
# @raise [SeregaError] Attribute validation error
|
194
|
+
#
|
195
|
+
# @return [void]
|
196
|
+
#
|
197
|
+
def call(opts, serializer_class)
|
198
|
+
return unless opts.key?(:format)
|
199
|
+
|
200
|
+
formatter = opts[:format]
|
201
|
+
|
202
|
+
if formatter.is_a?(Symbol)
|
203
|
+
check_formatter_defined(serializer_class, formatter)
|
204
|
+
else
|
205
|
+
CheckFormatter.call(:format, formatter)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
private
|
210
|
+
|
211
|
+
def check_formatter_defined(serializer_class, formatter)
|
212
|
+
return if serializer_class.config.formatters.opts.key?(formatter)
|
213
|
+
|
214
|
+
raise Serega::SeregaError, "Formatter `#{formatter.inspect}` was not defined"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
#
|
220
|
+
# Validator for formatters defined as config options or directly as attribute :format option
|
221
|
+
#
|
222
|
+
class CheckFormatter
|
223
|
+
class << self
|
224
|
+
#
|
225
|
+
# Check formatter type and parameters
|
226
|
+
#
|
227
|
+
# @param formatter_name [Symbol] Name of formatter
|
228
|
+
# @param formatter [#call] Formatter callable object
|
229
|
+
#
|
230
|
+
# @return [void]
|
231
|
+
#
|
232
|
+
def call(formatter_name, formatter)
|
233
|
+
raise Serega::SeregaError, "Option #{formatter_name.inspect} must have callable value" unless formatter.respond_to?(:call)
|
234
|
+
|
235
|
+
SeregaValidations::Utils::CheckExtraKeywordArg.call(formatter, "#{formatter_name.inspect} value")
|
236
|
+
params_count = SeregaUtils::ParamsCount.call(formatter, max_count: 2)
|
237
|
+
|
238
|
+
if params_count > 2
|
239
|
+
raise SeregaError, "Formatter can have maximum 2 parameters (value to format, context)"
|
240
|
+
end
|
167
241
|
end
|
168
242
|
end
|
169
243
|
end
|
data/lib/serega/plugins/if/if.rb
CHANGED
@@ -61,10 +61,11 @@ class Serega
|
|
61
61
|
require_relative "validations/check_opt_unless"
|
62
62
|
require_relative "validations/check_opt_unless_value"
|
63
63
|
|
64
|
-
serializer_class::SeregaAttribute.include(
|
64
|
+
serializer_class::SeregaAttribute.include(AttributeInstanceMethods)
|
65
|
+
serializer_class::SeregaAttributeNormalizer.include(AttributeNormalizerInstanceMethods)
|
65
66
|
serializer_class::SeregaPlanPoint.include(PlanPointInstanceMethods)
|
66
67
|
serializer_class::CheckAttributeParams.include(CheckAttributeParamsInstanceMethods)
|
67
|
-
serializer_class::SeregaObjectSerializer.include(
|
68
|
+
serializer_class::SeregaObjectSerializer.include(ObjectSerializerInstanceMethods)
|
68
69
|
end
|
69
70
|
|
70
71
|
#
|
@@ -79,12 +80,47 @@ class Serega
|
|
79
80
|
serializer_class.config.attribute_keys << :if << :if_value << :unless << :unless_value
|
80
81
|
end
|
81
82
|
|
83
|
+
#
|
84
|
+
# SeregaAttributeNormalizer additional/patched instance methods
|
85
|
+
#
|
86
|
+
# @see SeregaAttributeNormalizer::AttributeInstanceMethods
|
87
|
+
#
|
88
|
+
module AttributeNormalizerInstanceMethods
|
89
|
+
#
|
90
|
+
# Returns prepared attribute :if_options.
|
91
|
+
#
|
92
|
+
# @return [Hash] prepared options for :if plugin
|
93
|
+
#
|
94
|
+
def if_options
|
95
|
+
@if_options ||= {
|
96
|
+
if: prepare_if_option(init_opts[:if]),
|
97
|
+
unless: prepare_if_option(init_opts[:unless]),
|
98
|
+
if_value: prepare_if_option(init_opts[:if_value]),
|
99
|
+
unless_value: prepare_if_option(init_opts[:unless_value])
|
100
|
+
}.freeze
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def prepare_if_option(if_option)
|
106
|
+
return unless if_option
|
107
|
+
return proc { |val| val.public_send(if_option) } if if_option.is_a?(Symbol)
|
108
|
+
|
109
|
+
params_count = SeregaUtils::ParamsCount.call(if_option, max_count: 2)
|
110
|
+
case params_count
|
111
|
+
when 0 then proc { if_option.call }
|
112
|
+
when 1 then proc { |obj| if_option.call(obj) }
|
113
|
+
else if_option
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
82
118
|
#
|
83
119
|
# SeregaAttribute additional/patched instance methods
|
84
120
|
#
|
85
121
|
# @see Serega::SeregaAttribute
|
86
122
|
#
|
87
|
-
module
|
123
|
+
module AttributeInstanceMethods
|
88
124
|
# @return provided :if options
|
89
125
|
attr_reader :opt_if
|
90
126
|
|
@@ -92,7 +128,7 @@ class Serega
|
|
92
128
|
|
93
129
|
def set_normalized_vars(normalizer)
|
94
130
|
super
|
95
|
-
@opt_if =
|
131
|
+
@opt_if = normalizer.if_options
|
96
132
|
end
|
97
133
|
end
|
98
134
|
|
@@ -125,20 +161,8 @@ class Serega
|
|
125
161
|
opt_unless = attribute.opt_if[opt_unless_name]
|
126
162
|
return true if opt_if.nil? && opt_unless.nil?
|
127
163
|
|
128
|
-
res_if =
|
129
|
-
|
130
|
-
when NilClass then true
|
131
|
-
when Symbol then obj.public_send(opt_if)
|
132
|
-
else opt_if.call(obj, ctx)
|
133
|
-
end
|
134
|
-
|
135
|
-
res_unless =
|
136
|
-
case opt_unless
|
137
|
-
when NilClass then true
|
138
|
-
when Symbol then !obj.public_send(opt_unless)
|
139
|
-
else !opt_unless.call(obj, ctx)
|
140
|
-
end
|
141
|
-
|
164
|
+
res_if = opt_if ? opt_if.call(obj, ctx) : true
|
165
|
+
res_unless = opt_unless ? !opt_unless.call(obj, ctx) : true
|
142
166
|
res_if && res_unless
|
143
167
|
end
|
144
168
|
end
|
@@ -166,7 +190,7 @@ class Serega
|
|
166
190
|
#
|
167
191
|
# @see Serega::SeregaObjectSerializer
|
168
192
|
#
|
169
|
-
module
|
193
|
+
module ObjectSerializerInstanceMethods
|
170
194
|
private
|
171
195
|
|
172
196
|
def serialize_point(object, point, _container)
|
@@ -27,48 +27,16 @@ class Serega
|
|
27
27
|
|
28
28
|
def check_type(value)
|
29
29
|
return if value.is_a?(Symbol)
|
30
|
-
|
31
30
|
raise SeregaError, must_be_callable unless value.respond_to?(:call)
|
32
31
|
|
33
|
-
if
|
34
|
-
|
35
|
-
else
|
36
|
-
check_callable(value)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def check_block(block)
|
41
|
-
return if valid_parameters?(block, accepted_count: 0..2)
|
32
|
+
SeregaValidations::Utils::CheckExtraKeywordArg.call(value, ":if option")
|
33
|
+
params_count = SeregaUtils::ParamsCount.call(value, max_count: 2)
|
42
34
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
def check_callable(callable)
|
47
|
-
return if valid_parameters?(callable.method(:call), accepted_count: 2..2)
|
48
|
-
|
49
|
-
raise SeregaError, callable_parameters_error
|
50
|
-
end
|
51
|
-
|
52
|
-
def valid_parameters?(data, accepted_count:)
|
53
|
-
params = data.parameters
|
54
|
-
accepted_count.include?(params.count) && valid_parameters_types?(params)
|
55
|
-
end
|
56
|
-
|
57
|
-
def valid_parameters_types?(params)
|
58
|
-
params.all? do |param|
|
59
|
-
type = param[0]
|
60
|
-
(type == :req) || (type == :opt)
|
35
|
+
if params_count > 2
|
36
|
+
raise SeregaError, "Option :if value should have up to 2 parameters (object, context)"
|
61
37
|
end
|
62
38
|
end
|
63
39
|
|
64
|
-
def block_parameters_error
|
65
|
-
"Invalid attribute option :if. When it is a Proc it can have maximum two regular parameters (object, context)"
|
66
|
-
end
|
67
|
-
|
68
|
-
def callable_parameters_error
|
69
|
-
"Invalid attribute option :if. When it is a callable object it must have two regular parameters (object, context)"
|
70
|
-
end
|
71
|
-
|
72
40
|
def must_be_callable
|
73
41
|
"Invalid attribute option :if. It must be a Symbol, a Proc or respond to :call"
|
74
42
|
end
|