serega 0.14.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
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