serega 0.11.2 → 0.14.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 +163 -13
- data/VERSION +1 -1
- data/lib/serega/attribute.rb +9 -4
- data/lib/serega/attribute_normalizer.rb +4 -13
- data/lib/serega/object_serializer.rb +11 -0
- data/lib/serega/plan.rb +20 -25
- data/lib/serega/plan_point.rb +13 -16
- data/lib/serega/plugins/batch/lib/loader.rb +25 -7
- data/lib/serega/plugins/batch/lib/modules/attribute_normalizer.rb +1 -9
- data/lib/serega/plugins/explicit_many_option/explicit_many_option.rb +69 -0
- data/lib/serega/plugins/explicit_many_option/validations/check_opt_many.rb +35 -0
- data/lib/serega/plugins/metadata/metadata.rb +5 -0
- data/lib/serega/plugins/openapi/lib/modules/config.rb +23 -0
- data/lib/serega/plugins/openapi/lib/openapi_config.rb +101 -0
- data/lib/serega/plugins/openapi/openapi.rb +245 -0
- data/lib/serega/plugins/preloads/lib/modules/attribute.rb +28 -0
- data/lib/serega/plugins/preloads/lib/modules/attribute_normalizer.rb +99 -0
- data/lib/serega/plugins/preloads/lib/modules/check_attribute_params.rb +22 -0
- data/lib/serega/plugins/preloads/lib/modules/config.rb +19 -0
- data/lib/serega/plugins/preloads/lib/modules/plan_point.rb +41 -0
- data/lib/serega/plugins/preloads/lib/preload_paths.rb +46 -0
- data/lib/serega/plugins/preloads/lib/preloads_config.rb +62 -0
- data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +20 -7
- data/lib/serega/plugins/preloads/preloads.rb +12 -210
- data/lib/serega/plugins/preloads/validations/check_opt_preload_path.rb +54 -15
- metadata +18 -7
- data/lib/serega/plugins/preloads/lib/main_preload_path.rb +0 -53
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module OpenAPI
|
6
|
+
#
|
7
|
+
# Config class additional/patched instance methods
|
8
|
+
#
|
9
|
+
# @see Serega::SeregaConfig
|
10
|
+
#
|
11
|
+
module ConfigInstanceMethods
|
12
|
+
#
|
13
|
+
# Returns openapi plugin config
|
14
|
+
#
|
15
|
+
# @return [Serega::SeregaPlugins::OpenAPI::OpenAPIConfig] configuration for openapi plugin
|
16
|
+
#
|
17
|
+
def openapi
|
18
|
+
@openapi ||= OpenAPIConfig.new(self.class.serializer_class, opts.fetch(:openapi))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module OpenAPI
|
6
|
+
#
|
7
|
+
# OpenAPI plugin config
|
8
|
+
#
|
9
|
+
class OpenAPIConfig
|
10
|
+
attr_reader :serializer_class, :opts
|
11
|
+
|
12
|
+
def initialize(serializer_class, opts)
|
13
|
+
@serializer_class = serializer_class
|
14
|
+
@opts = opts
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Saves new properties
|
19
|
+
#
|
20
|
+
# @param new_properties [Hash] new properties
|
21
|
+
#
|
22
|
+
# @return [Hash] OpenAPI properties
|
23
|
+
#
|
24
|
+
def properties(new_properties = FROZEN_EMPTY_HASH)
|
25
|
+
properties = opts[:properties]
|
26
|
+
return properties if new_properties.empty?
|
27
|
+
|
28
|
+
new_properties = SeregaUtils::EnumDeepDup.call(new_properties)
|
29
|
+
symbolize_keys!(new_properties)
|
30
|
+
|
31
|
+
new_properties.each do |attribute_name, new_attribute_properties|
|
32
|
+
check_attribute_exists(attribute_name)
|
33
|
+
check_properties_is_a_hash(attribute_name, new_attribute_properties)
|
34
|
+
|
35
|
+
properties[attribute_name] = symbolize_keys!(new_attribute_properties)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# @return [#call] builder of `$ref` attribute
|
41
|
+
#
|
42
|
+
def ref_builder
|
43
|
+
opts[:ref_builder]
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Sets new $ref option builder
|
48
|
+
#
|
49
|
+
# @param builder [#call] Callable object that accepts serializer_class and constructs $ref option string
|
50
|
+
#
|
51
|
+
# @return Specified new builder
|
52
|
+
#
|
53
|
+
def ref_builder=(builder)
|
54
|
+
raise SeregaError, "ref_builder must respond to #call" unless builder.respond_to?(:call)
|
55
|
+
opts[:ref_builder] = builder
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# @return [#call] builder of schema name
|
60
|
+
#
|
61
|
+
def schema_name_builder
|
62
|
+
opts[:schema_name_builder]
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# Sets new schema_name_builder
|
67
|
+
#
|
68
|
+
# @param builder [#call] Callable object that accepts serializer_class and
|
69
|
+
# constructs schema name to use in schemas list and in $ref option
|
70
|
+
#
|
71
|
+
# @return Specified new builder
|
72
|
+
#
|
73
|
+
def schema_name_builder=(builder)
|
74
|
+
raise SeregaError, "schema_name_builder must respond to #call" unless builder.respond_to?(:call)
|
75
|
+
opts[:schema_name_builder] = builder
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def check_attribute_exists(attribute_name)
|
81
|
+
return if serializer_class.attributes.key?(attribute_name)
|
82
|
+
|
83
|
+
raise SeregaError, "No attribute with name :#{attribute_name}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def check_properties_is_a_hash(attribute_name, new_attribute_properties)
|
87
|
+
return if new_attribute_properties.is_a?(Hash)
|
88
|
+
|
89
|
+
raise SeregaError, "Property #{attribute_name} value must be a Hash," \
|
90
|
+
" but #{new_attribute_properties.inspect} was provided"
|
91
|
+
end
|
92
|
+
|
93
|
+
def symbolize_keys!(opts)
|
94
|
+
opts.transform_keys! do |key|
|
95
|
+
key.to_sym
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
#
|
5
|
+
# Utility class to build OpenAPI schemas
|
6
|
+
#
|
7
|
+
class OpenAPI
|
8
|
+
#
|
9
|
+
# Constructs OpenAPI schemas for multiple serializers
|
10
|
+
#
|
11
|
+
# @params serializers [Class<Serega>] Serializers tobuild schemas,
|
12
|
+
# by default it is all serializers with :openapi plugin enabled
|
13
|
+
#
|
14
|
+
# @return [Hash] Schemas hash
|
15
|
+
#
|
16
|
+
def self.schemas(serializers = self.serializers)
|
17
|
+
serializers.each_with_object({}) do |serializer_class, schemas|
|
18
|
+
schema = serializer_class.openapi_schema
|
19
|
+
schema_name = serializer_class.openapi_schema_name
|
20
|
+
schemas[schema_name] = schema
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Returns list of serializers with :openapi plugin
|
26
|
+
#
|
27
|
+
def self.serializers
|
28
|
+
@serializers ||= []
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module SeregaPlugins
|
33
|
+
#
|
34
|
+
# Plugin :openapi
|
35
|
+
#
|
36
|
+
# Helps to build OpenAPI schemas
|
37
|
+
#
|
38
|
+
# This schemas can be easielty used with "rswag" gem by adding them to "config.swagger_docs"
|
39
|
+
# https://github.com/rswag/rswag#referenced-parameters-and-schema-definitions
|
40
|
+
#
|
41
|
+
# This plugin only adds type "object" or "array" for relationships and marks
|
42
|
+
# attributes as **required** if they have no :hide option set.
|
43
|
+
#
|
44
|
+
# OpenAPI properties will have no any "type" or other options specified by default,
|
45
|
+
# you should provide them in 'YourSerializer.openapi_properties' method.
|
46
|
+
# `openapi_properties` can be specified multiple time, in this case they wil be merged.
|
47
|
+
#
|
48
|
+
# After enabling this plugin attributes with :serializer option will have
|
49
|
+
# to have :many option set to construct "object" or "array" openapi type for relationships.
|
50
|
+
#
|
51
|
+
# OpenAPI `$ref` property will be added automatically for all relationships.
|
52
|
+
#
|
53
|
+
# Example constructing all serializers schemas:
|
54
|
+
# `Serega::OpenAPI.schemas`
|
55
|
+
#
|
56
|
+
# Example constructing specific serializers schemas:
|
57
|
+
# `Serega::OpenAPI.schemas(Serega::OpenAPI.serializers - [MyBaseSerializer])`
|
58
|
+
#
|
59
|
+
# Example constructing one serializer schema:
|
60
|
+
# `SomeSerializer.openapi_schema`
|
61
|
+
#
|
62
|
+
# @example
|
63
|
+
# class BaseSerializer < Serega
|
64
|
+
# plugin :openapi
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# class UserSerializer < BaseSerializer
|
68
|
+
# attribute :name
|
69
|
+
#
|
70
|
+
# openapi_properties(
|
71
|
+
# name: { type: :string }
|
72
|
+
# )
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# class PostSerializer < BaseSerializer
|
76
|
+
# attribute :text
|
77
|
+
# attribute :user, serializer: UserSerializer, many: false
|
78
|
+
# attribute :comments, serializer: PostSerializer, many: true, hide: true
|
79
|
+
#
|
80
|
+
# openapi_properties(
|
81
|
+
# text: { type: :string },
|
82
|
+
# user: { type: 'object' }, # `$ref` option will be added automatically when constructing schema
|
83
|
+
# comments: { type: 'array' } # `items` option with `$ref` will be added automatically when constructing schema
|
84
|
+
# )
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# puts Serega::OpenAPI.schemas
|
88
|
+
# =>
|
89
|
+
# {
|
90
|
+
# "PostSerializer" => {
|
91
|
+
# type: "object",
|
92
|
+
# properties: {
|
93
|
+
# text: {type: "string"},
|
94
|
+
# user: {:$ref => "#/components/schemas/UserSerializer"},
|
95
|
+
# comments: {type: "array", items: {:$ref => "#/components/schemas/PostSerializer"}}
|
96
|
+
# },
|
97
|
+
# required: [:text, :comments],
|
98
|
+
# additionalProperties: false
|
99
|
+
# },
|
100
|
+
# "UserSerializer" => {
|
101
|
+
# type: "object",
|
102
|
+
# properties: {
|
103
|
+
# name: {type: "string"}
|
104
|
+
# },
|
105
|
+
# required: [:name],
|
106
|
+
# additionalProperties: false
|
107
|
+
# }
|
108
|
+
# }
|
109
|
+
#
|
110
|
+
module OpenAPI
|
111
|
+
# Builder for schema name (used is schemas list). Returns serializer class name
|
112
|
+
DEFAULT_SCHEMA_NAME_BUILDER = ->(serializer_class) { serializer_class.name }
|
113
|
+
|
114
|
+
# Builder for $ref openapi property
|
115
|
+
DEFAULT_REF_BUILDER = ->(serializer_class) { "#/components/schemas/#{serializer_class.openapi_schema_name}" }
|
116
|
+
|
117
|
+
# @return [Symbol] Plugin name
|
118
|
+
def self.plugin_name
|
119
|
+
:openapi
|
120
|
+
end
|
121
|
+
|
122
|
+
# Checks requirements and loads additional plugins
|
123
|
+
#
|
124
|
+
# @param serializer_class [Class<Serega>] Current serializer class
|
125
|
+
# @param opts [Hash] loaded plugins opts
|
126
|
+
#
|
127
|
+
# @return [void]
|
128
|
+
#
|
129
|
+
def self.before_load_plugin(serializer_class, **opts)
|
130
|
+
unless serializer_class.plugin_used?(:explicit_many_option)
|
131
|
+
serializer_class.plugin :explicit_many_option
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# Applies plugin code to specific serializer
|
137
|
+
#
|
138
|
+
# @param serializer_class [Class<Serega>] Current serializer class
|
139
|
+
# @param _opts [Hash] Loaded plugins options
|
140
|
+
#
|
141
|
+
# @return [void]
|
142
|
+
#
|
143
|
+
def self.load_plugin(serializer_class, **opts)
|
144
|
+
require_relative "./lib/modules/config"
|
145
|
+
require_relative "./lib/openapi_config"
|
146
|
+
|
147
|
+
serializer_class.extend(ClassMethods)
|
148
|
+
serializer_class::SeregaConfig.include(ConfigInstanceMethods)
|
149
|
+
|
150
|
+
config = serializer_class.config
|
151
|
+
config.opts[:openapi] = {properties: {}}
|
152
|
+
openapi_config = serializer_class.config.openapi
|
153
|
+
openapi_config.schema_name_builder = opts[:schema_name_builder] || DEFAULT_SCHEMA_NAME_BUILDER
|
154
|
+
openapi_config.ref_builder = opts[:ref_builder] || DEFAULT_REF_BUILDER
|
155
|
+
end
|
156
|
+
|
157
|
+
#
|
158
|
+
# Adds config options and runs other callbacks after plugin was loaded
|
159
|
+
#
|
160
|
+
# @param serializer_class [Class<Serega>] Current serializer class
|
161
|
+
# @param opts [Hash] loaded plugins opts
|
162
|
+
#
|
163
|
+
# @return [void]
|
164
|
+
#
|
165
|
+
def self.after_load_plugin(serializer_class, **opts)
|
166
|
+
Serega::OpenAPI.serializers << serializer_class unless serializer_class.equal?(Serega)
|
167
|
+
end
|
168
|
+
|
169
|
+
#
|
170
|
+
# Serega additional/patched class methods
|
171
|
+
#
|
172
|
+
# @see Serega
|
173
|
+
#
|
174
|
+
module ClassMethods
|
175
|
+
#
|
176
|
+
# OpenAPI schema for current serializer
|
177
|
+
#
|
178
|
+
def openapi_schema
|
179
|
+
properties = SeregaUtils::EnumDeepDup.call(openapi_properties)
|
180
|
+
required_properties = []
|
181
|
+
|
182
|
+
attributes.each do |attribute_name, attribute|
|
183
|
+
add_openapi_property(properties, attribute_name, attribute)
|
184
|
+
add_openapi_required_property(required_properties, attribute_name, attribute)
|
185
|
+
end
|
186
|
+
|
187
|
+
{
|
188
|
+
type: "object",
|
189
|
+
properties: properties,
|
190
|
+
required: required_properties,
|
191
|
+
additionalProperties: false
|
192
|
+
}
|
193
|
+
end
|
194
|
+
|
195
|
+
#
|
196
|
+
# Adds new OpenAPI properties and returns all properties
|
197
|
+
#
|
198
|
+
# @param props [Hash] Specifies new properties
|
199
|
+
#
|
200
|
+
# @return [Hash] Specified OpenAPI properties
|
201
|
+
#
|
202
|
+
def openapi_properties(props = FROZEN_EMPTY_HASH)
|
203
|
+
config.openapi.properties(props)
|
204
|
+
end
|
205
|
+
|
206
|
+
#
|
207
|
+
# Builds OpenAPI schema name using configured builder
|
208
|
+
#
|
209
|
+
# @return [String] OpenAPI schema name
|
210
|
+
#
|
211
|
+
def openapi_schema_name
|
212
|
+
config.openapi.schema_name_builder.call(self)
|
213
|
+
end
|
214
|
+
|
215
|
+
private
|
216
|
+
|
217
|
+
def inherited(subclass)
|
218
|
+
super
|
219
|
+
Serega::OpenAPI.serializers << subclass
|
220
|
+
end
|
221
|
+
|
222
|
+
def add_openapi_property(properties, attribute_name, attribute)
|
223
|
+
property = properties[attribute_name] ||= {}
|
224
|
+
return unless attribute.relation?
|
225
|
+
|
226
|
+
ref = attribute.serializer.config.openapi.ref_builder.call(attribute.serializer)
|
227
|
+
|
228
|
+
if attribute.many
|
229
|
+
property[:type] = "array"
|
230
|
+
property[:items] ||= {}
|
231
|
+
property[:items][:$ref] ||= ref
|
232
|
+
else
|
233
|
+
property[:$ref] ||= ref
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def add_openapi_required_property(required_properties, attribute_name, attribute)
|
238
|
+
required_properties << attribute_name unless attribute.hide
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
register_plugin(OpenAPI.plugin_name, OpenAPI)
|
244
|
+
end
|
245
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module Preloads
|
6
|
+
#
|
7
|
+
# Serega::SeregaAttribute additional/patched instance methods
|
8
|
+
#
|
9
|
+
# @see Serega::SeregaAttribute::AttributeInstanceMethods
|
10
|
+
#
|
11
|
+
module AttributeInstanceMethods
|
12
|
+
# @return [Hash, nil] normalized preloads of current attribute
|
13
|
+
attr_reader :preloads
|
14
|
+
|
15
|
+
# @return [Array] normalized preloads_path of current attribute
|
16
|
+
attr_reader :preloads_path
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def set_normalized_vars(normalizer)
|
21
|
+
super
|
22
|
+
@preloads = normalizer.preloads
|
23
|
+
@preloads_path = normalizer.preloads_path
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module Preloads
|
6
|
+
#
|
7
|
+
# Serega::SeregaAttributeNormalizer additional/patched instance methods
|
8
|
+
#
|
9
|
+
# @see SeregaAttributeNormalizer::AttributeNormalizerInstanceMethods
|
10
|
+
#
|
11
|
+
module AttributeNormalizerInstanceMethods
|
12
|
+
# @return [Hash,nil] normalized attribute preloads
|
13
|
+
def preloads
|
14
|
+
return @preloads if instance_variable_defined?(:@preloads)
|
15
|
+
|
16
|
+
@preloads = prepare_preloads
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Array, nil] normalized attribute preloads path
|
20
|
+
def preloads_path
|
21
|
+
return @preloads_path if instance_variable_defined?(:@preloads_path)
|
22
|
+
|
23
|
+
@preloads_path = prepare_preloads_path
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
#
|
29
|
+
# Patched in:
|
30
|
+
# - plugin :batch (extension :preloads - skips auto preloads when batch option provided)
|
31
|
+
#
|
32
|
+
def prepare_preloads
|
33
|
+
opts = init_opts
|
34
|
+
preloads_provided = opts.key?(:preload)
|
35
|
+
preloads =
|
36
|
+
if preloads_provided
|
37
|
+
opts[:preload]
|
38
|
+
elsif opts.key?(:serializer) && self.class.serializer_class.config.preloads.auto_preload_attributes_with_serializer
|
39
|
+
key
|
40
|
+
elsif opts.key?(:delegate) && self.class.serializer_class.config.preloads.auto_preload_attributes_with_delegate
|
41
|
+
opts[:delegate].fetch(:to)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Nil and empty hash differs as we can preload nested results to
|
45
|
+
# empty hash, but we will skip nested preloading if nil or false provided
|
46
|
+
return if preloads_provided && !preloads
|
47
|
+
|
48
|
+
FormatUserPreloads.call(preloads)
|
49
|
+
end
|
50
|
+
|
51
|
+
def prepare_preloads_path
|
52
|
+
path = init_opts.fetch(:preload_path) { default_preload_path(preloads) }
|
53
|
+
|
54
|
+
if path && path[0].is_a?(Array)
|
55
|
+
prepare_many_preload_paths(path)
|
56
|
+
else
|
57
|
+
prepare_one_preload_path(path)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def prepare_one_preload_path(path)
|
62
|
+
return unless path
|
63
|
+
|
64
|
+
case path
|
65
|
+
when Array
|
66
|
+
path.map(&:to_sym).freeze
|
67
|
+
else
|
68
|
+
[path.to_sym].freeze
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def prepare_many_preload_paths(paths)
|
73
|
+
paths.map { |path| prepare_one_preload_path(path) }.freeze
|
74
|
+
end
|
75
|
+
|
76
|
+
def default_preload_path(preloads)
|
77
|
+
return FROZEN_EMPTY_ARRAY if !preloads || preloads.empty?
|
78
|
+
|
79
|
+
[preloads.keys.first]
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# Patch for original `prepare_hide` method
|
84
|
+
# @see
|
85
|
+
#
|
86
|
+
# Marks attribute hidden if auto_hide_attribute_with_preloads option was set and attribute has preloads
|
87
|
+
#
|
88
|
+
def prepare_hide
|
89
|
+
res = super
|
90
|
+
return res unless res.nil?
|
91
|
+
|
92
|
+
if preloads && !preloads.empty?
|
93
|
+
self.class.serializer_class.config.preloads.auto_hide_attributes_with_preload || nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module Preloads
|
6
|
+
#
|
7
|
+
# Serega::SeregaValidations::CheckAttributeParams additional/patched class methods
|
8
|
+
#
|
9
|
+
# @see Serega::SeregaValidations::CheckAttributeParams
|
10
|
+
#
|
11
|
+
module CheckAttributeParamsInstanceMethods
|
12
|
+
private
|
13
|
+
|
14
|
+
def check_opts
|
15
|
+
super
|
16
|
+
CheckOptPreload.call(opts)
|
17
|
+
CheckOptPreloadPath.call(opts)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module Preloads
|
6
|
+
#
|
7
|
+
# Config class additional/patched instance methods
|
8
|
+
#
|
9
|
+
# @see Serega::SeregaConfig
|
10
|
+
#
|
11
|
+
module ConfigInstanceMethods
|
12
|
+
# @return [Serega::SeregaPlugins::Preloads::PreloadsConfig] `preloads` plugin config
|
13
|
+
def preloads
|
14
|
+
@preloads ||= PreloadsConfig.new(opts.fetch(:preloads))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module Preloads
|
6
|
+
#
|
7
|
+
# Serega::SeregaPlanPoint additional/patched instance methods
|
8
|
+
#
|
9
|
+
# @see Serega::SeregaPlanPoint::InstanceMethods
|
10
|
+
#
|
11
|
+
module PlanPointInstanceMethods
|
12
|
+
#
|
13
|
+
# @return [Hash] preloads for nested attributes
|
14
|
+
#
|
15
|
+
attr_reader :preloads
|
16
|
+
|
17
|
+
#
|
18
|
+
# @return [Array<Symbol>] preloads path for current attribute
|
19
|
+
#
|
20
|
+
attr_reader :preloads_path
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def set_normalized_vars
|
25
|
+
super
|
26
|
+
|
27
|
+
@preloads = prepare_preloads
|
28
|
+
@preloads_path = prepare_preloads_path
|
29
|
+
end
|
30
|
+
|
31
|
+
def prepare_preloads
|
32
|
+
PreloadsConstructor.call(child_plan)
|
33
|
+
end
|
34
|
+
|
35
|
+
def prepare_preloads_path
|
36
|
+
attribute.preloads_path
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module Preloads
|
6
|
+
#
|
7
|
+
# Utility that helps to transform preloads to array of paths
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# call({ a: { b: { c: {}, d: {} } }, e: {} })
|
12
|
+
#
|
13
|
+
# => [
|
14
|
+
# [:a],
|
15
|
+
# [:a, :b],
|
16
|
+
# [:a, :b, :c],
|
17
|
+
# [:a, :b, :d],
|
18
|
+
# [:e]
|
19
|
+
# ]
|
20
|
+
class PreloadPaths
|
21
|
+
class << self
|
22
|
+
#
|
23
|
+
# Transforms user provided preloads to array of paths
|
24
|
+
#
|
25
|
+
# @param value [Array,Hash,String,Symbol,nil,false] preloads
|
26
|
+
#
|
27
|
+
# @return [Hash] preloads transformed to hash
|
28
|
+
#
|
29
|
+
def call(preloads, path = [], result = [])
|
30
|
+
preloads = FormatUserPreloads.call(preloads)
|
31
|
+
|
32
|
+
preloads.each do |key, nested_preloads|
|
33
|
+
path << key
|
34
|
+
result << path.dup
|
35
|
+
|
36
|
+
call(nested_preloads, path, result)
|
37
|
+
path.pop
|
38
|
+
end
|
39
|
+
|
40
|
+
result
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
module Preloads
|
6
|
+
#
|
7
|
+
# Config for `preloads` plugin
|
8
|
+
#
|
9
|
+
class PreloadsConfig
|
10
|
+
# @return [Hash] preloads plugin options
|
11
|
+
attr_reader :opts
|
12
|
+
|
13
|
+
#
|
14
|
+
# Initializes PreloadsConfig object
|
15
|
+
#
|
16
|
+
# @param opts [Hash] options
|
17
|
+
#
|
18
|
+
# @return [Serega::SeregaPlugins::Preloads::PreloadsConfig]
|
19
|
+
#
|
20
|
+
def initialize(opts)
|
21
|
+
@opts = opts
|
22
|
+
end
|
23
|
+
|
24
|
+
# @!method auto_preload_attributes_with_delegate
|
25
|
+
# @return [Boolean, nil] option value
|
26
|
+
#
|
27
|
+
# @!method auto_preload_attributes_with_delegate=(value)
|
28
|
+
# @param value [Boolean] New option value
|
29
|
+
# @return [Boolean] New option value
|
30
|
+
#
|
31
|
+
# @!method auto_preload_attributes_with_serializer
|
32
|
+
# @return [Boolean, nil] option value
|
33
|
+
#
|
34
|
+
# @!method auto_preload_attributes_with_serializer=(value)
|
35
|
+
# @param value [Boolean] New option value
|
36
|
+
# @return [Boolean] New option value
|
37
|
+
#
|
38
|
+
# @!method auto_hide_attributes_with_preload
|
39
|
+
# @return [Boolean, nil] option value
|
40
|
+
#
|
41
|
+
# @!method auto_hide_attributes_with_preload=(value)
|
42
|
+
# @param value [Boolean] New option value
|
43
|
+
# @return [Boolean] New option value
|
44
|
+
#
|
45
|
+
%i[
|
46
|
+
auto_preload_attributes_with_delegate
|
47
|
+
auto_preload_attributes_with_serializer
|
48
|
+
auto_hide_attributes_with_preload
|
49
|
+
].each do |method_name|
|
50
|
+
define_method(method_name) do
|
51
|
+
opts.fetch(method_name)
|
52
|
+
end
|
53
|
+
|
54
|
+
define_method("#{method_name}=") do |value|
|
55
|
+
raise SeregaError, "Must have boolean value, #{value.inspect} provided" if (value != true) && (value != false)
|
56
|
+
opts[method_name] = value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|