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
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
|