serega 0.4.0 → 0.5.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/VERSION +1 -1
- data/lib/serega/attribute.rb +6 -1
- data/lib/serega/config.rb +2 -2
- data/lib/serega/map.rb +2 -4
- data/lib/serega/map_point.rb +34 -0
- data/lib/serega/object_serializer.rb +67 -0
- data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +4 -0
- data/lib/serega/plugins/activerecord_preloads/lib/preloader.rb +27 -8
- data/lib/serega/plugins/batch/batch.rb +189 -0
- data/lib/serega/plugins/batch/lib/loader.rb +60 -0
- data/lib/serega/plugins/batch/lib/loaders.rb +32 -0
- data/lib/serega/plugins/batch/lib/plugins_extensions.rb +40 -0
- data/lib/serega/plugins/batch/lib/validations/check_batch_opt_key.rb +61 -0
- data/lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb +61 -0
- data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +47 -0
- data/lib/serega/plugins/context_metadata/context_metadata.rb +4 -4
- data/lib/serega/plugins/formatters/formatters.rb +25 -13
- data/lib/serega/plugins/hide_nil/hide_nil.rb +4 -4
- data/lib/serega/plugins/metadata/metadata.rb +11 -11
- data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +8 -6
- data/lib/serega/plugins/preloads/preloads.rb +12 -1
- data/lib/serega/plugins/presenter/presenter.rb +6 -5
- data/lib/serega/plugins/root/root.rb +12 -12
- data/lib/serega/plugins/string_modifiers/parse_string_modifiers.rb +1 -1
- data/lib/serega/serializer.rb +32 -0
- data/lib/serega.rb +15 -10
- metadata +12 -5
- data/lib/serega/convert.rb +0 -45
- data/lib/serega/convert_item.rb +0 -37
- data/lib/serega/plugins/lazy/lazy.rb +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5ed53d558202f013db9523c47d0d66f7c1122e5ef6cccebe590af9f53733e79c
|
4
|
+
data.tar.gz: ff3a809f7b783c4585c378d49d64f070db2c62952c574f7a9eff4a2928bab76c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50de7bfab75d1da976496e86fab440b295cc4c728a5692f1c5126bd76fab1b8c09ddb3de8d0adb3e89e269c79b70a98c41396334e1c8d3e1ecf805e6907b25e1
|
7
|
+
data.tar.gz: ad6d8e4b386bd44d54a90415829797b09b36ddcf22b8234ccaac453e0b6ff2b9ab4a107f5df6a7060b75fa7d0ed8a2ef4cc0e00f11861109c1b1005b61b83936
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/lib/serega/attribute.rb
CHANGED
@@ -77,7 +77,12 @@ class Serega
|
|
77
77
|
def value_block
|
78
78
|
return @value_block if instance_variable_defined?(:@value_block)
|
79
79
|
|
80
|
-
@value_block =
|
80
|
+
@value_block =
|
81
|
+
block ||
|
82
|
+
opts[:value] ||
|
83
|
+
const_block ||
|
84
|
+
delegate_block ||
|
85
|
+
keyword_block
|
81
86
|
end
|
82
87
|
|
83
88
|
#
|
data/lib/serega/config.rb
CHANGED
@@ -15,8 +15,8 @@ class Serega
|
|
15
15
|
serialize_keys: %i[context many].freeze,
|
16
16
|
check_initiate_params: true,
|
17
17
|
max_cached_map_per_serializer_count: 0,
|
18
|
-
to_json: SeregaJSON.adapter == :oj ? SeregaJSON::OjDump : SeregaJSON::JSONDump,
|
19
|
-
from_json: SeregaJSON.adapter == :oj ? SeregaJSON::OjLoad : SeregaJSON::JSONLoad
|
18
|
+
to_json: (SeregaJSON.adapter == :oj) ? SeregaJSON::OjDump : SeregaJSON::JSONDump,
|
19
|
+
from_json: (SeregaJSON.adapter == :oj) ? SeregaJSON::OjLoad : SeregaJSON::JSONLoad
|
20
20
|
}.freeze
|
21
21
|
# :nocov:
|
22
22
|
|
data/lib/serega/map.rb
CHANGED
@@ -36,7 +36,7 @@ class Serega
|
|
36
36
|
serializer_class.attributes.each_with_object([]) do |(name, attribute), map|
|
37
37
|
next unless attribute.visible?(only: only, except: except, with: with)
|
38
38
|
|
39
|
-
|
39
|
+
nested_points =
|
40
40
|
if attribute.relation?
|
41
41
|
construct_map(
|
42
42
|
attribute.serializer,
|
@@ -44,11 +44,9 @@ class Serega
|
|
44
44
|
with: with[name] || FROZEN_EMPTY_HASH,
|
45
45
|
except: except[name] || FROZEN_EMPTY_HASH
|
46
46
|
)
|
47
|
-
else
|
48
|
-
FROZEN_EMPTY_ARRAY
|
49
47
|
end
|
50
48
|
|
51
|
-
map <<
|
49
|
+
map << serializer_class::SeregaMapPoint.new(attribute, nested_points)
|
52
50
|
end
|
53
51
|
end
|
54
52
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
class SeregaMapPoint
|
5
|
+
module InstanceMethods
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
attr_reader :attribute, :nested_points
|
9
|
+
|
10
|
+
def_delegators :@attribute, :name, :value, :many
|
11
|
+
|
12
|
+
def initialize(attribute, nested_points)
|
13
|
+
@attribute = attribute
|
14
|
+
@nested_points = nested_points
|
15
|
+
end
|
16
|
+
|
17
|
+
def has_nested_points?
|
18
|
+
!nested_points.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
def nested_object_serializer
|
22
|
+
attribute.serializer::SeregaObjectSerializer
|
23
|
+
end
|
24
|
+
|
25
|
+
# TODO: remove, we use this method in tests only
|
26
|
+
def ==(other)
|
27
|
+
(other.attribute == attribute) && (other.nested_points == nested_points)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
extend Serega::SeregaHelpers::SerializerClassHelper
|
32
|
+
include InstanceMethods
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
class SeregaObjectSerializer
|
5
|
+
module SeregaObjectSerializerInstanceMethods
|
6
|
+
attr_reader :context, :points, :many, :opts
|
7
|
+
|
8
|
+
# @param context [Hash] Serialization context
|
9
|
+
# @param many [TrueClass|FalseClass] is object is enumerable
|
10
|
+
# @param points [Array<MapPoint>] Serialization points (attributes)
|
11
|
+
def initialize(context:, points:, many: nil, **opts)
|
12
|
+
@context = context
|
13
|
+
@points = points
|
14
|
+
@many = many
|
15
|
+
@opts = opts
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param object [Object] Serialized object
|
19
|
+
def serialize(object)
|
20
|
+
array?(object, many) ? serialize_array(object) : serialize_object(object)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def serialize_array(object)
|
26
|
+
object.map { |obj| serialize_object(obj) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def serialize_object(object)
|
30
|
+
return unless object
|
31
|
+
|
32
|
+
points.each_with_object({}) do |point, container|
|
33
|
+
attach_value(object, point, container)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def attach_value(object, point, container)
|
38
|
+
value = point.value(object, context)
|
39
|
+
final_value = final_value(value, point)
|
40
|
+
attach_final_value(final_value, point, container)
|
41
|
+
end
|
42
|
+
|
43
|
+
def attach_final_value(final_value, point, container)
|
44
|
+
container[point.name] = final_value
|
45
|
+
end
|
46
|
+
|
47
|
+
def final_value(value, point)
|
48
|
+
point.has_nested_points? ? relation_value(value, point) : value
|
49
|
+
end
|
50
|
+
|
51
|
+
def relation_value(value, point)
|
52
|
+
nested_points = point.nested_points
|
53
|
+
nested_serializer = point.nested_object_serializer
|
54
|
+
nested_many = point.many
|
55
|
+
serializer = nested_serializer.new(context: context, points: nested_points, many: nested_many, **opts)
|
56
|
+
serializer.serialize(value)
|
57
|
+
end
|
58
|
+
|
59
|
+
def array?(object, many)
|
60
|
+
many.nil? ? object.is_a?(Enumerable) : many
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
extend Serega::SeregaHelpers::SerializerClassHelper
|
65
|
+
include SeregaObjectSerializerInstanceMethods
|
66
|
+
end
|
67
|
+
end
|
@@ -14,6 +14,10 @@ class Serega
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.before_load_plugin(serializer_class, **opts)
|
17
|
+
if serializer_class.plugin_used?(:batch)
|
18
|
+
raise SeregaError, "Plugin `activerecord_preloads` must be loaded before `batch`"
|
19
|
+
end
|
20
|
+
|
17
21
|
serializer_class.plugin(:preloads, **opts) unless serializer_class.plugin_used?(:preloads)
|
18
22
|
end
|
19
23
|
|
@@ -6,6 +6,8 @@ class Serega
|
|
6
6
|
class Preloader
|
7
7
|
module ClassMethods
|
8
8
|
def preload(object, preloads)
|
9
|
+
return object if object.nil? || (object.is_a?(Array) && object.empty?) || preloads.empty?
|
10
|
+
|
9
11
|
preload_handler = handlers.find { |handler| handler.fit?(object) }
|
10
12
|
raise SeregaError, "Can't preload #{preloads.inspect} to #{object.inspect}" unless preload_handler
|
11
13
|
|
@@ -13,7 +15,12 @@ class Serega
|
|
13
15
|
end
|
14
16
|
|
15
17
|
def handlers
|
16
|
-
@handlers ||= [
|
18
|
+
@handlers ||= [
|
19
|
+
ActiverecordRelation,
|
20
|
+
ActiverecordObject,
|
21
|
+
ActiverecordArray,
|
22
|
+
ActiverecordEnumerator
|
23
|
+
].freeze
|
17
24
|
end
|
18
25
|
end
|
19
26
|
|
@@ -54,13 +61,9 @@ class Serega
|
|
54
61
|
end
|
55
62
|
|
56
63
|
def preload(objects, preloads)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
objects
|
61
|
-
else
|
62
|
-
objects.preload(preloads).load
|
63
|
-
end
|
64
|
+
objects.load
|
65
|
+
Loader.call(objects.to_a, preloads)
|
66
|
+
objects
|
64
67
|
end
|
65
68
|
end
|
66
69
|
|
@@ -90,6 +93,22 @@ class Serega
|
|
90
93
|
|
91
94
|
extend ClassMethods
|
92
95
|
end
|
96
|
+
|
97
|
+
class ActiverecordEnumerator
|
98
|
+
module ClassMethods
|
99
|
+
def fit?(objects)
|
100
|
+
objects.is_a?(Enumerator) &&
|
101
|
+
ActiverecordArray.fit?(objects.to_a)
|
102
|
+
end
|
103
|
+
|
104
|
+
def preload(objects, preloads)
|
105
|
+
ActiverecordArray.preload(objects.to_a, preloads)
|
106
|
+
objects
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
extend ClassMethods
|
111
|
+
end
|
93
112
|
end
|
94
113
|
end
|
95
114
|
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module Batch
|
6
|
+
def self.plugin_name
|
7
|
+
:batch
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.load_plugin(serializer_class, **_opts)
|
11
|
+
require_relative "./lib/loader"
|
12
|
+
require_relative "./lib/loaders"
|
13
|
+
require_relative "./lib/validations/check_batch_opt_key"
|
14
|
+
require_relative "./lib/validations/check_batch_opt_loader"
|
15
|
+
require_relative "./lib/validations/check_opt_batch"
|
16
|
+
|
17
|
+
serializer_class.extend(ClassMethods)
|
18
|
+
serializer_class::CheckAttributeParams.include(CheckAttributeParamsInstanceMethods)
|
19
|
+
serializer_class::SeregaAttribute.include(AttributeInstanceMethods)
|
20
|
+
serializer_class::SeregaMapPoint.include(MapPointInstanceMethods)
|
21
|
+
serializer_class::SeregaSerializer.include(SeregaSerializerInstanceMethods)
|
22
|
+
serializer_class::SeregaObjectSerializer.include(SeregaObjectSerializerInstanceMethods)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.after_load_plugin(serializer_class, **opts)
|
26
|
+
config = serializer_class.config
|
27
|
+
config.attribute_keys << :batch
|
28
|
+
config.opts[:batch] = {loaders: {}}
|
29
|
+
serializer_class::SeregaConfig.include(ConfigInstanceMethods)
|
30
|
+
|
31
|
+
batch_loaders_class = Class.new(SeregaBatchLoaders)
|
32
|
+
batch_loaders_class.serializer_class = serializer_class
|
33
|
+
serializer_class.const_set(:SeregaBatchLoaders, batch_loaders_class)
|
34
|
+
|
35
|
+
batch_loader_class = Class.new(SeregaBatchLoader)
|
36
|
+
batch_loader_class.serializer_class = serializer_class
|
37
|
+
serializer_class.const_set(:SeregaBatchLoader, batch_loader_class)
|
38
|
+
|
39
|
+
if serializer_class.plugin_used?(:activerecord_preloads)
|
40
|
+
require_relative "./lib/plugins_extensions"
|
41
|
+
serializer_class::SeregaBatchLoader.include(PluginsExtensions::ActiveRecordPreloads::BatchLoaderInstanceMethods)
|
42
|
+
end
|
43
|
+
|
44
|
+
if serializer_class.plugin_used?(:formatters)
|
45
|
+
require_relative "./lib/plugins_extensions"
|
46
|
+
serializer_class::SeregaBatchLoader.include(PluginsExtensions::Formatters::BatchLoaderInstanceMethods)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class BatchLoadersConfig
|
51
|
+
attr_reader :opts
|
52
|
+
|
53
|
+
def initialize(opts)
|
54
|
+
@opts = opts
|
55
|
+
end
|
56
|
+
|
57
|
+
def define(loader_name, &block)
|
58
|
+
unless block
|
59
|
+
raise SeregaError, "Block must be given to batch_loaders.define method"
|
60
|
+
end
|
61
|
+
|
62
|
+
params = block.parameters
|
63
|
+
if params.count > 3 || !params.map!(&:first).all? { |type| (type == :req) || (type == :opt) }
|
64
|
+
raise SeregaError, "Block can have maximum 3 regular parameters"
|
65
|
+
end
|
66
|
+
|
67
|
+
opts[loader_name] = block
|
68
|
+
end
|
69
|
+
|
70
|
+
def fetch(loader_name)
|
71
|
+
opts[loader_name] || (raise SeregaError, "Batch loader with name `#{loader_name.inspect}` was not defined. Define example: config.batch_loaders.define(:#{loader_name}) { |keys, ctx, points| ... }")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class BatchModel
|
76
|
+
attr_reader :opts, :loaders, :many
|
77
|
+
|
78
|
+
def initialize(opts, loaders, many)
|
79
|
+
@opts = opts
|
80
|
+
@loaders = loaders
|
81
|
+
@many = many
|
82
|
+
end
|
83
|
+
|
84
|
+
def loader
|
85
|
+
@batch_loader ||= begin
|
86
|
+
loader = opts[:loader]
|
87
|
+
loader = loaders.fetch(loader) if loader.is_a?(Symbol)
|
88
|
+
loader
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def key
|
93
|
+
@batch_key ||= begin
|
94
|
+
key = opts[:key]
|
95
|
+
key.is_a?(Symbol) ? proc { |object| object.public_send(key) } : key
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def default_value
|
100
|
+
if opts.key?(:default)
|
101
|
+
opts[:default]
|
102
|
+
elsif many
|
103
|
+
FROZEN_EMPTY_ARRAY
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
module ConfigInstanceMethods
|
109
|
+
def batch_loaders
|
110
|
+
@batch_loaders ||= BatchLoadersConfig.new(opts.fetch(:batch).fetch(:loaders))
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
module ClassMethods
|
115
|
+
private
|
116
|
+
|
117
|
+
def inherited(subclass)
|
118
|
+
super
|
119
|
+
|
120
|
+
batch_loaders_class = Class.new(self::SeregaBatchLoaders)
|
121
|
+
batch_loaders_class.serializer_class = subclass
|
122
|
+
subclass.const_set(:SeregaBatchLoaders, batch_loaders_class)
|
123
|
+
|
124
|
+
batch_loader_class = Class.new(self::SeregaBatchLoader)
|
125
|
+
batch_loader_class.serializer_class = subclass
|
126
|
+
subclass.const_set(:SeregaBatchLoader, batch_loader_class)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
module CheckAttributeParamsInstanceMethods
|
131
|
+
def check_opts
|
132
|
+
super
|
133
|
+
|
134
|
+
CheckOptBatch.call(opts, block)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
module AttributeInstanceMethods
|
139
|
+
def batch
|
140
|
+
opts[:batch]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
module MapPointInstanceMethods
|
145
|
+
def batch
|
146
|
+
return @batch if instance_variable_defined?(:@batch)
|
147
|
+
|
148
|
+
@batch = begin
|
149
|
+
opts = attribute.batch
|
150
|
+
BatchModel.new(opts, self.class.serializer_class.config.batch_loaders, many) if opts
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
module SeregaSerializerInstanceMethods
|
156
|
+
def initialize(**_args)
|
157
|
+
super
|
158
|
+
opts[:batch_loaders] = self.class.serializer_class::SeregaBatchLoaders.new
|
159
|
+
end
|
160
|
+
|
161
|
+
def serialize(*)
|
162
|
+
result = super
|
163
|
+
|
164
|
+
opts[:batch_loaders].load_all
|
165
|
+
|
166
|
+
result
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
module SeregaObjectSerializerInstanceMethods
|
171
|
+
private
|
172
|
+
|
173
|
+
def attach_value(object, point, container)
|
174
|
+
batch = point.batch
|
175
|
+
|
176
|
+
if batch
|
177
|
+
key = batch.key.call(object, context)
|
178
|
+
opts[:batch_loaders].get(point, self).remember(key, container)
|
179
|
+
container[point.name] = nil # Reserve attribute place in resulted hash. We will set correct value later
|
180
|
+
else
|
181
|
+
super
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
register_plugin(Batch.plugin_name, Batch)
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module Batch
|
6
|
+
class SeregaBatchLoader
|
7
|
+
module InstanceMethods
|
8
|
+
attr_reader :object_serializer, :point
|
9
|
+
|
10
|
+
def initialize(object_serializer, point)
|
11
|
+
@object_serializer = object_serializer
|
12
|
+
@point = point
|
13
|
+
end
|
14
|
+
|
15
|
+
def remember(key, container)
|
16
|
+
(keys[key] ||= []) << container
|
17
|
+
end
|
18
|
+
|
19
|
+
def load
|
20
|
+
keys_values = keys_values()
|
21
|
+
|
22
|
+
each_key do |key, container|
|
23
|
+
value = keys_values.fetch(key) { point.batch.default_value }
|
24
|
+
final_value = object_serializer.__send__(:final_value, value, point)
|
25
|
+
object_serializer.__send__(:attach_final_value, final_value, point, container)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def each_key
|
32
|
+
keys.each do |key, containers|
|
33
|
+
containers.each do |container|
|
34
|
+
yield(key, container)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def keys_values
|
40
|
+
ids = keys.keys
|
41
|
+
|
42
|
+
point.batch.loader.call(ids, object_serializer.context, point.nested_points).tap do |vals|
|
43
|
+
next if vals.is_a?(Hash)
|
44
|
+
|
45
|
+
attribute_name = "#{point.class.serializer_class}.#{point.name}"
|
46
|
+
raise SeregaError, "Batch loader for `#{attribute_name}` must return Hash, but #{vals.inspect} was returned"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def keys
|
51
|
+
@keys ||= {}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
include InstanceMethods
|
56
|
+
extend Serega::SeregaHelpers::SerializerClassHelper
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module Batch
|
6
|
+
class SeregaBatchLoaders
|
7
|
+
module InstanceMethods
|
8
|
+
def get(point, object_serializer)
|
9
|
+
batch_loaders[point] ||= self.class.serializer_class::SeregaBatchLoader.new(object_serializer, point)
|
10
|
+
end
|
11
|
+
|
12
|
+
def load_all
|
13
|
+
return unless defined?(@batch_loaders)
|
14
|
+
|
15
|
+
while (_point, batch_loader = batch_loaders.shift)
|
16
|
+
batch_loader.load
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def batch_loaders
|
23
|
+
@batch_loaders ||= {}.compare_by_identity
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
include InstanceMethods
|
28
|
+
extend Serega::SeregaHelpers::SerializerClassHelper
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module Batch
|
6
|
+
module PluginsExtensions
|
7
|
+
module ActiveRecordPreloads
|
8
|
+
module BatchLoaderInstanceMethods
|
9
|
+
def keys_values(*)
|
10
|
+
data = super
|
11
|
+
|
12
|
+
if point.has_nested_points?
|
13
|
+
associations = point.preloads
|
14
|
+
return data if associations.empty?
|
15
|
+
|
16
|
+
ActiverecordPreloads::Preloader.preload(data.values.flatten(1), associations)
|
17
|
+
end
|
18
|
+
|
19
|
+
data
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module Formatters
|
25
|
+
module BatchLoaderInstanceMethods
|
26
|
+
# Format values after they are prepared
|
27
|
+
def keys_values(*)
|
28
|
+
data = super
|
29
|
+
|
30
|
+
formatter = point.attribute.formatter_resolved
|
31
|
+
data.transform_values! { |value| formatter.call(value) } if formatter
|
32
|
+
|
33
|
+
data
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module Batch
|
6
|
+
class CheckBatchOptKey
|
7
|
+
class << self
|
8
|
+
def call(key)
|
9
|
+
return if key.is_a?(Symbol)
|
10
|
+
|
11
|
+
raise SeregaError, must_be_callable unless key.respond_to?(:call)
|
12
|
+
|
13
|
+
if key.is_a?(Proc)
|
14
|
+
check_block(key)
|
15
|
+
else
|
16
|
+
check_callable(key)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def check_block(block)
|
23
|
+
return if valid_parameters?(block, accepted_count: 0..2)
|
24
|
+
|
25
|
+
raise SeregaError, block_parameters_error
|
26
|
+
end
|
27
|
+
|
28
|
+
def check_callable(callable)
|
29
|
+
return if valid_parameters?(callable.method(:call), accepted_count: 2..2)
|
30
|
+
|
31
|
+
raise SeregaError, callable_parameters_error
|
32
|
+
end
|
33
|
+
|
34
|
+
def valid_parameters?(data, accepted_count:)
|
35
|
+
params = data.parameters
|
36
|
+
accepted_count.include?(params.count) && valid_parameters_types?(params)
|
37
|
+
end
|
38
|
+
|
39
|
+
def valid_parameters_types?(params)
|
40
|
+
params.all? do |param|
|
41
|
+
type = param[0]
|
42
|
+
(type == :req) || (type == :opt)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def block_parameters_error
|
47
|
+
"Invalid :batch option :key. When it is a Proc it can have maximum two regular parameters (object, context)"
|
48
|
+
end
|
49
|
+
|
50
|
+
def callable_parameters_error
|
51
|
+
"Invalid :batch option :key. When it is a callable object it must have two regular parameters (object, context)"
|
52
|
+
end
|
53
|
+
|
54
|
+
def must_be_callable
|
55
|
+
"Invalid :batch option :key. It must be a Symbol, a Proc or respond to :call"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module Batch
|
6
|
+
class CheckBatchOptLoader
|
7
|
+
class << self
|
8
|
+
def call(loader)
|
9
|
+
return if loader.is_a?(Symbol)
|
10
|
+
|
11
|
+
raise SeregaError, must_be_callable unless loader.respond_to?(:call)
|
12
|
+
|
13
|
+
if loader.is_a?(Proc)
|
14
|
+
check_block(loader)
|
15
|
+
else
|
16
|
+
check_callable(loader)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def check_block(block)
|
23
|
+
return if valid_parameters?(block, accepted_count: 0..3)
|
24
|
+
|
25
|
+
raise SeregaError, block_parameters_error
|
26
|
+
end
|
27
|
+
|
28
|
+
def check_callable(callable)
|
29
|
+
return if valid_parameters?(callable.method(:call), accepted_count: 3..3)
|
30
|
+
|
31
|
+
raise SeregaError, callable_parameters_error
|
32
|
+
end
|
33
|
+
|
34
|
+
def valid_parameters?(data, accepted_count:)
|
35
|
+
params = data.parameters
|
36
|
+
accepted_count.include?(params.count) && valid_parameters_types?(params)
|
37
|
+
end
|
38
|
+
|
39
|
+
def valid_parameters_types?(params)
|
40
|
+
params.all? do |param|
|
41
|
+
type = param[0]
|
42
|
+
(type == :req) || (type == :opt)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def block_parameters_error
|
47
|
+
"Invalid :batch option :loader. When it is a Proc it can have maximum three regular parameters (keys, context, points)"
|
48
|
+
end
|
49
|
+
|
50
|
+
def callable_parameters_error
|
51
|
+
"Invalid :batch option :loader. When it is a callable object it must have three regular parameters (keys, context, points)"
|
52
|
+
end
|
53
|
+
|
54
|
+
def must_be_callable
|
55
|
+
"Invalid :batch option :loader. It must be a Symbol, a Proc or respond to :call"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|