serega 0.20.1 → 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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +113 -226
  3. data/VERSION +1 -1
  4. data/lib/serega/attribute.rb +31 -3
  5. data/lib/serega/attribute_normalizer.rb +143 -42
  6. data/lib/serega/batch/attribute_loader.rb +70 -0
  7. data/lib/serega/batch/attribute_loaders.rb +59 -0
  8. data/lib/serega/batch/auto_resolver.rb +24 -0
  9. data/lib/serega/batch/auto_resolver_factory.rb +48 -0
  10. data/lib/serega/batch/loader.rb +67 -0
  11. data/lib/serega/config.rb +59 -1
  12. data/lib/serega/object_serializer.rb +11 -9
  13. data/lib/serega/plan.rb +16 -0
  14. data/lib/serega/plan_point.rb +31 -5
  15. data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +42 -23
  16. data/lib/serega/plugins/activerecord_preloads/lib/active_record_objects.rb +31 -0
  17. data/lib/serega/plugins/camel_case/camel_case.rb +1 -1
  18. data/lib/serega/plugins/formatters/formatters.rb +79 -22
  19. data/lib/serega/plugins/if/if.rb +41 -22
  20. data/lib/serega/plugins/if/validations/check_opt_if.rb +27 -6
  21. data/lib/serega/plugins/if/validations/check_opt_if_value.rb +27 -6
  22. data/lib/serega/plugins/if/validations/check_opt_unless.rb +27 -6
  23. data/lib/serega/plugins/if/validations/check_opt_unless_value.rb +27 -6
  24. data/lib/serega/plugins/metadata/meta_attribute.rb +9 -10
  25. data/lib/serega/plugins/metadata/validations/check_block.rb +2 -4
  26. data/lib/serega/plugins/metadata/validations/check_opt_value.rb +2 -3
  27. data/lib/serega/plugins/presenter/presenter.rb +1 -1
  28. data/lib/serega/plugins/string_modifiers/parse_string_modifiers.rb +43 -30
  29. data/lib/serega/utils/format_user_preloads.rb +58 -0
  30. data/lib/serega/utils/method_signature.rb +89 -0
  31. data/lib/serega/utils/preload_paths.rb +53 -0
  32. data/lib/serega/utils/preloads_constructor.rb +77 -0
  33. data/lib/serega/validations/attribute/check_block.rb +38 -6
  34. data/lib/serega/validations/attribute/check_opt_batch.rb +101 -0
  35. data/lib/serega/validations/attribute/check_opt_const.rb +1 -0
  36. data/lib/serega/validations/attribute/check_opt_delegate.rb +1 -0
  37. data/lib/serega/validations/attribute/check_opt_method.rb +1 -0
  38. data/lib/serega/{plugins/preloads/validations → validations/attribute}/check_opt_preload.rb +2 -2
  39. data/lib/serega/{plugins/preloads/validations → validations/attribute}/check_opt_preload_path.rb +3 -3
  40. data/lib/serega/validations/attribute/check_opt_value.rb +35 -9
  41. data/lib/serega/validations/check_attribute_params.rb +3 -2
  42. data/lib/serega/validations/check_batch_loader_params.rb +80 -0
  43. data/lib/serega/validations/initiate/check_modifiers.rb +1 -1
  44. data/lib/serega.rb +110 -11
  45. metadata +17 -37
  46. data/lib/serega/plugins/batch/batch.rb +0 -168
  47. data/lib/serega/plugins/batch/lib/batch_config.rb +0 -77
  48. data/lib/serega/plugins/batch/lib/loader.rb +0 -113
  49. data/lib/serega/plugins/batch/lib/loaders.rb +0 -45
  50. data/lib/serega/plugins/batch/lib/modules/attribute.rb +0 -26
  51. data/lib/serega/plugins/batch/lib/modules/attribute_normalizer.rb +0 -78
  52. data/lib/serega/plugins/batch/lib/modules/check_attribute_params.rb +0 -22
  53. data/lib/serega/plugins/batch/lib/modules/config.rb +0 -23
  54. data/lib/serega/plugins/batch/lib/modules/object_serializer.rb +0 -46
  55. data/lib/serega/plugins/batch/lib/modules/plan_point.rb +0 -22
  56. data/lib/serega/plugins/batch/lib/plugins_extensions/activerecord_preloads.rb +0 -43
  57. data/lib/serega/plugins/batch/lib/plugins_extensions/formatters.rb +0 -54
  58. data/lib/serega/plugins/batch/lib/plugins_extensions/if.rb +0 -31
  59. data/lib/serega/plugins/batch/lib/plugins_extensions/preloads.rb +0 -34
  60. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_id_method.rb +0 -43
  61. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb +0 -59
  62. data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +0 -62
  63. data/lib/serega/plugins/preloads/lib/format_user_preloads.rb +0 -60
  64. data/lib/serega/plugins/preloads/lib/modules/attribute.rb +0 -28
  65. data/lib/serega/plugins/preloads/lib/modules/attribute_normalizer.rb +0 -99
  66. data/lib/serega/plugins/preloads/lib/modules/check_attribute_params.rb +0 -22
  67. data/lib/serega/plugins/preloads/lib/modules/config.rb +0 -19
  68. data/lib/serega/plugins/preloads/lib/modules/plan_point.rb +0 -41
  69. data/lib/serega/plugins/preloads/lib/preload_paths.rb +0 -53
  70. data/lib/serega/plugins/preloads/lib/preloads_config.rb +0 -62
  71. data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +0 -79
  72. data/lib/serega/plugins/preloads/preloads.rb +0 -162
  73. data/lib/serega/utils/params_count.rb +0 -50
  74. 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: 11094f59dcd5d1d918604998ac267dbd13297bf0f70a9f3da5fd5ba74eb76b06
4
- data.tar.gz: 97d1a419769b73937afde735e24210690e2f7702943d60db5be4252301e07255
3
+ metadata.gz: e72fdcef5f9baae349a3eae9c55029ef979cffdaabb5ab74e2f5228acaf93cf5
4
+ data.tar.gz: ef9375aa580c68856c78a016fc3dfcfef49f534d00c96af4deb4cbce20d9ce84
5
5
  SHA512:
6
- metadata.gz: f9e3bfb207894f09c9c88658bad62e2a28e8b4d44648a74f807d332862b0cd7bdc67870f0d06786de0eb64537fd1a824c5d4f375406b71d18410b63c5a8c5320
7
- data.tar.gz: 1171f2aabb7ce1c63a9e2f95f5413e9d80a882326ada4fac561b527c8a1adcc263ff19d6224eb070f0ebb333254ff48c9c7f56861ff6418732848cde3ffbc5b5
6
+ metadata.gz: 8ee1f484b9b1b66ada9570086f687c74f2b833c302e4685946d3787d6bce92e06d670e4ef0eae23a13c6c4c5090cc025c3bd73f5ea03c002c5bdcaffb2377238
7
+ data.tar.gz: 43ef3d1bbc6b2dd209674d237287ae68efead6e17bfc9ea1d67b8957998d7fb8f346119f8e12c7469e5d26a1f9bda099f507506ca32a2b08eed611c529105dd1
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
+ # Serega Ruby Serializer
2
+
1
3
  [![Gem Version](https://badge.fury.io/rb/serega.svg)](https://badge.fury.io/rb/serega)
2
4
  [![GitHub Actions](https://github.com/aglushkov/serega/actions/workflows/main.yml/badge.svg?event=push)](https://github.com/aglushkov/serega/actions/workflows/main.yml)
3
5
  [![Test Coverage](https://api.codeclimate.com/v1/badges/f10c0659e16e25e49faa/test_coverage)](https://codeclimate.com/github/aglushkov/serega/test_coverage)
4
6
  [![Maintainability](https://api.codeclimate.com/v1/badges/f10c0659e16e25e49faa/maintainability)](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][batch], [preloads][preloads] or
22
- [activerecord_preloads][activerecord_preloads] plugins)
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 keeping
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 serialized object
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 attribute
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 `object.is_a?(Enumerable)`
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` can be specified when enabled `:preloads` plugin
114
- # It allows to specify associations to preload to attribute value
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: `username,enemies(username,email)`. This can be very useful
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
- ## Plugins
416
+ ## Preloads
366
417
 
367
- ### Plugin :preloads
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
- Allows to define `:preloads` to attributes and then allows to merge preloads
370
- from serialized attributes and return single associations hash.
422
+ Configuration options:
371
423
 
372
- Plugin accepts options:
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
- - `auto_preload_attributes_with_delegate` - default `false`
375
- - `auto_preload_attributes_with_serializer` - default `false`
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
- plugin :preloads,
390
- auto_preload_attributes_with_delegate: true,
391
- auto_preload_attributes_with_serializer: true,
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
- #### SPECIFIC CASE #1: Serializing the same object in association
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
- #### SPECIFIC CASE #2: Serializing multiple associations as a single relation
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
- #### SPECIFIC CASE #3: Preload association through another association
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 please specify `preload_path` manually.
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
- 📌 Plugin `:preloads` only allows to group preloads together in single Hash, but
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 are only [activerecord_preloads][activerecord_preloads] plugin that can
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
- ### Plugin :activerecord_preloads
564
+ ## Plugins
517
565
 
518
- (depends on [preloads][preloads] plugin, that must be loaded first)
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 relations), merges them into a single associations hash, and then
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
- plugin :preloads,
529
- auto_preload_attributes_with_delegate: true,
530
- auto_preload_attributes_with_serializer: true,
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,167 +596,7 @@ UserSerializer.to_h(user)
549
596
  # => preloads {users_stats: {}, albums: { downloads: {} }}
550
597
  ```
551
598
 
552
- ### Plugin :batch
553
-
554
- Helps to omit N+1.
555
-
556
- User must specify how attribute values are loaded -
557
- `attribute :foo, batch: {loader: SomeLoader, id_method: :id}`.
558
-
559
- The result must be returned as Hash, where each key is one of the provided IDs.
560
-
561
- ```ruby
562
- class AppSerializer
563
- plugin :batch
564
- end
565
-
566
- class UserSerializer < AppSerializer
567
- attribute :comments_count,
568
- batch: { loader: SomeLoader, id_method: :id }
569
-
570
- attribute :company,
571
- batch: { loader: SomeLoader, id_method: :id },
572
- serializer: CompanySerializer
573
- end
574
- ```
575
-
576
- #### Option :loader
577
-
578
- Loaders can be defined as a Proc, a callable value, or a named Symbol
579
- Named loaders should be predefined with
580
- `config.batch.define(:loader_name) { |ids| ... })`
581
-
582
- The loader can accept 1 to 3 arguments:
583
-
584
- 1. List of IDs (each ID will be found by using the `:id_method` option)
585
- 1. Context
586
- 1. PlanPoint - a special object containing information about current
587
- attribute and all children and parent attributes. It can be used to preload
588
- required associations to batch values.
589
- See [example](examples/batch_loader.rb) how
590
- to find required preloads when using the `:preloads` plugin.
591
-
592
- ```ruby
593
- class AppSerializer < Serega
594
- plugin :batch, id_method: :id
595
- end
596
-
597
- class UserSerializer < Serega
598
- # Define loader as a callable object
599
- attribute :comments_count,
600
- batch: { loader: CountLoader }
601
-
602
- # Define loader as a Proc
603
- attribute :comments_count,
604
- batch: { loader: proc { |ids| CountLoader.call(ids) } }
605
-
606
- # Define loader as a Symbol
607
- config.batch.define(:comments_count_loader) { |ids| CountLoader.call(ids }
608
- attribute :comments_count, batch: { loader: :comments_count_loader }
609
- end
610
-
611
- class CountLoader
612
- def self.call(user_ids)
613
- Comment.where(user_id: user_ids).group(:user_id).count
614
- end
615
- end
616
- ```
617
-
618
- #### Option :id_method
619
-
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.
623
-
624
- ```ruby
625
- class SomeSerializer
626
- plugin :batch, id_method: :id
627
- end
628
-
629
- class UserSerializer < AppSerializer
630
- attribute :comments_count,
631
- batch: { loader: CommentsCountBatchLoader } # no :id_method here anymore
632
-
633
- attribute :company,
634
- batch: { loader: UserCompanyBatchLoader }, # no :id_method here anymore
635
- serializer: CompanySerializer
636
- end
637
-
638
- ```
639
-
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.
643
-
644
- ```ruby
645
- class SomeSerializer
646
- plugin :batch, id_method: :id # global id_method is `:id`
647
- end
648
-
649
- class UserSerializer < AppSerializer
650
- # :user_id will be used as default `id_method` for all batch attributes
651
- config.batch.id_method = :user_id
652
-
653
- # id_method is :user_id
654
- attribute :comments_count,
655
- batch: { loader: CommentsCountBatchLoader }
656
-
657
-
658
- # id_method is :user_id
659
- attribute :company,
660
- batch: { loader: UserCompanyBatchLoader }, serializer: CompanySerializer
661
-
662
- # id_method is :uuid
663
- attribute :points_amount,
664
- batch: { loader: PointsBatchLoader, id_method: :uuid }
665
- end
666
- ```
667
-
668
- #### Default value
669
-
670
- The default value for attributes without found value can be specified via
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.
674
-
675
- ```ruby
676
- class UserSerializer < AppSerializer
677
- # Missing values become empty arrays, as the `many: true` option is specified
678
- attribute :companies,
679
- batch: {loader: proc {}},
680
- serializer: CompanySerializer,
681
- many: true
682
-
683
- # Missing values become `0` as specified directly
684
- attribute :points_amount, batch: { loader: proc {} }, default: 0
685
- end
686
- ```
687
-
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.
691
-
692
- Look at [select serialized fields](#selecting-fields) for more information
693
- about hiding/showing attributes.
694
-
695
- ```ruby
696
- class AppSerializer
697
- plugin :batch, auto_hide: true
698
- end
699
-
700
- class UserSerializer < AppSerializer
701
- config.batch.auto_hide = false
702
- end
703
- ```
704
-
705
- ---
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.
710
-
711
- The best way would be to create one parent `AppSerializer < Serega` serializer
712
- 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
713
600
 
714
601
  ### Plugin :root
715
602
 
@@ -1101,13 +988,13 @@ Bug reports, pull requests and improvements ideas are very welcome!
1101
988
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
1102
989
 
1103
990
  [activerecord_preloads]: #plugin-activerecord_preloads
1104
- [batch]: #plugin-batch
991
+ [batch-loading]: #batch-loading
1105
992
  [camel_case]: #plugin-camel_case
1106
993
  [context_metadata]: #plugin-context_metadata
1107
994
  [depth_limit]: #plugin-depth_limit
1108
995
  [formatters]: #plugin-formatters
1109
996
  [metadata]: #plugin-metadata
1110
- [preloads]: #plugin-preloads
997
+ [preloads]: #preloads
1111
998
  [presenter]: #plugin-presenter
1112
999
  [root]: #plugin-root
1113
1000
  [string_modifiers]: #plugin-string_modifiers
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.20.1
1
+ 0.30.0
@@ -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
- value_block.call(object, context)
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