json-schema-serializer 1.6.0 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +138 -1
- data/Steepfile +5 -0
- data/json-schema-serializer.gemspec +1 -0
- data/lib/json/schema/serializer.rb +56 -14
- data/lib/json/schema/serializer/version.rb +1 -1
- data/sig/json/schema/serializer.rbs +59 -0
- metadata +24 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f891febd1f289cd33579bb3045a776978fce4efe25e1971b563a618fe5522f8
|
4
|
+
data.tar.gz: e9f6317e62a8a57d1f999d10977dd61e9b570ccc71a59182780a2ca78f4e5283
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f56d91ec30edc66a16ca515ea2876427099e9ae452a7ca601208fd52d0b6f89cd312302e6f99429a9288cf5eee22ff54fd1abc06133b41bb07d7b771dafe7c3a
|
7
|
+
data.tar.gz: 8b00053eb322dd58488d9895afe5179d3dc9a7d3560c92cb865897276d372a5c692e992c79df1a25aeafb109c53cc2ae9b3ab1b38dc8447678c58a7235f8601b
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 2.0.0
|
4
|
+
|
5
|
+
- fix: Non-primitive (arrays, objects, etc.) "default" schemas and injections have strange results.
|
6
|
+
- If you were using a non-primitive (array, object, etc.) "default" schema with injection, it could be a breaking change!
|
7
|
+
|
8
|
+
## 1.7.0
|
9
|
+
|
10
|
+
- add: `with_context!` api
|
11
|
+
- add: `:inject_by_keyword` option
|
12
|
+
|
13
|
+
## 1.6.0
|
14
|
+
|
15
|
+
- add: inject context
|
16
|
+
|
3
17
|
## 1.5.0
|
4
18
|
|
5
19
|
- add: many options
|
data/README.md
CHANGED
@@ -157,6 +157,133 @@ serializer_injected_with_context = JSON::Schema::Serializer.new(
|
|
157
157
|
|
158
158
|
serializer_injected_with_context.serialize({ id: 1 })
|
159
159
|
# => { "id" => 1, "score" => 100 }
|
160
|
+
|
161
|
+
#
|
162
|
+
# inject in serializer
|
163
|
+
#
|
164
|
+
|
165
|
+
class ParentSerializer
|
166
|
+
include JSON::Schema::Serializer::WithContext
|
167
|
+
|
168
|
+
def initialize(model, context = nil)
|
169
|
+
@model = model
|
170
|
+
@context = context
|
171
|
+
end
|
172
|
+
|
173
|
+
def id
|
174
|
+
@model[:id]
|
175
|
+
end
|
176
|
+
|
177
|
+
def score
|
178
|
+
@context[:parent_scores][@model[:id]]
|
179
|
+
end
|
180
|
+
|
181
|
+
def child
|
182
|
+
# it can be
|
183
|
+
# with_context(context) { data }
|
184
|
+
# with_context(data, context)
|
185
|
+
# with_context(data: data, context: context)
|
186
|
+
with_context(@context.merge(child_scores: { 1 => 100, 2 => 200 })) do
|
187
|
+
@model[:child]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class ChildSerializer
|
193
|
+
def initialize(model, context = nil)
|
194
|
+
@model = model
|
195
|
+
@context = context
|
196
|
+
end
|
197
|
+
|
198
|
+
def id
|
199
|
+
@model[:id]
|
200
|
+
end
|
201
|
+
|
202
|
+
def score
|
203
|
+
@context[:child_scores][@model[:id]]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
serializer_injected_with_context_in_serializer = JSON::Schema::Serializer.new(
|
208
|
+
{
|
209
|
+
type: :object,
|
210
|
+
inject: :Parent,
|
211
|
+
properties: {
|
212
|
+
id: { type: :integer },
|
213
|
+
score: { type: :integer },
|
214
|
+
child: {
|
215
|
+
type: :object,
|
216
|
+
inject: :Child,
|
217
|
+
properties: {
|
218
|
+
id: { type: :integer },
|
219
|
+
score: { type: :integer },
|
220
|
+
},
|
221
|
+
},
|
222
|
+
},
|
223
|
+
},
|
224
|
+
{
|
225
|
+
inject_key: :inject,
|
226
|
+
injectors: {
|
227
|
+
Parent: ParentSerializer,
|
228
|
+
Child: ChildSerializer,
|
229
|
+
},
|
230
|
+
inject_context: { 1 => 10, 2 => 20 },
|
231
|
+
},
|
232
|
+
)
|
233
|
+
|
234
|
+
serializer_injected_with_context_in_serializer.serialize({ id: 1, child: { id: 2 } })
|
235
|
+
# => { "id" => 1, "score" => 10, "child" => { "id" => 2, "score" => 200 } }
|
236
|
+
|
237
|
+
#
|
238
|
+
# also you can inject context with arraylike data
|
239
|
+
#
|
240
|
+
|
241
|
+
class ItemsSerializer
|
242
|
+
include JSON::Schema::Serializer::WithContext
|
243
|
+
|
244
|
+
def initialize(models, context = nil)
|
245
|
+
@models = models
|
246
|
+
@context = context
|
247
|
+
end
|
248
|
+
|
249
|
+
def map(&block)
|
250
|
+
context = (@context || {}).merge(scores: {...})
|
251
|
+
@models.map { |model| block.call(with_context(model, context)) }
|
252
|
+
# CAUTION!
|
253
|
+
# not like below!
|
254
|
+
# with_context(@models.map(&block), context)
|
255
|
+
# with_context(context) { @models.map(&block) }
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
#
|
260
|
+
# inject model can initialize by keywords
|
261
|
+
#
|
262
|
+
|
263
|
+
class KeywordSerializer
|
264
|
+
def initialize(data:, context: nil)
|
265
|
+
@data = data
|
266
|
+
@context = context
|
267
|
+
end
|
268
|
+
|
269
|
+
...
|
270
|
+
end
|
271
|
+
|
272
|
+
serializer_with_keyword_init_inject = JSON::Schema::Serializer.new(
|
273
|
+
{
|
274
|
+
type: :object,
|
275
|
+
inject: :Keyword,
|
276
|
+
properties: { ... },
|
277
|
+
},
|
278
|
+
{
|
279
|
+
inject_key: :inject,
|
280
|
+
injectors: {
|
281
|
+
Keyword: KeywordSerializer,
|
282
|
+
Child: ChildSerializer,
|
283
|
+
},
|
284
|
+
inject_by_keyword: true, # <- keyword_init!
|
285
|
+
},
|
286
|
+
)
|
160
287
|
```
|
161
288
|
|
162
289
|
### "additionalProperties"
|
@@ -256,12 +383,14 @@ new({
|
|
256
383
|
}, { schema_key_transform_for_output: ->(name) { name.underscore } }).serialize({ userCount: 1 }) == { "user_count" => 1 }
|
257
384
|
```
|
258
385
|
|
259
|
-
#### options[:injectors] [Hashlike<String, Class>, Class], options[:inject_key] [String, Symbol], options[:inject_context] [any]
|
386
|
+
#### options[:injectors] [Hashlike<String, Class>, Class], options[:inject_key] [String, Symbol], options[:inject_context] [any], options[:inject_by_keyword] [Boolean]
|
260
387
|
|
261
388
|
If schema has inject key, the serializer treats data by `injectors[inject_key].new(data)` (or `injectors.send(inject_key).new(data)`).
|
262
389
|
|
263
390
|
And if `inject_context` is present, `injectors[inject_key].new(data, inject_context)` (or `injectors.send(inject_key).new(data, inject_context)`).
|
264
391
|
|
392
|
+
And if `inject_by_keyword` is true, `new(data, inject_context)` will be `new(data: data, context: inject_context)`.
|
393
|
+
|
265
394
|
See examples in [Usage](#usage).
|
266
395
|
|
267
396
|
CAUTION: In many case you should define the `nil?` method in the injector class because Injector always initialized by `Injector.new(obj)` even if obj == nil.
|
@@ -324,6 +453,14 @@ Serialize the object data by the schema.
|
|
324
453
|
|
325
454
|
Serialize target object. The serializer tries data["foo"], data[:foo] and data.foo!
|
326
455
|
|
456
|
+
## JSON::Schema::Serializer::WithContext API
|
457
|
+
|
458
|
+
### #with_context!(data, context), #with_context!(data: data, context: context), #with_context!(context) { data }
|
459
|
+
|
460
|
+
If you use `with_context!(data, context)` as the return value of the serializer, then "child" serializer can use that context.
|
461
|
+
|
462
|
+
See examples in [Usage](#usage).
|
463
|
+
|
327
464
|
## License
|
328
465
|
|
329
466
|
Zlib License
|
data/Steepfile
ADDED
@@ -38,4 +38,5 @@ Gem::Specification.new do |spec|
|
|
38
38
|
spec.add_development_dependency "rubocop-config-prettier", "~> 0.1"
|
39
39
|
spec.add_development_dependency "pry-byebug", "~> 3.7"
|
40
40
|
spec.add_development_dependency "yard", "~> 0.9"
|
41
|
+
spec.add_development_dependency "steep", ">= 0.39"
|
41
42
|
end
|
@@ -6,36 +6,78 @@ module JSON
|
|
6
6
|
class Schema
|
7
7
|
class Serializer
|
8
8
|
def initialize(schema, options = nil) # rubocop:disable Airbnb/OptArgParameters
|
9
|
-
@schema =
|
9
|
+
@schema =
|
10
|
+
if options && (resolver = options[:resolver])
|
11
|
+
resolver.call(schema)
|
12
|
+
else
|
13
|
+
schema
|
14
|
+
end
|
10
15
|
@options = options || {}
|
11
16
|
end
|
12
17
|
|
13
18
|
def serialize(data)
|
14
|
-
Walker.walk(@schema, data, true, @options)
|
19
|
+
Walker.walk(@schema, data, true, false, @options)
|
20
|
+
end
|
21
|
+
|
22
|
+
DataWithContext = Struct.new(:data, :context, keyword_init: true)
|
23
|
+
|
24
|
+
module WithContext
|
25
|
+
ARG2_NOT_GIVEN = :__json_schema_serializer_arg2_not_given__
|
26
|
+
|
27
|
+
def with_context!(arg1, arg2 = ARG2_NOT_GIVEN) # rubocop:disable Airbnb/OptArgParameters
|
28
|
+
if block_given?
|
29
|
+
DataWithContext.new(data: yield, context: arg1)
|
30
|
+
elsif arg2 == ARG2_NOT_GIVEN
|
31
|
+
DataWithContext.new(arg1)
|
32
|
+
else
|
33
|
+
DataWithContext.new(data: arg1, context: arg2)
|
34
|
+
end
|
35
|
+
end
|
15
36
|
end
|
16
37
|
|
17
38
|
class Walker
|
18
39
|
class << self
|
19
40
|
TimeWithZone = defined?(ActiveSupport::TimeWithZone) ? ActiveSupport::TimeWithZone : nil
|
20
41
|
|
21
|
-
def walk(schema, obj, required, options)
|
42
|
+
def walk(schema, obj, required, using_default, options)
|
22
43
|
type = try_hash(schema, :type)
|
23
44
|
default = try_hash(schema, :default)
|
24
45
|
format = try_hash(schema, :format)
|
25
|
-
|
46
|
+
if obj.nil?
|
47
|
+
using_default = true
|
48
|
+
obj = default
|
49
|
+
end
|
26
50
|
|
27
51
|
if options[:inject_key]
|
28
52
|
inject_key = try_hash(schema, options[:inject_key])
|
29
53
|
injector = try_hash(options[:injectors], inject_key) if inject_key
|
30
|
-
if
|
54
|
+
if obj.instance_of?(JSON::Schema::Serializer::DataWithContext)
|
55
|
+
options = options.merge(inject_context: obj.context)
|
56
|
+
obj = obj.data
|
57
|
+
if obj.nil?
|
58
|
+
using_default = true
|
59
|
+
obj = default
|
60
|
+
end
|
61
|
+
end
|
62
|
+
if injector && !using_default
|
31
63
|
if options[:inject_context]
|
32
|
-
obj =
|
64
|
+
obj =
|
65
|
+
if options[:inject_by_keyword]
|
66
|
+
injector.new(data: obj, context: options[:inject_context])
|
67
|
+
else
|
68
|
+
injector.new(obj, options[:inject_context])
|
69
|
+
end
|
33
70
|
else
|
34
|
-
obj =
|
71
|
+
obj =
|
72
|
+
if options[:inject_by_keyword]
|
73
|
+
injector.new(data: obj)
|
74
|
+
else
|
75
|
+
injector.new(obj)
|
76
|
+
end
|
35
77
|
end
|
36
78
|
end
|
37
79
|
end
|
38
|
-
type_coerce(schema, detect_type(type, obj), format, obj, required, options)
|
80
|
+
type_coerce(schema, detect_type(type, obj), format, obj, required, using_default, options)
|
39
81
|
end
|
40
82
|
|
41
83
|
def detect_type(type, obj)
|
@@ -118,7 +160,7 @@ module JSON
|
|
118
160
|
end
|
119
161
|
end
|
120
162
|
|
121
|
-
def type_coerce(schema, type, format, obj, required, options)
|
163
|
+
def type_coerce(schema, type, format, obj, required, using_default, options)
|
122
164
|
return nil if !required && obj.nil?
|
123
165
|
|
124
166
|
case type.to_s
|
@@ -175,8 +217,8 @@ module JSON
|
|
175
217
|
nil
|
176
218
|
elsif options[:empty_string_boolean_coerce_null] && obj == ""
|
177
219
|
nil
|
178
|
-
elsif options[:false_values]
|
179
|
-
!
|
220
|
+
elsif (false_values = options[:false_values])
|
221
|
+
!false_values.include?(obj)
|
180
222
|
elsif options[:no_boolean_coerce]
|
181
223
|
obj == true
|
182
224
|
else
|
@@ -187,7 +229,7 @@ module JSON
|
|
187
229
|
return options[:null_through] ? nil : [] if obj.nil? || !obj.respond_to?(:map)
|
188
230
|
return options[:null_through] ? nil : [] if options[:guard_primitive_in_structure] && is_primitive?(obj)
|
189
231
|
|
190
|
-
obj.map { |item| walk(items_schema, item, true, options) }
|
232
|
+
obj.map { |item| walk(items_schema, item, true, using_default, options) }
|
191
233
|
when "object"
|
192
234
|
return nil if obj.nil? && options[:null_through]
|
193
235
|
return options[:null_through] ? nil : {} if options[:guard_primitive_in_structure] && is_primitive?(obj)
|
@@ -201,7 +243,7 @@ module JSON
|
|
201
243
|
properties_schema.map do |name, property_schema|
|
202
244
|
input_key = input_key_transform ? input_key_transform.call(name.to_s) : name
|
203
245
|
output_key = output_key_transform ? output_key_transform.call(name.to_s) : name.to_s
|
204
|
-
[output_key, walk(property_schema, try_hash(obj, input_key), required_schema.include?(name.to_s), options)]
|
246
|
+
[output_key, walk(property_schema, try_hash(obj, input_key), required_schema.include?(name.to_s), using_default, options)]
|
205
247
|
end.to_h
|
206
248
|
if additional_properties_schema
|
207
249
|
not_additional_keys_array = properties_schema.keys.map(&:to_s)
|
@@ -210,7 +252,7 @@ module JSON
|
|
210
252
|
ret.merge(
|
211
253
|
additional_keys.map do |name|
|
212
254
|
output_key = output_key_transform ? output_key_transform.call(name.to_s) : name.to_s
|
213
|
-
[output_key, walk(additional_properties_schema, try_hash(obj, name), false, options)]
|
255
|
+
[output_key, walk(additional_properties_schema, try_hash(obj, name), false, using_default, options)]
|
214
256
|
end.to_h,
|
215
257
|
)
|
216
258
|
else
|
@@ -0,0 +1,59 @@
|
|
1
|
+
type jsonSchema = untyped
|
2
|
+
type jsonData = untyped
|
3
|
+
type jsonSchemaContext = untyped
|
4
|
+
type jsonSingleType = "null" | "string" | "number" | "integer" | "boolean" | "array" | "object"
|
5
|
+
type jsonType = jsonSingleType | Array[jsonSingleType]
|
6
|
+
|
7
|
+
# Classes
|
8
|
+
module JSON
|
9
|
+
class Schema
|
10
|
+
class Serializer
|
11
|
+
VERSION: String
|
12
|
+
|
13
|
+
type serializerOptions = {
|
14
|
+
inject_key: String | Symbol?,
|
15
|
+
inject_context: jsonSchemaContext?,
|
16
|
+
injectors: Hash[String | Symbol, Class] | Class?,
|
17
|
+
inject_by_keyword: bool?,
|
18
|
+
resolver: (^(jsonData data) -> jsonData)?,
|
19
|
+
null_through: bool?,
|
20
|
+
empty_string_number_coerce_null: bool?,
|
21
|
+
empty_string_boolean_coerce_null: bool?,
|
22
|
+
false_values: Enumerable[untyped]?,
|
23
|
+
no_boolean_coerce: bool?,
|
24
|
+
guard_primitive_in_structure: bool?,
|
25
|
+
schema_key_transform_for_input: (^(String name) -> String)?,
|
26
|
+
schema_key_transform_for_output: (^(String name) -> String)?
|
27
|
+
}
|
28
|
+
@schema: jsonSchema
|
29
|
+
@options: serializerOptions
|
30
|
+
|
31
|
+
def initialize: (jsonSchema schema, ?serializerOptions options) -> void
|
32
|
+
def serialize: (jsonData data) -> untyped
|
33
|
+
|
34
|
+
class DataWithContext < Struct[untyped]
|
35
|
+
attr_accessor data(): jsonData
|
36
|
+
attr_accessor context(): jsonSchemaContext
|
37
|
+
def initialize: (data: jsonData, context: jsonSchemaContext) -> void
|
38
|
+
end
|
39
|
+
|
40
|
+
module WithContext
|
41
|
+
ARG2_NOT_GIVEN: :__json_schema_serializer_arg2_not_given__
|
42
|
+
|
43
|
+
def with_context!: (jsonSchemaContext context) { () -> jsonData } -> DataWithContext
|
44
|
+
| (jsonData data, jsonSchemaContext context) -> DataWithContext
|
45
|
+
| ({ data: jsonData, context: jsonSchemaContext } arg) -> DataWithContext
|
46
|
+
end
|
47
|
+
|
48
|
+
class Walker
|
49
|
+
TimeWithZone: nil
|
50
|
+
|
51
|
+
def self.walk: (jsonSchema schema, jsonData obj, bool required, bool using_default, serializerOptions options) -> untyped
|
52
|
+
def self.detect_type: (jsonType type_, jsonData obj) -> jsonSingleType
|
53
|
+
def self.type_coerce: (jsonSchema schema, jsonType type_, String format, jsonData obj, bool required, bool using_default, serializerOptions options) -> untyped
|
54
|
+
def self.try_hash: (untyped obj, String | Symbol? name) -> untyped
|
55
|
+
def self.is_primitive?: (untyped obj) -> bool
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: json-schema-serializer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 2.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Narazaka
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-12-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -164,7 +164,21 @@ dependencies:
|
|
164
164
|
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0.9'
|
167
|
-
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: steep
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0.39'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0.39'
|
181
|
+
description:
|
168
182
|
email:
|
169
183
|
- info@narazaka.net
|
170
184
|
executables: []
|
@@ -182,12 +196,14 @@ files:
|
|
182
196
|
- LICENSE
|
183
197
|
- README.md
|
184
198
|
- Rakefile
|
199
|
+
- Steepfile
|
185
200
|
- bin/console
|
186
201
|
- bin/fmt
|
187
202
|
- bin/setup
|
188
203
|
- json-schema-serializer.gemspec
|
189
204
|
- lib/json/schema/serializer.rb
|
190
205
|
- lib/json/schema/serializer/version.rb
|
206
|
+
- sig/json/schema/serializer.rbs
|
191
207
|
homepage: https://github.com/Narazaka/json-schema-serializer
|
192
208
|
licenses:
|
193
209
|
- Zlib
|
@@ -195,8 +211,8 @@ metadata:
|
|
195
211
|
homepage_uri: https://github.com/Narazaka/json-schema-serializer
|
196
212
|
source_code_uri: https://github.com/Narazaka/json-schema-serializer.git
|
197
213
|
changelog_uri: https://github.com/Narazaka/json-schema-serializer/blob/master/CHANGELOG.md
|
198
|
-
documentation_uri: https://www.rubydoc.info/gems/json-schema-serializer/1.
|
199
|
-
post_install_message:
|
214
|
+
documentation_uri: https://www.rubydoc.info/gems/json-schema-serializer/2.1.1
|
215
|
+
post_install_message:
|
200
216
|
rdoc_options: []
|
201
217
|
require_paths:
|
202
218
|
- lib
|
@@ -211,9 +227,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
211
227
|
- !ruby/object:Gem::Version
|
212
228
|
version: '0'
|
213
229
|
requirements: []
|
214
|
-
|
215
|
-
|
216
|
-
signing_key:
|
230
|
+
rubygems_version: 3.2.3
|
231
|
+
signing_key:
|
217
232
|
specification_version: 4
|
218
233
|
summary: JSON Schema based serializer
|
219
234
|
test_files: []
|