serega 0.40.0 → 0.41.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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +21 -0
  3. data/README.md +204 -108
  4. data/VERSION +1 -1
  5. data/lib/serega/attribute.rb +2 -0
  6. data/lib/serega/attribute_normalizer.rb +2 -0
  7. data/lib/serega/attribute_value_resolvers/batch.rb +3 -0
  8. data/lib/serega/attribute_value_resolvers/const.rb +3 -0
  9. data/lib/serega/attribute_value_resolvers/delegate.rb +4 -0
  10. data/lib/serega/attribute_value_resolvers/hash_access.rb +6 -0
  11. data/lib/serega/attribute_value_resolvers/keyword.rb +3 -0
  12. data/lib/serega/data_builder.rb +2 -0
  13. data/lib/serega/engine/level.rb +2 -0
  14. data/lib/serega/engine/level_queue.rb +2 -0
  15. data/lib/serega/engine/loader.rb +3 -0
  16. data/lib/serega/helpers/serializer_class_helper.rb +2 -0
  17. data/lib/serega/object_serializer.rb +2 -0
  18. data/lib/serega/plan.rb +3 -0
  19. data/lib/serega/plan_point.rb +2 -0
  20. data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +22 -16
  21. data/lib/serega/plugins/activerecord_preloads/lib/preloader.rb +6 -0
  22. data/lib/serega/plugins/camel_case/camel_case.rb +13 -13
  23. data/lib/serega/plugins/context_metadata/context_metadata.rb +8 -1
  24. data/lib/serega/plugins/depth_limit/depth_limit.rb +15 -12
  25. data/lib/serega/plugins/explicit_many_option/explicit_many_option.rb +12 -10
  26. data/lib/serega/plugins/explicit_many_option/validations/check_opt_many.rb +1 -0
  27. data/lib/serega/plugins/formatters/formatters.rb +18 -10
  28. data/lib/serega/plugins/if/if.rb +29 -32
  29. data/lib/serega/plugins/if/validations/check_opt_if.rb +1 -0
  30. data/lib/serega/plugins/if/validations/check_opt_if_value.rb +1 -0
  31. data/lib/serega/plugins/if/validations/check_opt_unless.rb +1 -0
  32. data/lib/serega/plugins/if/validations/check_opt_unless_value.rb +1 -0
  33. data/lib/serega/plugins/metadata/meta_attribute.rb +2 -0
  34. data/lib/serega/plugins/metadata/metadata.rb +14 -12
  35. data/lib/serega/plugins/metadata/validations/check_block.rb +1 -0
  36. data/lib/serega/plugins/metadata/validations/check_opt_const.rb +1 -0
  37. data/lib/serega/plugins/metadata/validations/check_opt_hide_empty.rb +1 -0
  38. data/lib/serega/plugins/metadata/validations/check_opt_hide_nil.rb +1 -0
  39. data/lib/serega/plugins/metadata/validations/check_opt_value.rb +1 -0
  40. data/lib/serega/plugins/metadata/validations/check_opts.rb +1 -0
  41. data/lib/serega/plugins/metadata/validations/check_path.rb +1 -0
  42. data/lib/serega/plugins/presenter/presenter.rb +10 -7
  43. data/lib/serega/plugins/root/root.rb +12 -6
  44. data/lib/serega/plugins/string_modifiers/parse_string_modifiers.rb +1 -0
  45. data/lib/serega/plugins/string_modifiers/string_modifiers.rb +21 -0
  46. data/lib/serega/plugins.rb +2 -0
  47. data/lib/serega/utils/collection_detector.rb +2 -0
  48. data/lib/serega/utils/enum_deep_dup.rb +2 -0
  49. data/lib/serega/utils/enum_deep_freeze.rb +2 -0
  50. data/lib/serega/utils/method_signature.rb +2 -0
  51. data/lib/serega/utils/serialized_attribute_error.rb +2 -0
  52. data/lib/serega/utils/symbol_name.rb +2 -0
  53. data/lib/serega/utils/to_hash.rb +2 -0
  54. data/lib/serega/validations/attribute/check_block.rb +3 -0
  55. data/lib/serega/validations/attribute/check_name.rb +3 -0
  56. data/lib/serega/validations/attribute/check_opt_base_serializer.rb +3 -0
  57. data/lib/serega/validations/attribute/check_opt_batch.rb +3 -0
  58. data/lib/serega/validations/attribute/check_opt_const.rb +3 -0
  59. data/lib/serega/validations/attribute/check_opt_delegate.rb +3 -0
  60. data/lib/serega/validations/attribute/check_opt_hash_access.rb +3 -0
  61. data/lib/serega/validations/attribute/check_opt_hide.rb +3 -0
  62. data/lib/serega/validations/attribute/check_opt_many.rb +3 -0
  63. data/lib/serega/validations/attribute/check_opt_method.rb +3 -0
  64. data/lib/serega/validations/attribute/check_opt_preload.rb +3 -0
  65. data/lib/serega/validations/attribute/check_opt_serializer.rb +3 -0
  66. data/lib/serega/validations/attribute/check_opt_value.rb +3 -0
  67. data/lib/serega/validations/check_attribute_params.rb +3 -0
  68. data/lib/serega/validations/check_batch_loader_params.rb +3 -0
  69. data/lib/serega/validations/check_initiate_params.rb +3 -0
  70. data/lib/serega/validations/check_serialize_params.rb +3 -0
  71. data/lib/serega/validations/initiate/check_modifiers.rb +3 -0
  72. data/lib/serega/validations/utils/check_allowed_keys.rb +3 -0
  73. data/lib/serega/validations/utils/check_opt_is_bool.rb +3 -0
  74. data/lib/serega/validations/utils/check_opt_is_hash.rb +3 -0
  75. data/lib/serega/validations/utils/check_opt_is_string_or_symbol.rb +3 -0
  76. data/lib/serega.rb +95 -1
  77. metadata +13 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42da0ff66dc5eeb282fbb72144f78bcfc8ded1aa8609122ded10093f6c83dd96
4
- data.tar.gz: f9fb2a2628756540cd08a5449a21883b0cf28ec45db8f461ee0944fd0f3812e9
3
+ metadata.gz: 4d54f033e70155c517c02d15cd9a4eb5399e385b0ffdd68d8c6c2b390201a007
4
+ data.tar.gz: 23257bef39a343bea74c063f452fa1feed58738c454e467838cceb2fecdbe862
5
5
  SHA512:
6
- metadata.gz: 1355573b7cb8d30ffbf844be46c03c4a5233fa9c190fa5e03fd39c0d1a396b9f7a19e7e2d4cf3afbd0445cd1d8dfc8ebbd1df6d77d91d338c0ede561c836fb99
7
- data.tar.gz: 90ccd454b970598f9921ec1a6c9feab325a373d5628f884515141d34a3a72e7012273e37aa903046e064876f92bd956783ef3a9b93732ee23a8bcc8bec154ee3
6
+ metadata.gz: 2a0df718af2b8f019af2fe9dc8bb82dc21a03631a211ecc00a3fd4e83d6ed3324505727c9124975181869fd11fb452e7670a060ae7e04eddd161186e3353366f
7
+ data.tar.gz: e5bc125a847e929bdaeb7ce044adbb4e866324d702032ee19abd76208949f406b166f4ecf111453d0f7130aa3dfb7d36e3269010df8adc7e05c50eba3c3a52cd
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Andrey Glushkov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md CHANGED
@@ -4,7 +4,8 @@
4
4
  [![GitHub Actions][build-badge]][build]
5
5
 
6
6
  The Serega Ruby Serializer provides easy and powerful DSL to describe your
7
- objects and serialize them to Hash or JSON.
7
+ objects and serialize them to Hash, ready for further conversion to JSON or
8
+ other formats.
8
9
 
9
10
  ---
10
11
 
@@ -18,6 +19,7 @@ It has some great features:
18
19
  - Secure from malicious queries with [depth_limit][depth_limit] plugin
19
20
  - Solutions for N+1 problem (via built-in [batch loading](#batch-loading), [preloads][preloads] or
20
21
  [activerecord_preloads][activerecord_preloads] plugin)
22
+ - Load serialized objects by ids ([prepare_initial_objects](#prepare-initial-objects))
21
23
  - Built-in object presenter ([presenter][presenter] plugin)
22
24
  - Adding custom metadata (via [metadata][metadata] or
23
25
  [context_metadata][context_metadata] plugins)
@@ -28,10 +30,51 @@ It has some great features:
28
30
  - Auto camelCase keys - [camel_case][camel_case] plugin
29
31
  - Serializing Hash records - [hash_access][hash_access] attribute option
30
32
 
33
+ ## Table of Contents
34
+
35
+ <!-- toc -->
36
+
37
+ - [Installation](#installation)
38
+ - [Usage](#usage)
39
+ - [Define serializers](#define-serializers)
40
+ - [Adding attributes](#adding-attributes)
41
+ - [Defining a nested serializer with a block](#defining-a-nested-serializer-with-a-block)
42
+ - [Serializing](#serializing)
43
+ - [Selecting Fields](#selecting-fields)
44
+ - [Using Context](#using-context)
45
+ - [Batch Loading](#batch-loading)
46
+ - [Prepare Initial Objects](#prepare-initial-objects)
47
+ - [Configuration](#configuration)
48
+ - [Preloads](#preloads)
49
+ - [Serializing the same object in association](#serializing-the-same-object-in-association)
50
+ - [Custom preloading](#custom-preloading)
51
+ - [Serializing Hash records](#serializing-hash-records)
52
+ - [Plugins](#plugins)
53
+ - [Plugin :activerecord_preloads](#plugin-activerecord_preloads)
54
+ - [Plugin :root](#plugin-root)
55
+ - [Plugin :metadata](#plugin-metadata)
56
+ - [Plugin :context_metadata](#plugin-context_metadata)
57
+ - [Plugin :formatters](#plugin-formatters)
58
+ - [Plugin :presenter](#plugin-presenter)
59
+ - [Plugin :string_modifiers](#plugin-string_modifiers)
60
+ - [Plugin :if](#plugin-if)
61
+ - [Plugin :camel_case](#plugin-camel_case)
62
+ - [Plugin :depth_limit](#plugin-depth_limit)
63
+ - [Plugin :explicit_many_option](#plugin-explicit_many_option)
64
+ - [Errors](#errors)
65
+ - [Release](#release)
66
+ - [Development](#development)
67
+ - [Contributing](#contributing)
68
+ - [License](#license)
69
+
70
+ <!-- tocstop -->
71
+
31
72
  ## Installation
32
73
 
33
74
  `bundle add serega`
34
75
 
76
+ ## Usage
77
+
35
78
  ### Define serializers
36
79
 
37
80
  Most apps should define **base serializer** with common plugins and settings to
@@ -462,6 +505,77 @@ class UserSerializer < Serega
462
505
  end
463
506
  ```
464
507
 
508
+ ### Prepare Initial Objects
509
+
510
+ `prepare_initial_objects` replaces the serialized objects before serialization
511
+ starts, so a serializer can accept ids or other references and load the records
512
+ itself.
513
+
514
+ ```ruby
515
+ class UserSerializer < Serega
516
+ # OccamsRecord returns read-only records — faster and with a much lower
517
+ # memory footprint than ActiveRecord models
518
+ prepare_initial_objects { |user_ids| OccamsRecord.query(User.where(id: user_ids)).run }
519
+
520
+ attribute :first_name
521
+ end
522
+
523
+ UserSerializer.to_h(["17", "42"]) # => [{first_name: "Ann"}, {first_name: "Bob"}]
524
+ ```
525
+
526
+ The handler accepts the serialization context as a second positional or a
527
+ `:ctx` keyword argument, and can be provided as a callable value:
528
+
529
+ ```ruby
530
+ class UserSerializer < Serega
531
+ prepare_initial_objects { |ids, ctx| OccamsRecord.query(User.where(id: ids, account: ctx[:account])).run }
532
+ # or
533
+ prepare_initial_objects { |ids, ctx:| OccamsRecord.query(User.where(id: ids, account: ctx[:account])).run }
534
+ # or
535
+ prepare_initial_objects UsersLoader
536
+ end
537
+ ```
538
+
539
+ The handler runs once per serialization, before the `:many` option is detected,
540
+ so it can turn a single object into a collection and back:
541
+
542
+ ```ruby
543
+ class UserSerializer < Serega
544
+ prepare_initial_objects { |user_id| OccamsRecord.query(User.where(id: user_id)).run }
545
+
546
+ attribute :first_name
547
+ end
548
+
549
+ UserSerializer.to_h("17") # => [{first_name: "Ann"}] - an array, as the handler returned a collection
550
+ ```
551
+
552
+ A provided `:many` option is used as is. A handler returning `nil` serializes
553
+ `nil`, and errors raised inside the handler are not wrapped.
554
+
555
+ Declared preloads are applied to the prepared objects, so the handler pairs
556
+ with [preloads][preloads] and the
557
+ [activerecord_preloads][activerecord_preloads] plugin:
558
+
559
+ ```ruby
560
+ class UserSerializer < Serega
561
+ plugin :activerecord_preloads
562
+
563
+ prepare_initial_objects { |user_ids| User.where(id: user_ids) }
564
+
565
+ attribute :albums_count, preload: :albums, value: proc { |user| user.albums.size }
566
+ end
567
+ ```
568
+
569
+ ⚠️ The `:preload` option needs ActiveRecord objects. When the handler returns
570
+ anything else — OccamsRecord read-only records, Structs, Hashes, plain objects
571
+ — preloading raises `Serega::SeregaError` (`Can't preload ... to ...`) during
572
+ serialization. Load that data with [batch loading](#batch-loading) instead,
573
+ which works with any objects.
574
+
575
+ The handler runs for the serialized objects only, and not for objects of nested
576
+ serializers. It is inherited by subclasses, so declare it on concrete
577
+ serializers rather than on a base serializer shared by all of them.
578
+
465
579
  ## Configuration
466
580
 
467
581
  Here are the default options. Other options can be added with plugins.
@@ -591,7 +705,7 @@ end
591
705
 
592
706
  ---
593
707
 
594
- ### SPECIFIC CASE: Serializing the same object in association
708
+ ### Serializing the same object in association
595
709
 
596
710
  For example, you show your current user as "user" and use the same user object
597
711
  to serialize "user_stats". `UserStatSerializer` relies on user fields and any
@@ -749,31 +863,38 @@ handler; to use a different ORM, register your own instead.
749
863
  ```ruby
750
864
  class AppSerializer < Serega
751
865
  config.auto_preload = true
752
- config.hide_by_default = false
753
-
754
866
  plugin :activerecord_preloads
755
867
  end
756
868
 
869
+ class AlbumSerializer < AppSerializer
870
+ # no preloads
871
+ attribute :title
872
+
873
+ # preloads :downloads, as manually specified
874
+ attribute :downloads_count, preload: :downloads, value: proc { |album| album.downloads.count }
875
+ end
876
+
757
877
  class UserSerializer < AppSerializer
878
+ # no preloads
758
879
  attribute :username
880
+
881
+ # preloads :user_stats, as auto_preload is enabled for :delegate attributes
759
882
  attribute :comments_count, delegate: { to: :user_stats }
760
- attribute :albums, serializer: AlbumSerializer
761
- end
762
883
 
763
- class AlbumSerializer < AppSerializer
764
- attribute :title
765
- attribute :downloads_count, preload: :downloads,
766
- value: proc { |album| album.downloads.count }
884
+ # preloads :albums, as auto_preload is enabled for :serializer attributes
885
+ attribute :albums, serializer: AlbumSerializer
767
886
  end
768
887
 
769
- UserSerializer.to_h(user)
770
- # preloads :user_stats and :albums on the user; the AlbumSerializer level
771
- # preloads :downloads on the albums
888
+ UserSerializer.to_h(users)
889
+ # 1 query to load :user_stats for all users
890
+ # + 1 query to load :albums for all users
891
+ # + 1 query to load :downloads for all albums
892
+ # = 3 queries total, regardless of how many users/albums are serialized
772
893
  ```
773
894
 
774
895
  ### Plugin :root
775
896
 
776
- Allows to add root key to your serialized data
897
+ Adds a root key to serialized data.
777
898
 
778
899
  Accepts options:
779
900
 
@@ -786,56 +907,47 @@ Adds additional config options:
786
907
  - config.root.one
787
908
  - config.root.many
788
909
  - config.root.one=
789
- - config.root_many=
910
+ - config.root.many=
790
911
 
791
912
  The default root is `:data`.
792
913
 
793
- The root key can be changed per serialization.
794
-
795
914
  ```ruby
796
- # @example Change root per serialization:
915
+ class UserSerializer < Serega
916
+ plugin :root # default root is :data
917
+ end
797
918
 
798
- class UserSerializer < Serega
799
- plugin :root
800
- end
919
+ class UserSerializer < Serega
920
+ plugin :root, root: :users
921
+ end
801
922
 
802
- UserSerializer.to_h(nil) # => {:data=>nil}
803
- UserSerializer.to_h(nil, root: :user) # => {:user=>nil}
804
- UserSerializer.to_h(nil, root: nil) # => nil
805
- ```
923
+ class UserSerializer < Serega
924
+ plugin :root, root_one: :user, root_many: :people
925
+ end
806
926
 
807
- The root key can be removed for all responses by providing the `root: nil`
808
- plugin option.
927
+ class UserSerializer < Serega
928
+ plugin :root, root: nil # no root key by default
929
+ end
930
+ ```
809
931
 
810
- In this case, no root key will be added. But it still can be added manually.
932
+ The root key can also be changed per serialization, or removed entirely by
933
+ providing `root: nil` (it can still be added back per serialization).
811
934
 
812
935
  ```ruby
813
- #@example Define :root plugin with different options
814
-
815
- class UserSerializer < Serega
816
- plugin :root # default root is :data
817
- end
818
-
819
- class UserSerializer < Serega
820
- plugin :root, root: :users
821
- end
822
-
823
- class UserSerializer < Serega
824
- plugin :root, root_one: :user, root_many: :people
825
- end
936
+ class UserSerializer < Serega
937
+ plugin :root
938
+ end
826
939
 
827
- class UserSerializer < Serega
828
- plugin :root, root: nil # no root key by default
829
- end
940
+ UserSerializer.to_h(nil) # => {:data=>nil}
941
+ UserSerializer.to_h(nil, root: :user) # => {:user=>nil}
942
+ UserSerializer.to_h(nil, root: nil) # => nil
830
943
  ```
831
944
 
832
945
  ### Plugin :metadata
833
946
 
834
947
  Depends on: [`:root`][root] plugin, that must be loaded first
835
948
 
836
- Adds ability to describe metadata and adds it to serialized response
837
-
838
- Adds class-level `.meta_attribute` method. It accepts:
949
+ Adds metadata to the serialized response via the class-level `meta_attribute`
950
+ method, which accepts:
839
951
 
840
952
  - `*path` [Array of Symbols] - nested hash keys.
841
953
  - `**options` [Hash]
@@ -875,7 +987,8 @@ AppSerializer.to_h(nil)
875
987
 
876
988
  Depends on: [`:root`][root] plugin, that must be loaded first
877
989
 
878
- Allows to provide metadata and attach it to serialized response.
990
+ Adds metadata supplied per serialization call (as opposed to `:metadata`,
991
+ which is defined statically on the serializer).
879
992
 
880
993
  Accepts option `:context_metadata_key` with the name of the root metadata keyword.
881
994
  By default, it has the `:meta` value.
@@ -899,41 +1012,39 @@ UserSerializer.to_h(nil, meta: { version: '1.0.1' })
899
1012
 
900
1013
  ### Plugin :formatters
901
1014
 
902
- Allows to define `formatters` and apply them to attribute values.
903
-
904
- Config option `config.formatters.add` can be used to add formatters.
1015
+ Defines named value formatters once and applies them to any attribute.
905
1016
 
906
- Attribute option `:format` can be used with the name of formatter or with
907
- callable instance.
1017
+ Use `config.formatters.add()` to register formatters. The `:format`
1018
+ attribute option then accepts a formatter name or a callable directly.
908
1019
 
909
- Formatters can accept up to 2 parameters (formatted object, context)
1020
+ Formatters receive up to 2 parameters: the value and the context.
910
1021
 
911
1022
  ```ruby
912
1023
  class AppSerializer < Serega
913
1024
  plugin :formatters, formatters: {
914
- iso8601: ->(value) { time.iso8601.round(6) },
1025
+ iso8601: ->(value) { value.iso8601 },
915
1026
  on_off: ->(value) { value ? 'ON' : 'OFF' },
916
- money: ->(value, ctx) { value / 10**ctx[:digits) }
1027
+ money: ->(value) { value.round(2) },
917
1028
  date: DateTypeFormatter # callable
918
1029
  }
919
1030
  end
920
1031
 
921
1032
  class UserSerializer < Serega
922
- # Additionally, we can add formatters via config in subclasses
1033
+ # Additionally we can add formatters via config in subclasses
923
1034
  config.formatters.add(
924
- iso8601: ->(value) { time.iso8601.round(6) },
1035
+ iso8601: ->(value) { value.iso8601 },
925
1036
  on_off: ->(value) { value ? 'ON' : 'OFF' },
926
1037
  money: ->(value) { value.round(2) }
927
1038
  )
928
1039
 
929
1040
  # Using predefined formatter
930
1041
  attribute :commission, format: :money
931
- attribute :is_logined, format: :on_off
1042
+ attribute :is_logged_in, format: :on_off
932
1043
  attribute :created_at, format: :iso8601
933
1044
  attribute :updated_at, format: :iso8601
934
1045
 
935
1046
  # Using `callable` formatter
936
- attribute :score_percent, format: PercentFormmatter # callable class
1047
+ attribute :score_percent, format: PercentFormatter # callable class
937
1048
  attribute :score_percent, format: proc { |percent| "#{percent.round(2)}%" }
938
1049
  end
939
1050
  ```
@@ -995,12 +1106,11 @@ callables keep receiving the raw objects.
995
1106
 
996
1107
  ### Plugin :string_modifiers
997
1108
 
998
- Allows to specify modifiers as strings.
999
-
1000
- Serialized attributes must be split with `,` and nested attributes must be
1001
- defined inside brackets `()`.
1109
+ Allows `:only`, `:except` and `:with` to be given as a single comma-separated
1110
+ string, with nested attributes in parentheses. Useful for accepting a field
1111
+ list straight from a query parameter.
1002
1112
 
1003
- Modifiers can still be provided the old way using nested hashes or arrays.
1113
+ Modifiers can still be provided the old way, as nested hashes or arrays.
1004
1114
 
1005
1115
  ```ruby
1006
1116
  PostSerializer.plugin :string_modifiers
@@ -1008,25 +1118,19 @@ PostSerializer.new(only: "id,user(id,username)").to_h(post)
1008
1118
  PostSerializer.new(except: "user(username,email)").to_h(post)
1009
1119
  PostSerializer.new(with: "user(email)").to_h(post)
1010
1120
 
1011
- # Modifiers can still be provided the old way using nested hashes or arrays.
1012
1121
  PostSerializer.new(with: {user: %i[email, username]}).to_h(post)
1013
1122
  ```
1014
1123
 
1015
1124
  ### Plugin :if
1016
1125
 
1017
- Plugin adds `:if, :unless, :if_value, :unless_value` options to
1018
- attributes so we can remove attributes from the response in various ways.
1126
+ Adds `:if`, `:unless`, `:if_value`, `:unless_value` attribute options to
1127
+ conditionally remove attributes from the response.
1019
1128
 
1020
- Use `:if` and `:unless` when you want to hide attributes before finding
1021
- attribute value, and use `:if_value` and `:unless_value` to hide attributes
1022
- after getting the final value.
1023
-
1024
- Options `:if` and `:unless` accept currently serialized object and context as
1025
- parameters. Options `:if_value` and `:unless_value` accept already found
1026
- serialized value and context as parameters.
1027
-
1028
- Options `:if_value` and `:unless_value` cannot be used with the `:serializer` option.
1029
- Use `:if` and `:unless` in this case.
1129
+ `:if`/`:unless` receive the serialized object and context, and are checked
1130
+ before the attribute value is found. `:if_value`/`:unless_value` receive the
1131
+ already-found value and context, checked after. The latter two cannot be
1132
+ used with the `:serializer` option, since a relationship has no "serialized
1133
+ value" of its own — use `:if`/`:unless` instead.
1030
1134
 
1031
1135
  See also a `:hide` option that is available without any plugins to hide
1032
1136
  attribute without conditions.
@@ -1058,24 +1162,18 @@ Look at [select serialized fields](#selecting-fields) for `:hide` usage examples
1058
1162
 
1059
1163
  ### Plugin :camel_case
1060
1164
 
1061
- By default, when we add an attribute like `attribute :first_name` it means:
1062
-
1063
- - adding a `:first_name` key to the resulting hash
1064
- - adding a `#first_name` method call result as value
1165
+ Without this plugin, responding with *camelCased* keys means specifying the
1166
+ attribute name and method directly for each attribute:
1167
+ `attribute :firstName, method: first_name`
1065
1168
 
1066
- But it's often desired to respond with *camelCased* keys.
1067
- By default, this can be achieved by specifying the attribute name and method directly
1068
- for each attribute: `attribute :firstName, method: first_name`
1169
+ This plugin camelCases every attribute name automatically, replacing `_x`
1170
+ with `X` throughout the string. The transformation runs once, when the
1171
+ attribute is defined, not on every serialization.
1069
1172
 
1070
- This plugin transforms all attribute names automatically.
1071
- We use a simple regular expression to replace `_x` with `X` for the whole string.
1072
- We make this transformation only once when the attribute is defined.
1173
+ Provide a custom transformation when adding the plugin, for example
1174
+ `plugin :camel_case, transform: ->(name) { name.camelize }`
1073
1175
 
1074
- You can provide custom transformation when adding the plugin,
1075
- for example `plugin :camel_case, transform: ->(name) { name.camelize }`
1076
-
1077
- For any attribute camelCase-behavior can be skipped when
1078
- the `camel_case: false` attribute option provided.
1176
+ Skip camelCase for a single attribute with `camel_case: false`.
1079
1177
 
1080
1178
  This plugin transforms only attribute keys, without affecting the `root`,
1081
1179
  `metadata` and `context_metadata` plugins keys.
@@ -1140,27 +1238,25 @@ end
1140
1238
 
1141
1239
  ### Plugin :explicit_many_option
1142
1240
 
1143
- The plugin requires adding a `:many` option when adding relationships
1144
- (attributes with the `:serializer` option or a block defining a nested
1145
- serializer).
1146
-
1147
- Adding this plugin makes it clearer to find if some relationship is an array or
1148
- a single object.
1241
+ Requires the `:many` option on every relationship attribute (an attribute
1242
+ with the `:serializer` option or a block defining a nested serializer), so
1243
+ it's always explicit whether it returns one object or many.
1149
1244
 
1150
1245
  ```ruby
1151
- class BaseSerializer < Serega
1152
- plugin :explicit_many_option
1153
- end
1246
+ class BaseSerializer < Serega
1247
+ plugin :explicit_many_option
1248
+ config.base_serializer = self
1249
+ end
1250
+
1251
+ class PostSerializer < BaseSerializer
1252
+ attribute :text
1154
1253
 
1155
- class UserSerializer < BaseSerializer
1254
+ attribute :user, many: false do
1156
1255
  attribute :name
1157
1256
  end
1158
1257
 
1159
- class PostSerializer < BaseSerializer
1160
- attribute :text
1161
- attribute :user, serializer: UserSerializer, many: false
1162
- attribute :comments, serializer: PostSerializer, many: true
1163
- end
1258
+ attribute :comments, serializer: PostSerializer, many: true
1259
+ end
1164
1260
  ```
1165
1261
 
1166
1262
  ## Errors
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.40.0
1
+ 0.41.0
@@ -4,10 +4,12 @@ class Serega
4
4
  #
5
5
  # Stores serialized attribute data
6
6
  #
7
+ # @private
7
8
  class SeregaAttribute
8
9
  #
9
10
  # Attribute instance methods
10
11
  #
12
+ # @private
11
13
  module AttributeInstanceMethods
12
14
  # Attribute initial params
13
15
  # @return [Hash] Attribute initial params
@@ -4,10 +4,12 @@ class Serega
4
4
  #
5
5
  # Prepares provided attribute options
6
6
  #
7
+ # @private
7
8
  class SeregaAttributeNormalizer
8
9
  #
9
10
  # AttributeNormalizer instance methods
10
11
  #
12
+ # @private
11
13
  module AttributeNormalizerInstanceMethods
12
14
  # Attribute initial params
13
15
  # @return [Hash] Attribute initial params
@@ -4,10 +4,12 @@ class Serega
4
4
  #
5
5
  # Attribute value resolvers
6
6
  #
7
+ # @private
7
8
  module AttributeValueResolvers
8
9
  #
9
10
  # Builds value resolver class for attributes with :batch option
10
11
  #
12
+ # @private
11
13
  class BatchResolver
12
14
  #
13
15
  # Generates callable block to find attribute value when attribute with :batch
@@ -55,6 +57,7 @@ class Serega
55
57
  #
56
58
  # Builds value resolver class for attributes with :batch option
57
59
  #
60
+ # @private
58
61
  class Batch
59
62
  def initialize(loader_name, id_method)
60
63
  @loader_name = loader_name
@@ -4,10 +4,12 @@ class Serega
4
4
  #
5
5
  # Attribute value resolvers
6
6
  #
7
+ # @private
7
8
  module AttributeValueResolvers
8
9
  #
9
10
  # Builds value resolver class for attributes with :const option
10
11
  #
12
+ # @private
11
13
  class ConstResolver
12
14
  #
13
15
  # Creates resolver that returns constant value
@@ -23,6 +25,7 @@ class Serega
23
25
  #
24
26
  # Value resolver class for attributes with :const option
25
27
  #
28
+ # @private
26
29
  class Const
27
30
  def initialize(const_value)
28
31
  @const_value = const_value
@@ -4,10 +4,12 @@ class Serega
4
4
  #
5
5
  # Attribute value resolvers
6
6
  #
7
+ # @private
7
8
  module AttributeValueResolvers
8
9
  #
9
10
  # Builds value resolver class for attributes with :delegate option
10
11
  #
12
+ # @private
11
13
  class DelegateResolver
12
14
  #
13
15
  # Creates resolver that delegates method call to another object
@@ -25,6 +27,7 @@ class Serega
25
27
  #
26
28
  # Value resolver class for attributes with :delegate (with :allow_nil) option
27
29
  #
30
+ # @private
28
31
  class DelegateAllowNil
29
32
  def initialize(delegate_to, method_name)
30
33
  @delegate_to = delegate_to
@@ -49,6 +52,7 @@ class Serega
49
52
  #
50
53
  # Value resolver class for attributes with :delegate (without :allow_nil) option
51
54
  #
55
+ # @private
52
56
  class Delegate
53
57
  def initialize(delegate_to, method_name)
54
58
  @delegate_to = delegate_to
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Serega
4
+ # @private
4
5
  module AttributeValueResolvers
5
6
  #
6
7
  # Builds value resolver for attributes with the :hash_access option
7
8
  #
9
+ # @private
8
10
  class HashAccessResolver
9
11
  # Allowed hash access modes
10
12
  MODES = %i[symbol string].freeze
@@ -27,6 +29,7 @@ class Serega
27
29
  # Builds value resolver for attributes with the :delegate option using
28
30
  # hash access on any of its steps
29
31
  #
32
+ # @private
30
33
  class HashAccessDelegateResolver
31
34
  #
32
35
  # Creates resolver that delegates through the provided step readers
@@ -45,6 +48,7 @@ class Serega
45
48
  #
46
49
  # Value resolver for attributes with the :hash_access option
47
50
  #
51
+ # @private
48
52
  class HashAccessKeyword
49
53
  def initialize(name, mode, allow_missing_key)
50
54
  @key = (mode == :symbol) ? name.to_sym : name.to_s
@@ -72,6 +76,7 @@ class Serega
72
76
  #
73
77
  # Value resolver for attributes with :hash_access and :delegate (without :allow_nil) options
74
78
  #
79
+ # @private
75
80
  class HashAccessDelegate
76
81
  def initialize(to_step, final_step)
77
82
  @to_step = to_step
@@ -92,6 +97,7 @@ class Serega
92
97
  #
93
98
  # Value resolver for attributes with :hash_access and :delegate (with :allow_nil) options
94
99
  #
100
+ # @private
95
101
  class HashAccessDelegateAllowNil
96
102
  def initialize(to_step, final_step)
97
103
  @to_step = to_step
@@ -4,10 +4,12 @@ class Serega
4
4
  #
5
5
  # Attribute value resolvers
6
6
  #
7
+ # @private
7
8
  module AttributeValueResolvers
8
9
  #
9
10
  # Builds value resolver class for attributes with :keyword option
10
11
  #
12
+ # @private
11
13
  class KeywordResolver
12
14
  #
13
15
  # Creates resolver that calls method on object
@@ -23,6 +25,7 @@ class Serega
23
25
  #
24
26
  # Value resolver class for attributes with :keyword option
25
27
  #
28
+ # @private
26
29
  class Keyword
27
30
  def initialize(keyword)
28
31
  @keyword = keyword