serega 0.18.0 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60cc0f4e593d4600bf316e9c18bd93804008e28deebd5f51c231b632bb3a4eff
4
- data.tar.gz: e3bdc8e6a0489a14916d84781dcf61c6a82dedb246a0b5366f324d78ca81d9b2
3
+ metadata.gz: 85e79f7011bb7c55c60c9b39e425faf9137fb4b2f2889332a2e75f1fea1268a7
4
+ data.tar.gz: 5358001a693ec3f6c40383cc12c8f44b37764bf5f1b9143bddba338756948e91
5
5
  SHA512:
6
- metadata.gz: 879f46f3bface078c156ad0c89e71b4abc08945754034235d03a688c5613e549d477ae1d24bfa6d0f2c44de1def6266d9b18677ab9fab53ef8341510075806d5
7
- data.tar.gz: 45b302b1efc001b9ff3ef35bb9d71bf05e3dcfffbfd35938cf938448b308e9c6c0f74a8f1c1eeceabf8a3bd0eb759f50dd053948664565fbeef2f4b8502841b2
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 to serialize them to Hash or JSON.
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 code dry
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 proc or callable object to define attribute value
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 :const specifies attribute with specific constant value
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 specify serializer as Class, String or Proc.
96
- # Use String or Proc if you have cross references in serializers.
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
- # Usually it is defined automatically by checking `is_a?(Enumerable)`
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`, `:unless`, `:if_value`, `:unless_value` can be specified
110
- # when enabled `:if` plugin. They hide attribute key and value from response.
111
- # See more usage examples in :if plugin section.
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 this characters as we want to be able to use attributes names in
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
- This check can be disabled this way:
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 you always serialize same attributes it will make sense to save instance
169
- of serializer and reuse this instance, it will be a bit faster (fields will be
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 only needed attributes:
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` modifier
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 provided non-existing attribute, `Serega::AttributeNotExist` error will be
214
- raised. This error can be muted with `check_initiate_params: false` parameter.
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 not existing attribute
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 not existing attribute and disabled validation
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 you can decide to use some context during serialization, like
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
- This is initial config options, other config options can be added by plugins
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. When Oj gem is loaded then default is
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 params `:with`, `:except`, `:only`
343
- # By default it is enabled. After disabling,
344
- # when provided not existed attribute it will be just skipped.
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
- This options are very handy if you want to forget about finding preloads manually.
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 option.
371
- Also automatically added preloads can be overwritten with manually specified
372
- `preload: :another_value`.
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
- Some examples, **please read comments in the code below**
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 so nothing should be preloaded
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 as association
435
+ #### SPECIFIC CASE #1: Serializing the same object in association
425
436
 
426
- For example you decided to show your current user as "user" and "user_stats".
427
- Where stats rely on user fields and some other associations.
428
- You should specify `preload: nil` to preload nested associations, if any, to "user".
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", "old_profile", and also
450
- profiles have "avatar" association. And you decided to serialize profiles in one
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 this associations automatically.
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
- Must be used to omit N+1 when loading attributes values.
554
+ Helps to omit N+1.
543
555
 
544
- User must provide batch loader object to attribute -
556
+ User must specify how attribute values are loaded -
545
557
  `attribute :foo, batch: {loader: SomeLoader, id_method: :id}`.
546
558
 
547
- Result must be returned as Hash, where each key is one of provided ids.
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
- Loader can accept 1 to 3 arguments:
582
+ The loader can accept 1 to 3 arguments:
571
583
 
572
- 1. List of ids (each id will be found by using `:id_method` option)
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 with `:preloads` plugin.
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
- Batch plugin can be added with global `:id_method` option. It can be a Symbol,
609
- Proc or any callable value, which can accept current object and current context.
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 `config.batch.id_method=`
628
- method or in specific attributes with `id_method` option.
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
- #### Option :default
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`. Attribute marked as `many: true` will be
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 specified with
676
- `auto_hide` option. Also `auto_hide` option can be changed with
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: `Batch` plugin must be added to all serializers that have
694
- `:batch` attributes inside nested serializers. For example when you serialize
695
- `User -> Album -> Song` and Song has `batch` attribute, then
696
- `batch` plugin must be added to the User serializer also.
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
- Best way would be to create one parent `AppSerializer < Serega` serializer
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
- Default root is `:data`.
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
- Root also can be changed per serialization.
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
- Also root can be removed for all responses by providing `root: nil`.
723
- In this case no root will be added to response, but you still can to add it per
724
- serialization
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
- Added class-level method `:meta_attribute`, to define metadata, it accepts:
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, `false` by default
772
- - `:hide_empty`, does not show metadata key if value is nil or empty,
773
- `false` by default
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 keyword that must be used to
806
- provide metadata. By default it is `:meta`
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
- Key can be changed in children serializers using config
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 on attribute values.
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 clear code by adding attribute names as methods to Presenter
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`, `:unless`, `:if_value`, `:unless_value` options to
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 finding final value.
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 :serializer option,
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` this means:
969
+ By default, when we add an attribute like `attribute :first_name` it means:
956
970
 
957
- - adding a `:first_name` key to resulted hash
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 its often desired to response with *camelCased* keys.
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` to `X` for the whole string.
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 your own callable transformation when defining plugin,
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
- without affecting `root`, `metadata`, `context_metadata` plugins keys.
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 require to serialize too much
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
- Limit can be checked or changed with next config options:
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 are no default limit, but it should be set when enabling plugin.
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
- Plugin requires to add :many option when adding relationships
1038
- (relationships are attributes with :serializer option specified)
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 returns array or single
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.18.0
1
+ 0.19.0
@@ -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
- prepare_init_block ||
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 default], :batch)
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.18.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-11 00:00:00.000000000 Z
11
+ date: 2023-12-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  JSON Serializer