serega 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +145 -86
- data/VERSION +1 -1
- data/lib/serega/attribute.rb +3 -3
- data/lib/serega/attribute_normalizer.rb +21 -2
- data/lib/serega/object_serializer.rb +1 -1
- data/lib/serega/plugins/batch/batch.rb +11 -56
- data/lib/serega/plugins/batch/lib/batch_config.rb +22 -27
- data/lib/serega/plugins/batch/lib/modules/attribute_normalizer.rb +27 -12
- data/lib/serega/plugins/batch/lib/modules/object_serializer.rb +2 -2
- data/lib/serega/plugins/batch/lib/modules/plan_point.rb +2 -19
- data/lib/serega/plugins/batch/lib/validations/check_batch_opt_id_method.rb +43 -0
- data/lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb +17 -31
- data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +10 -11
- data/lib/serega/plugins/formatters/formatters.rb +88 -14
- data/lib/serega/plugins/if/if.rb +43 -19
- data/lib/serega/plugins/if/validations/check_opt_if.rb +4 -36
- data/lib/serega/plugins/if/validations/check_opt_if_value.rb +7 -39
- data/lib/serega/plugins/if/validations/check_opt_unless.rb +4 -45
- data/lib/serega/plugins/if/validations/check_opt_unless_value.rb +7 -39
- data/lib/serega/plugins/metadata/meta_attribute.rb +21 -5
- data/lib/serega/plugins/metadata/metadata.rb +16 -7
- data/lib/serega/plugins/metadata/validations/check_block.rb +10 -10
- data/lib/serega/plugins/metadata/validations/check_opt_const.rb +38 -0
- data/lib/serega/plugins/metadata/validations/check_opt_value.rb +61 -0
- data/lib/serega/plugins/metadata/validations/check_opts.rb +24 -10
- data/lib/serega/utils/params_count.rb +50 -0
- data/lib/serega/utils/to_hash.rb +1 -1
- data/lib/serega/validations/attribute/check_block.rb +4 -5
- data/lib/serega/validations/attribute/check_opt_value.rb +16 -12
- data/lib/serega/validations/check_attribute_params.rb +1 -0
- data/lib/serega/validations/utils/check_extra_keyword_arg.rb +33 -0
- data/lib/serega.rb +2 -0
- metadata +8 -4
- data/lib/serega/plugins/batch/lib/validations/check_batch_opt_key.rb +0 -73
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60cc0f4e593d4600bf316e9c18bd93804008e28deebd5f51c231b632bb3a4eff
|
4
|
+
data.tar.gz: e3bdc8e6a0489a14916d84781dcf61c6a82dedb246a0b5366f324d78ca81d9b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 879f46f3bface078c156ad0c89e71b4abc08945754034235d03a688c5613e549d477ae1d24bfa6d0f2c44de1def6266d9b18677ab9fab53ef8341510075806d5
|
7
|
+
data.tar.gz: 45b302b1efc001b9ff3ef35bb9d71bf05e3dcfffbfd35938cf938448b308e9c6c0f74a8f1c1eeceabf8a3bd0eb759f50dd053948664565fbeef2f4b8502841b2
|
data/README.md
CHANGED
@@ -73,7 +73,8 @@ class UserSerializer < Serega
|
|
73
73
|
# Block is used to define attribute value
|
74
74
|
attribute(:first_name) { |user| user.profile&.first_name }
|
75
75
|
|
76
|
-
# Option :value can be used with callable object to define attribute value
|
76
|
+
# Option :value can be used with proc or callable object to define attribute value
|
77
|
+
attribute :first_name, value: UserProfile.new # must have #call method
|
77
78
|
attribute :first_name, value: proc { |user| user.profile&.first_name }
|
78
79
|
|
79
80
|
# Option :delegate can be used to define attribute value.
|
@@ -538,118 +539,165 @@ UserSerializer.to_h(user)
|
|
538
539
|
|
539
540
|
### Plugin :batch
|
540
541
|
|
541
|
-
|
542
|
+
Must be used to omit N+1 when loading attributes values.
|
542
543
|
|
543
|
-
|
544
|
+
User must provide batch loader object to attribute -
|
545
|
+
`attribute :foo, batch: {loader: SomeLoader, id_method: :id}`.
|
544
546
|
|
545
|
-
|
546
|
-
- load counters for multiple objects
|
547
|
-
- make any heavy calculations for multiple objects only once
|
548
|
-
|
549
|
-
After including plugin, attributes gain new `:batch` option:
|
547
|
+
Result must be returned as Hash, where each key is one of provided ids.
|
550
548
|
|
551
549
|
```ruby
|
552
|
-
|
550
|
+
class AppSerializer
|
551
|
+
plugin :batch
|
552
|
+
end
|
553
|
+
|
554
|
+
class UserSerializer < AppSerializer
|
555
|
+
attribute :comments_count,
|
556
|
+
batch: { loader: SomeLoader, id_method: :id }
|
557
|
+
|
558
|
+
attribute :company,
|
559
|
+
batch: { loader: SomeLoader, id_method: :id },
|
560
|
+
serializer: CompanySerializer
|
561
|
+
end
|
553
562
|
```
|
554
563
|
|
555
|
-
|
564
|
+
#### Option :loader
|
556
565
|
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
Key is optional if plugin was defined with `default_key` option.
|
561
|
-
- `default` (optional) - Default value for attribute.
|
562
|
-
By default it is `nil` or `[]` when attribute has option `many: true`
|
563
|
-
(ex: `attribute :tags, many: true, batch: { ... }`).
|
566
|
+
Loaders can be defined as a Proc, a callable value or a named Symbol
|
567
|
+
Named loaders should be predefined with
|
568
|
+
`config.batch.define(:loader_name) { |ids| ... })`
|
564
569
|
|
565
|
-
|
566
|
-
defined using `config.batch.define(:loader_name) { ... }` method.
|
570
|
+
Loader can accept 1 to 3 arguments:
|
567
571
|
|
568
|
-
|
572
|
+
1. List of ids (each id will be found by using `:id_method` option)
|
573
|
+
1. Context
|
574
|
+
1. PlanPoint - a special object containing information about current
|
575
|
+
attribute and all children and parent attributes. It can be used to preload
|
576
|
+
required associations to batch values.
|
577
|
+
See [example](examples/batch_loader.rb) how
|
578
|
+
to find required preloads when using with `:preloads` plugin.
|
569
579
|
|
570
|
-
|
571
|
-
|
580
|
+
```ruby
|
581
|
+
class AppSerializer < Serega
|
582
|
+
plugin :batch, id_method: :id
|
583
|
+
end
|
572
584
|
|
573
|
-
|
585
|
+
class UserSerializer < Serega
|
586
|
+
# Define loader as callable object
|
587
|
+
attribute :comments_count,
|
588
|
+
batch: { loader: CountLoader }
|
574
589
|
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
used for all attributes with :batch option specified.
|
590
|
+
# Define loader as a Proc
|
591
|
+
attribute :comments_count,
|
592
|
+
batch: { loader: proc { |ids| CountLoader.call(ids) } }
|
579
593
|
|
580
|
-
|
581
|
-
|
594
|
+
# Define loader as a Symbol
|
595
|
+
config.batch.define(:comments_count_loader) { |ids| CountLoader.call(ids }
|
596
|
+
attribute :comments_count, batch: { loader: :comments_count_loader }
|
597
|
+
end
|
598
|
+
|
599
|
+
class CountLoader
|
600
|
+
def self.call(user_ids)
|
601
|
+
Comment.where(user_id: user_ids).group(:user_id).count
|
602
|
+
end
|
603
|
+
end
|
582
604
|
```
|
583
605
|
|
584
|
-
|
606
|
+
#### Option :id_method
|
607
|
+
|
608
|
+
Batch plugin can be added with global `:id_method` option. It can be a Symbol,
|
609
|
+
Proc or any callable value, which can accept current object and current context.
|
585
610
|
|
586
611
|
```ruby
|
587
|
-
class
|
588
|
-
plugin :batch,
|
612
|
+
class SomeSerializer
|
613
|
+
plugin :batch, id_method: :id
|
589
614
|
end
|
590
615
|
|
591
616
|
class UserSerializer < AppSerializer
|
592
|
-
|
593
|
-
|
617
|
+
attribute :comments_count,
|
618
|
+
batch: { loader: CommentsCountBatchLoader } # no :id_method here anymore
|
619
|
+
|
620
|
+
attribute :company,
|
621
|
+
batch: { loader: UserCompanyBatchLoader }, # no :id_method here anymore
|
622
|
+
serializer: CompanySerializer
|
594
623
|
end
|
624
|
+
|
595
625
|
```
|
596
626
|
|
597
|
-
|
598
|
-
|
599
|
-
`:batch` attributes, but have nested serializers, that have some. For example
|
600
|
-
when you serialize `User -> Album -> Song` and Song has `:batch` attribute, then
|
601
|
-
`:batch` plugin must be added to the User serializer also. \
|
602
|
-
Best way would be to create one parent `AppSerializer < Serega` for all your
|
603
|
-
serializers and add `:batch` plugin only to this parent `AppSerializer`
|
627
|
+
However, global `id_method` option can be overwritten via `config.batch.id_method=`
|
628
|
+
method or in specific attributes with `id_method` option.
|
604
629
|
|
605
630
|
```ruby
|
606
|
-
class
|
607
|
-
plugin :batch,
|
631
|
+
class SomeSerializer
|
632
|
+
plugin :batch, id_method: :id # global id_method is `:id`
|
608
633
|
end
|
609
634
|
|
610
|
-
class
|
611
|
-
|
612
|
-
|
613
|
-
loader: CommentsCountBatchLoader, # callable(keys, context, plan_point)
|
614
|
-
key: :id, # can be skipped (as :id value is same as configured :default_key)
|
615
|
-
default: 0
|
616
|
-
}
|
635
|
+
class UserSerializer < AppSerializer
|
636
|
+
# :user_id will be used as default `id_method` for all batch attributes
|
637
|
+
config.batch.id_method = :user_id
|
617
638
|
|
618
|
-
#
|
619
|
-
# `config.batch.define(:posts_comments_counter) { ... }`
|
620
|
-
#
|
621
|
-
# Loader will receive array of ids, as `default_key: :id` plugin option was specified.
|
622
|
-
# Default value for not found counters is nil, as `:default` option not defined
|
639
|
+
# id_method is :user_id
|
623
640
|
attribute :comments_count,
|
624
|
-
batch: { loader:
|
641
|
+
batch: { loader: CommentsCountBatchLoader }
|
625
642
|
|
626
|
-
# Define batch loader with serializer
|
627
|
-
attribute :comments,
|
628
|
-
serializer: CommentSerializer,
|
629
|
-
batch: {loader: :posts_comments, default: []}
|
630
643
|
|
631
|
-
#
|
632
|
-
|
633
|
-
|
634
|
-
end
|
644
|
+
# id_method is :user_id
|
645
|
+
attribute :company,
|
646
|
+
batch: { loader: UserCompanyBatchLoader }, serializer: CompanySerializer
|
635
647
|
|
636
|
-
#
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
648
|
+
# id_method is :uuid
|
649
|
+
attribute :points_amount,
|
650
|
+
batch: { loader: PointsBatchLoader, id_method: :uuid }
|
651
|
+
end
|
652
|
+
```
|
653
|
+
|
654
|
+
#### Option :default
|
655
|
+
|
656
|
+
The default value for attributes without found value can be specified via
|
657
|
+
`:default` option. By default attributes without found value will be
|
658
|
+
serialized as `nil`. Attribute marked as `many: true` will be
|
659
|
+
serialized as empty array `[]`
|
660
|
+
|
661
|
+
```ruby
|
662
|
+
class UserSerializer < AppSerializer
|
663
|
+
# Missing values become empty arrays, as `many: true` option specified
|
664
|
+
attribute :companies,
|
665
|
+
batch: {loader: proc {}},
|
666
|
+
serializer: CompanySerializer,
|
667
|
+
many: true
|
668
|
+
|
669
|
+
# Missing values become `0` as specified directly
|
670
|
+
attribute :points_amount,
|
671
|
+
batch: { loader: proc {}, default: 0 }
|
672
|
+
end
|
673
|
+
```
|
674
|
+
|
675
|
+
Batch attributes can be marked as hidden by default if plugin specified with
|
676
|
+
`auto_hide` option. Also `auto_hide` option can be changed with
|
677
|
+
`config.batch.auto_hide=` method.
|
678
|
+
|
679
|
+
Look at [select serialized fields](#selecting-fields) for more information
|
680
|
+
about hiding/showing attributes.
|
681
|
+
|
682
|
+
```ruby
|
683
|
+
class AppSerializer
|
684
|
+
plugin :batch, auto_hide: true
|
685
|
+
end
|
686
|
+
|
687
|
+
class UserSerializer < AppSerializer
|
688
|
+
config.batch.auto_hide = false
|
650
689
|
end
|
651
690
|
```
|
652
691
|
|
692
|
+
---
|
693
|
+
⚠️ ATTENTION: `Batch` plugin must be added to all serializers that have
|
694
|
+
`:batch` attributes inside nested serializers. For example when you serialize
|
695
|
+
`User -> Album -> Song` and Song has `batch` attribute, then
|
696
|
+
`batch` plugin must be added to the User serializer also.
|
697
|
+
|
698
|
+
Best way would be to create one parent `AppSerializer < Serega` serializer
|
699
|
+
and add `:batch` plugin once to this parent serializer.
|
700
|
+
|
653
701
|
### Plugin :root
|
654
702
|
|
655
703
|
Allows to add root key to your serialized data
|
@@ -715,17 +763,24 @@ Adds ability to describe metadata and adds it to serialized response
|
|
715
763
|
|
716
764
|
Added class-level method `:meta_attribute`, to define metadata, it accepts:
|
717
765
|
|
718
|
-
-
|
719
|
-
-
|
720
|
-
|
766
|
+
- `*path` [Array of Symbols] - nested hash keys.
|
767
|
+
- `**options` [Hash]
|
768
|
+
|
769
|
+
- `:const` - describes metadata value (if it is constant)
|
770
|
+
- `:value` - describes metadata value as any `#callable` instance
|
771
|
+
- `:hide_nil` - does not show metadata key if value is nil, `false` by default
|
772
|
+
- `:hide_empty`, does not show metadata key if value is nil or empty,
|
773
|
+
`false` by default
|
774
|
+
|
775
|
+
- `&block` [Proc] - describes value for current meta attribute
|
721
776
|
|
722
777
|
```ruby
|
723
778
|
class AppSerializer < Serega
|
724
779
|
plugin :root
|
725
780
|
plugin :metadata
|
726
781
|
|
727
|
-
meta_attribute(:version
|
728
|
-
meta_attribute(:ab_tests, :names
|
782
|
+
meta_attribute(:version, const: '1.2.3')
|
783
|
+
meta_attribute(:ab_tests, :names, value: ABTests.new.method(:names))
|
729
784
|
meta_attribute(:meta, :paging, hide_nil: true) do |records, ctx|
|
730
785
|
next unless records.respond_to?(:total_count)
|
731
786
|
|
@@ -738,7 +793,7 @@ class AppSerializer < Serega
|
|
738
793
|
end
|
739
794
|
|
740
795
|
AppSerializer.to_h(nil)
|
741
|
-
# => {:data=>nil, :version=>"1.2.3", :ab_tests=>{:names=>
|
796
|
+
# => {:data=>nil, :version=>"1.2.3", :ab_tests=>{:names=> ... }}
|
742
797
|
```
|
743
798
|
|
744
799
|
### Plugin :context_metadata
|
@@ -773,15 +828,18 @@ Allows to define `formatters` and apply them on attribute values.
|
|
773
828
|
|
774
829
|
Config option `config.formatters.add` can be used to add formatters.
|
775
830
|
|
776
|
-
Attribute option `:format`
|
831
|
+
Attribute option `:format` can be used with name of formatter or with
|
777
832
|
callable instance.
|
778
833
|
|
834
|
+
Formatters can accept up to 2 parameters (formatted object, context)
|
835
|
+
|
779
836
|
```ruby
|
780
837
|
class AppSerializer < Serega
|
781
838
|
plugin :formatters, formatters: {
|
782
839
|
iso8601: ->(value) { time.iso8601.round(6) },
|
783
840
|
on_off: ->(value) { value ? 'ON' : 'OFF' },
|
784
|
-
money: ->(value) { value
|
841
|
+
money: ->(value, ctx) { value / 10**ctx[:digits) }
|
842
|
+
date: DateTypeFormatter # callable
|
785
843
|
}
|
786
844
|
end
|
787
845
|
|
@@ -1029,6 +1087,7 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
1029
1087
|
[batch]: #plugin-batch
|
1030
1088
|
[camel_case]: #plugin-camel_case
|
1031
1089
|
[context_metadata]: #plugin-context_metadata
|
1090
|
+
[depth_limit]: #plugin-depth_limit
|
1032
1091
|
[formatters]: #plugin-formatters
|
1033
1092
|
[metadata]: #plugin-metadata
|
1034
1093
|
[preloads]: #plugin-preloads
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.18.0
|
data/lib/serega/attribute.rb
CHANGED
@@ -61,10 +61,10 @@ class Serega
|
|
61
61
|
# Shows specified serializer class
|
62
62
|
# @return [Serega, nil] Attribute serializer if exists
|
63
63
|
def serializer
|
64
|
-
|
65
|
-
return
|
64
|
+
serializer = @serializer
|
65
|
+
return serializer if (serializer.is_a?(Class) && (serializer < Serega)) || !serializer
|
66
66
|
|
67
|
-
@serializer =
|
67
|
+
@serializer = serializer.is_a?(String) ? Object.const_get(serializer, false) : serializer.call
|
68
68
|
end
|
69
69
|
|
70
70
|
#
|
@@ -97,8 +97,8 @@ class Serega
|
|
97
97
|
# - plugin :formatters (wraps resulted block in formatter block and formats :const values)
|
98
98
|
#
|
99
99
|
def prepare_value_block
|
100
|
-
|
101
|
-
|
100
|
+
prepare_init_block ||
|
101
|
+
prepare_value_option_block ||
|
102
102
|
prepare_const_block ||
|
103
103
|
prepare_delegate_block ||
|
104
104
|
prepare_keyword_block
|
@@ -139,6 +139,25 @@ class Serega
|
|
139
139
|
end
|
140
140
|
end
|
141
141
|
|
142
|
+
def prepare_init_block
|
143
|
+
prepare_callable_proc(init_block)
|
144
|
+
end
|
145
|
+
|
146
|
+
def prepare_value_option_block
|
147
|
+
prepare_callable_proc(init_opts[:value])
|
148
|
+
end
|
149
|
+
|
150
|
+
def prepare_callable_proc(callable)
|
151
|
+
return unless callable
|
152
|
+
|
153
|
+
params_count = SeregaUtils::ParamsCount.call(callable, max_count: 2)
|
154
|
+
case params_count
|
155
|
+
when 0 then proc { |obj, _ctx| callable.call }
|
156
|
+
when 1 then proc { |obj, _ctx| callable.call(obj) }
|
157
|
+
else callable
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
142
161
|
def prepare_delegate_block
|
143
162
|
delegate = init_opts[:delegate]
|
144
163
|
return unless delegate
|
@@ -5,62 +5,17 @@ class Serega
|
|
5
5
|
#
|
6
6
|
# Plugin `:batch`
|
7
7
|
#
|
8
|
-
#
|
8
|
+
# Must be used to omit N+1 when loading attributes values.
|
9
9
|
#
|
10
|
-
#
|
11
|
-
# - load associations for multiple objects
|
12
|
-
# - load counters for multiple objects
|
13
|
-
# - make any heavy calculations for multiple objects only once
|
10
|
+
# @example Quick example
|
14
11
|
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
# - `key` (required) [Symbol, Proc, callable] - Defines current object identifier.
|
19
|
-
# Later `loader` will accept array of `keys` to find `values`.
|
20
|
-
# - `loader` (required) [Symbol, Proc, callable] - Defines how to fetch values for
|
21
|
-
# batch of keys. Receives 3 parameters: keys, context, plan_point.
|
22
|
-
# - `default` (optional) - Default value for attribute.
|
23
|
-
# By default it is `nil` or `[]` when attribute has option `many: true`
|
24
|
-
#
|
25
|
-
# If `:loader` was defined using name (as Symbol) then batch loader must be
|
26
|
-
# defined in serializer config: `config.batch.define(:loader_name) { ... }` method.
|
27
|
-
#
|
28
|
-
# *Result of this `:loader` callable must be a **Hash** where*:
|
29
|
-
# - keys - provided keys
|
30
|
-
# - values - values for according keys
|
31
|
-
#
|
32
|
-
# `Batch` plugin can be defined with two specific attributes:
|
33
|
-
# - `auto_hide: true` - Marks attributes with defined :batch as hidden, so it
|
34
|
-
# will not be serialized by default
|
35
|
-
# - `default_key: :id` - Set default object key (in this case :id) that will be used for all attributes with :batch option specified.
|
36
|
-
#
|
37
|
-
# This options (`auto_hide`, `default_key`) also can be set as config options in
|
38
|
-
# any nested serializer.
|
39
|
-
#
|
40
|
-
# @example
|
41
|
-
# class PostSerializer < Serega
|
42
|
-
# plugin :batch, auto_hide: true, default_key: :id
|
43
|
-
#
|
44
|
-
# # Define batch loader via callable class, it must accept three args (keys, context, plan_point)
|
45
|
-
# attribute :comments_count, batch: { loader: PostCommentsCountBatchLoader, default: 0}
|
46
|
-
#
|
47
|
-
# # Define batch loader via Symbol, later we should define this loader via config.batch.define(:posts_comments_counter) { ... }
|
48
|
-
# attribute :comments_count, batch: { loader: :posts_comments_counter, default: 0}
|
49
|
-
#
|
50
|
-
# # Define batch loader with serializer
|
51
|
-
# attribute :comments, serializer: CommentSerializer, batch: { loader: :posts_comments, default: []}
|
52
|
-
#
|
53
|
-
# # Resulted block must return hash like { key => value(s) }
|
54
|
-
# config.batch.define(:posts_comments_counter) do |keys|
|
55
|
-
# Comment.group(:post_id).where(post_id: keys).count
|
56
|
-
# end
|
12
|
+
# class AppSerializer
|
13
|
+
# plugin :batch, id_method: :id
|
14
|
+
# end
|
57
15
|
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
# config.batch.define(:posts_comments) do |keys, context, plan_point|
|
62
|
-
# Comment.where(post_id: keys).where(is_spam: false).group_by(&:post_id)
|
63
|
-
# end
|
16
|
+
# class UserSerializer < AppSerializer
|
17
|
+
# attribute :comments_count, batch: { loader: CommentsCountBatchLoader, default: 0 }
|
18
|
+
# attribute :company, serializer: CompanySerializer, batch: { loader: UserCompanyBatchLoader }
|
64
19
|
# end
|
65
20
|
#
|
66
21
|
module Batch
|
@@ -88,7 +43,7 @@ class Serega
|
|
88
43
|
require_relative "lib/modules/config"
|
89
44
|
require_relative "lib/modules/object_serializer"
|
90
45
|
require_relative "lib/modules/plan_point"
|
91
|
-
require_relative "lib/validations/
|
46
|
+
require_relative "lib/validations/check_batch_opt_id_method"
|
92
47
|
require_relative "lib/validations/check_batch_opt_loader"
|
93
48
|
require_relative "lib/validations/check_opt_batch"
|
94
49
|
|
@@ -138,9 +93,9 @@ class Serega
|
|
138
93
|
|
139
94
|
config = serializer_class.config
|
140
95
|
config.attribute_keys << :batch
|
141
|
-
config.opts[:batch] = {loaders: {},
|
96
|
+
config.opts[:batch] = {loaders: {}, id_method: nil, auto_hide: false}
|
142
97
|
config.batch.auto_hide = opts[:auto_hide] if opts.key?(:auto_hide)
|
143
|
-
config.batch.
|
98
|
+
config.batch.id_method = opts[:id_method] if opts.key?(:id_method)
|
144
99
|
end
|
145
100
|
|
146
101
|
#
|
@@ -17,22 +17,25 @@ class Serega
|
|
17
17
|
# Defines batch loader
|
18
18
|
#
|
19
19
|
# @param loader_name [Symbol] Batch loader name, that is used when defining attribute with batch loader.
|
20
|
-
# @param block [Proc] Block that can accept 3 parameters -
|
21
|
-
# and returns hash
|
20
|
+
# @param block [Proc] Block that can accept 3 parameters - ids, context, plan_point
|
21
|
+
# and returns hash with ids as keys and values are batch loaded objects
|
22
22
|
#
|
23
23
|
# @return [void]
|
24
24
|
#
|
25
|
-
def define(loader_name, &block)
|
26
|
-
|
27
|
-
raise SeregaError, "
|
25
|
+
def define(loader_name, callable = nil, &block)
|
26
|
+
if (!callable && !block) || (callable && block)
|
27
|
+
raise SeregaError, "Batch loader can be specified with one of arguments - callable value or &block"
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
callable ||= block
|
31
|
+
SeregaValidations::Utils::CheckExtraKeywordArg.call(callable, "batch loader `#{loader_name}`")
|
32
|
+
params_count = SeregaUtils::ParamsCount.call(callable, max_count: 3)
|
33
|
+
|
34
|
+
if params_count > 3
|
35
|
+
raise SeregaError, "Batch loader can have maximum 3 parameters (ids, context, plan)"
|
33
36
|
end
|
34
37
|
|
35
|
-
loaders[loader_name] =
|
38
|
+
loaders[loader_name] = callable
|
36
39
|
end
|
37
40
|
|
38
41
|
# Shows defined loaders
|
@@ -41,16 +44,6 @@ class Serega
|
|
41
44
|
opts[:loaders]
|
42
45
|
end
|
43
46
|
|
44
|
-
#
|
45
|
-
# Finds previously defined batch loader by name
|
46
|
-
#
|
47
|
-
# @param loader_name [Symbol]
|
48
|
-
#
|
49
|
-
# @return [Proc] batch loader block
|
50
|
-
def fetch_loader(loader_name)
|
51
|
-
loaders[loader_name] || (raise SeregaError, "Batch loader with name `#{loader_name.inspect}` was not defined. Define example: config.batch.define(:#{loader_name}) { |keys, ctx, points| ... }")
|
52
|
-
end
|
53
|
-
|
54
47
|
# Shows option to auto hide attributes with :batch specified
|
55
48
|
# @return [Boolean, nil] option value
|
56
49
|
def auto_hide
|
@@ -64,17 +57,19 @@ class Serega
|
|
64
57
|
opts[:auto_hide] = value
|
65
58
|
end
|
66
59
|
|
67
|
-
# Shows
|
68
|
-
# @return [Symbol, nil]
|
69
|
-
def
|
70
|
-
opts[:
|
60
|
+
# Shows method name or callable object needed to get object identifier for batch load
|
61
|
+
# @return [Symbol, #call, nil] Default method name or callable object to get identifier
|
62
|
+
def id_method
|
63
|
+
opts[:id_method]
|
71
64
|
end
|
72
65
|
|
73
|
-
#
|
66
|
+
# Sets new identifier method name or callable value needed for batch loading
|
67
|
+
#
|
68
|
+
# @param value [Symbol, #call] New :id_method value
|
74
69
|
# @return [Boolean] New option value
|
75
|
-
def
|
76
|
-
|
77
|
-
opts[:
|
70
|
+
def id_method=(value)
|
71
|
+
CheckBatchOptIdMethod.call(value)
|
72
|
+
opts[:id_method] = value
|
78
73
|
end
|
79
74
|
end
|
80
75
|
end
|
@@ -42,22 +42,37 @@ class Serega
|
|
42
42
|
batch = init_opts[:batch]
|
43
43
|
return unless batch
|
44
44
|
|
45
|
-
|
46
|
-
loader = batch[:loader]
|
45
|
+
loader = prepare_batch_loader(batch[:loader])
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
proc_key =
|
51
|
-
if key.is_a?(Symbol)
|
52
|
-
proc { |object| object.public_send(key) }
|
53
|
-
else
|
54
|
-
key
|
55
|
-
end
|
47
|
+
id_method = batch[:id_method] || self.class.serializer_class.config.batch.id_method
|
48
|
+
id_method = prepare_batch_id_method(id_method)
|
56
49
|
|
57
|
-
# take default value
|
58
50
|
default = batch.fetch(:default) { many ? FROZEN_EMPTY_ARRAY : nil }
|
59
51
|
|
60
|
-
{loader: loader,
|
52
|
+
{loader: loader, id_method: id_method, default: default}
|
53
|
+
end
|
54
|
+
|
55
|
+
def prepare_batch_id_method(id_method)
|
56
|
+
return proc { |object| object.public_send(id_method) } if id_method.is_a?(Symbol)
|
57
|
+
|
58
|
+
params_count = SeregaUtils::ParamsCount.call(id_method, max_count: 2)
|
59
|
+
case params_count
|
60
|
+
when 0 then proc { id_method.call }
|
61
|
+
when 1 then proc { |object| id_method.call(object) }
|
62
|
+
else id_method
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def prepare_batch_loader(loader)
|
67
|
+
loader = self.class.serializer_class.config.batch.loaders.fetch(loader) if loader.is_a?(Symbol)
|
68
|
+
|
69
|
+
params_count = SeregaUtils::ParamsCount.call(loader, max_count: 3)
|
70
|
+
case params_count
|
71
|
+
when 0 then proc { loader.call }
|
72
|
+
when 1 then proc { |object| loader.call(object) }
|
73
|
+
when 2 then proc { |object, context| loader.call(object, context) }
|
74
|
+
else loader
|
75
|
+
end
|
61
76
|
end
|
62
77
|
end
|
63
78
|
end
|
@@ -19,8 +19,8 @@ class Serega
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def remember_key_for_batch_loading(batch, object, point, container)
|
22
|
-
|
23
|
-
batch_loader(point).remember(
|
22
|
+
id = batch[:id_method].call(object, context)
|
23
|
+
batch_loader(point).remember(id, container)
|
24
24
|
container[point.name] = nil # Reserve attribute place in resulted hash. We will set correct value later
|
25
25
|
end
|
26
26
|
|
@@ -13,25 +13,8 @@ class Serega
|
|
13
13
|
# Returns attribute :batch option with prepared loader
|
14
14
|
# @return [Hash] attribute :batch option
|
15
15
|
#
|
16
|
-
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def set_normalized_vars
|
21
|
-
super
|
22
|
-
@batch = prepare_batch
|
23
|
-
end
|
24
|
-
|
25
|
-
def prepare_batch
|
26
|
-
batch = attribute.batch
|
27
|
-
if batch
|
28
|
-
loader = batch[:loader]
|
29
|
-
if loader.is_a?(Symbol)
|
30
|
-
batch_config = attribute.class.serializer_class.config.batch
|
31
|
-
batch[:loader] = batch_config.fetch_loader(loader)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
batch
|
16
|
+
def batch
|
17
|
+
attribute.batch
|
35
18
|
end
|
36
19
|
end
|
37
20
|
end
|