serega 0.10.0 → 0.11.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 +319 -141
- data/VERSION +1 -1
- data/lib/serega/attribute.rb +37 -105
- data/lib/serega/attribute_normalizer.rb +176 -0
- data/lib/serega/config.rb +26 -9
- data/lib/serega/object_serializer.rb +11 -10
- data/lib/serega/plan.rb +128 -0
- data/lib/serega/plan_point.rb +94 -0
- data/lib/serega/plugins/batch/batch.rb +187 -41
- data/lib/serega/plugins/batch/lib/loader.rb +4 -4
- data/lib/serega/plugins/batch/lib/loaders.rb +3 -3
- data/lib/serega/plugins/batch/lib/plugins_extensions/activerecord_preloads.rb +2 -2
- data/lib/serega/plugins/batch/lib/plugins_extensions/formatters.rb +19 -1
- data/lib/serega/plugins/batch/lib/plugins_extensions/preloads.rb +6 -4
- data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +9 -5
- data/lib/serega/plugins/formatters/formatters.rb +28 -31
- data/lib/serega/plugins/if/if.rb +24 -6
- data/lib/serega/plugins/preloads/lib/format_user_preloads.rb +11 -13
- data/lib/serega/plugins/preloads/lib/main_preload_path.rb +2 -2
- data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +21 -15
- data/lib/serega/plugins/preloads/preloads.rb +72 -40
- data/lib/serega/plugins/presenter/presenter.rb +0 -9
- data/lib/serega/plugins/string_modifiers/parse_string_modifiers.rb +11 -1
- data/lib/serega/plugins.rb +3 -3
- data/lib/serega/utils/enum_deep_dup.rb +18 -18
- data/lib/serega/utils/enum_deep_freeze.rb +35 -0
- data/lib/serega/utils/to_hash.rb +17 -9
- data/lib/serega.rb +22 -15
- metadata +6 -6
- data/lib/serega/map.rb +0 -91
- data/lib/serega/map_point.rb +0 -66
- data/lib/serega/plugins/batch/lib/batch_option_model.rb +0 -73
- data/lib/serega/plugins/preloads/lib/enum_deep_freeze.rb +0 -26
@@ -3,33 +3,62 @@
|
|
3
3
|
class Serega
|
4
4
|
module SeregaPlugins
|
5
5
|
#
|
6
|
-
# Plugin
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
6
|
+
# Plugin `:batch`
|
7
|
+
#
|
8
|
+
# Adds ability to load nested attributes values in batches.
|
9
|
+
#
|
10
|
+
# It can be used to find value for attributes in optimal way:
|
11
|
+
# - load associations for multiple objects
|
12
|
+
# - load counters for multiple objects
|
13
|
+
# - make any heavy calculations for multiple objects only once
|
14
|
+
#
|
15
|
+
# After including plugin, attributes gain new `:batch` option.
|
16
|
+
#
|
17
|
+
# `:batch` option must be a hash with this keys:
|
18
|
+
# - `key` (required) [Symbol, Proc, callable] - Defines current object identifier.
|
19
|
+
# Later `loader` will accept array of `keys` to find `values`.
|
20
|
+
# - `loader` (required) [Symbol, Proc, callable] - Defines how to fetch values for
|
21
|
+
# batch of keys. Receives 3 parameters: keys, context, plan_point.
|
22
|
+
# - `default` (optional) - Default value for attribute.
|
23
|
+
# By default it is `nil` or `[]` when attribute has option `many: true`
|
24
|
+
#
|
25
|
+
# If `:loader` was defined using name (as Symbol) then batch loader must be
|
26
|
+
# defined in serializer config: `config.batch.define(:loader_name) { ... }` method.
|
27
|
+
#
|
28
|
+
# *Result of this `:loader` callable must be a **Hash** where*:
|
29
|
+
# - keys - provided keys
|
30
|
+
# - values - values for according keys
|
31
|
+
#
|
32
|
+
# `Batch` plugin can be defined with two specific attributes:
|
33
|
+
# - `auto_hide: true` - Marks attributes with defined :batch as hidden, so it
|
34
|
+
# will not be serialized by default
|
35
|
+
# - `default_key: :id` - Set default object key (in this case :id) that will be used for all attributes with :batch option specified.
|
36
|
+
#
|
37
|
+
# This options (`auto_hide`, `default_key`) also can be set as config options in
|
38
|
+
# any nested serializer.
|
10
39
|
#
|
11
40
|
# @example
|
12
41
|
# class PostSerializer < Serega
|
13
|
-
# plugin :batch
|
42
|
+
# plugin :batch, auto_hide: true, default_key: :id
|
14
43
|
#
|
15
|
-
# # Define batch loader via callable class, it must accept three args (keys, context,
|
16
|
-
# attribute :comments_count, batch: {
|
44
|
+
# # Define batch loader via callable class, it must accept three args (keys, context, plan_point)
|
45
|
+
# attribute :comments_count, batch: { loader: PostCommentsCountBatchLoader, default: 0}
|
17
46
|
#
|
18
|
-
# # Define batch loader via Symbol, later we should define this loader via config.
|
19
|
-
# attribute :comments_count, batch: {
|
47
|
+
# # Define batch loader via Symbol, later we should define this loader via config.batch.define(:posts_comments_counter) { ... }
|
48
|
+
# attribute :comments_count, batch: { loader: :posts_comments_counter, default: 0}
|
20
49
|
#
|
21
50
|
# # Define batch loader with serializer
|
22
|
-
# attribute :comments, serializer: CommentSerializer, batch: {
|
51
|
+
# attribute :comments, serializer: CommentSerializer, batch: { loader: :posts_comments, default: []}
|
23
52
|
#
|
24
53
|
# # Resulted block must return hash like { key => value(s) }
|
25
|
-
# config.
|
54
|
+
# config.batch.define(:posts_comments_counter) do |keys|
|
26
55
|
# Comment.group(:post_id).where(post_id: keys).count
|
27
56
|
# end
|
28
57
|
#
|
29
58
|
# # We can return objects that will be automatically serialized if attribute defined with :serializer
|
30
59
|
# # Parameter `context` can be used when loading batch
|
31
|
-
# # Parameter `
|
32
|
-
# config.
|
60
|
+
# # Parameter `plan_point` can be used to find nested attributes that will be serialized (`plan_point.preloads`)
|
61
|
+
# config.batch.define(:posts_comments) do |keys, context, plan_point|
|
33
62
|
# Comment.where(post_id: keys).where(is_spam: false).group_by(&:post_id)
|
34
63
|
# end
|
35
64
|
# end
|
@@ -50,7 +79,6 @@ class Serega
|
|
50
79
|
# @return [void]
|
51
80
|
#
|
52
81
|
def self.load_plugin(serializer_class, **_opts)
|
53
|
-
require_relative "./lib/batch_option_model"
|
54
82
|
require_relative "./lib/loader"
|
55
83
|
require_relative "./lib/loaders"
|
56
84
|
require_relative "./lib/validations/check_batch_opt_key"
|
@@ -61,7 +89,8 @@ class Serega
|
|
61
89
|
serializer_class.include(InstanceMethods)
|
62
90
|
serializer_class::CheckAttributeParams.include(CheckAttributeParamsInstanceMethods)
|
63
91
|
serializer_class::SeregaAttribute.include(AttributeInstanceMethods)
|
64
|
-
serializer_class::
|
92
|
+
serializer_class::SeregaAttributeNormalizer.include(AttributeNormalizerInstanceMethods)
|
93
|
+
serializer_class::SeregaPlanPoint.include(PlanPointInstanceMethods)
|
65
94
|
serializer_class::SeregaObjectSerializer.include(SeregaObjectSerializerInstanceMethods)
|
66
95
|
end
|
67
96
|
|
@@ -74,9 +103,6 @@ class Serega
|
|
74
103
|
# @return [void]
|
75
104
|
#
|
76
105
|
def self.after_load_plugin(serializer_class, **opts)
|
77
|
-
config = serializer_class.config
|
78
|
-
config.attribute_keys << :batch
|
79
|
-
config.opts[:batch] = {loaders: {}}
|
80
106
|
serializer_class::SeregaConfig.include(ConfigInstanceMethods)
|
81
107
|
|
82
108
|
batch_loaders_class = Class.new(SeregaBatchLoaders)
|
@@ -95,18 +121,24 @@ class Serega
|
|
95
121
|
if serializer_class.plugin_used?(:formatters)
|
96
122
|
require_relative "./lib/plugins_extensions/formatters"
|
97
123
|
serializer_class::SeregaBatchLoader.include(PluginsExtensions::Formatters::BatchLoaderInstanceMethods)
|
124
|
+
serializer_class::SeregaAttribute.include(PluginsExtensions::Formatters::SeregaAttributeInstanceMethods)
|
98
125
|
end
|
99
126
|
|
100
127
|
if serializer_class.plugin_used?(:preloads)
|
101
128
|
require_relative "./lib/plugins_extensions/preloads"
|
102
|
-
serializer_class::
|
129
|
+
serializer_class::SeregaAttributeNormalizer.include(PluginsExtensions::Preloads::AttributeNormalizerInstanceMethods)
|
103
130
|
end
|
131
|
+
|
132
|
+
config = serializer_class.config
|
133
|
+
config.attribute_keys << :batch
|
134
|
+
config.opts[:batch] = {loaders: {}, default_key: nil, auto_hide: false}
|
135
|
+
config.batch.auto_hide = opts[:auto_hide] if opts.key?(:auto_hide)
|
104
136
|
end
|
105
137
|
|
106
138
|
#
|
107
139
|
# Batch loader config
|
108
140
|
#
|
109
|
-
class
|
141
|
+
class BatchConfig
|
110
142
|
attr_reader :opts
|
111
143
|
|
112
144
|
def initialize(opts)
|
@@ -117,22 +149,28 @@ class Serega
|
|
117
149
|
# Defines batch loader
|
118
150
|
#
|
119
151
|
# @param loader_name [Symbol] Batch loader name, that is used when defining attribute with batch loader.
|
120
|
-
# @param block [Proc] Block that can accept 3 parameters - keys, context,
|
152
|
+
# @param block [Proc] Block that can accept 3 parameters - keys, context, plan_point
|
121
153
|
# and returns hash where ids are keys and values are batch loaded objects/
|
122
154
|
#
|
123
155
|
# @return [void]
|
124
156
|
#
|
125
157
|
def define(loader_name, &block)
|
126
158
|
unless block
|
127
|
-
raise SeregaError, "Block must be given to
|
159
|
+
raise SeregaError, "Block must be given to #define method"
|
128
160
|
end
|
129
161
|
|
130
162
|
params = block.parameters
|
131
|
-
if params.count > 3 || !params.
|
163
|
+
if params.count > 3 || !params.all? { |param| (param[0] == :req) || (param[0] == :opt) }
|
132
164
|
raise SeregaError, "Block can have maximum 3 regular parameters"
|
133
165
|
end
|
134
166
|
|
135
|
-
|
167
|
+
loaders[loader_name] = block
|
168
|
+
end
|
169
|
+
|
170
|
+
# Shows defined loaders
|
171
|
+
# @return [Hash] defined loaders
|
172
|
+
def loaders
|
173
|
+
opts[:loaders]
|
136
174
|
end
|
137
175
|
|
138
176
|
#
|
@@ -141,8 +179,34 @@ class Serega
|
|
141
179
|
# @param loader_name [Symbol]
|
142
180
|
#
|
143
181
|
# @return [Proc] batch loader block
|
144
|
-
def
|
145
|
-
|
182
|
+
def fetch_loader(loader_name)
|
183
|
+
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| ... }")
|
184
|
+
end
|
185
|
+
|
186
|
+
# Shows option to auto hide attributes with :batch specified
|
187
|
+
# @return [Boolean, nil] option value
|
188
|
+
def auto_hide
|
189
|
+
opts[:auto_hide]
|
190
|
+
end
|
191
|
+
|
192
|
+
# @param value [Boolean] New :auto_hide option value
|
193
|
+
# @return [Boolean] New option value
|
194
|
+
def auto_hide=(value)
|
195
|
+
raise SeregaError, "Must have boolean value, #{value.inspect} provided" if (value != true) && (value != false)
|
196
|
+
opts[:auto_hide] = value
|
197
|
+
end
|
198
|
+
|
199
|
+
# Shows default key for :batch option
|
200
|
+
# @return [Symbol, nil] default key for :batch option
|
201
|
+
def default_key
|
202
|
+
opts[:default_key]
|
203
|
+
end
|
204
|
+
|
205
|
+
# @param value [Symbol] New :default_key option value
|
206
|
+
# @return [Boolean] New option value
|
207
|
+
def default_key=(value)
|
208
|
+
raise SeregaError, "Must be a Symbol, #{value.inspect} provided" unless value.is_a?(Symbol)
|
209
|
+
opts[:default_key] = value
|
146
210
|
end
|
147
211
|
end
|
148
212
|
|
@@ -155,10 +219,10 @@ class Serega
|
|
155
219
|
#
|
156
220
|
# Returns all batch loaders registered for current serializer
|
157
221
|
#
|
158
|
-
# @return [Serega::SeregaPlugins::Batch::
|
222
|
+
# @return [Serega::SeregaPlugins::Batch::BatchConfig] configuration for batch loaded attributes
|
159
223
|
#
|
160
|
-
def
|
161
|
-
@
|
224
|
+
def batch
|
225
|
+
@batch ||= BatchConfig.new(opts.fetch(:batch))
|
162
226
|
end
|
163
227
|
end
|
164
228
|
|
@@ -194,7 +258,7 @@ class Serega
|
|
194
258
|
def check_opts
|
195
259
|
super
|
196
260
|
|
197
|
-
CheckOptBatch.call(opts, block)
|
261
|
+
CheckOptBatch.call(opts, block, self.class.serializer_class)
|
198
262
|
end
|
199
263
|
end
|
200
264
|
|
@@ -207,29 +271,111 @@ class Serega
|
|
207
271
|
#
|
208
272
|
# @return [nil, Hash] :batch option
|
209
273
|
#
|
210
|
-
|
211
|
-
|
274
|
+
attr_reader :batch
|
275
|
+
|
276
|
+
private
|
277
|
+
|
278
|
+
def set_normalized_vars(normalizer)
|
279
|
+
super
|
280
|
+
@batch = normalizer.batch
|
212
281
|
end
|
213
282
|
end
|
214
283
|
|
215
284
|
#
|
216
|
-
#
|
285
|
+
# SeregaAttributeNormalizer additional/patched instance methods
|
217
286
|
#
|
218
|
-
# @see
|
287
|
+
# @see SeregaAttributeNormalizer::AttributeInstanceMethods
|
219
288
|
#
|
220
|
-
module
|
289
|
+
module AttributeNormalizerInstanceMethods
|
221
290
|
#
|
222
|
-
# Returns
|
291
|
+
# Returns normalized attribute :batch option with prepared :key and
|
292
|
+
# :default options. Option :loader will be prepared at serialization
|
293
|
+
# time as loaders are usually defined after attributes.
|
223
294
|
#
|
224
|
-
# @return [
|
295
|
+
# @return [Hash] attribute :batch normalized options
|
225
296
|
#
|
226
297
|
def batch
|
227
298
|
return @batch if instance_variable_defined?(:@batch)
|
228
299
|
|
229
|
-
@batch =
|
230
|
-
|
231
|
-
|
300
|
+
@batch = prepare_batch
|
301
|
+
end
|
302
|
+
|
303
|
+
private
|
304
|
+
|
305
|
+
#
|
306
|
+
# Patch for original `prepare_hide` method
|
307
|
+
#
|
308
|
+
# Marks attribute hidden if auto_hide option was set and attribute has batch loader
|
309
|
+
#
|
310
|
+
def prepare_hide
|
311
|
+
res = super
|
312
|
+
return res unless res.nil?
|
313
|
+
|
314
|
+
if batch
|
315
|
+
self.class.serializer_class.config.batch.auto_hide || nil
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def prepare_batch
|
320
|
+
batch = init_opts[:batch]
|
321
|
+
return unless batch
|
322
|
+
|
323
|
+
# take loader
|
324
|
+
loader = batch[:loader]
|
325
|
+
|
326
|
+
# take key
|
327
|
+
key = batch[:key] || self.class.serializer_class.config.batch.default_key
|
328
|
+
proc_key =
|
329
|
+
if key.is_a?(Symbol)
|
330
|
+
proc do |object|
|
331
|
+
handle_no_method_error { object.public_send(key) }
|
332
|
+
end
|
333
|
+
else
|
334
|
+
key
|
335
|
+
end
|
336
|
+
|
337
|
+
# take default value
|
338
|
+
default = batch.fetch(:default) { many ? FROZEN_EMPTY_ARRAY : nil }
|
339
|
+
|
340
|
+
{loader: loader, key: proc_key, default: default}
|
341
|
+
end
|
342
|
+
|
343
|
+
def handle_no_method_error
|
344
|
+
yield
|
345
|
+
rescue NoMethodError => error
|
346
|
+
raise error, "NoMethodError when serializing '#{name}' attribute in #{self.class.serializer_class}\n\n#{error.message}", error.backtrace
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
#
|
351
|
+
# Serega::SeregaPlanPoint additional/patched class methods
|
352
|
+
#
|
353
|
+
# @see SeregaAttribute
|
354
|
+
#
|
355
|
+
module PlanPointInstanceMethods
|
356
|
+
#
|
357
|
+
# Returns attribute :batch option with prepared loader
|
358
|
+
# @return [Hash] attribute :batch option
|
359
|
+
#
|
360
|
+
attr_reader :batch
|
361
|
+
|
362
|
+
private
|
363
|
+
|
364
|
+
def set_normalized_vars
|
365
|
+
super
|
366
|
+
@batch = prepare_batch
|
367
|
+
end
|
368
|
+
|
369
|
+
def prepare_batch
|
370
|
+
batch = attribute.batch
|
371
|
+
if batch
|
372
|
+
loader = batch[:loader]
|
373
|
+
if loader.is_a?(Symbol)
|
374
|
+
batch_config = attribute.class.serializer_class.config.batch
|
375
|
+
batch[:loader] = batch_config.fetch_loader(loader)
|
376
|
+
end
|
232
377
|
end
|
378
|
+
batch
|
233
379
|
end
|
234
380
|
end
|
235
381
|
|
@@ -268,7 +414,7 @@ class Serega
|
|
268
414
|
end
|
269
415
|
|
270
416
|
def remember_key_for_batch_loading(batch, object, point, container)
|
271
|
-
key = batch
|
417
|
+
key = batch[:key].call(object, context)
|
272
418
|
opts[:batch_loaders].get(point, self).remember(key, container)
|
273
419
|
container[point.name] = nil # Reserve attribute place in resulted hash. We will set correct value later
|
274
420
|
end
|
@@ -12,7 +12,7 @@ class Serega
|
|
12
12
|
# Batch Loader instance methods
|
13
13
|
#
|
14
14
|
module InstanceMethods
|
15
|
-
# @return [Serega::
|
15
|
+
# @return [Serega::SeregaPlanPoint]
|
16
16
|
attr_reader :point
|
17
17
|
|
18
18
|
# @return [Serega::SeregaObjectSerializer]
|
@@ -22,7 +22,7 @@ class Serega
|
|
22
22
|
# Initializes new SeregaBatchLoader
|
23
23
|
#
|
24
24
|
# @param object_serializer [Serega::SeregaObjectSerializer]
|
25
|
-
# @param point [Serega::
|
25
|
+
# @param point [Serega::SeregaPlanPoint]
|
26
26
|
#
|
27
27
|
# @return [Serega::SeregaPlugins::Batch::SeregaBatchLoader]
|
28
28
|
#
|
@@ -52,7 +52,7 @@ class Serega
|
|
52
52
|
keys_values = keys_values()
|
53
53
|
|
54
54
|
each_key do |key, container|
|
55
|
-
value = keys_values.fetch(key) { point.batch
|
55
|
+
value = keys_values.fetch(key) { point.batch[:default] }
|
56
56
|
final_value = object_serializer.__send__(:final_value, value, point)
|
57
57
|
object_serializer.__send__(:attach_final_value, final_value, point, container)
|
58
58
|
end
|
@@ -74,7 +74,7 @@ class Serega
|
|
74
74
|
def keys_values
|
75
75
|
ids = keys.keys
|
76
76
|
|
77
|
-
point.batch
|
77
|
+
point.batch[:loader].call(ids, object_serializer.context, point).tap do |vals|
|
78
78
|
next if vals.is_a?(Hash)
|
79
79
|
|
80
80
|
attribute_name = "#{point.class.serializer_class}.#{point.name}"
|
@@ -10,13 +10,13 @@ class Serega
|
|
10
10
|
#
|
11
11
|
# Initializes or fetches already initialized batch loader
|
12
12
|
#
|
13
|
-
# @param
|
13
|
+
# @param plan_point [Serega::SeregaPlanPoint] current plan point
|
14
14
|
# @param object_serializer[Serega::SeregaObjectSerializer] current object serializer
|
15
15
|
#
|
16
16
|
# @return [Serega::SeregaPlugins::Batch::SeregaBatchLoader] Batch Loader
|
17
17
|
#
|
18
|
-
def get(
|
19
|
-
batch_loaders[
|
18
|
+
def get(plan_point, object_serializer)
|
19
|
+
batch_loaders[plan_point] ||= self.class.serializer_class::SeregaBatchLoader.new(object_serializer, plan_point)
|
20
20
|
end
|
21
21
|
|
22
22
|
#
|
@@ -20,10 +20,10 @@ class Serega
|
|
20
20
|
private
|
21
21
|
|
22
22
|
# Preloads required associations to batch-loaded records
|
23
|
-
def keys_values
|
23
|
+
def keys_values
|
24
24
|
data = super
|
25
25
|
|
26
|
-
if point.
|
26
|
+
if point.child_plan
|
27
27
|
associations = point.preloads
|
28
28
|
return data if associations.empty?
|
29
29
|
|
@@ -20,7 +20,7 @@ class Serega
|
|
20
20
|
private
|
21
21
|
|
22
22
|
# Format values after they are prepared
|
23
|
-
def keys_values
|
23
|
+
def keys_values
|
24
24
|
data = super
|
25
25
|
|
26
26
|
formatter = point.attribute.formatter
|
@@ -29,6 +29,24 @@ class Serega
|
|
29
29
|
data
|
30
30
|
end
|
31
31
|
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# SeregaAttribute additional/patched instance methods
|
35
|
+
#
|
36
|
+
# @see Serega::SeregaAttribute
|
37
|
+
#
|
38
|
+
module SeregaAttributeInstanceMethods
|
39
|
+
# Normalized formatter block or callable instance
|
40
|
+
# @return [#call, nil] Normalized formatter block or callable instance
|
41
|
+
attr_reader :formatter
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def set_normalized_vars(normalizer)
|
46
|
+
super
|
47
|
+
@formatter = normalizer.formatter
|
48
|
+
end
|
49
|
+
end
|
32
50
|
end
|
33
51
|
end
|
34
52
|
end
|
@@ -14,14 +14,16 @@ class Serega
|
|
14
14
|
#
|
15
15
|
# Attribute additional/patched instance methods
|
16
16
|
#
|
17
|
-
# @see Serega::SeregaPlugins::Preloads::
|
17
|
+
# @see Serega::SeregaPlugins::Preloads::AttributeNormalizerInstanceMethods
|
18
18
|
#
|
19
|
-
module
|
19
|
+
module AttributeNormalizerInstanceMethods
|
20
20
|
private
|
21
21
|
|
22
22
|
# Do not add any preloads automatically when batch option provided
|
23
|
-
def
|
24
|
-
|
23
|
+
def prepare_preloads
|
24
|
+
opts = init_opts
|
25
|
+
return if opts.key?(:batch) && !opts.key?(:preload)
|
26
|
+
|
25
27
|
super
|
26
28
|
end
|
27
29
|
end
|
@@ -17,7 +17,7 @@ class Serega
|
|
17
17
|
#
|
18
18
|
# @return [void]
|
19
19
|
#
|
20
|
-
def call(opts, block)
|
20
|
+
def call(opts, block, serializer_class)
|
21
21
|
return unless opts.key?(:batch)
|
22
22
|
|
23
23
|
SeregaValidations::Utils::CheckOptIsHash.call(opts, :batch)
|
@@ -25,21 +25,25 @@ class Serega
|
|
25
25
|
batch = opts[:batch]
|
26
26
|
SeregaValidations::Utils::CheckAllowedKeys.call(batch, %i[key loader default])
|
27
27
|
|
28
|
-
check_batch_opt_key(batch
|
29
|
-
check_batch_opt_loader(batch
|
28
|
+
check_batch_opt_key(batch, serializer_class)
|
29
|
+
check_batch_opt_loader(batch)
|
30
30
|
|
31
31
|
check_usage_with_other_params(opts, block)
|
32
32
|
end
|
33
33
|
|
34
34
|
private
|
35
35
|
|
36
|
-
def check_batch_opt_key(
|
36
|
+
def check_batch_opt_key(batch, serializer_class)
|
37
|
+
return if !batch.key?(:key) && serializer_class.config.batch.default_key
|
38
|
+
|
39
|
+
key = batch[:key]
|
37
40
|
raise SeregaError, "Option :key must present inside :batch option" unless key
|
38
41
|
|
39
42
|
CheckBatchOptKey.call(key)
|
40
43
|
end
|
41
44
|
|
42
|
-
def check_batch_opt_loader(
|
45
|
+
def check_batch_opt_loader(batch)
|
46
|
+
loader = batch[:loader]
|
43
47
|
raise SeregaError, "Option :loader must present inside :batch option" unless loader
|
44
48
|
|
45
49
|
CheckBatchOptLoader.call(loader)
|
@@ -67,7 +67,7 @@ class Serega
|
|
67
67
|
#
|
68
68
|
def self.load_plugin(serializer_class, **_opts)
|
69
69
|
serializer_class::SeregaConfig.include(ConfigInstanceMethods)
|
70
|
-
serializer_class::
|
70
|
+
serializer_class::SeregaAttributeNormalizer.include(AttributeNormalizerInstanceMethods)
|
71
71
|
end
|
72
72
|
|
73
73
|
#
|
@@ -115,10 +115,10 @@ class Serega
|
|
115
115
|
#
|
116
116
|
# Config class additional/patched instance methods
|
117
117
|
#
|
118
|
-
# @see
|
118
|
+
# @see SeregaConfig
|
119
119
|
#
|
120
120
|
module ConfigInstanceMethods
|
121
|
-
# @return [
|
121
|
+
# @return [SeregaPlugins::Formatters::FormattersConfig] current formatters config
|
122
122
|
def formatters
|
123
123
|
@formatters ||= FormattersConfig.new(opts.fetch(:formatters))
|
124
124
|
end
|
@@ -127,46 +127,43 @@ class Serega
|
|
127
127
|
#
|
128
128
|
# Attribute class additional/patched instance methods
|
129
129
|
#
|
130
|
-
# @see
|
130
|
+
# @see SeregaAttributeNormalizer
|
131
131
|
#
|
132
|
-
module
|
133
|
-
#
|
132
|
+
module AttributeNormalizerInstanceMethods
|
133
|
+
# Block or callable instance that will format attribute values
|
134
|
+
# @return [Proc, #call, nil] Block or callable instance that will format attribute values
|
134
135
|
def formatter
|
135
136
|
return @formatter if instance_variable_defined?(:@formatter)
|
136
137
|
|
137
|
-
@formatter =
|
138
|
+
@formatter = prepare_formatter
|
138
139
|
end
|
139
140
|
|
140
|
-
|
141
|
-
def value_block
|
142
|
-
return @value_block if instance_variable_defined?(:@value_block)
|
143
|
-
return @value_block = super unless formatter
|
144
|
-
|
145
|
-
new_value_block = formatted_block(formatter, super)
|
141
|
+
private
|
146
142
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
143
|
+
def prepare_value_block
|
144
|
+
return super unless formatter
|
145
|
+
|
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
|
151
156
|
end
|
152
|
-
|
153
|
-
@value_block = new_value_block
|
154
157
|
end
|
155
158
|
|
156
|
-
|
157
|
-
|
158
|
-
def formatter_resolved
|
159
|
-
formatter = opts[:format]
|
159
|
+
def prepare_formatter
|
160
|
+
formatter = init_opts[:format]
|
160
161
|
return unless formatter
|
161
162
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
def formatted_block(formatter, original_block)
|
167
|
-
proc do |object, context|
|
168
|
-
value = original_block.call(object, context)
|
169
|
-
formatter.call(value)
|
163
|
+
if formatter.is_a?(Symbol)
|
164
|
+
self.class.serializer_class.config.formatters.opts.fetch(formatter)
|
165
|
+
else
|
166
|
+
formatter # already callable
|
170
167
|
end
|
171
168
|
end
|
172
169
|
end
|
data/lib/serega/plugins/if/if.rb
CHANGED
@@ -61,7 +61,8 @@ class Serega
|
|
61
61
|
require_relative "./validations/check_opt_unless"
|
62
62
|
require_relative "./validations/check_opt_unless_value"
|
63
63
|
|
64
|
-
serializer_class::
|
64
|
+
serializer_class::SeregaAttribute.include(SeregaAttributeInstanceMethods)
|
65
|
+
serializer_class::SeregaPlanPoint.include(PlanPointInstanceMethods)
|
65
66
|
serializer_class::CheckAttributeParams.include(CheckAttributeParamsInstanceMethods)
|
66
67
|
serializer_class::SeregaObjectSerializer.include(SeregaObjectSerializerInstanceMethods)
|
67
68
|
end
|
@@ -79,11 +80,28 @@ class Serega
|
|
79
80
|
end
|
80
81
|
|
81
82
|
#
|
82
|
-
#
|
83
|
+
# SeregaAttribute additional/patched instance methods
|
83
84
|
#
|
84
|
-
# @see Serega::
|
85
|
+
# @see Serega::SeregaAttribute
|
85
86
|
#
|
86
|
-
module
|
87
|
+
module SeregaAttributeInstanceMethods
|
88
|
+
# @return provided :if options
|
89
|
+
attr_reader :opt_if
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def set_normalized_vars(normalizer)
|
94
|
+
super
|
95
|
+
@opt_if = initials[:opts].slice(:if, :if_value, :unless, :unless_value).freeze
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Serega::SeregaPlanPoint additional/patched instance methods
|
101
|
+
#
|
102
|
+
# @see Serega::SeregaPlanPoint::InstanceMethods
|
103
|
+
#
|
104
|
+
module PlanPointInstanceMethods
|
87
105
|
#
|
88
106
|
# @return [Boolean] Should we show attribute or not
|
89
107
|
# Conditions for this checks are specified by :if and :unless attribute options.
|
@@ -103,8 +121,8 @@ class Serega
|
|
103
121
|
private
|
104
122
|
|
105
123
|
def check_if_unless(obj, ctx, opt_if_name, opt_unless_name)
|
106
|
-
opt_if = attribute.
|
107
|
-
opt_unless = attribute.
|
124
|
+
opt_if = attribute.opt_if[opt_if_name]
|
125
|
+
opt_unless = attribute.opt_if[opt_unless_name]
|
108
126
|
return true if opt_if.nil? && opt_unless.nil?
|
109
127
|
|
110
128
|
res_if =
|