serega 0.16.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +22 -10
  3. data/VERSION +1 -1
  4. data/lib/serega/attribute.rb +3 -3
  5. data/lib/serega/attribute_normalizer.rb +21 -2
  6. data/lib/serega/plugins/batch/lib/batch_config.rb +11 -8
  7. data/lib/serega/plugins/batch/lib/modules/attribute_normalizer.rb +26 -11
  8. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_key.rb +5 -35
  9. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb +5 -35
  10. data/lib/serega/plugins/formatters/formatters.rb +88 -14
  11. data/lib/serega/plugins/if/if.rb +43 -19
  12. data/lib/serega/plugins/if/validations/check_opt_if.rb +4 -36
  13. data/lib/serega/plugins/if/validations/check_opt_if_value.rb +7 -39
  14. data/lib/serega/plugins/if/validations/check_opt_unless.rb +4 -45
  15. data/lib/serega/plugins/if/validations/check_opt_unless_value.rb +7 -39
  16. data/lib/serega/plugins/metadata/meta_attribute.rb +21 -5
  17. data/lib/serega/plugins/metadata/metadata.rb +16 -7
  18. data/lib/serega/plugins/metadata/validations/check_block.rb +10 -10
  19. data/lib/serega/plugins/metadata/validations/check_opt_const.rb +38 -0
  20. data/lib/serega/plugins/metadata/validations/check_opt_value.rb +61 -0
  21. data/lib/serega/plugins/metadata/validations/check_opts.rb +24 -10
  22. data/lib/serega/utils/params_count.rb +50 -0
  23. data/lib/serega/utils/to_hash.rb +1 -1
  24. data/lib/serega/validations/attribute/check_block.rb +4 -5
  25. data/lib/serega/validations/attribute/check_opt_value.rb +16 -12
  26. data/lib/serega/validations/check_attribute_params.rb +1 -0
  27. data/lib/serega/validations/utils/check_extra_keyword_arg.rb +33 -0
  28. data/lib/serega.rb +2 -0
  29. metadata +7 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b291b12115b8ca239642972b103f896f13f02874acdc5663a8f404e02673ca8d
4
- data.tar.gz: 18bd88ca4bd77ca147161189efe57b2c544da6cc1b460fc7c694855d4b22edc9
3
+ metadata.gz: 47eacc613b0a58af4dc552e09e0f55ed731f7e2a852e5c668794e7eb5128be69
4
+ data.tar.gz: bb9fec8432d12108e374bf15da74a230a4efda994835e9480e795b7222bb9ced
5
5
  SHA512:
6
- metadata.gz: d5c5114ed1c1b8b5a0714a340aca263983f5f8cdb24d15c8138124d64520ebafe03f6c64a7985f131ee399fab287ee578889b1affa1e2aa6f4ddacfbb9c8517e
7
- data.tar.gz: 4d8191c7081b4650e3328141ec3cc2dc9128a7c4da3a38107e5f2c107d1d70446b67e718a3ffa2ff76e700f80dbdf975da9512d089c86ec552c3dc8a390345d1
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, plan_point.
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
- - *path [Array of Symbols] - nested hash keys.
719
- - **options [Hash] - defaults are `hide_nil: false, hide_empty: false`
720
- - &block [Proc] - describes value for current meta attribute
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) { '1.2.3' }
728
- meta_attribute(:ab_tests, :names) { %i[foo bar] }
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=>[:foo, :bar]}}
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` now can be used with name of formatter or with
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.round(2) }
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.16.0
1
+ 0.17.0
@@ -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
- ser = @serializer
65
- return ser if (ser.is_a?(Class) && (ser < Serega)) || !ser
64
+ serializer = @serializer
65
+ return serializer if (serializer.is_a?(Class) && (serializer < Serega)) || !serializer
66
66
 
67
- @serializer = ser.is_a?(String) ? Object.const_get(ser, false) : ser.call
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
- init_block ||
101
- init_opts[:value] ||
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
- unless block
27
- raise SeregaError, "Block must be given to #define method"
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
- params = block.parameters
31
- if params.count > 3 || !params.all? { |param| (param[0] == :req) || (param[0] == :opt) }
32
- raise SeregaError, "Block can have maximum 3 regular parameters"
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] = block
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, ctx, points| ... }")
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
- # take loader
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
- proc_key =
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: proc_key, default: default}
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
- if key.is_a?(Proc)
26
- check_block(key)
27
- else
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 check_block(block)
35
- return if valid_parameters?(block, accepted_count: 0..2)
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
- if loader.is_a?(Proc)
26
- check_block(loader)
27
- else
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 check_block(block)
35
- return if valid_parameters?(block, accepted_count: 0..3)
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
- if init_opts.key?(:const)
147
- # Format const value in advance
148
- const_value = formatter.call(init_opts[:const])
149
- proc { const_value }
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
- self.class.serializer_class.config.formatters.opts.fetch(formatter)
165
- else
166
- formatter # already callable
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
@@ -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(SeregaAttributeInstanceMethods)
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(SeregaObjectSerializerInstanceMethods)
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 SeregaAttributeInstanceMethods
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 = initials[:opts].slice(:if, :if_value, :unless, :unless_value).freeze
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
- case opt_if
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 SeregaObjectSerializerInstanceMethods
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 value.is_a?(Proc)
34
- check_block(value)
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
- raise SeregaError, block_parameters_error
44
- end
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