serega 0.32.0 → 0.33.1
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 +22 -25
- data/VERSION +1 -1
- data/lib/serega/attribute_normalizer.rb +4 -17
- data/lib/serega/attribute_value_resolvers/batch.rb +66 -0
- data/lib/serega/attribute_value_resolvers/const.rb +41 -0
- data/lib/serega/attribute_value_resolvers/delegate.rb +69 -0
- data/lib/serega/attribute_value_resolvers/keyword.rb +42 -0
- data/lib/serega/batch/attribute_loader.rb +2 -2
- data/lib/serega/config.rb +13 -29
- data/lib/serega/plugins/if/if.rb +20 -1
- data/lib/serega.rb +10 -92
- metadata +6 -7
- data/lib/serega/batch/auto_resolver.rb +0 -24
- data/lib/serega/batch/auto_resolver_factory.rb +0 -48
- data/lib/serega/json/adapter.rb +0 -23
- data/lib/serega/json/json.rb +0 -37
- data/lib/serega/json/oj.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 94fe2e370dea04c0bedcb7bed7f7f3b6f9ad9ff49a4c39367429929b5bc68e7e
|
4
|
+
data.tar.gz: 65cbab835785d14d8c17aba1ba6eff21b8abb9689eba2e4f15e28d68ee60cea9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9600ce857e2f2fd0836da34cec422eda2472e945a9c3fd94d4e4c9de7fb95e3ddb147b5ac59dc3c575695787fcb9634b9c1fc7cc512590b2da17e6ec492e9271
|
7
|
+
data.tar.gz: 7fa17d2686eb63a64b38cd7fea6a006de17497425a77f17a8619d112de8ca7b361549bcc5c0b03fd06c2bf842aec04f366d3aa5c63ae3b40b5e794b666a3a327
|
data/README.md
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/serega)
|
4
4
|
[](https://github.com/aglushkov/serega/actions/workflows/main.yml)
|
5
|
-
[](https://codeclimate.com/github/aglushkov/serega/test_coverage)
|
6
|
-
[](https://codeclimate.com/github/aglushkov/serega/maintainability)
|
7
5
|
|
8
6
|
The Serega Ruby Serializer provides easy and powerful DSL to describe your
|
9
7
|
objects and serialize them to Hash or JSON.
|
@@ -143,9 +141,8 @@ end
|
|
143
141
|
|
144
142
|
### Serializing
|
145
143
|
|
146
|
-
We can serialize objects using class
|
147
|
-
same instance methods `#
|
148
|
-
The `to_h` method is also aliased as `call`.
|
144
|
+
We can serialize objects using class method `.call` aliased ad `.to_h` and
|
145
|
+
same instance methods `#call` and its alias `#to_h`.
|
149
146
|
|
150
147
|
```ruby
|
151
148
|
user = OpenStruct.new(username: 'serega')
|
@@ -156,12 +153,6 @@ end
|
|
156
153
|
|
157
154
|
UserSerializer.to_h(user) # => {username: "serega"}
|
158
155
|
UserSerializer.to_h([user]) # => [{username: "serega"}]
|
159
|
-
|
160
|
-
UserSerializer.to_json(user) # => '{"username":"serega"}'
|
161
|
-
UserSerializer.to_json([user]) # => '[{"username":"serega"}]'
|
162
|
-
|
163
|
-
UserSerializer.as_json(user) # => {"username":"serega"}
|
164
|
-
UserSerializer.as_json([user]) # => [{"username":"serega"}]
|
165
156
|
```
|
166
157
|
|
167
158
|
If serialized fields are constant, then it's a good idea to initiate the
|
@@ -333,8 +324,8 @@ Named loaders can be defined using the `batch_loader` class method and reused ac
|
|
333
324
|
```ruby
|
334
325
|
class UserSerializer < Serega
|
335
326
|
# Define named loaders
|
336
|
-
|
337
|
-
|
327
|
+
batch :comments_count, ->(users) { Comment.where(user: users).group(:user_id).count }
|
328
|
+
batch :comments_count, CommentsCountLoader # Example with callable class
|
338
329
|
|
339
330
|
# Full attribute example
|
340
331
|
attribute :comments_count, batch: { use: :comments_count },
|
@@ -373,8 +364,8 @@ Custom loaders can be provided directly using the `:use` option with any callabl
|
|
373
364
|
```ruby
|
374
365
|
class UserSerializer < Serega
|
375
366
|
# Define named loaders
|
376
|
-
|
377
|
-
|
367
|
+
batch :facebook_likes, FacebookLikesLoader
|
368
|
+
batch :twitter_likes, TwitterLikesLoader
|
378
369
|
|
379
370
|
# Summarize likes
|
380
371
|
attribute :likes_count,
|
@@ -389,16 +380,22 @@ Here are the default options. Other options can be added with plugins.
|
|
389
380
|
|
390
381
|
```ruby
|
391
382
|
class AppSerializer < Serega
|
392
|
-
#
|
393
|
-
#
|
394
|
-
#
|
395
|
-
config.
|
396
|
-
|
397
|
-
#
|
398
|
-
#
|
399
|
-
#
|
400
|
-
|
401
|
-
|
383
|
+
# With `activerecord_preloads` plugin it automatically adds `preload` option
|
384
|
+
# to attributes with `:delegate` or `:serializer` option.
|
385
|
+
# It helps to preload associations automatically, omitting N+1 requests.
|
386
|
+
config.auto_preload = false
|
387
|
+
|
388
|
+
# Automatically marks as hidden attributes with `:preload` or `:batch` options.
|
389
|
+
# By default is false. Useful option to not make extra DB requests if attribute
|
390
|
+
# was not requested
|
391
|
+
config.auto_hide = false
|
392
|
+
|
393
|
+
# Default method used on serialized object to resolve batch value
|
394
|
+
# For example:
|
395
|
+
# attribute :counter, batch: CounterBatchLoader
|
396
|
+
# # Attribute values will be resolved as:
|
397
|
+
# proc { |object, batches:| batches[:counter][object.id] }
|
398
|
+
config.batch_id_option = :id
|
402
399
|
|
403
400
|
# Disable/enable validation of modifiers (`:with, :except, :only`)
|
404
401
|
# By default, this validation is enabled.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.33.1
|
@@ -191,22 +191,18 @@ class Serega
|
|
191
191
|
def prepare_const_block
|
192
192
|
return unless init_opts.key?(:const)
|
193
193
|
|
194
|
-
|
195
|
-
proc { const }
|
194
|
+
AttributeValueResolvers::ConstResolver.get(init_opts[:const])
|
196
195
|
end
|
197
196
|
|
198
197
|
def prepare_keyword_block
|
199
|
-
|
200
|
-
proc do |object|
|
201
|
-
object.public_send(key_method_name)
|
202
|
-
end
|
198
|
+
AttributeValueResolvers::KeywordResolver.get(method_name)
|
203
199
|
end
|
204
200
|
|
205
201
|
def prepare_batch_loader_block
|
206
202
|
batch_opt = init_opts[:batch]
|
207
203
|
return unless batch_opt
|
208
204
|
|
209
|
-
|
205
|
+
AttributeValueResolvers::BatchResolver.get(self.class.serializer_class, name, batch_opt)
|
210
206
|
end
|
211
207
|
|
212
208
|
def prepare_batch_loaders
|
@@ -238,16 +234,7 @@ class Serega
|
|
238
234
|
delegate_to = delegate[:to]
|
239
235
|
|
240
236
|
allow_nil = delegate.fetch(:allow_nil) { config.delegate_default_allow_nil }
|
241
|
-
|
242
|
-
if allow_nil
|
243
|
-
proc do |object|
|
244
|
-
object.public_send(delegate_to)&.public_send(key_method_name)
|
245
|
-
end
|
246
|
-
else
|
247
|
-
proc do |object|
|
248
|
-
object.public_send(delegate_to).public_send(key_method_name)
|
249
|
-
end
|
250
|
-
end
|
237
|
+
AttributeValueResolvers::DelegateResolver.get(delegate_to, key_method_name, allow_nil)
|
251
238
|
end
|
252
239
|
|
253
240
|
# Prepares preloads
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
#
|
5
|
+
# Attribute value resolvers
|
6
|
+
#
|
7
|
+
module AttributeValueResolvers
|
8
|
+
#
|
9
|
+
# Builds value resolver class for attributes with :batch option
|
10
|
+
#
|
11
|
+
class BatchResolver
|
12
|
+
#
|
13
|
+
# Generates callable block to find attribute value when attribute with :batch
|
14
|
+
# option has no block or manual :value option.
|
15
|
+
#
|
16
|
+
# In other cases we should never get here as attribute value/block option must be manually defined.
|
17
|
+
#
|
18
|
+
# It handles this cases:
|
19
|
+
# - `attribute :foo, batch: true`
|
20
|
+
# - `attribute :foo, batch: FooLoader`
|
21
|
+
# - `attribute :foo, batch: { id: :foo_id }`
|
22
|
+
# - `attribute :foo, batch: { use: FooLoader, id: foo_id }`
|
23
|
+
# - `attribute :foo, batch: { use: :foo_loader, id: foo_id }`
|
24
|
+
#
|
25
|
+
def self.get(serializer_class, attribute_name, batch_opt)
|
26
|
+
default_method = serializer_class.config.batch_id_option
|
27
|
+
|
28
|
+
if batch_opt == true # ex: `batch: true`
|
29
|
+
batch_name = attribute_name
|
30
|
+
batch_id_method = default_method
|
31
|
+
elsif batch_opt.respond_to?(:call) # ex: `batch: FooLoader`
|
32
|
+
serializer_class.batch(attribute_name, batch_opt)
|
33
|
+
batch_name = attribute_name
|
34
|
+
batch_id_method = default_method
|
35
|
+
else
|
36
|
+
use = batch_opt[:use]
|
37
|
+
batch_id_method = batch_opt[:id] || default_method
|
38
|
+
|
39
|
+
if use.respond_to?(:call) # ex: `batch: { use: FooLoader }`
|
40
|
+
batch_name = attribute_name
|
41
|
+
serializer_class.batch(batch_name, use)
|
42
|
+
else # ex: `batch: { use: :foo }` || batch: { id: :some_id }
|
43
|
+
batch_name = use || attribute_name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Batch.new(batch_name, batch_id_method)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# Builds value resolver class for attributes with :batch option
|
53
|
+
#
|
54
|
+
class Batch
|
55
|
+
def initialize(loader_name, id_method)
|
56
|
+
@loader_name = loader_name
|
57
|
+
@id_method = id_method
|
58
|
+
end
|
59
|
+
|
60
|
+
# Finds object attribute value from hash of batch_loaded values
|
61
|
+
def call(obj, batches:)
|
62
|
+
batches.fetch(@loader_name)[obj.public_send(@id_method)]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
#
|
5
|
+
# Attribute value resolvers
|
6
|
+
#
|
7
|
+
module AttributeValueResolvers
|
8
|
+
#
|
9
|
+
# Builds value resolver class for attributes with :const option
|
10
|
+
#
|
11
|
+
class ConstResolver
|
12
|
+
#
|
13
|
+
# Creates resolver that returns constant value
|
14
|
+
#
|
15
|
+
# @param const_value [Object] constant value to return
|
16
|
+
# @return [Const] resolver instance
|
17
|
+
#
|
18
|
+
def self.get(const_value)
|
19
|
+
Const.new(const_value)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Value resolver class for attributes with :const option
|
25
|
+
#
|
26
|
+
class Const
|
27
|
+
def initialize(const_value)
|
28
|
+
@const_value = const_value
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Returns the constant value
|
33
|
+
#
|
34
|
+
# @return [Object] the constant value
|
35
|
+
#
|
36
|
+
def call
|
37
|
+
@const_value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
#
|
5
|
+
# Attribute value resolvers
|
6
|
+
#
|
7
|
+
module AttributeValueResolvers
|
8
|
+
#
|
9
|
+
# Builds value resolver class for attributes with :delegate option
|
10
|
+
#
|
11
|
+
class DelegateResolver
|
12
|
+
#
|
13
|
+
# Creates resolver that delegates method call to another object
|
14
|
+
#
|
15
|
+
# @param delegate_to [Symbol] method to call on object to get delegated object
|
16
|
+
# @param method_name [Symbol] method to call on delegated object
|
17
|
+
# @param allow_nil [Boolean] whether to use safe navigation when delegated object is nil
|
18
|
+
# @return [Delegate, DelegateAllowNil] resolver instance
|
19
|
+
#
|
20
|
+
def self.get(delegate_to, method_name, allow_nil)
|
21
|
+
allow_nil ? DelegateAllowNil.new(delegate_to, method_name) : Delegate.new(delegate_to, method_name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Value resolver class for attributes with :delegate (with :allow_nil) option
|
27
|
+
#
|
28
|
+
class DelegateAllowNil
|
29
|
+
def initialize(delegate_to, method_name)
|
30
|
+
@delegate_to = delegate_to
|
31
|
+
@method_name = method_name
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Delegates method call to another object with safe navigation
|
36
|
+
#
|
37
|
+
# @param object [Object] the object to delegate from
|
38
|
+
# @return [Object, nil] result of delegated method call or nil if delegated object is nil
|
39
|
+
#
|
40
|
+
def call(object)
|
41
|
+
object.public_send(delegate_to)&.public_send(method_name)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :delegate_to, :method_name
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Value resolver class for attributes with :delegate (without :allow_nil) option
|
51
|
+
#
|
52
|
+
class Delegate
|
53
|
+
def initialize(delegate_to, method_name)
|
54
|
+
@delegate_to = delegate_to
|
55
|
+
@method_name = method_name
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Delegates method call to another object
|
60
|
+
#
|
61
|
+
# @param object [Object] the object to delegate from
|
62
|
+
# @return [Object] result of delegated method call
|
63
|
+
#
|
64
|
+
def call(object)
|
65
|
+
object.public_send(@delegate_to).public_send(@method_name)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
#
|
5
|
+
# Attribute value resolvers
|
6
|
+
#
|
7
|
+
module AttributeValueResolvers
|
8
|
+
#
|
9
|
+
# Builds value resolver class for attributes with :keyword option
|
10
|
+
#
|
11
|
+
class KeywordResolver
|
12
|
+
#
|
13
|
+
# Creates resolver that calls method on object
|
14
|
+
#
|
15
|
+
# @param keyword [Symbol] method name to call on object
|
16
|
+
# @return [Keyword] resolver instance
|
17
|
+
#
|
18
|
+
def self.get(keyword)
|
19
|
+
Keyword.new(keyword)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Value resolver class for attributes with :keyword option
|
25
|
+
#
|
26
|
+
class Keyword
|
27
|
+
def initialize(keyword)
|
28
|
+
@keyword = keyword
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Calls the keyword method on the object
|
33
|
+
#
|
34
|
+
# @param object [Object] the object to call method on
|
35
|
+
# @return [Object] result of method call
|
36
|
+
#
|
37
|
+
def call(object)
|
38
|
+
object.public_send(@keyword)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -13,7 +13,7 @@ class Serega
|
|
13
13
|
def initialize(point)
|
14
14
|
@point = point
|
15
15
|
@objects = []
|
16
|
-
@serialized_object_attachers =
|
16
|
+
@serialized_object_attachers = []
|
17
17
|
end
|
18
18
|
|
19
19
|
# Stores object with attacher to find and attach attribute values in batch later.
|
@@ -22,7 +22,7 @@ class Serega
|
|
22
22
|
# @return [void]
|
23
23
|
def store(object, attacher)
|
24
24
|
objects << object
|
25
|
-
serialized_object_attachers[object
|
25
|
+
serialized_object_attachers << [object, attacher]
|
26
26
|
end
|
27
27
|
|
28
28
|
# Loads serialized values for all stored objects for current attribute
|
data/lib/serega/config.rb
CHANGED
@@ -33,12 +33,7 @@ class Serega
|
|
33
33
|
max_cached_plans_per_serializer_count: 0,
|
34
34
|
auto_preload: {has_delegate_option: false, has_serializer_option: false},
|
35
35
|
auto_hide: {has_preload_option: false, has_batch_option: false},
|
36
|
-
|
37
|
-
# auto_preload_attributes_with_serializer: false,
|
38
|
-
# auto_hide_attributes_with_preload: false,
|
39
|
-
# hide_batch_attributes: false,
|
40
|
-
to_json: (SeregaJSON.adapter == :oj) ? SeregaJSON::OjDump : SeregaJSON::JSONDump,
|
41
|
-
from_json: (SeregaJSON.adapter == :oj) ? SeregaJSON::OjLoad : SeregaJSON::JSONLoad
|
36
|
+
batch_id_option: :id
|
42
37
|
}.freeze
|
43
38
|
# :nocov:
|
44
39
|
|
@@ -82,7 +77,7 @@ class Serega
|
|
82
77
|
opts.fetch(:attribute_keys)
|
83
78
|
end
|
84
79
|
|
85
|
-
# Returns options names allowed in `
|
80
|
+
# Returns options names allowed in `call, to_h` methods
|
86
81
|
# @return [Array<Symbol>] Allowed options keys for serialization
|
87
82
|
def serialize_keys
|
88
83
|
opts.fetch(:serialize_keys)
|
@@ -191,30 +186,19 @@ class Serega
|
|
191
186
|
opts[:check_attribute_name] = value
|
192
187
|
end
|
193
188
|
|
194
|
-
# Returns current
|
195
|
-
|
196
|
-
|
197
|
-
opts.fetch(:to_json)
|
189
|
+
# Returns current batch_id_option
|
190
|
+
def batch_id_option
|
191
|
+
opts.fetch(:batch_id_option)
|
198
192
|
end
|
199
193
|
|
200
|
-
# Sets
|
201
|
-
#
|
202
|
-
# @
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
# @return [#call] Callable that used to parse JSON
|
209
|
-
def from_json
|
210
|
-
opts.fetch(:from_json)
|
211
|
-
end
|
212
|
-
|
213
|
-
# Sets current `from_json` adapter
|
214
|
-
# @param value [#call] Callable that used to parse JSON
|
215
|
-
# @return [#call] Provided callable object
|
216
|
-
def from_json=(value)
|
217
|
-
opts[:from_json] = value
|
194
|
+
# Sets :batch_id_option config option
|
195
|
+
#
|
196
|
+
# @param value [Symbol] Set :batch_id_option config option
|
197
|
+
#
|
198
|
+
# @return [Symbol] New :check_attribute_name config option
|
199
|
+
def batch_id_option=(value)
|
200
|
+
raise SeregaError, "Must have Symbol value, #{value.inspect} provided" unless value.is_a?(Symbol)
|
201
|
+
opts[:batch_id_option] = value
|
218
202
|
end
|
219
203
|
end
|
220
204
|
|
data/lib/serega/plugins/if/if.rb
CHANGED
@@ -128,12 +128,31 @@ class Serega
|
|
128
128
|
|
129
129
|
def prepare_if_option(if_option)
|
130
130
|
return unless if_option
|
131
|
-
return
|
131
|
+
return KeywordConditionResolver.new(if_option) if if_option.is_a?(Symbol)
|
132
132
|
|
133
133
|
if_option
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
137
|
+
#
|
138
|
+
# Resolves keyword-based conditions for if/unless options
|
139
|
+
#
|
140
|
+
class KeywordConditionResolver
|
141
|
+
def initialize(keyword)
|
142
|
+
@keyword = keyword
|
143
|
+
end
|
144
|
+
|
145
|
+
#
|
146
|
+
# Calls the keyword method on the object
|
147
|
+
#
|
148
|
+
# @param object [Object] the object to call method on
|
149
|
+
# @return [Object] result of method call
|
150
|
+
#
|
151
|
+
def call(object)
|
152
|
+
object.public_send(@keyword)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
137
156
|
#
|
138
157
|
# SeregaAttribute additional/patched instance methods
|
139
158
|
#
|
data/lib/serega.rb
CHANGED
@@ -23,14 +23,14 @@ require_relative "serega/utils/preload_paths"
|
|
23
23
|
require_relative "serega/utils/preloads_constructor"
|
24
24
|
require_relative "serega/utils/symbol_name"
|
25
25
|
require_relative "serega/utils/to_hash"
|
26
|
-
require_relative "serega/
|
27
|
-
|
26
|
+
require_relative "serega/attribute_value_resolvers/batch"
|
27
|
+
require_relative "serega/attribute_value_resolvers/const"
|
28
|
+
require_relative "serega/attribute_value_resolvers/delegate"
|
29
|
+
require_relative "serega/attribute_value_resolvers/keyword"
|
28
30
|
require_relative "serega/attribute"
|
29
31
|
require_relative "serega/attribute_normalizer"
|
30
32
|
require_relative "serega/batch/attribute_loader"
|
31
33
|
require_relative "serega/batch/attribute_loaders"
|
32
|
-
require_relative "serega/batch/auto_resolver"
|
33
|
-
require_relative "serega/batch/auto_resolver_factory"
|
34
34
|
require_relative "serega/batch/loader"
|
35
35
|
require_relative "serega/validations/utils/check_allowed_keys"
|
36
36
|
require_relative "serega/validations/utils/check_opt_is_bool"
|
@@ -78,7 +78,7 @@ class Serega
|
|
78
78
|
check_serialize_params_class.serializer_class = self
|
79
79
|
const_set(:CheckSerializeParams, check_serialize_params_class)
|
80
80
|
|
81
|
-
# Validates `Serializer.
|
81
|
+
# Validates `Serializer.batch` params
|
82
82
|
check_batch_loader_params_class = Class.new(SeregaValidations::CheckBatchLoaderParams)
|
83
83
|
check_batch_loader_params_class.serializer_class = self
|
84
84
|
const_set(:CheckBatchLoaderParams, check_batch_loader_params_class)
|
@@ -155,7 +155,7 @@ class Serega
|
|
155
155
|
end
|
156
156
|
|
157
157
|
#
|
158
|
-
# Lists batch loaders
|
158
|
+
# Lists defined batch loaders
|
159
159
|
#
|
160
160
|
# @return [Hash] batch loaders list
|
161
161
|
#
|
@@ -184,7 +184,7 @@ class Serega
|
|
184
184
|
# Defines a batch loader
|
185
185
|
#
|
186
186
|
# @example
|
187
|
-
#
|
187
|
+
# batch :tags, PostTagsLoader
|
188
188
|
#
|
189
189
|
# @example with block
|
190
190
|
# batch_loader(:tags) do |posts|
|
@@ -208,7 +208,7 @@ class Serega
|
|
208
208
|
#
|
209
209
|
# @return [#call] Batch loader
|
210
210
|
#
|
211
|
-
def
|
211
|
+
def batch(name, value = nil, &block)
|
212
212
|
raise SeregaError, "Batch loader must be defined with a callable value or block" if (value && block) || (!value && !block)
|
213
213
|
|
214
214
|
batch_loader = self::SeregaBatchLoader.new(name: name, block: value || block)
|
@@ -245,59 +245,7 @@ class Serega
|
|
245
245
|
new(modifiers_opts).to_h(object, serialize_opts)
|
246
246
|
end
|
247
247
|
|
248
|
-
|
249
|
-
# Serializes provided object to Hash
|
250
|
-
#
|
251
|
-
# @param object [Object] Serialized object
|
252
|
-
# @param opts [Hash, nil] Serializer modifiers and other instantiating options
|
253
|
-
# @option opts [Array, Hash, String, Symbol] :only The only attributes to serialize
|
254
|
-
# @option opts [Array, Hash, String, Symbol] :except Attributes to hide
|
255
|
-
# @option opts [Array, Hash, String, Symbol] :with Attributes (usually hidden) to serialize additionally
|
256
|
-
# @option opts [Boolean] :validate Validates provided modifiers (Default is true)
|
257
|
-
# @option opts [Hash] :context Serialization context
|
258
|
-
# @option opts [Boolean] :many Set true if provided multiple objects (Default `object.is_a?(Enumerable)`)
|
259
|
-
#
|
260
|
-
# @return [Hash] Serialization result
|
261
|
-
#
|
262
|
-
def to_h(object, opts = nil)
|
263
|
-
call(object, opts)
|
264
|
-
end
|
265
|
-
|
266
|
-
#
|
267
|
-
# Serializes provided object to JSON string
|
268
|
-
#
|
269
|
-
# @param object [Object] Serialized object
|
270
|
-
# @param opts [Hash, nil] Serializer modifiers and other instantiating options
|
271
|
-
# @option opts [Array, Hash, String, Symbol] :only The only attributes to serialize
|
272
|
-
# @option opts [Array, Hash, String, Symbol] :except Attributes to hide
|
273
|
-
# @option opts [Array, Hash, String, Symbol] :with Attributes (usually hidden) to serialize additionally
|
274
|
-
# @option opts [Boolean] :validate Validates provided modifiers (Default is true)
|
275
|
-
# @option opts [Hash] :context Serialization context
|
276
|
-
# @option opts [Boolean] :many Set true if provided multiple objects (Default `object.is_a?(Enumerable)`)
|
277
|
-
#
|
278
|
-
# @return [String] Serialization result
|
279
|
-
#
|
280
|
-
def to_json(object, opts = nil)
|
281
|
-
config.to_json.call(to_h(object, opts))
|
282
|
-
end
|
283
|
-
|
284
|
-
#
|
285
|
-
# Serializes provided object as JSON
|
286
|
-
#
|
287
|
-
# @param object [Object] Serialized object
|
288
|
-
# @param opts [Hash, nil] Serializer modifiers and other instantiating options
|
289
|
-
# @option opts [Array, Hash, String, Symbol] :only The only attributes to serialize
|
290
|
-
# @option opts [Array, Hash, String, Symbol] :except Attributes to hide
|
291
|
-
# @option opts [Array, Hash, String, Symbol] :with Attributes (usually hidden) to serialize additionally
|
292
|
-
# @option opts [Boolean] :validate Validates provided modifiers (Default is true)
|
293
|
-
# @option opts [Hash] :context Serialization context
|
294
|
-
# @option opts [Boolean] :many Set true if provided multiple objects (Default `object.is_a?(Enumerable)`)
|
295
|
-
#
|
296
|
-
# @return [Hash] Serialization result
|
297
|
-
#
|
298
|
-
def as_json(object, opts = nil)
|
299
|
-
config.from_json.call(to_json(object, opts))
|
300
|
-
end
|
248
|
+
alias_method :to_h, :call
|
301
249
|
|
302
250
|
private
|
303
251
|
|
@@ -362,7 +310,7 @@ class Serega
|
|
362
310
|
|
363
311
|
# Assign same batch loaders
|
364
312
|
batch_loaders.each_value do |loader|
|
365
|
-
subclass.
|
313
|
+
subclass.batch(loader.name, loader.block)
|
366
314
|
end
|
367
315
|
|
368
316
|
super
|
@@ -431,36 +379,6 @@ class Serega
|
|
431
379
|
@preloads ||= SeregaUtils::PreloadsConstructor.call(plan)
|
432
380
|
end
|
433
381
|
|
434
|
-
#
|
435
|
-
# Serializes provided object to JSON string
|
436
|
-
#
|
437
|
-
# @param object [Object] Serialized object
|
438
|
-
# @param opts [Hash, nil] Serializer modifiers and other instantiating options
|
439
|
-
# @option opts [Hash] :context Serialization context
|
440
|
-
# @option opts [Boolean] :many Set true if provided multiple objects (Default `object.is_a?(Enumerable)`)
|
441
|
-
#
|
442
|
-
# @return [Hash] Serialization result
|
443
|
-
#
|
444
|
-
def to_json(object, opts = nil)
|
445
|
-
hash = to_h(object, opts)
|
446
|
-
config.to_json.call(hash)
|
447
|
-
end
|
448
|
-
|
449
|
-
#
|
450
|
-
# Serializes provided object as JSON
|
451
|
-
#
|
452
|
-
# @param object [Object] Serialized object
|
453
|
-
# @param opts [Hash, nil] Serializer modifiers and other instantiating options
|
454
|
-
# @option opts [Hash] :context Serialization context
|
455
|
-
# @option opts [Boolean] :many Set true if provided multiple objects (Default `object.is_a?(Enumerable)`)
|
456
|
-
#
|
457
|
-
# @return [Hash] Serialization result
|
458
|
-
#
|
459
|
-
def as_json(object, opts = nil)
|
460
|
-
json = to_json(object, opts)
|
461
|
-
config.from_json.call(json)
|
462
|
-
end
|
463
|
-
|
464
382
|
private
|
465
383
|
|
466
384
|
attr_reader :opts
|
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.
|
4
|
+
version: 0.33.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrey Glushkov
|
@@ -29,17 +29,16 @@ files:
|
|
29
29
|
- lib/serega.rb
|
30
30
|
- lib/serega/attribute.rb
|
31
31
|
- lib/serega/attribute_normalizer.rb
|
32
|
+
- lib/serega/attribute_value_resolvers/batch.rb
|
33
|
+
- lib/serega/attribute_value_resolvers/const.rb
|
34
|
+
- lib/serega/attribute_value_resolvers/delegate.rb
|
35
|
+
- lib/serega/attribute_value_resolvers/keyword.rb
|
32
36
|
- lib/serega/batch/attribute_loader.rb
|
33
37
|
- lib/serega/batch/attribute_loaders.rb
|
34
|
-
- lib/serega/batch/auto_resolver.rb
|
35
|
-
- lib/serega/batch/auto_resolver_factory.rb
|
36
38
|
- lib/serega/batch/loader.rb
|
37
39
|
- lib/serega/config.rb
|
38
40
|
- lib/serega/errors.rb
|
39
41
|
- lib/serega/helpers/serializer_class_helper.rb
|
40
|
-
- lib/serega/json/adapter.rb
|
41
|
-
- lib/serega/json/json.rb
|
42
|
-
- lib/serega/json/oj.rb
|
43
42
|
- lib/serega/object_serializer.rb
|
44
43
|
- lib/serega/plan.rb
|
45
44
|
- lib/serega/plan_point.rb
|
@@ -122,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
121
|
- !ruby/object:Gem::Version
|
123
122
|
version: '0'
|
124
123
|
requirements: []
|
125
|
-
rubygems_version: 3.7.
|
124
|
+
rubygems_version: 3.7.2
|
126
125
|
specification_version: 4
|
127
126
|
summary: JSON Serializer
|
128
127
|
test_files: []
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Serega
|
4
|
-
module SeregaBatch
|
5
|
-
#
|
6
|
-
# Automatically generated resolver for batch_loader
|
7
|
-
#
|
8
|
-
class AutoResolver
|
9
|
-
attr_reader :loader_name
|
10
|
-
attr_reader :id_method
|
11
|
-
|
12
|
-
def initialize(loader_name, id_method)
|
13
|
-
@loader_name = loader_name
|
14
|
-
@id_method = id_method
|
15
|
-
end
|
16
|
-
|
17
|
-
# Finds object attribute value from hash of batch_loaded values for all
|
18
|
-
# serialized objects
|
19
|
-
def call(obj, batches:)
|
20
|
-
batches.fetch(loader_name)[obj.public_send(id_method)]
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Serega
|
4
|
-
module SeregaBatch
|
5
|
-
#
|
6
|
-
# Factory generates callable object that should be able to take
|
7
|
-
# batch loaded results, current object, and find attribute value for this
|
8
|
-
# object
|
9
|
-
#
|
10
|
-
class AutoResolverFactory
|
11
|
-
#
|
12
|
-
# Generates callable block to find attribute value when attribute with :batch
|
13
|
-
# option has no block or manual :value option.
|
14
|
-
#
|
15
|
-
# It handles this cases:
|
16
|
-
# - `attribute :foo, batch: true`
|
17
|
-
# - `attribute :foo, batch: FooLoader`
|
18
|
-
# - `attribute :foo, batch: { id: :foo_id }`
|
19
|
-
# - `attribute :foo, batch: { use: FooLoader, id: foo_id }`
|
20
|
-
# - `attribute :foo, batch: { use: :foo_loader, id: foo_id }`
|
21
|
-
#
|
22
|
-
# In other cases we should never call tis method here.
|
23
|
-
#
|
24
|
-
def self.get(serializer_class, attribute_name, batch_opt)
|
25
|
-
if batch_opt == true # ex: `batch: true`
|
26
|
-
loader_name = attribute_name
|
27
|
-
loader_id_method = :id
|
28
|
-
elsif batch_opt.respond_to?(:call) # ex: `batch: FooLoader`
|
29
|
-
serializer_class.batch_loader(attribute_name, batch_opt)
|
30
|
-
loader_name = attribute_name
|
31
|
-
loader_id_method = :id
|
32
|
-
else
|
33
|
-
use = batch_opt[:use]
|
34
|
-
loader_id_method = batch_opt[:id] || :id
|
35
|
-
|
36
|
-
if use.respond_to?(:call) # ex: `batch: { use: FooLoader }`
|
37
|
-
loader_name = attribute_name
|
38
|
-
serializer_class.batch_loader(loader_name, use)
|
39
|
-
else # ex: `batch: { use: :foo }` || batch: { id: :some_id }
|
40
|
-
loader_name = use || attribute_name
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
AutoResolver.new(loader_name, loader_id_method)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
data/lib/serega/json/adapter.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Serega
|
4
|
-
#
|
5
|
-
# JSON adapters
|
6
|
-
#
|
7
|
-
module SeregaJSON
|
8
|
-
# Current JSON adapter
|
9
|
-
#
|
10
|
-
# @return [Symbol] Current JSON adapter name - :oj or :json
|
11
|
-
def self.adapter
|
12
|
-
@adapter ||=
|
13
|
-
if defined?(::Oj)
|
14
|
-
require_relative "oj"
|
15
|
-
:oj
|
16
|
-
else
|
17
|
-
require "json"
|
18
|
-
require_relative "json"
|
19
|
-
:json
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
data/lib/serega/json/json.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Serega
|
4
|
-
module SeregaJSON
|
5
|
-
#
|
6
|
-
# JSON dump adapter for ::JSON
|
7
|
-
#
|
8
|
-
class JSONDump
|
9
|
-
#
|
10
|
-
# Dumps data to JSON string
|
11
|
-
#
|
12
|
-
# @param data [Object] Anything
|
13
|
-
#
|
14
|
-
# @return [String] Data serialized to JSON
|
15
|
-
#
|
16
|
-
def self.call(data)
|
17
|
-
::JSON.dump(data)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
#
|
22
|
-
# JSON parse adapter for ::JSON
|
23
|
-
#
|
24
|
-
class JSONLoad
|
25
|
-
#
|
26
|
-
# Loads object from JSON string
|
27
|
-
#
|
28
|
-
# @param json_string [String] JSON String
|
29
|
-
#
|
30
|
-
# @return [Object] Deserialized data
|
31
|
-
#
|
32
|
-
def self.call(json_string)
|
33
|
-
::JSON.parse(json_string)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
data/lib/serega/json/oj.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Serega
|
4
|
-
module SeregaJSON
|
5
|
-
#
|
6
|
-
# JSON dump adapter for ::Oj
|
7
|
-
#
|
8
|
-
class OjDump
|
9
|
-
# Default Oj serialization options
|
10
|
-
OPTS = {mode: :compat}.freeze
|
11
|
-
|
12
|
-
#
|
13
|
-
# Dumps data to JSON string
|
14
|
-
#
|
15
|
-
# @param data [Object] Anything
|
16
|
-
#
|
17
|
-
# @return [String] Data serialized to JSON
|
18
|
-
#
|
19
|
-
def self.call(data)
|
20
|
-
::Oj.dump(data, OPTS)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
#
|
25
|
-
# JSON parse adapter for ::Oj
|
26
|
-
#
|
27
|
-
class OjLoad
|
28
|
-
#
|
29
|
-
# Loads object from JSON string
|
30
|
-
#
|
31
|
-
# @param json_string [String] JSON String
|
32
|
-
#
|
33
|
-
# @return [Object] Deserialized data
|
34
|
-
#
|
35
|
-
def self.call(json_string)
|
36
|
-
::Oj.load(json_string)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|