serega 0.18.0 → 0.19.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 +164 -150
- data/VERSION +1 -1
- data/lib/serega/attribute.rb +6 -1
- data/lib/serega/attribute_normalizer.rb +31 -1
- data/lib/serega/config.rb +1 -1
- data/lib/serega/plugins/batch/batch.rb +1 -1
- data/lib/serega/plugins/batch/lib/modules/attribute_normalizer.rb +0 -2
- data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85e79f7011bb7c55c60c9b39e425faf9137fb4b2f2889332a2e75f1fea1268a7
|
4
|
+
data.tar.gz: 5358001a693ec3f6c40383cc12c8f44b37764bf5f1b9143bddba338756948e91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 66e705d78a97def601c4f3ad79b20a5599cd0b7885b26decea38437428ea18a9ac1d65a55d20042d2c79046343ec36c25ede2795ac1a697dd510976876a91eff
|
7
|
+
data.tar.gz: 2cb126604c9d0a8eb5679a85daa29bcd830996bca4820d8fadfec5847f78af74602cc1dc2693143cd86fa0160b0f6c61248fcf87a41d1e97ec5e732772962989
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
# Serega Ruby Serializer
|
7
7
|
|
8
8
|
The Serega Ruby Serializer provides easy and powerful DSL to describe your
|
9
|
-
objects and
|
9
|
+
objects and serialize them to Hash or JSON.
|
10
10
|
|
11
11
|
---
|
12
12
|
|
@@ -24,7 +24,8 @@ It has some great features:
|
|
24
24
|
- Adding custom metadata (via [metadata][metadata] or
|
25
25
|
[context_metadata][context_metadata] plugins)
|
26
26
|
- Value formatters ([formatters][formatters] plugin) helps to transform
|
27
|
-
time, date, money, percentage and any other values same way keeping
|
27
|
+
time, date, money, percentage, and any other values in the same way keeping
|
28
|
+
the code dry
|
28
29
|
- Conditional attributes - ([if][if] plugin)
|
29
30
|
- Auto camelCase keys - [camel_case][camel_case] plugin
|
30
31
|
|
@@ -67,13 +68,14 @@ class UserSerializer < Serega
|
|
67
68
|
# Regular attribute
|
68
69
|
attribute :first_name
|
69
70
|
|
70
|
-
# Option :method specifies method that must be called on serialized object
|
71
|
+
# Option :method specifies the method that must be called on the serialized object
|
71
72
|
attribute :first_name, method: :old_first_name
|
72
73
|
|
73
74
|
# Block is used to define attribute value
|
74
75
|
attribute(:first_name) { |user| user.profile&.first_name }
|
75
76
|
|
76
|
-
# Option :value can be used with
|
77
|
+
# Option :value can be used with a Proc or callable object to define attribute
|
78
|
+
# value
|
77
79
|
attribute :first_name, value: UserProfile.new # must have #call method
|
78
80
|
attribute :first_name, value: proc { |user| user.profile&.first_name }
|
79
81
|
|
@@ -85,35 +87,42 @@ class UserSerializer < Serega
|
|
85
87
|
# is user.profile.fname
|
86
88
|
attribute :first_name, delegate: { to: :profile, method: :fname }
|
87
89
|
|
88
|
-
# Option :
|
90
|
+
# Option :default can be used to replace possible nil values.
|
91
|
+
attribute :first_name, default: ''
|
92
|
+
attribute :is_active, default: false
|
93
|
+
attribute :comments_count, default: 0
|
94
|
+
|
95
|
+
# Option :const specifies attribute with a specific constant value
|
89
96
|
attribute(:type, const: 'user')
|
90
97
|
|
91
98
|
# Option :hide specifies attributes that should not be serialized by default
|
92
99
|
attribute :tags, hide: true
|
93
100
|
|
94
101
|
# Option :serializer specifies nested serializer for attribute
|
95
|
-
# We can
|
96
|
-
# Use String or Proc if you have cross
|
102
|
+
# We can define the `:serializer` value as a Class, String, or Proc.
|
103
|
+
# Use String or Proc if you have cross-references in serializers.
|
97
104
|
attribute :posts, serializer: PostSerializer
|
98
105
|
attribute :posts, serializer: "PostSerializer"
|
99
106
|
attribute :posts, serializer: -> { PostSerializer }
|
100
107
|
|
101
|
-
# Option `:many` specifies a has_many relationship
|
102
|
-
#
|
108
|
+
# Option `:many` specifies a has_many relationship. It is optional.
|
109
|
+
# If not specified, it is defined during serialization by checking `object.is_a?(Enumerable)`
|
110
|
+
# Also the `:many` changes the default value from `nil` to `[]`.
|
103
111
|
attribute :posts, serializer: PostSerializer, many: true
|
104
112
|
|
105
113
|
# Option `:preload` can be specified when enabled `:preloads` plugin
|
106
114
|
# It allows to specify associations to preload to attribute value
|
107
115
|
attribute(:email, preload: :emails) { |user| user.emails.find(&:verified?) }
|
108
116
|
|
109
|
-
# Options `:if
|
110
|
-
# when
|
111
|
-
#
|
117
|
+
# Options `:if, :unless, :if_value and :unless_value` can be specified
|
118
|
+
# when `:if` plugin is enabled. They hide the attribute key and value from the
|
119
|
+
# response.
|
120
|
+
# See more usage examples in the `:if` plugin section.
|
112
121
|
attribute :email, if: proc { |user, ctx| user == ctx[:current_user] }
|
113
122
|
attribute :email, if_value: :present?
|
114
123
|
|
115
124
|
# Option `:format` can be specified when enabled `:formatters` plugin
|
116
|
-
# It changes attribute value
|
125
|
+
# It changes the attribute value
|
117
126
|
attribute :created_at, format: :iso_time
|
118
127
|
attribute :updated_at, format: :iso_time
|
119
128
|
|
@@ -127,10 +136,10 @@ end
|
|
127
136
|
⚠️ Attribute names are checked to include only "a-z", "A-Z", "0-9", "\_", "-",
|
128
137
|
"~" characters.
|
129
138
|
|
130
|
-
We allow ONLY
|
139
|
+
We allow ONLY these characters as we want to be able to use attribute names in
|
131
140
|
URLs without escaping.
|
132
141
|
|
133
|
-
|
142
|
+
The check can be turned off:
|
134
143
|
|
135
144
|
```ruby
|
136
145
|
# Disable globally
|
@@ -146,7 +155,7 @@ end
|
|
146
155
|
|
147
156
|
We can serialize objects using class methods `.to_h`, `.to_json`, `.as_json` and
|
148
157
|
same instance methods `#to_h`, `#to_json`, `#as_json`.
|
149
|
-
`to_h` method is also aliased as `call`.
|
158
|
+
The `to_h` method is also aliased as `call`.
|
150
159
|
|
151
160
|
```ruby
|
152
161
|
user = OpenStruct.new(username: 'serega')
|
@@ -165,9 +174,9 @@ UserSerializer.as_json(user) # => {"username":"serega"}
|
|
165
174
|
UserSerializer.as_json([user]) # => [{"username":"serega"}]
|
166
175
|
```
|
167
176
|
|
168
|
-
If
|
169
|
-
|
170
|
-
prepared only once).
|
177
|
+
If serialized fields are constant, then it's a good idea to initiate the
|
178
|
+
serializer and reuse it.
|
179
|
+
It will be a bit faster (the serialization plan will be prepared only once).
|
171
180
|
|
172
181
|
```ruby
|
173
182
|
# Example with all fields
|
@@ -182,7 +191,7 @@ serializer.to_h(user2)
|
|
182
191
|
```
|
183
192
|
|
184
193
|
---
|
185
|
-
⚠️ When you serialize `Struct` object, specify manually `many: false`. As Struct
|
194
|
+
⚠️ When you serialize the `Struct` object, specify manually `many: false`. As Struct
|
186
195
|
is Enumerable and we check `object.is_a?(Enumerable)` to detect if we should
|
187
196
|
return array.
|
188
197
|
|
@@ -192,26 +201,27 @@ UserSerializer.to_h(user_struct, many: false)
|
|
192
201
|
|
193
202
|
### Selecting Fields
|
194
203
|
|
195
|
-
By default all attributes are serialized (except marked as `hide: true`).
|
204
|
+
By default, all attributes are serialized (except marked as `hide: true`).
|
196
205
|
|
197
|
-
We can provide **modifiers** to select
|
206
|
+
We can provide **modifiers** to select serialized attributes:
|
198
207
|
|
199
|
-
- *only* - lists attributes to serialize;
|
208
|
+
- *only* - lists specific attributes to serialize;
|
200
209
|
- *except* - lists attributes to not serialize;
|
201
210
|
- *with* - lists attributes to serialize additionally (By default all attributes
|
202
211
|
are exposed and will be serialized, but some attributes can be hidden when
|
203
|
-
they are defined with `hide: true` option, more on this below. `with`
|
204
|
-
can be used to expose such attributes).
|
212
|
+
they are defined with the `hide: true` option, more on this below. `with`
|
213
|
+
modifier can be used to expose such attributes).
|
205
214
|
|
206
|
-
Modifiers can be provided as Hash, Array, String, Symbol or their combinations.
|
215
|
+
Modifiers can be provided as Hash, Array, String, Symbol, or their combinations.
|
207
216
|
|
208
217
|
With plugin [string_modifiers][string_modifiers] we can provide modifiers as
|
209
218
|
single `String` with attributes split by comma `,` and nested values inside
|
210
219
|
brackets `()`, like: `username,enemies(username,email)`. This can be very useful
|
211
|
-
to accept list of fields in **GET** requests.
|
220
|
+
to accept the list of fields in **GET** requests.
|
212
221
|
|
213
|
-
When
|
214
|
-
raised. This error can be muted with `check_initiate_params: false`
|
222
|
+
When a non-existing attribute is provided, the `Serega::AttributeNotExist` error
|
223
|
+
will be raised. This error can be muted with the `check_initiate_params: false`
|
224
|
+
option.
|
215
225
|
|
216
226
|
```ruby
|
217
227
|
class UserSerializer < Serega
|
@@ -285,7 +295,7 @@ UserSerializer.to_h(bruce, with: fields_as_string)
|
|
285
295
|
# ]
|
286
296
|
# }
|
287
297
|
|
288
|
-
# With
|
298
|
+
# With no existing attribute
|
289
299
|
fields = %i[first_name enemy]
|
290
300
|
fields_as_string = 'first_name,enemy'
|
291
301
|
UserSerializer.new(only: fields).to_h(bruce)
|
@@ -293,7 +303,7 @@ UserSerializer.to_h(bruce, only: fields)
|
|
293
303
|
UserSerializer.to_h(bruce, only: fields_as_string)
|
294
304
|
# => raises Serega::AttributeNotExist, "Attribute 'enemy' not exists"
|
295
305
|
|
296
|
-
# With
|
306
|
+
# With no existing attribute and disabled validation
|
297
307
|
fields = %i[first_name enemy]
|
298
308
|
fields_as_string = 'first_name,enemy'
|
299
309
|
UserSerializer.new(only: fields, check_initiate_params: false).to_h(bruce)
|
@@ -304,7 +314,7 @@ UserSerializer.to_h(bruce, only: fields_as_string, check_initiate_params: false)
|
|
304
314
|
|
305
315
|
### Using Context
|
306
316
|
|
307
|
-
Sometimes
|
317
|
+
Sometimes it can be required to use the context during serialization, like
|
308
318
|
current_user or any.
|
309
319
|
|
310
320
|
```ruby
|
@@ -324,28 +334,28 @@ UserSerializer.new.to_h(user, context: {current_user: user}) # same
|
|
324
334
|
|
325
335
|
## Configuration
|
326
336
|
|
327
|
-
|
337
|
+
Here are the default options. Other options can be added with plugins.
|
328
338
|
|
329
339
|
```ruby
|
330
340
|
class AppSerializer < Serega
|
331
341
|
# Configure adapter to serialize to JSON.
|
332
|
-
# It is `JSON.dump` by default.
|
333
|
-
# `Oj.dump(data, mode: :compat)`
|
342
|
+
# It is `JSON.dump` by default. But if the Oj gem is loaded, then the default
|
343
|
+
# is changed to `Oj.dump(data, mode: :compat)`
|
334
344
|
config.to_json = ->(data) { Oj.dump(data, mode: :compat) }
|
335
345
|
|
336
346
|
# Configure adapter to de-serialize JSON.
|
337
|
-
# De-serialization is used only for `#as_json` method.
|
347
|
+
# De-serialization is used only for the `#as_json` method.
|
338
348
|
# It is `JSON.parse` by default.
|
339
|
-
# When Oj gem is loaded then default is `Oj.load(data)`
|
349
|
+
# When the Oj gem is loaded, then the default is `Oj.load(data)`
|
340
350
|
config.from_json = ->(data) { Oj.load(data) }
|
341
351
|
|
342
|
-
# Disable/enable validation of modifiers
|
343
|
-
# By default
|
344
|
-
#
|
352
|
+
# Disable/enable validation of modifiers (`:with, :except, :only`)
|
353
|
+
# By default, this validation is enabled.
|
354
|
+
# After disabling, all requested incorrect attributes will be skipped.
|
345
355
|
config.check_initiate_params = false # default is true, enabled
|
346
356
|
|
347
357
|
# Stores in memory prepared `plans` - list of serialized attributes.
|
348
|
-
# Next time serialization happens with same modifiers (`only, except, with`),
|
358
|
+
# Next time serialization happens with the same modifiers (`only, except, with`),
|
349
359
|
# we will reuse already prepared `plans`.
|
350
360
|
# This defines storage size (count of stored `plans` with different modifiers).
|
351
361
|
config.max_cached_plans_per_serializer_count = 50 # default is 0, disabled
|
@@ -365,13 +375,14 @@ Plugin accepts options:
|
|
365
375
|
- `auto_preload_attributes_with_serializer` - default `false`
|
366
376
|
- `auto_hide_attributes_with_preload` - default `false`
|
367
377
|
|
368
|
-
|
378
|
+
These options are extremely useful if you want to forget about finding preloads
|
379
|
+
manually.
|
369
380
|
|
370
|
-
Preloads can be disabled with `preload: false` attribute option
|
371
|
-
|
372
|
-
`preload: :
|
381
|
+
Preloads can be disabled with the `preload: false` attribute option.
|
382
|
+
Automatically added preloads can be overwritten with the manually specified
|
383
|
+
`preload: :xxx` option.
|
373
384
|
|
374
|
-
|
385
|
+
For some examples, **please read the comments in the code below**
|
375
386
|
|
376
387
|
```ruby
|
377
388
|
class AppSerializer < Serega
|
@@ -402,8 +413,8 @@ class AlbumSerializer < AppSerializer
|
|
402
413
|
attribute :images_count, delegate: { to: :album_stats }
|
403
414
|
end
|
404
415
|
|
405
|
-
# By default preloads are empty, as we specify `auto_hide_attributes_with_preload`
|
406
|
-
# so attributes with preloads will be skipped
|
416
|
+
# By default, preloads are empty, as we specify `auto_hide_attributes_with_preload`
|
417
|
+
# so attributes with preloads will be skipped and nothing will be preloaded
|
407
418
|
UserSerializer.new.preloads
|
408
419
|
# => {}
|
409
420
|
|
@@ -421,11 +432,12 @@ UserSerializer.new(
|
|
421
432
|
|
422
433
|
---
|
423
434
|
|
424
|
-
#### SPECIFIC CASE #1: Serializing same object
|
435
|
+
#### SPECIFIC CASE #1: Serializing the same object in association
|
425
436
|
|
426
|
-
For example you
|
427
|
-
|
428
|
-
You should specify `preload: nil` to preload
|
437
|
+
For example, you show your current user as "user" and use the same user object
|
438
|
+
to serialize "user_stats". `UserStatSerializer` relies on user fields and any
|
439
|
+
other user associations. You should specify `preload: nil` to preload
|
440
|
+
`UserStatSerializer` nested associations to the "user" object.
|
429
441
|
|
430
442
|
```ruby
|
431
443
|
class AppSerializer < Serega
|
@@ -444,11 +456,11 @@ class UserSerializer < AppSerializer
|
|
444
456
|
end
|
445
457
|
```
|
446
458
|
|
447
|
-
#### SPECIFIC CASE #2: Serializing multiple associations as single relation
|
459
|
+
#### SPECIFIC CASE #2: Serializing multiple associations as a single relation
|
448
460
|
|
449
|
-
For example "user" has two relations - "new_profile"
|
450
|
-
profiles have "avatar" association. And you decided to serialize profiles in
|
451
|
-
array. You can specify `preload_path: [[:new_profile], [:old_profile]]` to
|
461
|
+
For example, "user" has two relations - "new_profile" and "old_profile". Also
|
462
|
+
profiles have the "avatar" association. And you decided to serialize profiles in
|
463
|
+
one array. You can specify `preload_path: [[:new_profile], [:old_profile]]` to
|
452
464
|
achieve this:
|
453
465
|
|
454
466
|
```ruby
|
@@ -488,7 +500,7 @@ attribute :image,
|
|
488
500
|
preload_path: [:attachment] # or preload_path: [:attachment, :blob]
|
489
501
|
```
|
490
502
|
|
491
|
-
In this case we don't know if preloads defined in ImageSerializer, should be
|
503
|
+
In this case, we don't know if preloads defined in ImageSerializer, should be
|
492
504
|
preloaded to `attachment` or `blob`, so please specify `preload_path` manually.
|
493
505
|
You can specify `preload_path: nil` if you are sure that there are no preloads
|
494
506
|
inside ImageSerializer.
|
@@ -499,7 +511,7 @@ inside ImageSerializer.
|
|
499
511
|
they should be preloaded manually.
|
500
512
|
|
501
513
|
There are only [activerecord_preloads][activerecord_preloads] plugin that can
|
502
|
-
be used to preload
|
514
|
+
be used to preload these associations automatically.
|
503
515
|
|
504
516
|
### Plugin :activerecord_preloads
|
505
517
|
|
@@ -508,7 +520,7 @@ be used to preload this associations automatically.
|
|
508
520
|
Automatically preloads associations to serialized objects.
|
509
521
|
|
510
522
|
It takes all defined preloads from serialized attributes (including attributes
|
511
|
-
from serialized relations), merges them into single associations hash and then
|
523
|
+
from serialized relations), merges them into a single associations hash, and then
|
512
524
|
uses ActiveRecord::Associations::Preloader to preload associations to objects.
|
513
525
|
|
514
526
|
```ruby
|
@@ -539,12 +551,12 @@ UserSerializer.to_h(user)
|
|
539
551
|
|
540
552
|
### Plugin :batch
|
541
553
|
|
542
|
-
|
554
|
+
Helps to omit N+1.
|
543
555
|
|
544
|
-
User must
|
556
|
+
User must specify how attribute values are loaded -
|
545
557
|
`attribute :foo, batch: {loader: SomeLoader, id_method: :id}`.
|
546
558
|
|
547
|
-
|
559
|
+
The result must be returned as Hash, where each key is one of the provided IDs.
|
548
560
|
|
549
561
|
```ruby
|
550
562
|
class AppSerializer
|
@@ -563,19 +575,19 @@ end
|
|
563
575
|
|
564
576
|
#### Option :loader
|
565
577
|
|
566
|
-
Loaders can be defined as a Proc, a callable value or a named Symbol
|
578
|
+
Loaders can be defined as a Proc, a callable value, or a named Symbol
|
567
579
|
Named loaders should be predefined with
|
568
580
|
`config.batch.define(:loader_name) { |ids| ... })`
|
569
581
|
|
570
|
-
|
582
|
+
The loader can accept 1 to 3 arguments:
|
571
583
|
|
572
|
-
1. List of
|
584
|
+
1. List of IDs (each ID will be found by using the `:id_method` option)
|
573
585
|
1. Context
|
574
586
|
1. PlanPoint - a special object containing information about current
|
575
587
|
attribute and all children and parent attributes. It can be used to preload
|
576
588
|
required associations to batch values.
|
577
589
|
See [example](examples/batch_loader.rb) how
|
578
|
-
to find required preloads when using
|
590
|
+
to find required preloads when using the `:preloads` plugin.
|
579
591
|
|
580
592
|
```ruby
|
581
593
|
class AppSerializer < Serega
|
@@ -583,7 +595,7 @@ class AppSerializer < Serega
|
|
583
595
|
end
|
584
596
|
|
585
597
|
class UserSerializer < Serega
|
586
|
-
# Define loader as callable object
|
598
|
+
# Define loader as a callable object
|
587
599
|
attribute :comments_count,
|
588
600
|
batch: { loader: CountLoader }
|
589
601
|
|
@@ -605,8 +617,9 @@ end
|
|
605
617
|
|
606
618
|
#### Option :id_method
|
607
619
|
|
608
|
-
|
609
|
-
Proc or any callable value
|
620
|
+
The `:batch` plugin can be added with the global `:id_method` option. It can be
|
621
|
+
a Symbol, Proc or any callable value that can accept the current object and
|
622
|
+
context.
|
610
623
|
|
611
624
|
```ruby
|
612
625
|
class SomeSerializer
|
@@ -624,8 +637,9 @@ end
|
|
624
637
|
|
625
638
|
```
|
626
639
|
|
627
|
-
However, global `id_method` option can be overwritten via
|
628
|
-
method or in specific attributes with `id_method`
|
640
|
+
However, the global `id_method` option can be overwritten via
|
641
|
+
`config.batch.id_method=` method or in specific attributes with the `id_method`
|
642
|
+
option.
|
629
643
|
|
630
644
|
```ruby
|
631
645
|
class SomeSerializer
|
@@ -651,30 +665,29 @@ class UserSerializer < AppSerializer
|
|
651
665
|
end
|
652
666
|
```
|
653
667
|
|
654
|
-
####
|
668
|
+
#### Default value
|
655
669
|
|
656
670
|
The default value for attributes without found value can be specified via
|
657
|
-
`:default` option. By default attributes without found value will be
|
658
|
-
serialized as `nil
|
659
|
-
serialized as empty array `[]`
|
671
|
+
`:default` option. By default, attributes without found value will be
|
672
|
+
serialized as a `nil` value. Attributes marked as `many: true` will be
|
673
|
+
serialized as empty array `[]` values.
|
660
674
|
|
661
675
|
```ruby
|
662
676
|
class UserSerializer < AppSerializer
|
663
|
-
# Missing values become empty arrays, as `many: true` option specified
|
677
|
+
# Missing values become empty arrays, as the `many: true` option is specified
|
664
678
|
attribute :companies,
|
665
679
|
batch: {loader: proc {}},
|
666
680
|
serializer: CompanySerializer,
|
667
681
|
many: true
|
668
682
|
|
669
683
|
# Missing values become `0` as specified directly
|
670
|
-
attribute :points_amount,
|
671
|
-
batch: { loader: proc {}, default: 0 }
|
684
|
+
attribute :points_amount, batch: { loader: proc {} }, default: 0
|
672
685
|
end
|
673
686
|
```
|
674
687
|
|
675
|
-
Batch attributes can be marked as hidden by default if plugin
|
676
|
-
`auto_hide` option.
|
677
|
-
`config.batch.auto_hide=` method.
|
688
|
+
Batch attributes can be marked as hidden by default if the plugin is enabled
|
689
|
+
with the `auto_hide` option. The `auto_hide` option can be changed with
|
690
|
+
the `config.batch.auto_hide=` method.
|
678
691
|
|
679
692
|
Look at [select serialized fields](#selecting-fields) for more information
|
680
693
|
about hiding/showing attributes.
|
@@ -690,13 +703,13 @@ end
|
|
690
703
|
```
|
691
704
|
|
692
705
|
---
|
693
|
-
⚠️ ATTENTION: `
|
694
|
-
`:batch` attributes inside nested serializers. For example when you serialize
|
695
|
-
`User -> Album -> Song` and Song has `batch` attribute, then
|
696
|
-
|
706
|
+
⚠️ ATTENTION: The `:batch` plugin must be added to all serializers that have
|
707
|
+
`:batch` attributes inside nested serializers. For example, when you serialize
|
708
|
+
the `User -> Album -> Song` and the Song has a `batch` attribute, then
|
709
|
+
the `:batch` plugin must be added to the User serializer.
|
697
710
|
|
698
|
-
|
699
|
-
and add `:batch` plugin once to this parent serializer.
|
711
|
+
The best way would be to create one parent `AppSerializer < Serega` serializer
|
712
|
+
and add the `:batch` plugin once to this parent serializer.
|
700
713
|
|
701
714
|
### Plugin :root
|
702
715
|
|
@@ -705,8 +718,8 @@ Allows to add root key to your serialized data
|
|
705
718
|
Accepts options:
|
706
719
|
|
707
720
|
- :root - specifies root for all responses
|
708
|
-
- :root_one - specifies root for single object serialization only
|
709
|
-
- :root_many - specifies root for multiple objects serialization only
|
721
|
+
- :root_one - specifies the root key for single object serialization only
|
722
|
+
- :root_many - specifies the root key for multiple objects serialization only
|
710
723
|
|
711
724
|
Adds additional config options:
|
712
725
|
|
@@ -715,13 +728,26 @@ Adds additional config options:
|
|
715
728
|
- config.root.one=
|
716
729
|
- config.root_many=
|
717
730
|
|
718
|
-
|
731
|
+
The default root is `:data`.
|
732
|
+
|
733
|
+
The root key can be changed per serialization.
|
734
|
+
|
735
|
+
```ruby
|
736
|
+
# @example Change root per serialization:
|
737
|
+
|
738
|
+
class UserSerializer < Serega
|
739
|
+
plugin :root
|
740
|
+
end
|
719
741
|
|
720
|
-
|
742
|
+
UserSerializer.to_h(nil) # => {:data=>nil}
|
743
|
+
UserSerializer.to_h(nil, root: :user) # => {:user=>nil}
|
744
|
+
UserSerializer.to_h(nil, root: nil) # => nil
|
745
|
+
```
|
721
746
|
|
722
|
-
|
723
|
-
|
724
|
-
|
747
|
+
The root key can be removed for all responses by providing the `root: nil`
|
748
|
+
plugin option.
|
749
|
+
|
750
|
+
In this case, no root key will be added. But it still can be added manually.
|
725
751
|
|
726
752
|
```ruby
|
727
753
|
#@example Define :root plugin with different options
|
@@ -739,40 +765,29 @@ serialization
|
|
739
765
|
end
|
740
766
|
|
741
767
|
class UserSerializer < Serega
|
742
|
-
plugin :root, root: nil # no root by default
|
768
|
+
plugin :root, root: nil # no root key by default
|
743
769
|
end
|
744
770
|
```
|
745
771
|
|
746
|
-
```ruby
|
747
|
-
# @example Change root per serialization:
|
748
|
-
|
749
|
-
class UserSerializer < Serega
|
750
|
-
plugin :root
|
751
|
-
end
|
752
|
-
|
753
|
-
UserSerializer.to_h(nil) # => {:data=>nil}
|
754
|
-
UserSerializer.to_h(nil, root: :user) # => {:user=>nil}
|
755
|
-
UserSerializer.to_h(nil, root: nil) # => nil
|
756
|
-
```
|
757
|
-
|
758
772
|
### Plugin :metadata
|
759
773
|
|
760
774
|
Depends on: [`:root`][root] plugin, that must be loaded first
|
761
775
|
|
762
776
|
Adds ability to describe metadata and adds it to serialized response
|
763
777
|
|
764
|
-
|
778
|
+
Adds class-level `.meta_attribute` method. It accepts:
|
765
779
|
|
766
780
|
- `*path` [Array of Symbols] - nested hash keys.
|
767
781
|
- `**options` [Hash]
|
768
782
|
|
769
783
|
- `:const` - describes metadata value (if it is constant)
|
770
784
|
- `:value` - describes metadata value as any `#callable` instance
|
771
|
-
- `:hide_nil` - does not show metadata key if value is nil
|
772
|
-
|
773
|
-
|
785
|
+
- `:hide_nil` - does not show the metadata key if the value is nil.
|
786
|
+
It is `false` by default
|
787
|
+
- `:hide_empty` - does not show the metadata key if the value is nil or empty.
|
788
|
+
It is `false` by default.
|
774
789
|
|
775
|
-
- `&block` [Proc] - describes value for current meta attribute
|
790
|
+
- `&block` [Proc] - describes value for the current meta attribute
|
776
791
|
|
777
792
|
```ruby
|
778
793
|
class AppSerializer < Serega
|
@@ -802,11 +817,11 @@ Depends on: [`:root`][root] plugin, that must be loaded first
|
|
802
817
|
|
803
818
|
Allows to provide metadata and attach it to serialized response.
|
804
819
|
|
805
|
-
Accepts option `:context_metadata_key` with name of
|
806
|
-
|
820
|
+
Accepts option `:context_metadata_key` with the name of the root metadata keyword.
|
821
|
+
By default, it has the `:meta` value.
|
807
822
|
|
808
|
-
|
809
|
-
`config.context_metadata.key=(value)
|
823
|
+
The key can be changed in children serializers using this method:
|
824
|
+
`config.context_metadata.key=(value)`.
|
810
825
|
|
811
826
|
```ruby
|
812
827
|
class UserSerializer < Serega
|
@@ -824,11 +839,11 @@ UserSerializer.to_h(nil, meta: { version: '1.0.1' })
|
|
824
839
|
|
825
840
|
### Plugin :formatters
|
826
841
|
|
827
|
-
Allows to define `formatters` and apply them
|
842
|
+
Allows to define `formatters` and apply them to attribute values.
|
828
843
|
|
829
844
|
Config option `config.formatters.add` can be used to add formatters.
|
830
845
|
|
831
|
-
Attribute option `:format` can be used with name of formatter or with
|
846
|
+
Attribute option `:format` can be used with the name of formatter or with
|
832
847
|
callable instance.
|
833
848
|
|
834
849
|
Formatters can accept up to 2 parameters (formatted object, context)
|
@@ -844,7 +859,7 @@ class AppSerializer < Serega
|
|
844
859
|
end
|
845
860
|
|
846
861
|
class UserSerializer < Serega
|
847
|
-
# Additionally we can add formatters via config in subclasses
|
862
|
+
# Additionally, we can add formatters via config in subclasses
|
848
863
|
config.formatters.add(
|
849
864
|
iso8601: ->(value) { time.iso8601.round(6) },
|
850
865
|
on_off: ->(value) { value ? 'ON' : 'OFF' },
|
@@ -865,7 +880,7 @@ end
|
|
865
880
|
|
866
881
|
### Plugin :presenter
|
867
882
|
|
868
|
-
Helps to write
|
883
|
+
Helps to write clean code by using a Presenter class.
|
869
884
|
|
870
885
|
```ruby
|
871
886
|
class UserSerializer < Serega
|
@@ -891,9 +906,9 @@ end
|
|
891
906
|
Allows to specify modifiers as strings.
|
892
907
|
|
893
908
|
Serialized attributes must be split with `,` and nested attributes must be
|
894
|
-
defined inside brackets `(
|
909
|
+
defined inside brackets `()`.
|
895
910
|
|
896
|
-
Modifiers can still be provided old way using nested hashes or arrays.
|
911
|
+
Modifiers can still be provided the old way using nested hashes or arrays.
|
897
912
|
|
898
913
|
```ruby
|
899
914
|
PostSerializer.plugin :string_modifiers
|
@@ -901,25 +916,24 @@ PostSerializer.new(only: "id,user(id,username)").to_h(post)
|
|
901
916
|
PostSerializer.new(except: "user(username,email)").to_h(post)
|
902
917
|
PostSerializer.new(with: "user(email)").to_h(post)
|
903
918
|
|
904
|
-
# Modifiers can still be provided old way using nested hashes or arrays.
|
919
|
+
# Modifiers can still be provided the old way using nested hashes or arrays.
|
905
920
|
PostSerializer.new(with: {user: %i[email, username]}).to_h(post)
|
906
921
|
```
|
907
922
|
|
908
923
|
### Plugin :if
|
909
924
|
|
910
|
-
Plugin adds `:if
|
911
|
-
attributes so we can remove attributes from response in various ways.
|
925
|
+
Plugin adds `:if, :unless, :if_value, :unless_value` options to
|
926
|
+
attributes so we can remove attributes from the response in various ways.
|
912
927
|
|
913
928
|
Use `:if` and `:unless` when you want to hide attributes before finding
|
914
929
|
attribute value, and use `:if_value` and `:unless_value` to hide attributes
|
915
|
-
after
|
930
|
+
after getting the final value.
|
916
931
|
|
917
932
|
Options `:if` and `:unless` accept currently serialized object and context as
|
918
933
|
parameters. Options `:if_value` and `:unless_value` accept already found
|
919
934
|
serialized value and context as parameters.
|
920
935
|
|
921
|
-
Options `:if_value` and `:unless_value` cannot be used with
|
922
|
-
as serialized objects have no "serialized value".
|
936
|
+
Options `:if_value` and `:unless_value` cannot be used with the `:serializer` option.
|
923
937
|
Use `:if` and `:unless` in this case.
|
924
938
|
|
925
939
|
See also a `:hide` option that is available without any plugins to hide
|
@@ -952,27 +966,27 @@ Look at [select serialized fields](#selecting-fields) for `:hide` usage examples
|
|
952
966
|
|
953
967
|
### Plugin :camel_case
|
954
968
|
|
955
|
-
By default when we add attribute like `attribute :first_name`
|
969
|
+
By default, when we add an attribute like `attribute :first_name` it means:
|
956
970
|
|
957
|
-
- adding a `:first_name` key to
|
971
|
+
- adding a `:first_name` key to the resulting hash
|
958
972
|
- adding a `#first_name` method call result as value
|
959
973
|
|
960
|
-
But
|
961
|
-
By default this can be achieved by specifying attribute name and method directly
|
974
|
+
But it's often desired to respond with *camelCased* keys.
|
975
|
+
By default, this can be achieved by specifying the attribute name and method directly
|
962
976
|
for each attribute: `attribute :firstName, method: first_name`
|
963
977
|
|
964
978
|
This plugin transforms all attribute names automatically.
|
965
|
-
We use simple regular expression to replace `_x`
|
966
|
-
We make this transformation only once when attribute is defined.
|
979
|
+
We use a simple regular expression to replace `_x` with `X` for the whole string.
|
980
|
+
We make this transformation only once when the attribute is defined.
|
967
981
|
|
968
|
-
You can provide
|
982
|
+
You can provide custom transformation when adding the plugin,
|
969
983
|
for example `plugin :camel_case, transform: ->(name) { name.camelize }`
|
970
984
|
|
971
985
|
For any attribute camelCase-behavior can be skipped when
|
972
|
-
`camel_case: false` attribute option provided.
|
986
|
+
the `camel_case: false` attribute option provided.
|
973
987
|
|
974
|
-
This plugin transforms only attribute keys,
|
975
|
-
|
988
|
+
This plugin transforms only attribute keys, without affecting the `root`,
|
989
|
+
`metadata` and `context_metadata` plugins keys.
|
976
990
|
|
977
991
|
If you wish to [select serialized fields](#selecting-fields), you should
|
978
992
|
provide them camelCased.
|
@@ -1000,7 +1014,7 @@ UserSerializer.new(only: %i[firstName lastName]).to_h(user)
|
|
1000
1014
|
|
1001
1015
|
### Plugin :depth_limit
|
1002
1016
|
|
1003
|
-
Helps to secure from malicious queries that
|
1017
|
+
Helps to secure from malicious queries that serialize too much
|
1004
1018
|
or from accidental serializing of objects with cyclic relations.
|
1005
1019
|
|
1006
1020
|
Depth limit is checked when constructing a serialization plan, that is when
|
@@ -1008,19 +1022,19 @@ Depth limit is checked when constructing a serialization plan, that is when
|
|
1008
1022
|
It can be useful to instantiate serializer before any other business logic
|
1009
1023
|
to get possible errors earlier.
|
1010
1024
|
|
1011
|
-
Any class-level serialization methods also check depth limit as they also
|
1025
|
+
Any class-level serialization methods also check the depth limit as they also
|
1012
1026
|
instantiate serializer.
|
1013
1027
|
|
1014
|
-
When depth limit is exceeded `Serega::DepthLimitError` is raised.
|
1015
|
-
Depth limit error details can be found in additional
|
1028
|
+
When the depth limit is exceeded `Serega::DepthLimitError` is raised.
|
1029
|
+
Depth limit error details can be found in the additional
|
1016
1030
|
`Serega::DepthLimitError#details` method
|
1017
1031
|
|
1018
|
-
|
1032
|
+
The limit can be checked or changed with the next config options:
|
1019
1033
|
|
1020
1034
|
- `config.depth_limit.limit`
|
1021
1035
|
- `config.depth_limit.limit=`
|
1022
1036
|
|
1023
|
-
There
|
1037
|
+
There is no default limit, but it should be set when enabling the plugin.
|
1024
1038
|
|
1025
1039
|
```ruby
|
1026
1040
|
class AppSerializer < Serega
|
@@ -1034,11 +1048,11 @@ end
|
|
1034
1048
|
|
1035
1049
|
### Plugin :explicit_many_option
|
1036
1050
|
|
1037
|
-
|
1038
|
-
(
|
1051
|
+
The plugin requires adding a `:many` option when adding relationships
|
1052
|
+
(attributes with the `:serializer` option).
|
1039
1053
|
|
1040
|
-
Adding this plugin makes it clearer to find if relationship
|
1041
|
-
object
|
1054
|
+
Adding this plugin makes it clearer to find if some relationship is an array or
|
1055
|
+
a single object.
|
1042
1056
|
|
1043
1057
|
```ruby
|
1044
1058
|
class BaseSerializer < Serega
|
@@ -1058,8 +1072,8 @@ object
|
|
1058
1072
|
|
1059
1073
|
## Errors
|
1060
1074
|
|
1061
|
-
- `Serega::SeregaError` is a base error raised by this gem.
|
1062
|
-
- `Serega::AttributeNotExist` error is raised when validating attributes in
|
1075
|
+
- The `Serega::SeregaError` is a base error raised by this gem.
|
1076
|
+
- The `Serega::AttributeNotExist` error is raised when validating attributes in
|
1063
1077
|
`:only, :except, :with` modifiers
|
1064
1078
|
|
1065
1079
|
## Release
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.19.0
|
data/lib/serega/attribute.rb
CHANGED
@@ -21,6 +21,10 @@ class Serega
|
|
21
21
|
# @return [Boolean, nil] Attribute :many option
|
22
22
|
attr_reader :many
|
23
23
|
|
24
|
+
# Attribute :default option
|
25
|
+
# @return [Object, nil] Attribute :default option
|
26
|
+
attr_reader :default
|
27
|
+
|
24
28
|
# Attribute :hide option
|
25
29
|
# @return [Boolean, nil] Attribute :hide option
|
26
30
|
attr_reader :hide
|
@@ -108,9 +112,10 @@ class Serega
|
|
108
112
|
|
109
113
|
def set_normalized_vars(normalizer)
|
110
114
|
@name = normalizer.name
|
115
|
+
@many = normalizer.many
|
116
|
+
@default = normalizer.default
|
111
117
|
@value_block = normalizer.value_block
|
112
118
|
@hide = normalizer.hide
|
113
|
-
@many = normalizer.many
|
114
119
|
@serializer = normalizer.serializer
|
115
120
|
end
|
116
121
|
end
|
@@ -86,6 +86,19 @@ class Serega
|
|
86
86
|
@serializer = prepare_serializer
|
87
87
|
end
|
88
88
|
|
89
|
+
#
|
90
|
+
# Shows the default attribute value. It is a value that replaces found nils.
|
91
|
+
#
|
92
|
+
# When custom :default is not specified, we set empty array as default when `many: true` specified
|
93
|
+
#
|
94
|
+
# @return [Object] Attribute default value
|
95
|
+
#
|
96
|
+
def default
|
97
|
+
return @default if instance_variable_defined?(:@default)
|
98
|
+
|
99
|
+
@default = prepare_default
|
100
|
+
end
|
101
|
+
|
89
102
|
private
|
90
103
|
|
91
104
|
def prepare_name
|
@@ -97,11 +110,14 @@ class Serega
|
|
97
110
|
# - plugin :formatters (wraps resulted block in formatter block and formats :const values)
|
98
111
|
#
|
99
112
|
def prepare_value_block
|
100
|
-
|
113
|
+
value_block =
|
114
|
+
prepare_init_block ||
|
101
115
|
prepare_value_option_block ||
|
102
116
|
prepare_const_block ||
|
103
117
|
prepare_delegate_block ||
|
104
118
|
prepare_keyword_block
|
119
|
+
|
120
|
+
prepare_value_block_with_default(value_block)
|
105
121
|
end
|
106
122
|
|
107
123
|
#
|
@@ -158,6 +174,20 @@ class Serega
|
|
158
174
|
end
|
159
175
|
end
|
160
176
|
|
177
|
+
def prepare_value_block_with_default(callable)
|
178
|
+
default_value = default
|
179
|
+
return callable if default_value.nil?
|
180
|
+
|
181
|
+
proc { |obj, ctx|
|
182
|
+
res = callable.call(obj, ctx)
|
183
|
+
res.nil? ? default_value : res
|
184
|
+
}
|
185
|
+
end
|
186
|
+
|
187
|
+
def prepare_default
|
188
|
+
init_opts.fetch(:default) { many ? FROZEN_EMPTY_ARRAY : nil }
|
189
|
+
end
|
190
|
+
|
161
191
|
def prepare_delegate_block
|
162
192
|
delegate = init_opts[:delegate]
|
163
193
|
return unless delegate
|
data/lib/serega/config.rb
CHANGED
@@ -15,7 +15,7 @@ class Serega
|
|
15
15
|
DEFAULTS = {
|
16
16
|
plugins: [],
|
17
17
|
initiate_keys: %i[only with except check_initiate_params].freeze,
|
18
|
-
attribute_keys: %i[method value serializer many hide const delegate].freeze,
|
18
|
+
attribute_keys: %i[method value serializer many hide const delegate default].freeze,
|
19
19
|
serialize_keys: %i[context many].freeze,
|
20
20
|
check_attribute_name: true,
|
21
21
|
check_initiate_params: true,
|
@@ -14,7 +14,7 @@ class Serega
|
|
14
14
|
# end
|
15
15
|
#
|
16
16
|
# class UserSerializer < AppSerializer
|
17
|
-
# attribute :comments_count, batch: { loader: CommentsCountBatchLoader, default: 0
|
17
|
+
# attribute :comments_count, batch: { loader: CommentsCountBatchLoader }, default: 0
|
18
18
|
# attribute :company, serializer: CompanySerializer, batch: { loader: UserCompanyBatchLoader }
|
19
19
|
# end
|
20
20
|
#
|
@@ -47,8 +47,6 @@ class Serega
|
|
47
47
|
id_method = batch[:id_method] || self.class.serializer_class.config.batch.id_method
|
48
48
|
id_method = prepare_batch_id_method(id_method)
|
49
49
|
|
50
|
-
default = batch.fetch(:default) { many ? FROZEN_EMPTY_ARRAY : nil }
|
51
|
-
|
52
50
|
{loader: loader, id_method: id_method, default: default}
|
53
51
|
end
|
54
52
|
|
@@ -23,7 +23,7 @@ class Serega
|
|
23
23
|
SeregaValidations::Utils::CheckOptIsHash.call(opts, :batch)
|
24
24
|
|
25
25
|
batch = opts[:batch]
|
26
|
-
SeregaValidations::Utils::CheckAllowedKeys.call(batch, %i[id_method loader
|
26
|
+
SeregaValidations::Utils::CheckAllowedKeys.call(batch, %i[id_method loader], :batch)
|
27
27
|
|
28
28
|
check_batch_opt_id_method(batch, serializer_class)
|
29
29
|
check_batch_opt_loader(batch, serializer_class)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: serega
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.19.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrey Glushkov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-12-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |
|
14
14
|
JSON Serializer
|