serega 0.10.0 → 0.11.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 +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 =
|