serega 0.37.0 → 0.37.2
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 +30 -3
- data/VERSION +1 -1
- data/lib/serega/attribute_normalizer.rb +17 -8
- data/lib/serega/batch/attribute_loader.rb +6 -9
- data/lib/serega/batch/attribute_loaders.rb +9 -0
- data/lib/serega/object_serializer.rb +1 -8
- data/lib/serega/plugins/if/if.rb +5 -2
- data/lib/serega/utils/serialized_attribute_error.rb +28 -0
- data/lib/serega.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fe536c6d684f24508a5153d1fc679cd451d2360ab3d752ff2ba08a2249b264c9
|
|
4
|
+
data.tar.gz: 0110de5fca4df251b1101daaba232bf1fcace80b6290bb95e35a8c892fc1ce1b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5a7f60a35e6791337f15772efeb2dfa0363ff96bb99f815c849c1fb5b40125854802b39ef492bea078f6bb614171b29b8f0e5409e421c52e6f24bf39eabfffdd
|
|
7
|
+
data.tar.gz: 2d3adb93d754ed3fb42581e5a45493e30e3f1fbb1cda8c68956e5cc047be42417f8cd487074cae5ce11fe72dbfa633ba32ccdc52dd002de17f349b7532c10f4f
|
data/README.md
CHANGED
|
@@ -530,12 +530,12 @@ attribute is serialized:
|
|
|
530
530
|
|
|
531
531
|
```ruby
|
|
532
532
|
class UserSerializer < Serega
|
|
533
|
+
attribute :name
|
|
534
|
+
attribute :posts, serializer: PostSerializer, preload: :posts
|
|
535
|
+
|
|
533
536
|
# `objects` is the gathered users; `preloads` is this attribute's
|
|
534
537
|
# `:preload` value (`:posts`). `MyORM.preload` does the eager loading.
|
|
535
538
|
preload_with { |objects, preloads| MyORM.preload(objects, preloads) }
|
|
536
|
-
|
|
537
|
-
attribute :name
|
|
538
|
-
attribute :posts, serializer: PostSerializer, preload: :posts
|
|
539
539
|
end
|
|
540
540
|
```
|
|
541
541
|
|
|
@@ -547,6 +547,33 @@ objects can be plain Ruby objects too: the handler decides where the data comes
|
|
|
547
547
|
from (another ORM, an HTTP API, a cache) and how to attach it to the objects,
|
|
548
548
|
by whatever rules you define.
|
|
549
549
|
|
|
550
|
+
The handler runs **once per preloaded attribute**, not once per distinct
|
|
551
|
+
`:preload` value. If the same `:preload` is declared on several attributes, the
|
|
552
|
+
handler is invoked once for each of them over the same objects, so it should
|
|
553
|
+
check whether the data is already loaded before loading it again. (The
|
|
554
|
+
`:activerecord_preloads` handler relies on `ActiveRecord::Associations::Preloader`,
|
|
555
|
+
which already skips associations that are loaded.)
|
|
556
|
+
|
|
557
|
+
Because you choose the `:preload` value, it doubles as a **discriminator**: give
|
|
558
|
+
two attributes different `:preload` values and branch on `preloads` inside the
|
|
559
|
+
handler — no need for the value to be a real association name.
|
|
560
|
+
|
|
561
|
+
```ruby
|
|
562
|
+
class PostSerializer < Serega
|
|
563
|
+
attribute :owner, serializer: UserSerializer, preload: :owner
|
|
564
|
+
attribute :author, serializer: UserSerializer, preload: :author
|
|
565
|
+
|
|
566
|
+
# Both attributes load the same person, but from different sources.
|
|
567
|
+
preload_with do |objects, preloads|
|
|
568
|
+
case preloads
|
|
569
|
+
when :owner then load_owners(objects) # e.g. from the DB
|
|
570
|
+
when :author then load_authors(objects) # e.g. from an external API
|
|
571
|
+
else MyORM.preload(objects, preloads)
|
|
572
|
+
end
|
|
573
|
+
end
|
|
574
|
+
end
|
|
575
|
+
```
|
|
576
|
+
|
|
550
577
|
---
|
|
551
578
|
|
|
552
579
|
## Plugins
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.37.
|
|
1
|
+
0.37.2
|
|
@@ -9,10 +9,6 @@ class Serega
|
|
|
9
9
|
# AttributeNormalizer instance methods
|
|
10
10
|
#
|
|
11
11
|
module AttributeNormalizerInstanceMethods
|
|
12
|
-
# Identity loader that routes relation/preload attributes through the batch
|
|
13
|
-
# mechanism. Its result is never read — the attribute's value block still
|
|
14
|
-
# computes the value.
|
|
15
|
-
AUTO_BATCH_LOADER = proc { |records| records.to_h { |record| [record, record] } }
|
|
16
12
|
# Attribute initial params
|
|
17
13
|
# @return [Hash] Attribute initial params
|
|
18
14
|
attr_reader :init_name, :init_opts, :init_block
|
|
@@ -152,13 +148,24 @@ class Serega
|
|
|
152
148
|
|
|
153
149
|
hide_setting = config.hide_by_default
|
|
154
150
|
return true if hide_setting == true
|
|
155
|
-
return true if hide_setting == :auto &&
|
|
151
|
+
return true if hide_setting == :auto && preload_or_batch?
|
|
156
152
|
|
|
157
153
|
# Return nil for undefined value which means "not hide" but allows
|
|
158
154
|
# to change this value by plugins
|
|
159
155
|
nil
|
|
160
156
|
end
|
|
161
157
|
|
|
158
|
+
# The `hide_by_default = :auto` criterion: hide attributes that fetch extra
|
|
159
|
+
# data on demand — those declared with `:preload` or an explicit `:batch`.
|
|
160
|
+
#
|
|
161
|
+
# This is intentionally narrower than a "goes through the batch mechanism"
|
|
162
|
+
# check (`SeregaPlanPoint#batch?`): a plain relation (`serializer:` without
|
|
163
|
+
# `:preload`) is batch-processed too, but it only serializes an already-loaded
|
|
164
|
+
# nested object, so it stays visible by default.
|
|
165
|
+
def preload_or_batch?
|
|
166
|
+
!!(preloads || init_opts.key?(:batch))
|
|
167
|
+
end
|
|
168
|
+
|
|
162
169
|
def config
|
|
163
170
|
self.class.serializer_class.config
|
|
164
171
|
end
|
|
@@ -197,9 +204,11 @@ class Serega
|
|
|
197
204
|
return explicit_batch_loaders(batch_opt) if batch_opt
|
|
198
205
|
return FROZEN_EMPTY_ARRAY unless serializer || preloads
|
|
199
206
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
207
|
+
# Relations and preloads only need to be routed through the batch phase
|
|
208
|
+
# (so objects are gathered per level and preloads run) — there is nothing
|
|
209
|
+
# to load, since the value comes from the attribute's own resolver. Mark
|
|
210
|
+
# them with the reserved marker; the batch machinery skips it.
|
|
211
|
+
[SeregaBatch::AUTO_BATCH_LOADER_NAME]
|
|
203
212
|
end
|
|
204
213
|
|
|
205
214
|
def explicit_batch_loaders(batch_opt)
|
|
@@ -41,7 +41,7 @@ class Serega
|
|
|
41
41
|
|
|
42
42
|
handler.call(level.objects, preloads)
|
|
43
43
|
rescue => error
|
|
44
|
-
|
|
44
|
+
SeregaUtils::SerializedAttributeError.call(error, point)
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
# Loads a single named batch for this level's objects. Caching across
|
|
@@ -51,16 +51,20 @@ class Serega
|
|
|
51
51
|
def load_batch(loader)
|
|
52
52
|
level.load(loader)
|
|
53
53
|
rescue => error
|
|
54
|
-
|
|
54
|
+
SeregaUtils::SerializedAttributeError.call(error, point)
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
# Attaches already loaded batch values to every stored object.
|
|
58
|
+
# Value resolution happens here (inside the attacher), so errors are
|
|
59
|
+
# annotated with the attribute, matching the synchronous walk.
|
|
58
60
|
# @param batches [Hash] Loaded values grouped by batch loader name
|
|
59
61
|
# @return [void]
|
|
60
62
|
def attach(batches)
|
|
61
63
|
serialized_object_attachers.each do |object, attacher|
|
|
62
64
|
attacher.call(object, batches)
|
|
63
65
|
end
|
|
66
|
+
rescue => error
|
|
67
|
+
SeregaUtils::SerializedAttributeError.call(error, point)
|
|
64
68
|
end
|
|
65
69
|
|
|
66
70
|
attr_reader :point
|
|
@@ -68,13 +72,6 @@ class Serega
|
|
|
68
72
|
|
|
69
73
|
private
|
|
70
74
|
|
|
71
|
-
def reraise_with_serialized_attribute_details(error)
|
|
72
|
-
raise error.exception(<<~MESSAGE.strip)
|
|
73
|
-
#{error.message}
|
|
74
|
-
(when serializing '#{point.name}' attribute in #{point.class.serializer_class})
|
|
75
|
-
MESSAGE
|
|
76
|
-
end
|
|
77
|
-
|
|
78
75
|
attr_reader :serialized_object_attachers
|
|
79
76
|
end
|
|
80
77
|
|
|
@@ -5,6 +5,11 @@ class Serega
|
|
|
5
5
|
# Batch feature main module
|
|
6
6
|
#
|
|
7
7
|
module SeregaBatch
|
|
8
|
+
# Reserved batch-loader name that marks relation/preload attributes as
|
|
9
|
+
# batch-processed. It has no registered loader — the attribute's value comes
|
|
10
|
+
# from its own resolver — so `load_batches` skips it (nothing to load).
|
|
11
|
+
AUTO_BATCH_LOADER_NAME = :__auto_batch__
|
|
12
|
+
|
|
8
13
|
#
|
|
9
14
|
# Batch loaders
|
|
10
15
|
#
|
|
@@ -98,6 +103,10 @@ class Serega
|
|
|
98
103
|
|
|
99
104
|
batches = {}
|
|
100
105
|
point.batch_loaders.each do |batch_loader_name|
|
|
106
|
+
# Auto-batched relations/preloads carry the marker, not a real loader:
|
|
107
|
+
# their value comes from their own resolver, so there is nothing to load.
|
|
108
|
+
next if batch_loader_name == AUTO_BATCH_LOADER_NAME
|
|
109
|
+
|
|
101
110
|
loader = serializer_class.batch_loaders[batch_loader_name]
|
|
102
111
|
batches[batch_loader_name] = attribute_loader.load_batch(loader)
|
|
103
112
|
end
|
|
@@ -55,7 +55,7 @@ class Serega
|
|
|
55
55
|
plan.points.each_with_object({}) do |point, container|
|
|
56
56
|
serialize_point(object, point, container)
|
|
57
57
|
rescue => error
|
|
58
|
-
|
|
58
|
+
SeregaUtils::SerializedAttributeError.call(error, point)
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
@@ -103,13 +103,6 @@ class Serega
|
|
|
103
103
|
def array?(object, many)
|
|
104
104
|
many.nil? ? object.is_a?(Enumerable) : many
|
|
105
105
|
end
|
|
106
|
-
|
|
107
|
-
def reraise_with_serialized_attribute_details(error, point)
|
|
108
|
-
raise error.exception(<<~MESSAGE.strip)
|
|
109
|
-
#{error.message}
|
|
110
|
-
(when serializing '#{point.name}' attribute in #{self.class.serializer_class})
|
|
111
|
-
MESSAGE
|
|
112
|
-
end
|
|
113
106
|
end
|
|
114
107
|
|
|
115
108
|
extend Serega::SeregaHelpers::SerializerClassHelper
|
data/lib/serega/plugins/if/if.rb
CHANGED
|
@@ -275,8 +275,11 @@ class Serega
|
|
|
275
275
|
super
|
|
276
276
|
end
|
|
277
277
|
|
|
278
|
-
def attach_final_value(value, point,
|
|
279
|
-
|
|
278
|
+
def attach_final_value(value, point, container)
|
|
279
|
+
unless point.satisfy_if_value_conditions?(value, context)
|
|
280
|
+
container.delete(point.name) # remove reserved placeholder
|
|
281
|
+
return KEY_SKIPPED
|
|
282
|
+
end
|
|
280
283
|
|
|
281
284
|
super
|
|
282
285
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Serega
|
|
4
|
+
module SeregaUtils
|
|
5
|
+
#
|
|
6
|
+
# Reraises an error, adding which attribute and serializer were being
|
|
7
|
+
# serialized when it happened. Shared by the synchronous serialization walk
|
|
8
|
+
# and the batch attach phase so the message stays identical for every
|
|
9
|
+
# attribute, whether its value is resolved inline or during batch loading.
|
|
10
|
+
#
|
|
11
|
+
module SerializedAttributeError
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
#
|
|
15
|
+
# @param error [Exception] Original error
|
|
16
|
+
# @param point [SeregaPlanPoint] Plan point being serialized
|
|
17
|
+
#
|
|
18
|
+
# @return [void]
|
|
19
|
+
#
|
|
20
|
+
def call(error, point)
|
|
21
|
+
raise error.exception(<<~MESSAGE.strip)
|
|
22
|
+
#{error.message}
|
|
23
|
+
(when serializing '#{point.name}' attribute in #{point.class.serializer_class})
|
|
24
|
+
MESSAGE
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/serega.rb
CHANGED
|
@@ -22,6 +22,7 @@ require_relative "serega/helpers/serializer_class_helper"
|
|
|
22
22
|
require_relative "serega/utils/enum_deep_dup"
|
|
23
23
|
require_relative "serega/utils/enum_deep_freeze"
|
|
24
24
|
require_relative "serega/utils/method_signature"
|
|
25
|
+
require_relative "serega/utils/serialized_attribute_error"
|
|
25
26
|
require_relative "serega/utils/symbol_name"
|
|
26
27
|
require_relative "serega/utils/to_hash"
|
|
27
28
|
require_relative "serega/attribute_value_resolvers/batch"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: serega
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.37.
|
|
4
|
+
version: 0.37.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrey Glushkov
|
|
@@ -74,6 +74,7 @@ files:
|
|
|
74
74
|
- lib/serega/utils/enum_deep_dup.rb
|
|
75
75
|
- lib/serega/utils/enum_deep_freeze.rb
|
|
76
76
|
- lib/serega/utils/method_signature.rb
|
|
77
|
+
- lib/serega/utils/serialized_attribute_error.rb
|
|
77
78
|
- lib/serega/utils/symbol_name.rb
|
|
78
79
|
- lib/serega/utils/to_hash.rb
|
|
79
80
|
- lib/serega/validations/attribute/check_block.rb
|