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.
- checksums.yaml +4 -4
- data/LICENSE.txt +21 -0
- data/README.md +204 -108
- data/VERSION +1 -1
- data/lib/serega/attribute.rb +2 -0
- data/lib/serega/attribute_normalizer.rb +2 -0
- data/lib/serega/attribute_value_resolvers/batch.rb +3 -0
- data/lib/serega/attribute_value_resolvers/const.rb +3 -0
- data/lib/serega/attribute_value_resolvers/delegate.rb +4 -0
- data/lib/serega/attribute_value_resolvers/hash_access.rb +6 -0
- data/lib/serega/attribute_value_resolvers/keyword.rb +3 -0
- data/lib/serega/data_builder.rb +2 -0
- data/lib/serega/engine/level.rb +2 -0
- data/lib/serega/engine/level_queue.rb +2 -0
- data/lib/serega/engine/loader.rb +3 -0
- data/lib/serega/helpers/serializer_class_helper.rb +2 -0
- data/lib/serega/object_serializer.rb +2 -0
- data/lib/serega/plan.rb +3 -0
- data/lib/serega/plan_point.rb +2 -0
- data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +22 -16
- data/lib/serega/plugins/activerecord_preloads/lib/preloader.rb +6 -0
- data/lib/serega/plugins/camel_case/camel_case.rb +13 -13
- data/lib/serega/plugins/context_metadata/context_metadata.rb +8 -1
- data/lib/serega/plugins/depth_limit/depth_limit.rb +15 -12
- data/lib/serega/plugins/explicit_many_option/explicit_many_option.rb +12 -10
- data/lib/serega/plugins/explicit_many_option/validations/check_opt_many.rb +1 -0
- data/lib/serega/plugins/formatters/formatters.rb +18 -10
- data/lib/serega/plugins/if/if.rb +29 -32
- data/lib/serega/plugins/if/validations/check_opt_if.rb +1 -0
- data/lib/serega/plugins/if/validations/check_opt_if_value.rb +1 -0
- data/lib/serega/plugins/if/validations/check_opt_unless.rb +1 -0
- data/lib/serega/plugins/if/validations/check_opt_unless_value.rb +1 -0
- data/lib/serega/plugins/metadata/meta_attribute.rb +2 -0
- data/lib/serega/plugins/metadata/metadata.rb +14 -12
- data/lib/serega/plugins/metadata/validations/check_block.rb +1 -0
- data/lib/serega/plugins/metadata/validations/check_opt_const.rb +1 -0
- data/lib/serega/plugins/metadata/validations/check_opt_hide_empty.rb +1 -0
- data/lib/serega/plugins/metadata/validations/check_opt_hide_nil.rb +1 -0
- data/lib/serega/plugins/metadata/validations/check_opt_value.rb +1 -0
- data/lib/serega/plugins/metadata/validations/check_opts.rb +1 -0
- data/lib/serega/plugins/metadata/validations/check_path.rb +1 -0
- data/lib/serega/plugins/presenter/presenter.rb +10 -7
- data/lib/serega/plugins/root/root.rb +12 -6
- data/lib/serega/plugins/string_modifiers/parse_string_modifiers.rb +1 -0
- data/lib/serega/plugins/string_modifiers/string_modifiers.rb +21 -0
- data/lib/serega/plugins.rb +2 -0
- data/lib/serega/utils/collection_detector.rb +2 -0
- data/lib/serega/utils/enum_deep_dup.rb +2 -0
- data/lib/serega/utils/enum_deep_freeze.rb +2 -0
- data/lib/serega/utils/method_signature.rb +2 -0
- data/lib/serega/utils/serialized_attribute_error.rb +2 -0
- data/lib/serega/utils/symbol_name.rb +2 -0
- data/lib/serega/utils/to_hash.rb +2 -0
- data/lib/serega/validations/attribute/check_block.rb +3 -0
- data/lib/serega/validations/attribute/check_name.rb +3 -0
- data/lib/serega/validations/attribute/check_opt_base_serializer.rb +3 -0
- data/lib/serega/validations/attribute/check_opt_batch.rb +3 -0
- data/lib/serega/validations/attribute/check_opt_const.rb +3 -0
- data/lib/serega/validations/attribute/check_opt_delegate.rb +3 -0
- data/lib/serega/validations/attribute/check_opt_hash_access.rb +3 -0
- data/lib/serega/validations/attribute/check_opt_hide.rb +3 -0
- data/lib/serega/validations/attribute/check_opt_many.rb +3 -0
- data/lib/serega/validations/attribute/check_opt_method.rb +3 -0
- data/lib/serega/validations/attribute/check_opt_preload.rb +3 -0
- data/lib/serega/validations/attribute/check_opt_serializer.rb +3 -0
- data/lib/serega/validations/attribute/check_opt_value.rb +3 -0
- data/lib/serega/validations/check_attribute_params.rb +3 -0
- data/lib/serega/validations/check_batch_loader_params.rb +3 -0
- data/lib/serega/validations/check_initiate_params.rb +3 -0
- data/lib/serega/validations/check_serialize_params.rb +3 -0
- data/lib/serega/validations/initiate/check_modifiers.rb +3 -0
- data/lib/serega/validations/utils/check_allowed_keys.rb +3 -0
- data/lib/serega/validations/utils/check_opt_is_bool.rb +3 -0
- data/lib/serega/validations/utils/check_opt_is_hash.rb +3 -0
- data/lib/serega/validations/utils/check_opt_is_string_or_symbol.rb +3 -0
- data/lib/serega.rb +95 -1
- metadata +13 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4d54f033e70155c517c02d15cd9a4eb5399e385b0ffdd68d8c6c2b390201a007
|
|
4
|
+
data.tar.gz: 23257bef39a343bea74c063f452fa1feed58738c454e467838cceb2fecdbe862
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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
|
-
###
|
|
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
|
-
|
|
764
|
-
attribute :
|
|
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(
|
|
770
|
-
#
|
|
771
|
-
#
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
915
|
+
class UserSerializer < Serega
|
|
916
|
+
plugin :root # default root is :data
|
|
917
|
+
end
|
|
797
918
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
919
|
+
class UserSerializer < Serega
|
|
920
|
+
plugin :root, root: :users
|
|
921
|
+
end
|
|
801
922
|
|
|
802
|
-
UserSerializer
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
```
|
|
923
|
+
class UserSerializer < Serega
|
|
924
|
+
plugin :root, root_one: :user, root_many: :people
|
|
925
|
+
end
|
|
806
926
|
|
|
807
|
-
|
|
808
|
-
plugin
|
|
927
|
+
class UserSerializer < Serega
|
|
928
|
+
plugin :root, root: nil # no root key by default
|
|
929
|
+
end
|
|
930
|
+
```
|
|
809
931
|
|
|
810
|
-
|
|
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
|
-
|
|
814
|
-
|
|
815
|
-
|
|
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
|
-
|
|
828
|
-
|
|
829
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
907
|
-
callable
|
|
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
|
|
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) {
|
|
1025
|
+
iso8601: ->(value) { value.iso8601 },
|
|
915
1026
|
on_off: ->(value) { value ? 'ON' : 'OFF' },
|
|
916
|
-
money: ->(value
|
|
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
|
|
1033
|
+
# Additionally we can add formatters via config in subclasses
|
|
923
1034
|
config.formatters.add(
|
|
924
|
-
iso8601: ->(value) {
|
|
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 :
|
|
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:
|
|
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
|
|
999
|
-
|
|
1000
|
-
|
|
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
|
|
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
|
-
|
|
1018
|
-
|
|
1126
|
+
Adds `:if`, `:unless`, `:if_value`, `:unless_value` attribute options to
|
|
1127
|
+
conditionally remove attributes from the response.
|
|
1019
1128
|
|
|
1020
|
-
|
|
1021
|
-
attribute value
|
|
1022
|
-
after
|
|
1023
|
-
|
|
1024
|
-
|
|
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
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
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
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
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
|
-
|
|
1071
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
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
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
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
|
-
|
|
1254
|
+
attribute :user, many: false do
|
|
1156
1255
|
attribute :name
|
|
1157
1256
|
end
|
|
1158
1257
|
|
|
1159
|
-
|
|
1160
|
-
|
|
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.
|
|
1
|
+
0.41.0
|
data/lib/serega/attribute.rb
CHANGED
|
@@ -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
|