json-schema-serializer 1.5.1 → 2.0.1

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: 37e694cb4884a1ca5064e0d0ec1ec8e8c71b4033f802c60eda6c409cbc47eae2
4
- data.tar.gz: abbc2a9dae69d938498280fcd70b27ee2b0d4d575271112b3196b9655b1a0686
3
+ metadata.gz: 98a8247eb1adc250801738b4056a8cb48af71a3b58f47636993a46aab2593951
4
+ data.tar.gz: 6eaab852b30a1b1ad23d3ced5a55fb17a010ca5a97f259e23f61ebe8589eedf6
5
5
  SHA512:
6
- metadata.gz: 20ccc5691d1928f7869be012a310096f4e8c1ae0a879a954126cd1c478e7596a2d7f523e22f1efba91e8c2735bea6c32960da6db9d0c2b973f49feec6d8e6c5f
7
- data.tar.gz: b9c86efa2961c16b00c9ba072277e7d98e12a8c6f6d4e86dbef6c366785bdcd054c35766a227586907e4e5ea3a05eb5a2fdcd59265dfa15b89ad629fea1e580e
6
+ metadata.gz: 4271739806fd52a9363a68459a760f3e5753fe7ea293fe9dd2ccc722b3204cb4c1673937de900891d013dcd7bc5e5177c4f2893c41aaecf81ad6358fe37cd430
7
+ data.tar.gz: 496676d7b7a47a46ecadb2ef6b55dad7fb26417821ac54737991d2d7e5254f903fc57310a30c51930e40e0da73e61c4caa0c74e9382be43345e0d866b4579588
File without changes
data/.gitignore CHANGED
File without changes
File without changes
data/.rspec CHANGED
File without changes
File without changes
File without changes
@@ -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/Gemfile CHANGED
File without changes
data/LICENSE CHANGED
File without changes
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"
@@ -185,7 +357,7 @@ options
185
357
 
186
358
  schema object `$ref` resolver
187
359
 
188
- #### options[:input_key_transform] [Proc]
360
+ #### options[:schema_key_transform_for_input] [Proc]
189
361
 
190
362
  input key transform
191
363
 
@@ -195,10 +367,10 @@ new({
195
367
  properties: {
196
368
  userCount: { type: :integer },
197
369
  },
198
- }, { input_key_transform: ->(name) { name.underscore } }).serialize({ user_count: 1 }) == { "userCount" => 1 }
370
+ }, { schema_key_transform_for_input: ->(name) { name.underscore } }).serialize({ user_count: 1 }) == { "userCount" => 1 }
199
371
  ```
200
372
 
201
- #### options[:output_key_transform] [Proc]
373
+ #### options[:schema_key_transform_for_output] [Proc]
202
374
 
203
375
  output key transform
204
376
 
@@ -208,15 +380,21 @@ new({
208
380
  properties: {
209
381
  userCount: { type: :integer },
210
382
  },
211
- }, { output_key_transform: ->(name) { name.underscore } }).serialize({ userCount: 1 }) == { "user_count" => 1 }
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
data/Rakefile CHANGED
File without changes
File without changes
data/bin/fmt CHANGED
File without changes
data/bin/setup CHANGED
File without changes
@@ -28,9 +28,10 @@ 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"
@@ -5,31 +5,74 @@ require "set"
5
5
  module JSON
6
6
  class Schema
7
7
  class Serializer
8
- def initialize(schema, options = nil)
8
+ def initialize(schema, options = nil) # rubocop:disable Airbnb/OptArgParameters
9
9
  @schema = options && options[:resolver] ? options[:resolver].call(schema) : schema
10
10
  @options = options || {}
11
11
  end
12
12
 
13
13
  def serialize(data)
14
- Walker.walk(@schema, data, true, @options)
14
+ Walker.walk(@schema, data, true, false, @options)
15
+ end
16
+
17
+ DataWithContext = Struct.new(:data, :context, keyword_init: true)
18
+
19
+ module WithContext
20
+ ARG2_NOT_GIVEN = :__json_schema_serializer_arg2_not_given__
21
+
22
+ def with_context!(arg1, arg2 = ARG2_NOT_GIVEN) # rubocop:disable Airbnb/OptArgParameters
23
+ if block_given?
24
+ DataWithContext.new(data: yield, context: arg1)
25
+ elsif arg2 == ARG2_NOT_GIVEN
26
+ DataWithContext.new(arg1)
27
+ else
28
+ DataWithContext.new(data: arg1, context: arg2)
29
+ end
30
+ end
15
31
  end
16
32
 
17
33
  class Walker
18
34
  class << self
19
35
  TimeWithZone = defined?(ActiveSupport::TimeWithZone) ? ActiveSupport::TimeWithZone : nil
20
36
 
21
- def walk(schema, obj, required, options)
37
+ def walk(schema, obj, required, using_default, options)
22
38
  type = try_hash(schema, :type)
23
39
  default = try_hash(schema, :default)
24
40
  format = try_hash(schema, :format)
25
- obj = default if obj.nil?
41
+ if obj.nil?
42
+ using_default = true
43
+ obj = default
44
+ end
26
45
 
27
46
  if options[:inject_key]
28
47
  inject_key = try_hash(schema, options[:inject_key])
29
48
  injector = try_hash(options[:injectors], inject_key) if inject_key
30
- obj = injector.new(obj) if injector
49
+ if obj.instance_of?(JSON::Schema::Serializer::DataWithContext)
50
+ options = options.merge(inject_context: obj.context)
51
+ obj = obj.data
52
+ if obj.nil?
53
+ using_default = true
54
+ obj = default
55
+ end
56
+ end
57
+ if injector && !using_default
58
+ if options[:inject_context]
59
+ obj =
60
+ if options[:inject_by_keyword]
61
+ injector.new(data: obj, context: options[:inject_context])
62
+ else
63
+ injector.new(obj, options[:inject_context])
64
+ end
65
+ else
66
+ obj =
67
+ if options[:inject_by_keyword]
68
+ injector.new(data: obj)
69
+ else
70
+ injector.new(obj)
71
+ end
72
+ end
73
+ end
31
74
  end
32
- type_coerce(schema, detect_type(type, obj), format, obj, required, options)
75
+ type_coerce(schema, detect_type(type, obj), format, obj, required, using_default, options)
33
76
  end
34
77
 
35
78
  def detect_type(type, obj)
@@ -112,7 +155,7 @@ module JSON
112
155
  end
113
156
  end
114
157
 
115
- def type_coerce(schema, type, format, obj, required, options)
158
+ def type_coerce(schema, type, format, obj, required, using_default, options)
116
159
  return nil if !required && obj.nil?
117
160
 
118
161
  case type.to_s
@@ -181,7 +224,7 @@ module JSON
181
224
  return options[:null_through] ? nil : [] if obj.nil? || !obj.respond_to?(:map)
182
225
  return options[:null_through] ? nil : [] if options[:guard_primitive_in_structure] && is_primitive?(obj)
183
226
 
184
- obj.map { |item| walk(items_schema, item, true, options) }
227
+ obj.map { |item| walk(items_schema, item, true, using_default, options) }
185
228
  when "object"
186
229
  return nil if obj.nil? && options[:null_through]
187
230
  return options[:null_through] ? nil : {} if options[:guard_primitive_in_structure] && is_primitive?(obj)
@@ -189,13 +232,13 @@ module JSON
189
232
  properties_schema = try_hash(schema, :properties)
190
233
  additional_properties_schema = try_hash(schema, :additionalProperties)
191
234
  required_schema = Set.new(try_hash(schema, :required)&.map(&:to_s))
192
- input_key_transform = options[:input_key_transform] # schema key -> input obj key
193
- output_key_transform = options[:output_key_transform] # schema key -> out
235
+ input_key_transform = options[:schema_key_transform_for_input] # schema key -> input obj key
236
+ output_key_transform = options[:schema_key_transform_for_output] # schema key -> out
194
237
  ret =
195
238
  properties_schema.map do |name, property_schema|
196
239
  input_key = input_key_transform ? input_key_transform.call(name.to_s) : name
197
240
  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)]
241
+ [output_key, walk(property_schema, try_hash(obj, input_key), required_schema.include?(name.to_s), using_default, options)]
199
242
  end.to_h
200
243
  if additional_properties_schema
201
244
  not_additional_keys_array = properties_schema.keys.map(&:to_s)
@@ -204,7 +247,7 @@ module JSON
204
247
  ret.merge(
205
248
  additional_keys.map do |name|
206
249
  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)]
250
+ [output_key, walk(additional_properties_schema, try_hash(obj, name), false, using_default, options)]
208
251
  end.to_h,
209
252
  )
210
253
  else
@@ -1,7 +1,7 @@
1
1
  module JSON
2
2
  class Schema
3
3
  class Serializer
4
- VERSION = "1.5.1".freeze
4
+ VERSION = "2.0.1".freeze
5
5
  end
6
6
  end
7
7
  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.1
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Narazaka
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-12-03 00:00:00.000000000 Z
11
+ date: 2020-11-06 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
@@ -181,7 +195,7 @@ metadata:
181
195
  homepage_uri: https://github.com/Narazaka/json-schema-serializer
182
196
  source_code_uri: https://github.com/Narazaka/json-schema-serializer.git
183
197
  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.1
198
+ documentation_uri: https://www.rubydoc.info/gems/json-schema-serializer/2.0.1
185
199
  post_install_message:
186
200
  rdoc_options: []
187
201
  require_paths:
@@ -197,8 +211,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
197
211
  - !ruby/object:Gem::Version
198
212
  version: '0'
199
213
  requirements: []
200
- rubyforge_project:
201
- rubygems_version: 2.7.6
214
+ rubygems_version: 3.0.3
202
215
  signing_key:
203
216
  specification_version: 4
204
217
  summary: JSON Schema based serializer