serega 0.21.0 → 0.30.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 +113 -229
- data/VERSION +1 -1
- data/lib/serega/attribute.rb +31 -3
- data/lib/serega/attribute_normalizer.rb +143 -42
- data/lib/serega/batch/attribute_loader.rb +70 -0
- data/lib/serega/batch/attribute_loaders.rb +59 -0
- data/lib/serega/batch/auto_resolver.rb +24 -0
- data/lib/serega/batch/auto_resolver_factory.rb +48 -0
- data/lib/serega/batch/loader.rb +67 -0
- data/lib/serega/config.rb +59 -1
- data/lib/serega/object_serializer.rb +11 -9
- data/lib/serega/plan.rb +16 -0
- data/lib/serega/plan_point.rb +31 -5
- data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +25 -13
- data/lib/serega/plugins/activerecord_preloads/lib/active_record_objects.rb +31 -0
- data/lib/serega/plugins/camel_case/camel_case.rb +1 -1
- data/lib/serega/plugins/formatters/formatters.rb +79 -22
- data/lib/serega/plugins/if/if.rb +41 -22
- data/lib/serega/plugins/if/validations/check_opt_if.rb +27 -6
- data/lib/serega/plugins/if/validations/check_opt_if_value.rb +27 -6
- data/lib/serega/plugins/if/validations/check_opt_unless.rb +27 -6
- data/lib/serega/plugins/if/validations/check_opt_unless_value.rb +27 -6
- data/lib/serega/plugins/metadata/meta_attribute.rb +9 -10
- data/lib/serega/plugins/metadata/validations/check_block.rb +2 -4
- data/lib/serega/plugins/metadata/validations/check_opt_value.rb +2 -3
- data/lib/serega/plugins/presenter/presenter.rb +1 -1
- data/lib/serega/utils/format_user_preloads.rb +58 -0
- data/lib/serega/utils/method_signature.rb +89 -0
- data/lib/serega/utils/preload_paths.rb +53 -0
- data/lib/serega/utils/preloads_constructor.rb +77 -0
- data/lib/serega/validations/attribute/check_block.rb +38 -6
- data/lib/serega/validations/attribute/check_opt_batch.rb +101 -0
- data/lib/serega/validations/attribute/check_opt_const.rb +1 -0
- data/lib/serega/validations/attribute/check_opt_delegate.rb +1 -0
- data/lib/serega/validations/attribute/check_opt_method.rb +1 -0
- data/lib/serega/{plugins/preloads/validations → validations/attribute}/check_opt_preload.rb +2 -2
- data/lib/serega/{plugins/preloads/validations → validations/attribute}/check_opt_preload_path.rb +3 -3
- data/lib/serega/validations/attribute/check_opt_value.rb +35 -9
- data/lib/serega/validations/check_attribute_params.rb +3 -2
- data/lib/serega/validations/check_batch_loader_params.rb +80 -0
- data/lib/serega/validations/initiate/check_modifiers.rb +1 -1
- data/lib/serega.rb +98 -7
- metadata +17 -37
- data/lib/serega/plugins/batch/batch.rb +0 -168
- data/lib/serega/plugins/batch/lib/batch_config.rb +0 -77
- data/lib/serega/plugins/batch/lib/loader.rb +0 -113
- data/lib/serega/plugins/batch/lib/loaders.rb +0 -45
- data/lib/serega/plugins/batch/lib/modules/attribute.rb +0 -26
- data/lib/serega/plugins/batch/lib/modules/attribute_normalizer.rb +0 -78
- data/lib/serega/plugins/batch/lib/modules/check_attribute_params.rb +0 -22
- data/lib/serega/plugins/batch/lib/modules/config.rb +0 -23
- data/lib/serega/plugins/batch/lib/modules/object_serializer.rb +0 -46
- data/lib/serega/plugins/batch/lib/modules/plan_point.rb +0 -22
- data/lib/serega/plugins/batch/lib/plugins_extensions/activerecord_preloads.rb +0 -43
- data/lib/serega/plugins/batch/lib/plugins_extensions/formatters.rb +0 -54
- data/lib/serega/plugins/batch/lib/plugins_extensions/if.rb +0 -31
- data/lib/serega/plugins/batch/lib/plugins_extensions/preloads.rb +0 -34
- data/lib/serega/plugins/batch/lib/validations/check_batch_opt_id_method.rb +0 -43
- data/lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb +0 -59
- data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +0 -62
- data/lib/serega/plugins/preloads/lib/format_user_preloads.rb +0 -60
- data/lib/serega/plugins/preloads/lib/modules/attribute.rb +0 -28
- data/lib/serega/plugins/preloads/lib/modules/attribute_normalizer.rb +0 -99
- data/lib/serega/plugins/preloads/lib/modules/check_attribute_params.rb +0 -22
- data/lib/serega/plugins/preloads/lib/modules/config.rb +0 -19
- data/lib/serega/plugins/preloads/lib/modules/plan_point.rb +0 -41
- data/lib/serega/plugins/preloads/lib/preload_paths.rb +0 -53
- data/lib/serega/plugins/preloads/lib/preloads_config.rb +0 -62
- data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +0 -79
- data/lib/serega/plugins/preloads/preloads.rb +0 -162
- data/lib/serega/utils/params_count.rb +0 -50
- data/lib/serega/validations/utils/check_extra_keyword_arg.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e72fdcef5f9baae349a3eae9c55029ef979cffdaabb5ab74e2f5228acaf93cf5
|
4
|
+
data.tar.gz: ef9375aa580c68856c78a016fc3dfcfef49f534d00c96af4deb4cbce20d9ce84
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ee1f484b9b1b66ada9570086f687c74f2b833c302e4685946d3787d6bce92e06d670e4ef0eae23a13c6c4c5090cc025c3bd73f5ea03c002c5bdcaffb2377238
|
7
|
+
data.tar.gz: 43ef3d1bbc6b2dd209674d237287ae68efead6e17bfc9ea1d67b8957998d7fb8f346119f8e12c7469e5d26a1f9bda099f507506ca32a2b08eed611c529105dd1
|
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
+
# Serega Ruby Serializer
|
2
|
+
|
1
3
|
[](https://badge.fury.io/rb/serega)
|
2
4
|
[](https://github.com/aglushkov/serega/actions/workflows/main.yml)
|
3
5
|
[](https://codeclimate.com/github/aglushkov/serega/test_coverage)
|
4
6
|
[](https://codeclimate.com/github/aglushkov/serega/maintainability)
|
5
7
|
|
6
|
-
# Serega Ruby Serializer
|
7
|
-
|
8
8
|
The Serega Ruby Serializer provides easy and powerful DSL to describe your
|
9
9
|
objects and serialize them to Hash or JSON.
|
10
10
|
|
@@ -18,14 +18,14 @@ It has some great features:
|
|
18
18
|
|
19
19
|
- Manually [select serialized fields](#selecting-fields)
|
20
20
|
- Secure from malicious queries with [depth_limit][depth_limit] plugin
|
21
|
-
- Solutions for N+1 problem (via [batch]
|
22
|
-
[activerecord_preloads][activerecord_preloads]
|
21
|
+
- Solutions for N+1 problem (via built-in [batch loading](#batch-loading), [preloads][preloads] or
|
22
|
+
[activerecord_preloads][activerecord_preloads] plugin)
|
23
23
|
- Built-in object presenter ([presenter][presenter] plugin)
|
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 in the same way
|
28
|
-
the code dry
|
27
|
+
time, date, money, percentage, and any other values in the same way
|
28
|
+
keeping the code dry
|
29
29
|
- Conditional attributes - ([if][if] plugin)
|
30
30
|
- Auto camelCase keys - [camel_case][camel_case] plugin
|
31
31
|
|
@@ -63,19 +63,28 @@ end
|
|
63
63
|
|
64
64
|
### Adding attributes
|
65
65
|
|
66
|
+
⚠️ Attribute names are checked to include only "a-z", "A-Z", "0-9", "\_", "-",
|
67
|
+
"~" characters.
|
68
|
+
|
69
|
+
We allow ONLY these characters as we want to be able to use attribute names in
|
70
|
+
URLs without escaping.
|
71
|
+
|
72
|
+
This rule can be turned off with simple config option: `config.check_attribute_name = false`
|
73
|
+
|
66
74
|
```ruby
|
67
75
|
class UserSerializer < Serega
|
68
76
|
# Regular attribute
|
69
77
|
attribute :first_name
|
70
78
|
|
71
|
-
# Option :method specifies the method that must be called on the
|
79
|
+
# Option :method specifies the method that must be called on the
|
80
|
+
# serialized object
|
72
81
|
attribute :first_name, method: :old_first_name
|
73
82
|
|
74
83
|
# Block is used to define attribute value
|
75
84
|
attribute(:first_name) { |user| user.profile&.first_name }
|
76
85
|
|
77
|
-
# Option :value can be used with a Proc or callable object to define
|
78
|
-
# value
|
86
|
+
# Option :value can be used with a Proc or callable object to define
|
87
|
+
# attribute value
|
79
88
|
attribute :first_name, value: UserProfile.new # must have #call method
|
80
89
|
attribute :first_name, value: proc { |user| user.profile&.first_name }
|
81
90
|
|
@@ -106,12 +115,13 @@ class UserSerializer < Serega
|
|
106
115
|
attribute :posts, serializer: -> { PostSerializer }
|
107
116
|
|
108
117
|
# Option `:many` specifies a has_many relationship. It is optional.
|
109
|
-
# If not specified, it is defined during serialization by checking
|
118
|
+
# If not specified, it is defined during serialization by checking
|
119
|
+
# `object.is_a?(Enumerable)`
|
110
120
|
# Also the `:many` changes the default value from `nil` to `[]`.
|
111
121
|
attribute :posts, serializer: PostSerializer, many: true
|
112
122
|
|
113
|
-
# Option `:preload`
|
114
|
-
#
|
123
|
+
# Option `:preload` allows to specify associations to preload to
|
124
|
+
# attribute value
|
115
125
|
attribute(:email, preload: :emails) { |user| user.emails.find(&:verified?) }
|
116
126
|
|
117
127
|
# Options `:if, :unless, :if_value and :unless_value` can be specified
|
@@ -131,26 +141,6 @@ class UserSerializer < Serega
|
|
131
141
|
end
|
132
142
|
```
|
133
143
|
|
134
|
-
---
|
135
|
-
|
136
|
-
⚠️ Attribute names are checked to include only "a-z", "A-Z", "0-9", "\_", "-",
|
137
|
-
"~" characters.
|
138
|
-
|
139
|
-
We allow ONLY these characters as we want to be able to use attribute names in
|
140
|
-
URLs without escaping.
|
141
|
-
|
142
|
-
The check can be turned off:
|
143
|
-
|
144
|
-
```ruby
|
145
|
-
# Disable globally
|
146
|
-
Serega.config.check_attribute_name = false
|
147
|
-
|
148
|
-
# Disable for specific serializer
|
149
|
-
class SomeSerializer < Serega
|
150
|
-
config.check_attribute_name = false
|
151
|
-
end
|
152
|
-
```
|
153
|
-
|
154
144
|
### Serializing
|
155
145
|
|
156
146
|
We can serialize objects using class methods `.to_h`, `.to_json`, `.as_json` and
|
@@ -216,7 +206,7 @@ Modifiers can be provided as Hash, Array, String, Symbol, or their combinations.
|
|
216
206
|
|
217
207
|
With plugin [string_modifiers][string_modifiers] we can provide modifiers as
|
218
208
|
single `String` with attributes split by comma `,` and nested values inside
|
219
|
-
brackets `()`, like: `
|
209
|
+
brackets `()`, like: `first_name,last_name,addresses(line1,line2)`. This can be very useful
|
220
210
|
to accept the list of fields in **GET** requests.
|
221
211
|
|
222
212
|
When a non-existing attribute is provided, the `Serega::AttributeNotExist` error
|
@@ -332,6 +322,67 @@ UserSerializer.new.to_h(user, context: {current_user: user}) # same
|
|
332
322
|
# => {:email=>"email@example.com"}
|
333
323
|
```
|
334
324
|
|
325
|
+
### Batch Loading
|
326
|
+
|
327
|
+
Serega includes built-in batch loading functionality to efficiently load data on demand and solve N+1 problems.
|
328
|
+
|
329
|
+
#### Defining Named Batch Loaders
|
330
|
+
|
331
|
+
Named loaders can be defined using the `batch_loader` class method and reused across attributes:
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
class UserSerializer < Serega
|
335
|
+
# Define named loaders
|
336
|
+
batch_loader :comments_count, ->(users) { Comment.where(user: users).group(:user_id).count }
|
337
|
+
batch_loader :comments_count, CommentsCountLoader # Example with callable class
|
338
|
+
|
339
|
+
# Full attribute example
|
340
|
+
attribute :comments_count, batch: { use: :comments_count },
|
341
|
+
value: proc { |user, batch:| batch[:comments_count][user.id] }
|
342
|
+
|
343
|
+
# Shorter version
|
344
|
+
attribute :comments_count, batch: { use: :comments_count, id: :id } # Equivalent, id: :id is default
|
345
|
+
|
346
|
+
# Shortest version
|
347
|
+
attribute :comments_count, batch: true # Equivalent
|
348
|
+
end
|
349
|
+
```
|
350
|
+
|
351
|
+
#### Using Custom Loaders
|
352
|
+
|
353
|
+
Custom loaders can be provided directly using the `:use` option with any callable object:
|
354
|
+
|
355
|
+
```ruby
|
356
|
+
class UserSerializer < Serega
|
357
|
+
# Inline proc loader
|
358
|
+
attribute :followers_count,
|
359
|
+
batch: { use: ->(users) { Follow.where(user: users).group(:user_id).count } }
|
360
|
+
|
361
|
+
# Callable class loader
|
362
|
+
attribute :likes_count, batch: { use: LikesCountLoader }
|
363
|
+
|
364
|
+
# Method reference
|
365
|
+
attribute :views_count, batch: { use: ViewsCounter.method(:batch_load) }
|
366
|
+
end
|
367
|
+
```
|
368
|
+
|
369
|
+
#### Using Multiple Loaders in Same Attribute
|
370
|
+
|
371
|
+
Custom loaders can be provided directly using the `:use` option with any callable object:
|
372
|
+
|
373
|
+
```ruby
|
374
|
+
class UserSerializer < Serega
|
375
|
+
# Define named loaders
|
376
|
+
batch_loader :facebook_likes, FacebookLikesLoader
|
377
|
+
batch_loader :twitter_likes, TwitterLikesLoader
|
378
|
+
|
379
|
+
# Summarize likes
|
380
|
+
attribute :likes_count,
|
381
|
+
batch: { use: [:facebook_likes, :twitter_likes] },
|
382
|
+
value: { |user, batch:| batch[:facebook_likes][user.id] + batch[:twitter_likes][user.id] }
|
383
|
+
end
|
384
|
+
```
|
385
|
+
|
335
386
|
## Configuration
|
336
387
|
|
337
388
|
Here are the default options. Other options can be added with plugins.
|
@@ -362,21 +413,20 @@ class AppSerializer < Serega
|
|
362
413
|
end
|
363
414
|
```
|
364
415
|
|
365
|
-
##
|
416
|
+
## Preloads
|
366
417
|
|
367
|
-
|
418
|
+
Serega includes built-in preloads functionality that allows you to define
|
419
|
+
`:preloads` to attributes and then merge preloads from serialized attributes
|
420
|
+
into a single associations hash.
|
368
421
|
|
369
|
-
|
370
|
-
from serialized attributes and return single associations hash.
|
422
|
+
Configuration options:
|
371
423
|
|
372
|
-
|
424
|
+
- `config.auto_preload_attributes_with_delegate` - default `false`
|
425
|
+
- `config.auto_preload_attributes_with_serializer` - default `false`
|
426
|
+
- `config.auto_hide_attributes_with_preload` - default `false`
|
373
427
|
|
374
|
-
|
375
|
-
|
376
|
-
- `auto_hide_attributes_with_preload` - default `false`
|
377
|
-
|
378
|
-
These options are extremely useful if you want to forget about finding preloads
|
379
|
-
manually.
|
428
|
+
These options are extremely useful if you want to forget about finding
|
429
|
+
preloads manually.
|
380
430
|
|
381
431
|
Preloads can be disabled with the `preload: false` attribute option.
|
382
432
|
Automatically added preloads can be overwritten with the manually specified
|
@@ -386,10 +436,9 @@ For some examples, **please read the comments in the code below**
|
|
386
436
|
|
387
437
|
```ruby
|
388
438
|
class AppSerializer < Serega
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
auto_hide_attributes_with_preload: true
|
439
|
+
config.auto_preload_attributes_with_delegate = true
|
440
|
+
config.auto_preload_attributes_with_serializer = true
|
441
|
+
config.auto_hide_attributes_with_preload = true
|
393
442
|
end
|
394
443
|
|
395
444
|
class UserSerializer < AppSerializer
|
@@ -432,7 +481,7 @@ UserSerializer.new(
|
|
432
481
|
|
433
482
|
---
|
434
483
|
|
435
|
-
|
484
|
+
### SPECIFIC CASE #1: Serializing the same object in association
|
436
485
|
|
437
486
|
For example, you show your current user as "user" and use the same user object
|
438
487
|
to serialize "user_stats". `UserStatSerializer` relies on user fields and any
|
@@ -456,7 +505,7 @@ class UserSerializer < AppSerializer
|
|
456
505
|
end
|
457
506
|
```
|
458
507
|
|
459
|
-
|
508
|
+
### SPECIFIC CASE #2: Serializing multiple associations as a single relation
|
460
509
|
|
461
510
|
For example, "user" has two relations - "new_profile" and "old_profile". Also
|
462
511
|
profiles have the "avatar" association. And you decided to serialize profiles in
|
@@ -490,7 +539,7 @@ UserSerializer.new.preloads
|
|
490
539
|
# => {:new_profile=>{:avatar=>{}}, :old_profile=>{:avatar=>{}}}
|
491
540
|
```
|
492
541
|
|
493
|
-
|
542
|
+
### SPECIFIC CASE #3: Preload association through another association
|
494
543
|
|
495
544
|
```ruby
|
496
545
|
attribute :image,
|
@@ -501,34 +550,32 @@ attribute :image,
|
|
501
550
|
```
|
502
551
|
|
503
552
|
In this case, we don't know if preloads defined in ImageSerializer, should be
|
504
|
-
preloaded to `attachment` or `blob`, so
|
553
|
+
preloaded to `attachment` or `blob`, so `preload_path` must be specified manually.
|
505
554
|
You can specify `preload_path: nil` if you are sure that there are no preloads
|
506
555
|
inside ImageSerializer.
|
507
556
|
|
508
557
|
---
|
509
558
|
|
510
|
-
📌
|
511
|
-
they should be preloaded manually.
|
559
|
+
📌 The built-in preloads functionality only allows to group preloads together
|
560
|
+
in single Hash, but they should be preloaded manually.
|
512
561
|
|
513
|
-
There
|
514
|
-
be used to preload these associations automatically.
|
562
|
+
There is the [activerecord_preloads][activerecord_preloads] plugin that can be used to preload these associations automatically.
|
515
563
|
|
516
|
-
|
564
|
+
## Plugins
|
517
565
|
|
518
|
-
|
566
|
+
### Plugin :activerecord_preloads
|
519
567
|
|
520
568
|
Automatically preloads associations to serialized objects.
|
521
569
|
|
522
570
|
It takes all defined preloads from serialized attributes (including attributes
|
523
|
-
from serialized
|
571
|
+
from serialized associations), merges them into a single associations hash, and then
|
524
572
|
uses ActiveRecord::Associations::Preloader to preload associations to objects.
|
525
573
|
|
526
574
|
```ruby
|
527
575
|
class AppSerializer < Serega
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
auto_hide_attributes_with_preload: false
|
576
|
+
config.auto_preload_attributes_with_delegate = true
|
577
|
+
config.auto_preload_attributes_with_serializer = true
|
578
|
+
config.auto_hide_attributes_with_preload = false
|
532
579
|
|
533
580
|
plugin :activerecord_preloads
|
534
581
|
end
|
@@ -549,170 +596,7 @@ UserSerializer.to_h(user)
|
|
549
596
|
# => preloads {users_stats: {}, albums: { downloads: {} }}
|
550
597
|
```
|
551
598
|
|
552
|
-
For testing purposes preloading can be done manually with
|
553
|
-
`#preload_association_to(obj)` instance method
|
554
|
-
|
555
|
-
### Plugin :batch
|
556
|
-
|
557
|
-
Helps to omit N+1.
|
558
|
-
|
559
|
-
User must specify how attribute values are loaded -
|
560
|
-
`attribute :foo, batch: {loader: SomeLoader, id_method: :id}`.
|
561
|
-
|
562
|
-
The result must be returned as Hash, where each key is one of the provided IDs.
|
563
|
-
|
564
|
-
```ruby
|
565
|
-
class AppSerializer
|
566
|
-
plugin :batch
|
567
|
-
end
|
568
|
-
|
569
|
-
class UserSerializer < AppSerializer
|
570
|
-
attribute :comments_count,
|
571
|
-
batch: { loader: SomeLoader, id_method: :id }
|
572
|
-
|
573
|
-
attribute :company,
|
574
|
-
batch: { loader: SomeLoader, id_method: :id },
|
575
|
-
serializer: CompanySerializer
|
576
|
-
end
|
577
|
-
```
|
578
|
-
|
579
|
-
#### Option :loader
|
580
|
-
|
581
|
-
Loaders can be defined as a Proc, a callable value, or a named Symbol
|
582
|
-
Named loaders should be predefined with
|
583
|
-
`config.batch.define(:loader_name) { |ids| ... })`
|
584
|
-
|
585
|
-
The loader can accept 1 to 3 arguments:
|
586
|
-
|
587
|
-
1. List of IDs (each ID will be found by using the `:id_method` option)
|
588
|
-
1. Context
|
589
|
-
1. PlanPoint - a special object containing information about current
|
590
|
-
attribute and all children and parent attributes. It can be used to preload
|
591
|
-
required associations to batch values.
|
592
|
-
See [example](examples/batch_loader.rb) how
|
593
|
-
to find required preloads when using the `:preloads` plugin.
|
594
|
-
|
595
|
-
```ruby
|
596
|
-
class AppSerializer < Serega
|
597
|
-
plugin :batch, id_method: :id
|
598
|
-
end
|
599
|
-
|
600
|
-
class UserSerializer < Serega
|
601
|
-
# Define loader as a callable object
|
602
|
-
attribute :comments_count,
|
603
|
-
batch: { loader: CountLoader }
|
604
|
-
|
605
|
-
# Define loader as a Proc
|
606
|
-
attribute :comments_count,
|
607
|
-
batch: { loader: proc { |ids| CountLoader.call(ids) } }
|
608
|
-
|
609
|
-
# Define loader as a Symbol
|
610
|
-
config.batch.define(:comments_count_loader) { |ids| CountLoader.call(ids }
|
611
|
-
attribute :comments_count, batch: { loader: :comments_count_loader }
|
612
|
-
end
|
613
|
-
|
614
|
-
class CountLoader
|
615
|
-
def self.call(user_ids)
|
616
|
-
Comment.where(user_id: user_ids).group(:user_id).count
|
617
|
-
end
|
618
|
-
end
|
619
|
-
```
|
620
|
-
|
621
|
-
#### Option :id_method
|
622
|
-
|
623
|
-
The `:batch` plugin can be added with the global `:id_method` option. It can be
|
624
|
-
a Symbol, Proc or any callable value that can accept the current object and
|
625
|
-
context.
|
626
|
-
|
627
|
-
```ruby
|
628
|
-
class SomeSerializer
|
629
|
-
plugin :batch, id_method: :id
|
630
|
-
end
|
631
|
-
|
632
|
-
class UserSerializer < AppSerializer
|
633
|
-
attribute :comments_count,
|
634
|
-
batch: { loader: CommentsCountBatchLoader } # no :id_method here anymore
|
635
|
-
|
636
|
-
attribute :company,
|
637
|
-
batch: { loader: UserCompanyBatchLoader }, # no :id_method here anymore
|
638
|
-
serializer: CompanySerializer
|
639
|
-
end
|
640
|
-
|
641
|
-
```
|
642
|
-
|
643
|
-
However, the global `id_method` option can be overwritten via
|
644
|
-
`config.batch.id_method=` method or in specific attributes with the `id_method`
|
645
|
-
option.
|
646
|
-
|
647
|
-
```ruby
|
648
|
-
class SomeSerializer
|
649
|
-
plugin :batch, id_method: :id # global id_method is `:id`
|
650
|
-
end
|
651
|
-
|
652
|
-
class UserSerializer < AppSerializer
|
653
|
-
# :user_id will be used as default `id_method` for all batch attributes
|
654
|
-
config.batch.id_method = :user_id
|
655
|
-
|
656
|
-
# id_method is :user_id
|
657
|
-
attribute :comments_count,
|
658
|
-
batch: { loader: CommentsCountBatchLoader }
|
659
|
-
|
660
|
-
|
661
|
-
# id_method is :user_id
|
662
|
-
attribute :company,
|
663
|
-
batch: { loader: UserCompanyBatchLoader }, serializer: CompanySerializer
|
664
|
-
|
665
|
-
# id_method is :uuid
|
666
|
-
attribute :points_amount,
|
667
|
-
batch: { loader: PointsBatchLoader, id_method: :uuid }
|
668
|
-
end
|
669
|
-
```
|
670
|
-
|
671
|
-
#### Default value
|
672
|
-
|
673
|
-
The default value for attributes without found value can be specified via
|
674
|
-
`:default` option. By default, attributes without found value will be
|
675
|
-
serialized as a `nil` value. Attributes marked as `many: true` will be
|
676
|
-
serialized as empty array `[]` values.
|
677
|
-
|
678
|
-
```ruby
|
679
|
-
class UserSerializer < AppSerializer
|
680
|
-
# Missing values become empty arrays, as the `many: true` option is specified
|
681
|
-
attribute :companies,
|
682
|
-
batch: {loader: proc {}},
|
683
|
-
serializer: CompanySerializer,
|
684
|
-
many: true
|
685
|
-
|
686
|
-
# Missing values become `0` as specified directly
|
687
|
-
attribute :points_amount, batch: { loader: proc {} }, default: 0
|
688
|
-
end
|
689
|
-
```
|
690
|
-
|
691
|
-
Batch attributes can be marked as hidden by default if the plugin is enabled
|
692
|
-
with the `auto_hide` option. The `auto_hide` option can be changed with
|
693
|
-
the `config.batch.auto_hide=` method.
|
694
|
-
|
695
|
-
Look at [select serialized fields](#selecting-fields) for more information
|
696
|
-
about hiding/showing attributes.
|
697
|
-
|
698
|
-
```ruby
|
699
|
-
class AppSerializer
|
700
|
-
plugin :batch, auto_hide: true
|
701
|
-
end
|
702
|
-
|
703
|
-
class UserSerializer < AppSerializer
|
704
|
-
config.batch.auto_hide = false
|
705
|
-
end
|
706
|
-
```
|
707
|
-
|
708
|
-
---
|
709
|
-
⚠️ ATTENTION: The `:batch` plugin must be added to all serializers that have
|
710
|
-
`:batch` attributes inside nested serializers. For example, when you serialize
|
711
|
-
the `User -> Album -> Song` and the Song has a `batch` attribute, then
|
712
|
-
the `:batch` plugin must be added to the User serializer.
|
713
|
-
|
714
|
-
The best way would be to create one parent `AppSerializer < Serega` serializer
|
715
|
-
and add the `:batch` plugin once to this parent serializer.
|
599
|
+
For testing purposes preloading can be done manually with `#preload_associations_to(obj)` instance method
|
716
600
|
|
717
601
|
### Plugin :root
|
718
602
|
|
@@ -1104,13 +988,13 @@ Bug reports, pull requests and improvements ideas are very welcome!
|
|
1104
988
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
1105
989
|
|
1106
990
|
[activerecord_preloads]: #plugin-activerecord_preloads
|
1107
|
-
[batch]: #
|
991
|
+
[batch-loading]: #batch-loading
|
1108
992
|
[camel_case]: #plugin-camel_case
|
1109
993
|
[context_metadata]: #plugin-context_metadata
|
1110
994
|
[depth_limit]: #plugin-depth_limit
|
1111
995
|
[formatters]: #plugin-formatters
|
1112
996
|
[metadata]: #plugin-metadata
|
1113
|
-
[preloads]: #
|
997
|
+
[preloads]: #preloads
|
1114
998
|
[presenter]: #plugin-presenter
|
1115
999
|
[root]: #plugin-root
|
1116
1000
|
[string_modifiers]: #plugin-string_modifiers
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.30.0
|
data/lib/serega/attribute.rb
CHANGED
@@ -29,6 +29,18 @@ class Serega
|
|
29
29
|
# @return [Boolean, nil] Attribute :hide option
|
30
30
|
attr_reader :hide
|
31
31
|
|
32
|
+
# Attribute :preloads option
|
33
|
+
# @return [Hash, nil] Attribute :preloads option
|
34
|
+
attr_reader :preloads
|
35
|
+
|
36
|
+
# Attribute :preloads_path option
|
37
|
+
# @return [Array, nil] Attribute :preloads_path option
|
38
|
+
attr_reader :preloads_path
|
39
|
+
|
40
|
+
# Batch loader names required to detect attribute value
|
41
|
+
# @return [Array<Symbol>] Batch loader names
|
42
|
+
attr_reader :batch_loaders
|
43
|
+
|
32
44
|
#
|
33
45
|
# Initializes new attribute
|
34
46
|
#
|
@@ -79,8 +91,20 @@ class Serega
|
|
79
91
|
#
|
80
92
|
# @return [Object] Serialized attribute value
|
81
93
|
#
|
82
|
-
def value(object, context)
|
83
|
-
|
94
|
+
def value(object, context, batches: nil)
|
95
|
+
# Signatires should match allowed signatures in CheckOptValue and CheckBlock
|
96
|
+
result =
|
97
|
+
case value_block_signature
|
98
|
+
when "1" then value_block.call(object)
|
99
|
+
when "1_ctx" then value_block.call(object, ctx: context)
|
100
|
+
when "1_batches" then value_block.call(object, batches: batches)
|
101
|
+
when "1_batches_ctx" then value_block.call(object, ctx: context, batches: batches)
|
102
|
+
when "2" then value_block.call(object, context)
|
103
|
+
when "2_batches_ctx" then value_block.call(object, context, ctx: context, batches: batches)
|
104
|
+
else value_block.call # signature is "0" - no parameters
|
105
|
+
end
|
106
|
+
|
107
|
+
result.nil? ? default : result
|
84
108
|
end
|
85
109
|
|
86
110
|
#
|
@@ -108,15 +132,19 @@ class Serega
|
|
108
132
|
|
109
133
|
private
|
110
134
|
|
111
|
-
attr_reader :value_block
|
135
|
+
attr_reader :value_block, :value_block_signature
|
112
136
|
|
113
137
|
def set_normalized_vars(normalizer)
|
114
138
|
@name = normalizer.name
|
115
139
|
@many = normalizer.many
|
116
140
|
@default = normalizer.default
|
117
141
|
@value_block = normalizer.value_block
|
142
|
+
@value_block_signature = normalizer.value_block_signature
|
118
143
|
@hide = normalizer.hide
|
119
144
|
@serializer = normalizer.serializer
|
145
|
+
@preloads = normalizer.preloads
|
146
|
+
@preloads_path = normalizer.preloads_path
|
147
|
+
@batch_loaders = normalizer.batch_loaders
|
120
148
|
end
|
121
149
|
end
|
122
150
|
|