json_skooma 0.2.5 → 0.2.7

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: eb2d7d35ec23d77daeda2fbc2e13a04a22265ecb549c2450b7f3e01fc0351a2a
4
- data.tar.gz: f9c354da7ac5cd8354a968bc728313af943189f9b06279d3812e91dd88086139
3
+ metadata.gz: bc94f3d758977e845fe5b92b683c57da06d23af3144cee65cd573679b0992b56
4
+ data.tar.gz: 1b9209442642b9bbe4170a400d164eae8ab4f58c4857ebd15c9a79a4532837f2
5
5
  SHA512:
6
- metadata.gz: 5352834d1a61ad40c9416bfad5fb8543da5d872151a4e6925194c42b87639dc125fd1268343b6fc6b8b7bb0b047144dd21e95049cddfec065bc5e3990b54768a
7
- data.tar.gz: 1095a00a437945ab384154da3f0f75a401636c39c21b9a7a2fd2b8de88140c909393c49f900c3295c289d6bb9795e238f3ccc2f7263348367076938ebf88bc8f
6
+ metadata.gz: 162215474dabeade31c53db14198eadb391f076b8305565c7f504ddbfe8cc3ea56654f7fdc0bbd4dca488f1adb814914b21e7d5780804d5d54efc4f11b66c8d3
7
+ data.tar.gz: f9158392915e5ea3985ad19f2df4e94ce3988b627f44b57cdbc3eb8b6a2dc01e475c40275e648baf6c64bb27168b292415ef663632f0becd6389f6c37e917863
data/CHANGELOG.md CHANGED
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning].
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.7] - 2026-06-10
11
+
12
+ ### Fixed
13
+
14
+ - Include the JSON Schema metaschema data files in the packaged gem. 0.2.6 was built without the `data/draft-*` submodules checked out, so `create_registry` crashed with `Sources::Error` on the released gem. ([@skryukov])
15
+
16
+ ## [0.2.6] - 2026-06-10 [YANKED]
17
+
18
+ ### Added
19
+
20
+ - New `:annotated` output format interleaves the instance data with annotations collected during evaluation (`title`/`description` by default, configurable via `keywords:`). Each node becomes `{"title" => ..., "value" => ...}`, mirroring the shape of the data — handy for rendering values alongside their schema-defined labels. ([@skryukov], [#13](https://github.com/skryukov/json_skooma/issues/13))
21
+ - `JSONSkooma::UnexpectedSchemaClassError < RegistryError` is now raised by `Registry#schema` when the resolved fragment does not match the expected class. Extensions can rescue this specific error instead of matching on message text. ([@skryukov])
22
+ - `Registry#load_json(uri)` is now public so extensions can load a raw document via registered sources. ([@skryukov])
23
+ - `Keywords::ValueSchemas.default_schema_class=` lets extensions set the fallback class used to wrap sub-schemas when a keyword does not specify its own `schema_value_class`. ([@skryukov])
24
+
10
25
  ## [0.2.5] - 2024-12-25
11
26
 
12
27
  ### Added
@@ -63,7 +78,9 @@ and this project adheres to [Semantic Versioning].
63
78
  [@killondark]: https://github.com/killondark
64
79
  [@skryukov]: https://github.com/skryukov
65
80
 
66
- [Unreleased]: https://github.com/skryukov/json_skooma/compare/v0.2.5...HEAD
81
+ [Unreleased]: https://github.com/skryukov/json_skooma/compare/v0.2.7...HEAD
82
+ [0.2.7]: https://github.com/skryukov/json_skooma/compare/v0.2.6...v0.2.7
83
+ [0.2.6]: https://github.com/skryukov/json_skooma/compare/v0.2.5...v0.2.6
67
84
  [0.2.5]: https://github.com/skryukov/json_skooma/compare/v0.2.4...v0.2.5
68
85
  [0.2.4]: https://github.com/skryukov/json_skooma/compare/v0.2.3...v0.2.4
69
86
  [0.2.3]: https://github.com/skryukov/json_skooma/compare/v0.2.2...v0.2.3
data/README.md CHANGED
@@ -156,6 +156,44 @@ schema_registry.add_source(
156
156
  # - http://remote.example/product_definition.yaml -> http://example.com/schemas/product_definition.yaml
157
157
  ```
158
158
 
159
+ ### Extracting annotations
160
+
161
+ The `:annotated` output format re-shapes collected annotations into a hash that mirrors your data: every node becomes a hash of its annotations plus a `"value"` key holding the original value. Useful for rendering data alongside the `title`/`description` texts defined in the schema.
162
+
163
+ ```ruby
164
+ schema = JSONSkooma::JSONSchema.new({
165
+ "$schema" => "https://json-schema.org/draft/2020-12/schema",
166
+ "type" => "object",
167
+ "properties" => {
168
+ "user_id" => {
169
+ "type" => "integer",
170
+ "title" => "User Identifier",
171
+ "description" => "A unique numeric ID for the user."
172
+ }
173
+ }
174
+ })
175
+
176
+ result = schema.evaluate({"user_id" => 123})
177
+
178
+ result.output(:annotated)
179
+ # {"user_id"=>
180
+ # {"title"=>"User Identifier",
181
+ # "description"=>"A unique numeric ID for the user.",
182
+ # "value"=>123}}
183
+
184
+ # Pick which annotation keywords to include (default: title and description):
185
+ result.output(:annotated, keywords: %w[title description default deprecated])
186
+
187
+ # Rename the wrapper key:
188
+ result.output(:annotated, value_key: "data")
189
+ ```
190
+
191
+ Notes:
192
+
193
+ - Annotations contributed through `$ref`/`allOf` are merged into the same location; if several subschemas annotate the same keyword at the same location, the last one wins.
194
+ - Annotations from failed subschemas (e.g. a non-matching `anyOf` branch) are dropped, as the spec prescribes.
195
+ - The root node is returned unwrapped to keep the output data-shaped, so annotations on the root schema itself are not included.
196
+
159
197
  ## Alternatives
160
198
 
161
199
  - [json_schemer](https://github.com/davishmcclurg/json_schemer) – Draft 4, 6, 7, 2019-09 and 2020-12 compliant
@@ -133,5 +133,61 @@ module JSONSkooma
133
133
  end
134
134
  end
135
135
  register :verbose, Verbose
136
+
137
+ # Re-shapes collected annotations into a hash that mirrors the instance
138
+ # data: every node (except the root) becomes a hash of its annotations
139
+ # plus a "value" key holding the original value (with nested nodes wrapped
140
+ # the same way). Annotations contributed through $ref/allOf land on the
141
+ # same instance location, so they merge naturally; annotations from failed
142
+ # subschemas are dropped, per the JSON Schema spec.
143
+ #
144
+ # result.output(:annotated)
145
+ # # => {"user_id" => {"title" => "User Identifier", "value" => 123}, ...}
146
+ #
147
+ # Options:
148
+ # keywords: list of annotation keywords to include (default: title, description)
149
+ # value_key: key under which the original value is placed (default: "value")
150
+ module Annotated
151
+ DEFAULT_KEYWORDS = %w[title description].freeze
152
+
153
+ class << self
154
+ def call(result, keywords: DEFAULT_KEYWORDS, value_key: "value", **_options)
155
+ annotations = {}
156
+ collect(result, keywords.map(&:to_s), annotations)
157
+ represent(result.instance, annotations, value_key, root: true)
158
+ end
159
+
160
+ private
161
+
162
+ def collect(node, keywords, annotations)
163
+ return unless node.valid?
164
+
165
+ if node.annotation && keywords.include?(node.key)
166
+ annotation = node.annotation
167
+ annotation = annotation.value if annotation.is_a?(JSONNode)
168
+ (annotations[node.instance.path.to_s] ||= {})[node.key] = annotation
169
+ end
170
+
171
+ node.each_children { |child| collect(child, keywords, annotations) }
172
+ end
173
+
174
+ def represent(instance, annotations, value_key, root: false)
175
+ value =
176
+ case instance.type
177
+ when "object"
178
+ instance.transform_values { |child| represent(child, annotations, value_key) }
179
+ when "array"
180
+ instance.map { |child| represent(child, annotations, value_key) }
181
+ else
182
+ instance.value
183
+ end
184
+
185
+ return value if root
186
+
187
+ (annotations[instance.path.to_s] || {}).merge(value_key => value)
188
+ end
189
+ end
190
+ end
191
+ register :annotated, Annotated
136
192
  end
137
193
  end
@@ -4,6 +4,8 @@ module JSONSkooma
4
4
  module Keywords
5
5
  module ValueSchemas
6
6
  class << self
7
+ attr_writer :default_schema_class
8
+
7
9
  def [](key)
8
10
  value_schemas&.[](key) or raise "Unknown value schema: #{key}, known schemas: #{value_schemas.keys.inspect}"
9
11
  end
@@ -12,6 +14,14 @@ module JSONSkooma
12
14
  (self.value_schemas ||= {})[key] = klass
13
15
  end
14
16
 
17
+ # Class used to wrap schema values when a keyword does not set its own
18
+ # `schema_value_class`. Extensions (e.g. Skooma) override this to plug
19
+ # their own JSONSchema subclass into every sub-schema created by the
20
+ # built-in applicator keywords.
21
+ def default_schema_class
22
+ @default_schema_class || JSONSchema
23
+ end
24
+
15
25
  private
16
26
 
17
27
  attr_accessor :value_schemas
@@ -21,7 +31,7 @@ module JSONSkooma
21
31
  def wrap_value(value)
22
32
  return super unless value.is_a?(Hash) || value.is_a?(TrueClass) || value.is_a?(FalseClass)
23
33
 
24
- (self.class.schema_value_class || JSONSchema).new(
34
+ (self.class.schema_value_class || ValueSchemas.default_schema_class).new(
25
35
  value,
26
36
  parent: parent_schema,
27
37
  key: key,
@@ -46,7 +56,7 @@ module JSONSkooma
46
56
  value,
47
57
  parent: parent_schema,
48
58
  key: key,
49
- item_class: self.class.schema_value_class || JSONSchema,
59
+ item_class: self.class.schema_value_class || ValueSchemas.default_schema_class,
50
60
  registry: parent_schema.registry,
51
61
  cache_id: parent_schema.cache_id
52
62
  )
@@ -69,7 +79,7 @@ module JSONSkooma
69
79
  value,
70
80
  parent: parent_schema,
71
81
  key: key,
72
- item_class: self.class.schema_value_class || JSONSchema,
82
+ item_class: self.class.schema_value_class || ValueSchemas.default_schema_class,
73
83
  registry: parent_schema.registry,
74
84
  cache_id: parent_schema.cache_id
75
85
  )
@@ -3,6 +3,19 @@
3
3
  module JSONSkooma
4
4
  class RegistryError < Error; end
5
5
 
6
+ # Raised when a ref resolves to an object that is not the class the caller expected.
7
+ # Extension points (e.g. typed OpenAPI refs) can rescue this and load the subtree
8
+ # as the right class without matching on error message text.
9
+ class UnexpectedSchemaClassError < RegistryError
10
+ attr_reader :uri, :expected_class
11
+
12
+ def initialize(uri, expected_class)
13
+ @uri = uri
14
+ @expected_class = expected_class
15
+ super("The object referenced by #{uri} is not #{expected_class}")
16
+ end
17
+ end
18
+
6
19
  class Registry
7
20
  class << self
8
21
  attr_accessor :registries
@@ -79,7 +92,7 @@ module JSONSkooma
79
92
  schema = JSONPointer.new(uri.fragment).eval(schema) if uri.fragment
80
93
  return schema if schema.is_a?(expected_class)
81
94
 
82
- raise RegistryError, "The object referenced by #{uri} is not #{expected_class}"
95
+ raise UnexpectedSchemaClassError.new(uri, expected_class)
83
96
  end
84
97
 
85
98
  def add_metaschema(uri, default_core_vocabulary_uri = nil, *default_vocabulary_uris)
@@ -113,8 +126,6 @@ module JSONSkooma
113
126
  @uri_sources[uri] = source
114
127
  end
115
128
 
116
- private
117
-
118
129
  def load_json(uri)
119
130
  candidates = @uri_sources
120
131
  .select { |source_uri| uri.to_s.start_with?(source_uri) }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSONSkooma
4
- VERSION = "0.2.5"
4
+ VERSION = "0.2.7"
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json_skooma
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Svyatoslav Kryukov
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-12-25 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: bigdecimal
@@ -236,7 +235,6 @@ metadata:
236
235
  homepage_uri: https://github.com/skryukov/json_skooma
237
236
  source_code_uri: https://github.com/skryukov/json_skooma
238
237
  rubygems_mfa_required: 'true'
239
- post_install_message:
240
238
  rdoc_options: []
241
239
  require_paths:
242
240
  - lib
@@ -251,8 +249,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
251
249
  - !ruby/object:Gem::Version
252
250
  version: '0'
253
251
  requirements: []
254
- rubygems_version: 3.5.23
255
- signing_key:
252
+ rubygems_version: 4.0.6
256
253
  specification_version: 4
257
254
  summary: I bring some sugar for your JSONs.
258
255
  test_files: []