json-schema-serializer 1.5.2 → 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d6ac2c2fbbc49625b87c22bba8061ef8aacff924a4e1bfa19faedbeeb3d6fcb7
4
- data.tar.gz: 4071e3b87030dc4bff3f5800a650618619b703121094afea5b9cae8b1f584689
3
+ metadata.gz: 688a9d9a5604e822768c5427964b4b63a4bc1e4a909709672a4ba091c9afd309
4
+ data.tar.gz: 842ed003063daf2b04c14cee4239d8b2b31c4bdc77426adbc3c231ac80922576
5
5
  SHA512:
6
- metadata.gz: c5372d62cecbea2832b93c09b4c3c7336a54d52e342f23d7c8689266d2b848d94ffb218cb19464ba16d74464da6ccc69c4706127f85acfa383f24cbb209d9cf0
7
- data.tar.gz: 5426726b5f922839c4e21b0172a1f9281167bc2c5f41ef18baa81995d2395949624627f6b786f2c764715fabef7ba299f21d316a40d4b456543755b9b68eb3e3
6
+ metadata.gz: 88ede4a87f75b6f176d177cb078d7336ba67f25923d890a95c727106a14e2c673f6a02593056a3f069b30d517b6b48f344075df7b30e0edac76e1bab58f4fb15
7
+ data.tar.gz: f797603f80e47cced28a52bddabf61cef290064339d8b25c32b89fd7ebafd2093c46eeaa9b79f6436b557d8315ec23c5153060058f364067f98243cf3222d924
@@ -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
@@ -112,6 +112,178 @@ serializer_injected = JSON::Schema::Serializer.new(
112
112
 
113
113
  serializer_injected.serialize([1, 2, 3])
114
114
  # => {"first"=>1, "count"=>3}
115
+
116
+ #
117
+ # object injector with context
118
+ #
119
+
120
+ class BarSerializer
121
+ def initialize(model, context = nil)
122
+ @model = model
123
+ @context = context
124
+ end
125
+
126
+ def id
127
+ @model[:id]
128
+ end
129
+
130
+ def score
131
+ @context[@model[:id]]
132
+ end
133
+ end
134
+
135
+ inject_context = {
136
+ 1 => 100,
137
+ 2 => 200,
138
+ }
139
+
140
+ serializer_injected_with_context = JSON::Schema::Serializer.new(
141
+ {
142
+ type: :object,
143
+ inject: :Bar,
144
+ properties: {
145
+ id: { type: :integer },
146
+ score: { type: :integer },
147
+ },
148
+ },
149
+ {
150
+ inject_key: :inject,
151
+ injectors: {
152
+ Bar: BarSerializer,
153
+ },
154
+ inject_context: inject_context,
155
+ },
156
+ )
157
+
158
+ serializer_injected_with_context.serialize({ id: 1 })
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
+ )
115
287
  ```
116
288
 
117
289
  ### "additionalProperties"
@@ -211,12 +383,18 @@ new({
211
383
  }, { schema_key_transform_for_output: ->(name) { name.underscore } }).serialize({ userCount: 1 }) == { "user_count" => 1 }
212
384
  ```
213
385
 
214
- #### options[:injectors] [Hashlike<String, Class>, Class], options[:inject_key] [String, Symbol]
386
+ #### options[:injectors] [Hashlike<String, Class>, Class], options[:inject_key] [String, Symbol], options[:inject_context] [any], options[:inject_by_keyword] [Boolean]
215
387
 
216
388
  If schema has inject key, the serializer treats data by `injectors[inject_key].new(data)` (or `injectors.send(inject_key).new(data)`).
217
389
 
390
+ And if `inject_context` is present, `injectors[inject_key].new(data, inject_context)` (or `injectors.send(inject_key).new(data, inject_context)`).
391
+
392
+ And if `inject_by_keyword` is true, `new(data, inject_context)` will be `new(data: data, context: inject_context)`.
393
+
218
394
  See examples in [Usage](#usage).
219
395
 
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.
397
+
220
398
  #### options[:null_through] [Boolean]
221
399
 
222
400
  If data is null, always serialize null.
@@ -275,6 +453,14 @@ Serialize the object data by the schema.
275
453
 
276
454
  Serialize target object. The serializer tries data["foo"], data[:foo] and data.foo!
277
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
+
278
464
  ## License
279
465
 
280
466
  Zlib License
@@ -0,0 +1,5 @@
1
+ target :lib do
2
+ signature "sig"
3
+
4
+ check "lib"
5
+ end
File without changes
data/bin/fmt CHANGED
File without changes
data/bin/setup CHANGED
File without changes
@@ -28,13 +28,15 @@ Gem::Specification.new do |spec|
28
28
  spec.require_paths = %w[lib]
29
29
 
30
30
  spec.add_development_dependency "bundler", "~> 2.0"
31
- spec.add_development_dependency "rake", "~> 10.5"
31
+ spec.add_development_dependency "rake", "~> 13.0"
32
32
  spec.add_development_dependency "rspec", "~> 3.9"
33
33
  spec.add_development_dependency "rspec-power_assert", "~> 1.1"
34
+ spec.add_development_dependency "simplecov", ">= 0.18"
34
35
  spec.add_development_dependency "rubocop", "~> 0.76"
35
36
  spec.add_development_dependency "rubocop-airbnb", "~> 3"
36
37
  spec.add_development_dependency "prettier", ">= 0.16"
37
38
  spec.add_development_dependency "rubocop-config-prettier", "~> 0.1"
38
39
  spec.add_development_dependency "pry-byebug", "~> 3.7"
39
40
  spec.add_development_dependency "yard", "~> 0.9"
41
+ spec.add_development_dependency "steep", ">= 0.39"
40
42
  end
@@ -5,31 +5,79 @@ require "set"
5
5
  module JSON
6
6
  class Schema
7
7
  class Serializer
8
- def initialize(schema, options = nil)
9
- @schema = options && options[:resolver] ? options[:resolver].call(schema) : schema
8
+ def initialize(schema, options = nil) # rubocop:disable Airbnb/OptArgParameters
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
- obj = default if obj.nil?
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
- obj = injector.new(obj) if injector
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
63
+ if options[:inject_context]
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
70
+ else
71
+ obj =
72
+ if options[:inject_by_keyword]
73
+ injector.new(data: obj)
74
+ else
75
+ injector.new(obj)
76
+ end
77
+ end
78
+ end
31
79
  end
32
- 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)
33
81
  end
34
82
 
35
83
  def detect_type(type, obj)
@@ -112,7 +160,7 @@ module JSON
112
160
  end
113
161
  end
114
162
 
115
- def type_coerce(schema, type, format, obj, required, options)
163
+ def type_coerce(schema, type, format, obj, required, using_default, options)
116
164
  return nil if !required && obj.nil?
117
165
 
118
166
  case type.to_s
@@ -169,8 +217,8 @@ module JSON
169
217
  nil
170
218
  elsif options[:empty_string_boolean_coerce_null] && obj == ""
171
219
  nil
172
- elsif options[:false_values]
173
- !options[:false_values].include?(obj)
220
+ elsif (false_values = options[:false_values])
221
+ !false_values.include?(obj)
174
222
  elsif options[:no_boolean_coerce]
175
223
  obj == true
176
224
  else
@@ -181,7 +229,7 @@ module JSON
181
229
  return options[:null_through] ? nil : [] if obj.nil? || !obj.respond_to?(:map)
182
230
  return options[:null_through] ? nil : [] if options[:guard_primitive_in_structure] && is_primitive?(obj)
183
231
 
184
- obj.map { |item| walk(items_schema, item, true, options) }
232
+ obj.map { |item| walk(items_schema, item, true, using_default, options) }
185
233
  when "object"
186
234
  return nil if obj.nil? && options[:null_through]
187
235
  return options[:null_through] ? nil : {} if options[:guard_primitive_in_structure] && is_primitive?(obj)
@@ -195,7 +243,7 @@ module JSON
195
243
  properties_schema.map do |name, property_schema|
196
244
  input_key = input_key_transform ? input_key_transform.call(name.to_s) : name
197
245
  output_key = output_key_transform ? output_key_transform.call(name.to_s) : name.to_s
198
- [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)]
199
247
  end.to_h
200
248
  if additional_properties_schema
201
249
  not_additional_keys_array = properties_schema.keys.map(&:to_s)
@@ -204,7 +252,7 @@ module JSON
204
252
  ret.merge(
205
253
  additional_keys.map do |name|
206
254
  output_key = output_key_transform ? output_key_transform.call(name.to_s) : name.to_s
207
- [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)]
208
256
  end.to_h,
209
257
  )
210
258
  else
@@ -1,7 +1,7 @@
1
1
  module JSON
2
2
  class Schema
3
3
  class Serializer
4
- VERSION = "1.5.2".freeze
4
+ VERSION = "2.1.0".freeze
5
5
  end
6
6
  end
7
7
  end
@@ -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.5.2
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Narazaka
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-12-03 00:00:00.000000000 Z
11
+ date: 2020-12-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.5'
33
+ version: '13.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.5'
40
+ version: '13.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0.18'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0.18'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rubocop
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -150,7 +164,21 @@ dependencies:
150
164
  - - "~>"
151
165
  - !ruby/object:Gem::Version
152
166
  version: '0.9'
153
- description:
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:
154
182
  email:
155
183
  - info@narazaka.net
156
184
  executables: []
@@ -168,12 +196,14 @@ files:
168
196
  - LICENSE
169
197
  - README.md
170
198
  - Rakefile
199
+ - Steepfile
171
200
  - bin/console
172
201
  - bin/fmt
173
202
  - bin/setup
174
203
  - json-schema-serializer.gemspec
175
204
  - lib/json/schema/serializer.rb
176
205
  - lib/json/schema/serializer/version.rb
206
+ - sig/json/schema/serializer.rbs
177
207
  homepage: https://github.com/Narazaka/json-schema-serializer
178
208
  licenses:
179
209
  - Zlib
@@ -181,8 +211,8 @@ metadata:
181
211
  homepage_uri: https://github.com/Narazaka/json-schema-serializer
182
212
  source_code_uri: https://github.com/Narazaka/json-schema-serializer.git
183
213
  changelog_uri: https://github.com/Narazaka/json-schema-serializer/blob/master/CHANGELOG.md
184
- documentation_uri: https://www.rubydoc.info/gems/json-schema-serializer/1.5.2
185
- post_install_message:
214
+ documentation_uri: https://www.rubydoc.info/gems/json-schema-serializer/2.1.0
215
+ post_install_message:
186
216
  rdoc_options: []
187
217
  require_paths:
188
218
  - lib
@@ -197,9 +227,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
197
227
  - !ruby/object:Gem::Version
198
228
  version: '0'
199
229
  requirements: []
200
- rubyforge_project:
201
- rubygems_version: 2.7.6
202
- signing_key:
230
+ rubygems_version: 3.2.3
231
+ signing_key:
203
232
  specification_version: 4
204
233
  summary: JSON Schema based serializer
205
234
  test_files: []