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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +113 -229
  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 +25 -13
  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/utils/format_user_preloads.rb +58 -0
  29. data/lib/serega/utils/method_signature.rb +89 -0
  30. data/lib/serega/utils/preload_paths.rb +53 -0
  31. data/lib/serega/utils/preloads_constructor.rb +77 -0
  32. data/lib/serega/validations/attribute/check_block.rb +38 -6
  33. data/lib/serega/validations/attribute/check_opt_batch.rb +101 -0
  34. data/lib/serega/validations/attribute/check_opt_const.rb +1 -0
  35. data/lib/serega/validations/attribute/check_opt_delegate.rb +1 -0
  36. data/lib/serega/validations/attribute/check_opt_method.rb +1 -0
  37. data/lib/serega/{plugins/preloads/validations → validations/attribute}/check_opt_preload.rb +2 -2
  38. data/lib/serega/{plugins/preloads/validations → validations/attribute}/check_opt_preload_path.rb +3 -3
  39. data/lib/serega/validations/attribute/check_opt_value.rb +35 -9
  40. data/lib/serega/validations/check_attribute_params.rb +3 -2
  41. data/lib/serega/validations/check_batch_loader_params.rb +80 -0
  42. data/lib/serega/validations/initiate/check_modifiers.rb +1 -1
  43. data/lib/serega.rb +98 -7
  44. metadata +17 -37
  45. data/lib/serega/plugins/batch/batch.rb +0 -168
  46. data/lib/serega/plugins/batch/lib/batch_config.rb +0 -77
  47. data/lib/serega/plugins/batch/lib/loader.rb +0 -113
  48. data/lib/serega/plugins/batch/lib/loaders.rb +0 -45
  49. data/lib/serega/plugins/batch/lib/modules/attribute.rb +0 -26
  50. data/lib/serega/plugins/batch/lib/modules/attribute_normalizer.rb +0 -78
  51. data/lib/serega/plugins/batch/lib/modules/check_attribute_params.rb +0 -22
  52. data/lib/serega/plugins/batch/lib/modules/config.rb +0 -23
  53. data/lib/serega/plugins/batch/lib/modules/object_serializer.rb +0 -46
  54. data/lib/serega/plugins/batch/lib/modules/plan_point.rb +0 -22
  55. data/lib/serega/plugins/batch/lib/plugins_extensions/activerecord_preloads.rb +0 -43
  56. data/lib/serega/plugins/batch/lib/plugins_extensions/formatters.rb +0 -54
  57. data/lib/serega/plugins/batch/lib/plugins_extensions/if.rb +0 -31
  58. data/lib/serega/plugins/batch/lib/plugins_extensions/preloads.rb +0 -34
  59. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_id_method.rb +0 -43
  60. data/lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb +0 -59
  61. data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +0 -62
  62. data/lib/serega/plugins/preloads/lib/format_user_preloads.rb +0 -60
  63. data/lib/serega/plugins/preloads/lib/modules/attribute.rb +0 -28
  64. data/lib/serega/plugins/preloads/lib/modules/attribute_normalizer.rb +0 -99
  65. data/lib/serega/plugins/preloads/lib/modules/check_attribute_params.rb +0 -22
  66. data/lib/serega/plugins/preloads/lib/modules/config.rb +0 -19
  67. data/lib/serega/plugins/preloads/lib/modules/plan_point.rb +0 -41
  68. data/lib/serega/plugins/preloads/lib/preload_paths.rb +0 -53
  69. data/lib/serega/plugins/preloads/lib/preloads_config.rb +0 -62
  70. data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +0 -79
  71. data/lib/serega/plugins/preloads/preloads.rb +0 -162
  72. data/lib/serega/utils/params_count.rb +0 -50
  73. 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: 9ea0f19662815497e76a3ace9b247e90268f7c44a3cf917b97a186887043fc7b
4
- data.tar.gz: fb13ebbaa26603c0eda053e78634aaecf1a8d040c73b5824892b03c2b0d2a70d
3
+ metadata.gz: e72fdcef5f9baae349a3eae9c55029ef979cffdaabb5ab74e2f5228acaf93cf5
4
+ data.tar.gz: ef9375aa580c68856c78a016fc3dfcfef49f534d00c96af4deb4cbce20d9ce84
5
5
  SHA512:
6
- metadata.gz: a5847251f29dfd44a7da2cd9b5a99411aa430323d1d57e7706e176cc234f96ff7575298c5909c753aa4f3a2895d41a48c3c6a5b67a95e0bdac935f38c5c6f15d
7
- data.tar.gz: 4fdf12f075816ea3ba155ace47b6f5e6c7b3b0bcd000179e43e4edf356e63b19360de227b6112b4fe98e310975da4afebc3d5c86476deadc749c4197330ddfd0
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,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]: #plugin-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]: #plugin-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.21.0
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