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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +85 -75
  3. data/VERSION +1 -1
  4. data/lib/serega/attribute.rb +2 -2
  5. data/lib/serega/attribute_normalizer.rb +8 -9
  6. data/lib/serega/config.rb +1 -1
  7. data/lib/serega/object_serializer.rb +1 -1
  8. data/lib/serega/plan.rb +11 -11
  9. data/lib/serega/plan_point.rb +5 -5
  10. data/lib/serega/plugins/activerecord_preloads/activerecord_preloads.rb +1 -1
  11. data/lib/serega/plugins/batch/batch.rb +15 -15
  12. data/lib/serega/plugins/batch/lib/validations/check_opt_batch.rb +2 -2
  13. data/lib/serega/plugins/camel_case/camel_case.rb +195 -0
  14. data/lib/serega/plugins/depth_limit/depth_limit.rb +185 -0
  15. data/lib/serega/plugins/explicit_many_option/explicit_many_option.rb +2 -5
  16. data/lib/serega/plugins/if/if.rb +4 -4
  17. data/lib/serega/plugins/metadata/metadata.rb +6 -6
  18. data/lib/serega/plugins/preloads/lib/modules/attribute_normalizer.rb +1 -1
  19. data/lib/serega/plugins/preloads/lib/preload_paths.rb +12 -5
  20. data/lib/serega/plugins/preloads/lib/preloads_constructor.rb +1 -1
  21. data/lib/serega/plugins/preloads/preloads.rb +12 -12
  22. data/lib/serega/plugins/root/root.rb +1 -1
  23. data/lib/serega/plugins/string_modifiers/string_modifiers.rb +1 -1
  24. data/lib/serega/validations/attribute/check_opt_const.rb +1 -1
  25. data/lib/serega/validations/attribute/check_opt_delegate.rb +9 -4
  26. data/lib/serega/validations/attribute/check_opt_many.rb +28 -11
  27. data/lib/serega/validations/attribute/{check_opt_key.rb → check_opt_method.rb} +8 -8
  28. data/lib/serega/validations/attribute/check_opt_value.rb +1 -1
  29. data/lib/serega/validations/check_attribute_params.rb +2 -2
  30. data/lib/serega/validations/check_initiate_params.rb +1 -1
  31. data/lib/serega/validations/check_serialize_params.rb +1 -1
  32. data/lib/serega/validations/utils/check_allowed_keys.rb +4 -2
  33. data/lib/serega.rb +24 -11
  34. metadata +5 -6
  35. data/lib/serega/plugins/openapi/lib/modules/config.rb +0 -23
  36. data/lib/serega/plugins/openapi/lib/openapi_config.rb +0 -101
  37. data/lib/serega/plugins/openapi/openapi.rb +0 -245
@@ -27,7 +27,7 @@ class Serega
27
27
  private
28
28
 
29
29
  def check_usage_with_other_params(opts, block)
30
- raise SeregaError, "Option :value can not be used together with option :key" if opts.key?(:key)
30
+ raise SeregaError, "Option :value can not be used together with option :method" if opts.key?(:method)
31
31
  raise SeregaError, "Option :value can not be used together with option :const" if opts.key?(:const)
32
32
  raise SeregaError, "Option :value can not be used together with block" if block
33
33
  end
@@ -58,12 +58,12 @@ class Serega
58
58
  # - plugin :if (checks :if, :if_value, :unless, :unless_value options)
59
59
  # - plugin :preloads (checks :preload option)
60
60
  def check_opts
61
- Utils::CheckAllowedKeys.call(opts, allowed_opts_keys)
61
+ Utils::CheckAllowedKeys.call(opts, allowed_opts_keys, :attribute)
62
62
 
63
63
  Attribute::CheckOptConst.call(opts, block)
64
64
  Attribute::CheckOptDelegate.call(opts, block)
65
65
  Attribute::CheckOptHide.call(opts)
66
- Attribute::CheckOptKey.call(opts, block)
66
+ Attribute::CheckOptMethod.call(opts, block)
67
67
  Attribute::CheckOptMany.call(opts)
68
68
  Attribute::CheckOptSerializer.call(opts)
69
69
  Attribute::CheckOptValue.call(opts, block)
@@ -35,7 +35,7 @@ class Serega
35
35
  private
36
36
 
37
37
  def check_allowed_keys
38
- Utils::CheckAllowedKeys.call(opts, serializer_class.config.initiate_keys)
38
+ Utils::CheckAllowedKeys.call(opts, serializer_class.config.initiate_keys, :initiate)
39
39
  end
40
40
 
41
41
  def check_modifiers
@@ -33,7 +33,7 @@ class Serega
33
33
  private
34
34
 
35
35
  def check_opts
36
- Utils::CheckAllowedKeys.call(opts, serializer_class.config.serialize_keys)
36
+ Utils::CheckAllowedKeys.call(opts, serializer_class.config.serialize_keys, :serialize)
37
37
 
38
38
  Utils::CheckOptIsHash.call(opts, :context)
39
39
  Utils::CheckOptIsBool.call(opts, :many)
@@ -18,11 +18,13 @@ class Serega
18
18
  # @raise [Serega::SeregaError] error when any hash key is not allowed
19
19
  #
20
20
  # @return [void]
21
- def self.call(opts, allowed_keys)
21
+ def self.call(opts, allowed_keys, parameter_name)
22
22
  opts.each_key do |key|
23
23
  next if allowed_keys.include?(key)
24
24
 
25
- raise SeregaError, "Invalid option #{key.inspect}. Allowed options are: #{allowed_keys.map(&:inspect).join(", ")}"
25
+ raise SeregaError,
26
+ "Invalid #{parameter_name} option #{key.inspect}." \
27
+ " Allowed options are: #{allowed_keys.map(&:inspect).sort.join(", ")}"
26
28
  end
27
29
  end
28
30
  end
data/lib/serega.rb CHANGED
@@ -32,8 +32,8 @@ require_relative "serega/validations/attribute/check_name"
32
32
  require_relative "serega/validations/attribute/check_opt_const"
33
33
  require_relative "serega/validations/attribute/check_opt_hide"
34
34
  require_relative "serega/validations/attribute/check_opt_delegate"
35
- require_relative "serega/validations/attribute/check_opt_key"
36
35
  require_relative "serega/validations/attribute/check_opt_many"
36
+ require_relative "serega/validations/attribute/check_opt_method"
37
37
  require_relative "serega/validations/attribute/check_opt_serializer"
38
38
  require_relative "serega/validations/attribute/check_opt_value"
39
39
  require_relative "serega/validations/initiate/check_modifiers"
@@ -172,7 +172,20 @@ class Serega
172
172
  new(modifiers_opts).to_h(object, serialize_opts)
173
173
  end
174
174
 
175
- # @see #call
175
+ #
176
+ # Serializes provided object to Hash
177
+ #
178
+ # @param object [Object] Serialized object
179
+ # @param opts [Hash, nil] Serializer modifiers and other instantiating options
180
+ # @option opts [Array, Hash, String, Symbol] :only The only attributes to serialize
181
+ # @option opts [Array, Hash, String, Symbol] :except Attributes to hide
182
+ # @option opts [Array, Hash, String, Symbol] :with Attributes (usually hidden) to serialize additionally
183
+ # @option opts [Boolean] :validate Validates provided modifiers (Default is true)
184
+ # @option opts [Hash] :context Serialization context
185
+ # @option opts [Boolean] :many Set true if provided multiple objects (Default `object.is_a?(Enumerable)`)
186
+ #
187
+ # @return [Hash] Serialization result
188
+ #
176
189
  def to_h(object, opts = nil)
177
190
  call(object, opts)
178
191
  end
@@ -283,8 +296,17 @@ class Serega
283
296
  def initialize(opts = nil)
284
297
  @opts = (opts.nil? || opts.empty?) ? FROZEN_EMPTY_HASH : parse_modifiers(opts)
285
298
  self.class::CheckInitiateParams.new(@opts).validate if opts&.fetch(:check_initiate_params) { config.check_initiate_params }
299
+
300
+ @plan = self.class::SeregaPlan.call(@opts)
286
301
  end
287
302
 
303
+ #
304
+ # Plan for serialization.
305
+ # This plan can be traversed to find serialized attributes and nested attributes.
306
+ #
307
+ # @return [Serega::SeregaPlan] Serialization plan
308
+ attr_reader :plan
309
+
288
310
  #
289
311
  # Serializes provided object to Hash
290
312
  #
@@ -338,15 +360,6 @@ class Serega
338
360
  config.from_json.call(json)
339
361
  end
340
362
 
341
- #
342
- # Plan for serialization.
343
- # This plan can be traversed to find serialized attributes and nested attributes.
344
- #
345
- # @return [Array<Serega::SeregaPlanPoint>] plan
346
- def plan
347
- @plan ||= self.class::SeregaPlan.call(opts)
348
- end
349
-
350
363
  private
351
364
 
352
365
  attr_reader :opts
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: serega
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Glushkov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-24 00:00:00.000000000 Z
11
+ date: 2023-10-14 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  JSON Serializer
@@ -58,7 +58,9 @@ files:
58
58
  - lib/serega/plugins/batch/lib/validations/check_batch_opt_key.rb
59
59
  - lib/serega/plugins/batch/lib/validations/check_batch_opt_loader.rb
60
60
  - lib/serega/plugins/batch/lib/validations/check_opt_batch.rb
61
+ - lib/serega/plugins/camel_case/camel_case.rb
61
62
  - lib/serega/plugins/context_metadata/context_metadata.rb
63
+ - lib/serega/plugins/depth_limit/depth_limit.rb
62
64
  - lib/serega/plugins/explicit_many_option/explicit_many_option.rb
63
65
  - lib/serega/plugins/explicit_many_option/validations/check_opt_many.rb
64
66
  - lib/serega/plugins/formatters/formatters.rb
@@ -74,9 +76,6 @@ files:
74
76
  - lib/serega/plugins/metadata/validations/check_opt_hide_nil.rb
75
77
  - lib/serega/plugins/metadata/validations/check_opts.rb
76
78
  - lib/serega/plugins/metadata/validations/check_path.rb
77
- - lib/serega/plugins/openapi/lib/modules/config.rb
78
- - lib/serega/plugins/openapi/lib/openapi_config.rb
79
- - lib/serega/plugins/openapi/openapi.rb
80
79
  - lib/serega/plugins/preloads/lib/format_user_preloads.rb
81
80
  - lib/serega/plugins/preloads/lib/modules/attribute.rb
82
81
  - lib/serega/plugins/preloads/lib/modules/attribute_normalizer.rb
@@ -102,8 +101,8 @@ files:
102
101
  - lib/serega/validations/attribute/check_opt_const.rb
103
102
  - lib/serega/validations/attribute/check_opt_delegate.rb
104
103
  - lib/serega/validations/attribute/check_opt_hide.rb
105
- - lib/serega/validations/attribute/check_opt_key.rb
106
104
  - lib/serega/validations/attribute/check_opt_many.rb
105
+ - lib/serega/validations/attribute/check_opt_method.rb
107
106
  - lib/serega/validations/attribute/check_opt_serializer.rb
108
107
  - lib/serega/validations/attribute/check_opt_value.rb
109
108
  - lib/serega/validations/check_attribute_params.rb
@@ -1,23 +0,0 @@
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
@@ -1,101 +0,0 @@
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
@@ -1,245 +0,0 @@
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