serega 0.14.0 → 0.16.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 +85 -75
- data/VERSION +1 -1
- data/lib/serega/attribute.rb +2 -2
- data/lib/serega/attribute_normalizer.rb +8 -9
- data/lib/serega/config.rb +1 -1
- data/lib/serega/object_serializer.rb +1 -1
- data/lib/serega/plan.rb +11 -11
- data/lib/serega/plan_point.rb +5 -5
- data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +1 -1
- data/lib/serega/plugins/batch/batch.rb +15 -15
- data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +2 -2
- data/lib/serega/plugins/camel_case/camel_case.rb +195 -0
- data/lib/serega/plugins/depth_limit/depth_limit.rb +185 -0
- data/lib/serega/plugins/explicit_many_option/explicit_many_option.rb +2 -5
- data/lib/serega/plugins/if/if.rb +4 -4
- data/lib/serega/plugins/metadata/metadata.rb +6 -6
- data/lib/serega/plugins/preloads/lib/modules/attribute_normalizer.rb +1 -1
- data/lib/serega/plugins/preloads/lib/preload_paths.rb +12 -5
- data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +1 -1
- data/lib/serega/plugins/preloads/preloads.rb +12 -12
- data/lib/serega/plugins/root/root.rb +1 -1
- data/lib/serega/plugins/string_modifiers/string_modifiers.rb +1 -1
- data/lib/serega/validations/attribute/check_opt_const.rb +1 -1
- data/lib/serega/validations/attribute/check_opt_delegate.rb +9 -4
- data/lib/serega/validations/attribute/check_opt_many.rb +28 -11
- data/lib/serega/validations/attribute/{check_opt_key.rb → check_opt_method.rb} +8 -8
- data/lib/serega/validations/attribute/check_opt_value.rb +1 -1
- data/lib/serega/validations/check_attribute_params.rb +2 -2
- data/lib/serega/validations/check_initiate_params.rb +1 -1
- data/lib/serega/validations/check_serialize_params.rb +1 -1
- data/lib/serega/validations/utils/check_allowed_keys.rb +4 -2
- data/lib/serega.rb +24 -11
- metadata +5 -6
- data/lib/serega/plugins/openapi/lib/modules/config.rb +0 -23
- data/lib/serega/plugins/openapi/lib/openapi_config.rb +0 -101
- data/lib/serega/plugins/openapi/openapi.rb +0 -245
@@ -0,0 +1,195 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
#
|
6
|
+
# Plugin :camel_case
|
7
|
+
#
|
8
|
+
# By default when we add attribute like `attribute :first_name` this means:
|
9
|
+
# - adding a `:first_name` key to resulted hash
|
10
|
+
# - adding a `#first_name` method call result as value
|
11
|
+
#
|
12
|
+
# But its often desired to response with *camelCased* keys.
|
13
|
+
# Earlier this can be achieved by specifying attribute name and method directly
|
14
|
+
# for each attribute: `attribute :firstName, method: first_name`
|
15
|
+
#
|
16
|
+
# Now this plugin transforms all attribute names automatically.
|
17
|
+
# We use simple regular expression to replace `_x` to `X` for the whole string.
|
18
|
+
# You can provide your own callable transformation when defining plugin,
|
19
|
+
# for example `plugin :camel_case, transform: ->(name) { name.camelize }`
|
20
|
+
#
|
21
|
+
# For any attribute camelCase-behavior can be skipped when
|
22
|
+
# `camel_case: false` attribute option provided.
|
23
|
+
#
|
24
|
+
# @example Define plugin
|
25
|
+
# class AppSerializer < Serega
|
26
|
+
# plugin :camel_case
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# class UserSerializer < AppSerializer
|
30
|
+
# attribute :first_name
|
31
|
+
# attribute :last_name
|
32
|
+
# attribute :full_name, camel_case: false, value: proc { |user| [user.first_name, user.last_name].compact.join(" ") }
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# require "ostruct"
|
36
|
+
# user = OpenStruct.new(first_name: "Bruce", last_name: "Wayne")
|
37
|
+
# UserSerializer.to_h(user) # {firstName: "Bruce", lastName: "Wayne", full_name: "Bruce Wayne"}
|
38
|
+
#
|
39
|
+
module CamelCase
|
40
|
+
# Default camel-case transformation
|
41
|
+
TRANSFORM_DEFAULT = proc { |attribute_name|
|
42
|
+
attribute_name.gsub!(/_[a-z]/) { |m| m[-1].upcase! }
|
43
|
+
attribute_name
|
44
|
+
}
|
45
|
+
|
46
|
+
# @return [Symbol] Plugin name
|
47
|
+
def self.plugin_name
|
48
|
+
:camel_case
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# Applies plugin code to specific serializer
|
53
|
+
#
|
54
|
+
# @param serializer_class [Class<Serega>] Current serializer class
|
55
|
+
# @param _opts [Hash] Loaded plugins options
|
56
|
+
#
|
57
|
+
# @return [void]
|
58
|
+
#
|
59
|
+
def self.load_plugin(serializer_class, **_opts)
|
60
|
+
serializer_class::SeregaConfig.include(ConfigInstanceMethods)
|
61
|
+
serializer_class::SeregaAttributeNormalizer.include(AttributeNormalizerInstanceMethods)
|
62
|
+
serializer_class::CheckAttributeParams.include(CheckAttributeParamsInstanceMethods)
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# Adds config options and runs other callbacks after plugin was loaded
|
67
|
+
#
|
68
|
+
# @param serializer_class [Class<Serega>] Current serializer class
|
69
|
+
# @param opts [Hash] loaded plugins opts
|
70
|
+
#
|
71
|
+
# @return [void]
|
72
|
+
#
|
73
|
+
def self.after_load_plugin(serializer_class, **opts)
|
74
|
+
config = serializer_class.config
|
75
|
+
config.opts[:camel_case] = {}
|
76
|
+
config.camel_case.transform = opts[:transform] || TRANSFORM_DEFAULT
|
77
|
+
|
78
|
+
config.attribute_keys << :camel_case
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# Config class additional/patched instance methods
|
83
|
+
#
|
84
|
+
# @see Serega::SeregaConfig
|
85
|
+
#
|
86
|
+
module ConfigInstanceMethods
|
87
|
+
# @return [Serega::SeregaPlugins::CamelCase::CamelCaseConfig] `camel_case` plugin config
|
88
|
+
def camel_case
|
89
|
+
@camel_case ||= CamelCaseConfig.new(opts.fetch(:camel_case))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# Serega::SeregaValidations::CheckAttributeParams additional/patched class methods
|
95
|
+
#
|
96
|
+
# @see Serega::SeregaValidations::CheckAttributeParams
|
97
|
+
#
|
98
|
+
module CheckAttributeParamsInstanceMethods
|
99
|
+
private
|
100
|
+
|
101
|
+
def check_opts
|
102
|
+
super
|
103
|
+
CheckOptCamelCase.call(opts)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
# Validator for attribute :camel_case option
|
109
|
+
#
|
110
|
+
class CheckOptCamelCase
|
111
|
+
class << self
|
112
|
+
#
|
113
|
+
# Checks attribute :camel_case option must be boolean
|
114
|
+
#
|
115
|
+
# @param opts [Hash] Attribute options
|
116
|
+
#
|
117
|
+
# @raise [SeregaError] Attribute validation error
|
118
|
+
#
|
119
|
+
# @return [void]
|
120
|
+
#
|
121
|
+
def call(opts)
|
122
|
+
camel_case_option_exists = opts.key?(:camel_case)
|
123
|
+
return unless camel_case_option_exists
|
124
|
+
|
125
|
+
value = opts[:camel_case]
|
126
|
+
return if value.equal?(true) || value.equal?(false)
|
127
|
+
|
128
|
+
raise SeregaError, "Attribute option :camel_case must have a boolean value, but #{value.class} was provided"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# CamelCase config object
|
134
|
+
class CamelCaseConfig
|
135
|
+
attr_reader :opts
|
136
|
+
|
137
|
+
#
|
138
|
+
# Initializes CamelCaseConfig object
|
139
|
+
#
|
140
|
+
# @param opts [Hash] camel_case plugin options
|
141
|
+
# @option opts [#call] :transform Callable object that transforms original attribute name
|
142
|
+
#
|
143
|
+
# @return [Serega::SeregaPlugins::CamelCase::CamelCaseConfig] CamelCaseConfig object
|
144
|
+
#
|
145
|
+
def initialize(opts)
|
146
|
+
@opts = opts
|
147
|
+
end
|
148
|
+
|
149
|
+
# @return [#call] defined object that transforms name
|
150
|
+
def transform
|
151
|
+
opts.fetch(:transform)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Sets transformation callable object
|
155
|
+
#
|
156
|
+
# @param value [#call] transformation
|
157
|
+
#
|
158
|
+
# @return [#call] camel_case plugin transformation callable object
|
159
|
+
def transform=(value)
|
160
|
+
raise SeregaError, "Transform value must respond to #call" unless value.respond_to?(:call)
|
161
|
+
|
162
|
+
params = value.is_a?(Proc) ? value.parameters : value.method(:call).parameters
|
163
|
+
if params.count != 1 || !params.all? { |param| (param[0] == :req) || (param[0] == :opt) }
|
164
|
+
raise SeregaError, "Transform value must respond to #call and accept 1 regular parameter"
|
165
|
+
end
|
166
|
+
|
167
|
+
opts[:transform] = value
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
#
|
172
|
+
# SeregaAttributeNormalizer additional/patched instance methods
|
173
|
+
#
|
174
|
+
# @see SeregaAttributeNormalizer::AttributeInstanceMethods
|
175
|
+
#
|
176
|
+
module AttributeNormalizerInstanceMethods
|
177
|
+
private
|
178
|
+
|
179
|
+
#
|
180
|
+
# Patch for original `prepare_name` method
|
181
|
+
#
|
182
|
+
# Makes camelCased name
|
183
|
+
#
|
184
|
+
def prepare_name
|
185
|
+
res = super
|
186
|
+
return res if init_opts[:camel_case] == false
|
187
|
+
|
188
|
+
self.class.serializer_class.config.camel_case.transform.call(res.to_s).to_sym
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
register_plugin(CamelCase.plugin_name, CamelCase)
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Serega
|
4
|
+
module SeregaPlugins
|
5
|
+
#
|
6
|
+
# Plugin :depth_limit
|
7
|
+
#
|
8
|
+
# Helps to secure from malicious queries that require to serialize too much
|
9
|
+
# or from accidental serializing of objects with cyclic relations.
|
10
|
+
#
|
11
|
+
# Depth limit is checked when constructing a serialization plan, that is when
|
12
|
+
# `#new` method is called, ex: `SomeSerializer.new(with: params[:with])`.
|
13
|
+
# It can be useful to instantiate serializer before any other business logic
|
14
|
+
# to get possible errors earlier.
|
15
|
+
#
|
16
|
+
# Any class-level serialization methods also check depth limit as they also instantiate serializer.
|
17
|
+
#
|
18
|
+
# When depth limit is exceeded `Serega::DepthLimitError` is raised.
|
19
|
+
# Depth limit error details can be found in additional `Serega::DepthLimitError#details` method
|
20
|
+
#
|
21
|
+
# Limit can be checked or changed with next config options:
|
22
|
+
#
|
23
|
+
# - config.depth_limit.limit
|
24
|
+
# - config.depth_limit.limit=
|
25
|
+
#
|
26
|
+
# There are no default limit, but it should be set when enabling plugin.
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
#
|
30
|
+
# class AppSerializer < Serega
|
31
|
+
# plugin :depth_limit, limit: 10 # set limit for all child classes
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# class UserSerializer < AppSerializer
|
35
|
+
# config.depth_limit.limit = 5 # overrides limit for UserSerializer
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
module DepthLimit
|
39
|
+
# @return [Symbol] Plugin name
|
40
|
+
def self.plugin_name
|
41
|
+
:depth_limit
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Applies plugin code to specific serializer
|
46
|
+
#
|
47
|
+
# @param serializer_class [Class<Serega>] Current serializer class
|
48
|
+
# @param _opts [Hash] Loaded plugins options
|
49
|
+
#
|
50
|
+
# @return [void]
|
51
|
+
#
|
52
|
+
def self.load_plugin(serializer_class, **_opts)
|
53
|
+
serializer_class::SeregaPlan.include(PlanInstanceMethods)
|
54
|
+
serializer_class::SeregaConfig.include(ConfigInstanceMethods)
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Adds config options and runs other callbacks after plugin was loaded
|
59
|
+
#
|
60
|
+
# @param serializer_class [Class<Serega>] Current serializer class
|
61
|
+
# @param opts [Hash] loaded plugins opts
|
62
|
+
#
|
63
|
+
# @return [void]
|
64
|
+
#
|
65
|
+
def self.after_load_plugin(serializer_class, **opts)
|
66
|
+
config = serializer_class.config
|
67
|
+
limit = opts.fetch(:limit) { raise SeregaError, "Please provide :limit option. Example: `plugin :depth_limit, limit: 10`" }
|
68
|
+
config.opts[:depth_limit] = {}
|
69
|
+
config.depth_limit.limit = limit
|
70
|
+
end
|
71
|
+
|
72
|
+
# DepthLimit config object
|
73
|
+
class DepthLimitConfig
|
74
|
+
attr_reader :opts
|
75
|
+
|
76
|
+
#
|
77
|
+
# Initializes DepthLimitConfig object
|
78
|
+
#
|
79
|
+
# @param opts [Hash] depth_limit plugin options
|
80
|
+
#
|
81
|
+
# @return [SeregaPlugins::DepthLimit::DepthLimitConfig] DepthLimitConfig object
|
82
|
+
#
|
83
|
+
def initialize(opts)
|
84
|
+
@opts = opts
|
85
|
+
end
|
86
|
+
|
87
|
+
# @return [Integer] defined depth limit
|
88
|
+
def limit
|
89
|
+
opts.fetch(:limit)
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# Set depth limit
|
94
|
+
#
|
95
|
+
# @param value [Integer] depth limit
|
96
|
+
#
|
97
|
+
# @return [Integer] depth limit
|
98
|
+
def limit=(value)
|
99
|
+
raise SeregaError, "Depth limit must be an Integer" unless value.is_a?(Integer)
|
100
|
+
|
101
|
+
opts[:limit] = value
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Serega::SeregaConfig additional/patched class methods
|
107
|
+
#
|
108
|
+
# @see Serega::SeregaConfig
|
109
|
+
#
|
110
|
+
module ConfigInstanceMethods
|
111
|
+
# @return [Serega::SeregaPlugins::DepthLimit::DepthLimitConfig] current depth_limit config
|
112
|
+
def depth_limit
|
113
|
+
@depth_limit ||= DepthLimitConfig.new(opts.fetch(:depth_limit))
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# SeregaPlan additional/patched instance methods
|
119
|
+
#
|
120
|
+
# @see SeregaPlan
|
121
|
+
#
|
122
|
+
module PlanInstanceMethods
|
123
|
+
#
|
124
|
+
# Initializes serialization plan
|
125
|
+
# Overrides original method (adds depth_limit validation)
|
126
|
+
#
|
127
|
+
def initialize(parent_plan_point, *)
|
128
|
+
check_depth_limit_exceeded(parent_plan_point)
|
129
|
+
super
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def check_depth_limit_exceeded(current_point)
|
135
|
+
plan = self
|
136
|
+
depth_level = 1
|
137
|
+
point = current_point
|
138
|
+
|
139
|
+
while point
|
140
|
+
depth_level += 1
|
141
|
+
plan = point.plan
|
142
|
+
point = plan.parent_plan_point
|
143
|
+
end
|
144
|
+
|
145
|
+
root_serializer = plan.class.serializer_class
|
146
|
+
root_depth_limit = root_serializer.config.depth_limit.limit
|
147
|
+
|
148
|
+
if depth_level > root_depth_limit
|
149
|
+
fields_chain = [current_point.name]
|
150
|
+
fields_chain << current_point.name while (current_point = current_point.plan.parent_plan_point)
|
151
|
+
details = "#{root_serializer} (depth limit: #{root_depth_limit}) -> #{fields_chain.reverse!.join(" -> ")}"
|
152
|
+
raise DepthLimitError.new("Depth limit was exceeded", details)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
register_plugin(DepthLimit.plugin_name, DepthLimit)
|
159
|
+
end
|
160
|
+
|
161
|
+
#
|
162
|
+
# Special error for depth_limit plugin
|
163
|
+
#
|
164
|
+
class DepthLimitError < SeregaError
|
165
|
+
#
|
166
|
+
# Details of why depth limit error happens.
|
167
|
+
#
|
168
|
+
# @return [String] error details
|
169
|
+
#
|
170
|
+
attr_reader :details
|
171
|
+
|
172
|
+
#
|
173
|
+
# Initializes new error
|
174
|
+
#
|
175
|
+
# @param message [String] Error message
|
176
|
+
# @param details [String] Error additional details
|
177
|
+
#
|
178
|
+
# @return [DepthLimitError] error instance
|
179
|
+
#
|
180
|
+
def initialize(message, details = nil)
|
181
|
+
super(message)
|
182
|
+
@details = details
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -8,10 +8,7 @@ class Serega
|
|
8
8
|
# Plugin requires to add :many option when adding relationships
|
9
9
|
# (relationships are attributes with :serializer option specified)
|
10
10
|
#
|
11
|
-
# Adding this plugin makes clearer to find if relationship returns array or single object
|
12
|
-
#
|
13
|
-
# Also some plugins like :openapi load this plugin automatically as they need to know if
|
14
|
-
# relationship is array
|
11
|
+
# Adding this plugin makes it clearer to find if relationship returns array or single object
|
15
12
|
#
|
16
13
|
# @example
|
17
14
|
# class BaseSerializer < Serega
|
@@ -43,7 +40,7 @@ class Serega
|
|
43
40
|
# @return [void]
|
44
41
|
#
|
45
42
|
def self.load_plugin(serializer_class, **_opts)
|
46
|
-
require_relative "
|
43
|
+
require_relative "validations/check_opt_many"
|
47
44
|
|
48
45
|
serializer_class::CheckAttributeParams.include(CheckAttributeParamsInstanceMethods)
|
49
46
|
end
|
data/lib/serega/plugins/if/if.rb
CHANGED
@@ -56,10 +56,10 @@ class Serega
|
|
56
56
|
# @return [void]
|
57
57
|
#
|
58
58
|
def self.load_plugin(serializer_class, **_opts)
|
59
|
-
require_relative "
|
60
|
-
require_relative "
|
61
|
-
require_relative "
|
62
|
-
require_relative "
|
59
|
+
require_relative "validations/check_opt_if"
|
60
|
+
require_relative "validations/check_opt_if_value"
|
61
|
+
require_relative "validations/check_opt_unless"
|
62
|
+
require_relative "validations/check_opt_unless_value"
|
63
63
|
|
64
64
|
serializer_class::SeregaAttribute.include(SeregaAttributeInstanceMethods)
|
65
65
|
serializer_class::SeregaPlanPoint.include(PlanPointInstanceMethods)
|
@@ -62,12 +62,12 @@ class Serega
|
|
62
62
|
serializer_class.include(InstanceMethods)
|
63
63
|
serializer_class::SeregaConfig.include(ConfigInstanceMethods)
|
64
64
|
|
65
|
-
require_relative "
|
66
|
-
require_relative "
|
67
|
-
require_relative "
|
68
|
-
require_relative "
|
69
|
-
require_relative "
|
70
|
-
require_relative "
|
65
|
+
require_relative "meta_attribute"
|
66
|
+
require_relative "validations/check_block"
|
67
|
+
require_relative "validations/check_opt_hide_nil"
|
68
|
+
require_relative "validations/check_opt_hide_empty"
|
69
|
+
require_relative "validations/check_opts"
|
70
|
+
require_relative "validations/check_path"
|
71
71
|
|
72
72
|
meta_attribute_class = Class.new(MetaAttribute)
|
73
73
|
meta_attribute_class.serializer_class = serializer_class
|
@@ -36,7 +36,7 @@ class Serega
|
|
36
36
|
if preloads_provided
|
37
37
|
opts[:preload]
|
38
38
|
elsif opts.key?(:serializer) && self.class.serializer_class.config.preloads.auto_preload_attributes_with_serializer
|
39
|
-
|
39
|
+
method_name
|
40
40
|
elsif opts.key?(:delegate) && self.class.serializer_class.config.preloads.auto_preload_attributes_with_delegate
|
41
41
|
opts[:delegate].fetch(:to)
|
42
42
|
end
|
@@ -22,18 +22,25 @@ class Serega
|
|
22
22
|
#
|
23
23
|
# Transforms user provided preloads to array of paths
|
24
24
|
#
|
25
|
-
# @param
|
25
|
+
# @param preloads [Array,Hash,String,Symbol,nil,false] association(s) to preload
|
26
26
|
#
|
27
27
|
# @return [Hash] preloads transformed to hash
|
28
28
|
#
|
29
|
-
def call(preloads
|
30
|
-
|
29
|
+
def call(preloads)
|
30
|
+
formatted_preloads = FormatUserPreloads.call(preloads)
|
31
|
+
return FROZEN_EMPTY_ARRAY if formatted_preloads.empty?
|
31
32
|
|
32
|
-
|
33
|
+
paths(formatted_preloads, [], [])
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def paths(formatted_preloads, path, result)
|
39
|
+
formatted_preloads.each do |key, nested_preloads|
|
33
40
|
path << key
|
34
41
|
result << path.dup
|
35
42
|
|
36
|
-
|
43
|
+
paths(nested_preloads, path, result)
|
37
44
|
path.pop
|
38
45
|
end
|
39
46
|
|
@@ -15,7 +15,7 @@ class Serega
|
|
15
15
|
#
|
16
16
|
# This options are very handy if you want to forget about finding preloads manually.
|
17
17
|
#
|
18
|
-
# Preloads can be disabled with `preload: false` attribute option
|
18
|
+
# Preloads can be disabled with `preload: false` attribute option.
|
19
19
|
# Also automatically added preloads can be overwritten with manually specified `preload: :another_value`.
|
20
20
|
#
|
21
21
|
# Some examples, **please read comments in the code below**
|
@@ -79,17 +79,17 @@ class Serega
|
|
79
79
|
# @return [void]
|
80
80
|
#
|
81
81
|
def self.load_plugin(serializer_class, **_opts)
|
82
|
-
require_relative "
|
83
|
-
require_relative "
|
84
|
-
require_relative "
|
85
|
-
require_relative "
|
86
|
-
require_relative "
|
87
|
-
require_relative "
|
88
|
-
require_relative "
|
89
|
-
require_relative "
|
90
|
-
require_relative "
|
91
|
-
require_relative "
|
92
|
-
require_relative "
|
82
|
+
require_relative "lib/format_user_preloads"
|
83
|
+
require_relative "lib/modules/attribute"
|
84
|
+
require_relative "lib/modules/attribute_normalizer"
|
85
|
+
require_relative "lib/modules/check_attribute_params"
|
86
|
+
require_relative "lib/modules/config"
|
87
|
+
require_relative "lib/modules/plan_point"
|
88
|
+
require_relative "lib/preload_paths"
|
89
|
+
require_relative "lib/preloads_config"
|
90
|
+
require_relative "lib/preloads_constructor"
|
91
|
+
require_relative "validations/check_opt_preload"
|
92
|
+
require_relative "validations/check_opt_preload_path"
|
93
93
|
|
94
94
|
serializer_class.include(InstanceMethods)
|
95
95
|
serializer_class::SeregaAttribute.include(AttributeInstanceMethods)
|
@@ -127,7 +127,7 @@ class Serega
|
|
127
127
|
# @option opts [Symbol, String, nil] :one root for single-object serialization
|
128
128
|
# @option opts [Symbol, String, nil] :many root for many-objects serialization
|
129
129
|
#
|
130
|
-
# @return [
|
130
|
+
# @return [SeregaPlugins::Root::RootConfig] RootConfig object
|
131
131
|
#
|
132
132
|
def initialize(opts)
|
133
133
|
@opts = opts
|
@@ -26,7 +26,7 @@ class Serega
|
|
26
26
|
private
|
27
27
|
|
28
28
|
def check_usage_with_other_params(opts, block)
|
29
|
-
raise SeregaError, "Option :const can not be used together with option :
|
29
|
+
raise SeregaError, "Option :const can not be used together with option :method" if opts.key?(:method)
|
30
30
|
raise SeregaError, "Option :const can not be used together with option :value" if opts.key?(:value)
|
31
31
|
raise SeregaError, "Option :const can not be used together with block" if block
|
32
32
|
end
|
@@ -32,8 +32,9 @@ class Serega
|
|
32
32
|
|
33
33
|
delegate_opts = opts[:delegate]
|
34
34
|
check_opt_delegate_to(delegate_opts)
|
35
|
-
|
35
|
+
check_opt_delegate_method(delegate_opts)
|
36
36
|
check_opt_delegate_allow_nil(delegate_opts)
|
37
|
+
check_opt_delegate_extra_opts(delegate_opts)
|
37
38
|
end
|
38
39
|
|
39
40
|
def check_opt_delegate_to(delegate_opts)
|
@@ -43,16 +44,20 @@ class Serega
|
|
43
44
|
Utils::CheckOptIsStringOrSymbol.call(delegate_opts, :to)
|
44
45
|
end
|
45
46
|
|
46
|
-
def
|
47
|
-
Utils::CheckOptIsStringOrSymbol.call(delegate_opts, :
|
47
|
+
def check_opt_delegate_method(delegate_opts)
|
48
|
+
Utils::CheckOptIsStringOrSymbol.call(delegate_opts, :method)
|
48
49
|
end
|
49
50
|
|
50
51
|
def check_opt_delegate_allow_nil(delegate_opts)
|
51
52
|
Utils::CheckOptIsBool.call(delegate_opts, :allow_nil)
|
52
53
|
end
|
53
54
|
|
55
|
+
def check_opt_delegate_extra_opts(delegate_opts)
|
56
|
+
Utils::CheckAllowedKeys.call(delegate_opts, %i[to method allow_nil], :delegate)
|
57
|
+
end
|
58
|
+
|
54
59
|
def check_usage_with_other_params(opts, block)
|
55
|
-
raise SeregaError, "Option :delegate can not be used together with option :
|
60
|
+
raise SeregaError, "Option :delegate can not be used together with option :method" if opts.key?(:method)
|
56
61
|
raise SeregaError, "Option :delegate can not be used together with option :const" if opts.key?(:const)
|
57
62
|
raise SeregaError, "Option :delegate can not be used together with option :value" if opts.key?(:value)
|
58
63
|
raise SeregaError, "Option :delegate can not be used together with block" if block
|
@@ -7,17 +7,34 @@ class Serega
|
|
7
7
|
# Attribute `:many` option validator
|
8
8
|
#
|
9
9
|
class CheckOptMany
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
10
|
+
class << self
|
11
|
+
#
|
12
|
+
# Checks attribute :many option
|
13
|
+
#
|
14
|
+
# @param opts [Hash] Attribute options
|
15
|
+
#
|
16
|
+
# @raise [SeregaError] SeregaError that option has invalid value
|
17
|
+
#
|
18
|
+
# @return [void]
|
19
|
+
#
|
20
|
+
def call(opts)
|
21
|
+
return unless opts.key?(:many)
|
22
|
+
|
23
|
+
check_many_option_makes_sence(opts)
|
24
|
+
Utils::CheckOptIsBool.call(opts, :many)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def check_many_option_makes_sence(opts)
|
30
|
+
return if many_option_makes_sence?(opts)
|
31
|
+
|
32
|
+
raise SeregaError, "Option :many can be provided only together with :serializer or :batch option"
|
33
|
+
end
|
34
|
+
|
35
|
+
def many_option_makes_sence?(opts)
|
36
|
+
opts[:serializer] || opts[:batch]
|
37
|
+
end
|
21
38
|
end
|
22
39
|
end
|
23
40
|
end
|
@@ -4,12 +4,12 @@ class Serega
|
|
4
4
|
module SeregaValidations
|
5
5
|
module Attribute
|
6
6
|
#
|
7
|
-
# Attribute `:
|
7
|
+
# Attribute `:method` option validator
|
8
8
|
#
|
9
|
-
class
|
9
|
+
class CheckOptMethod
|
10
10
|
class << self
|
11
11
|
#
|
12
|
-
# Checks attribute :
|
12
|
+
# Checks attribute :method option
|
13
13
|
#
|
14
14
|
# @param opts [Hash] Attribute options
|
15
15
|
#
|
@@ -18,18 +18,18 @@ class Serega
|
|
18
18
|
# @return [void]
|
19
19
|
#
|
20
20
|
def call(opts, block = nil)
|
21
|
-
return unless opts.key?(:
|
21
|
+
return unless opts.key?(:method)
|
22
22
|
|
23
23
|
check_usage_with_other_params(opts, block)
|
24
|
-
Utils::CheckOptIsStringOrSymbol.call(opts, :
|
24
|
+
Utils::CheckOptIsStringOrSymbol.call(opts, :method)
|
25
25
|
end
|
26
26
|
|
27
27
|
private
|
28
28
|
|
29
29
|
def check_usage_with_other_params(opts, block)
|
30
|
-
raise SeregaError, "Option :
|
31
|
-
raise SeregaError, "Option :
|
32
|
-
raise SeregaError, "Option :
|
30
|
+
raise SeregaError, "Option :method can not be used together with option :const" if opts.key?(:const)
|
31
|
+
raise SeregaError, "Option :method can not be used together with option :value" if opts.key?(:value)
|
32
|
+
raise SeregaError, "Option :method can not be used together with block" if block
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|