jsi 0.6.0 → 0.8.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 +4 -4
- data/.yardopts +6 -1
- data/CHANGELOG.md +33 -0
- data/LICENSE.md +1 -1
- data/README.md +29 -23
- data/jsi.gemspec +29 -0
- data/lib/jsi/base/mutability.rb +44 -0
- data/lib/jsi/base/node.rb +348 -0
- data/lib/jsi/base.rb +497 -339
- data/lib/jsi/jsi_coder.rb +19 -17
- data/lib/jsi/metaschema_node/bootstrap_schema.rb +61 -26
- data/lib/jsi/metaschema_node.rb +161 -133
- data/lib/jsi/ptr.rb +80 -47
- data/lib/jsi/schema/application/child_application/contains.rb +11 -2
- data/lib/jsi/schema/application/child_application/draft04.rb +0 -1
- data/lib/jsi/schema/application/child_application/draft06.rb +0 -1
- data/lib/jsi/schema/application/child_application/draft07.rb +0 -1
- data/lib/jsi/schema/application/child_application/items.rb +3 -3
- data/lib/jsi/schema/application/child_application/properties.rb +3 -3
- data/lib/jsi/schema/application/child_application.rb +0 -27
- data/lib/jsi/schema/application/inplace_application/dependencies.rb +1 -1
- data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -1
- data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -1
- data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -1
- data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +3 -3
- data/lib/jsi/schema/application/inplace_application/ref.rb +2 -2
- data/lib/jsi/schema/application/inplace_application/someof.rb +26 -11
- data/lib/jsi/schema/application/inplace_application.rb +0 -32
- data/lib/jsi/schema/draft04.rb +0 -1
- data/lib/jsi/schema/draft06.rb +0 -1
- data/lib/jsi/schema/draft07.rb +0 -1
- data/lib/jsi/schema/ref.rb +46 -19
- data/lib/jsi/schema/schema_ancestor_node.rb +69 -66
- data/lib/jsi/schema/validation/array.rb +3 -3
- data/lib/jsi/schema/validation/const.rb +1 -1
- data/lib/jsi/schema/validation/contains.rb +2 -2
- data/lib/jsi/schema/validation/dependencies.rb +1 -1
- data/lib/jsi/schema/validation/draft04/minmax.rb +8 -6
- data/lib/jsi/schema/validation/draft04.rb +0 -2
- data/lib/jsi/schema/validation/draft06.rb +0 -2
- data/lib/jsi/schema/validation/draft07.rb +0 -2
- data/lib/jsi/schema/validation/enum.rb +1 -1
- data/lib/jsi/schema/validation/ifthenelse.rb +5 -5
- data/lib/jsi/schema/validation/items.rb +7 -7
- data/lib/jsi/schema/validation/not.rb +1 -1
- data/lib/jsi/schema/validation/numeric.rb +5 -5
- data/lib/jsi/schema/validation/object.rb +2 -2
- data/lib/jsi/schema/validation/pattern.rb +2 -2
- data/lib/jsi/schema/validation/properties.rb +7 -7
- data/lib/jsi/schema/validation/property_names.rb +1 -1
- data/lib/jsi/schema/validation/ref.rb +2 -2
- data/lib/jsi/schema/validation/required.rb +1 -1
- data/lib/jsi/schema/validation/someof.rb +3 -3
- data/lib/jsi/schema/validation/string.rb +2 -2
- data/lib/jsi/schema/validation/type.rb +1 -1
- data/lib/jsi/schema/validation.rb +1 -3
- data/lib/jsi/schema.rb +443 -226
- data/lib/jsi/schema_classes.rb +241 -147
- data/lib/jsi/schema_registry.rb +78 -19
- data/lib/jsi/schema_set.rb +114 -28
- data/lib/jsi/simple_wrap.rb +18 -4
- data/lib/jsi/util/private/attr_struct.rb +141 -0
- data/lib/jsi/util/private/memo_map.rb +75 -0
- data/lib/jsi/util/private.rb +185 -0
- data/lib/jsi/{typelike_modules.rb → util/typelike.rb} +79 -105
- data/lib/jsi/util.rb +157 -153
- data/lib/jsi/validation/error.rb +4 -0
- data/lib/jsi/validation/result.rb +18 -32
- data/lib/jsi/version.rb +1 -1
- data/lib/jsi.rb +65 -39
- data/lib/schemas/json-schema.org/draft-04/schema.rb +160 -3
- data/lib/schemas/json-schema.org/draft-06/schema.rb +162 -3
- data/lib/schemas/json-schema.org/draft-07/schema.rb +189 -3
- metadata +27 -11
- data/lib/jsi/metaschema.rb +0 -7
- data/lib/jsi/pathed_node.rb +0 -116
- data/lib/jsi/schema/validation/core.rb +0 -39
- data/lib/jsi/util/attr_struct.rb +0 -106
data/lib/jsi/schema.rb
CHANGED
@@ -1,19 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module JSI
|
4
|
-
# JSI::Schema is a module which extends instances which represent JSON schemas.
|
4
|
+
# JSI::Schema is a module which extends {JSI::Base} instances which represent JSON schemas.
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
6
|
+
# This module is included on the {Schema#jsi_schema_module JSI Schema module} of any schema
|
7
|
+
# that describes other schemas, i.e. is a meta-schema (a {Schema::MetaSchema}).
|
8
|
+
# Therefore, any JSI instance described by a schema which is a {Schema::MetaSchema} is
|
9
|
+
# a schema and is extended by this module.
|
10
|
+
#
|
11
|
+
# The content of an instance which is a JSI::Schema (referred to in this context as schema_content) is
|
12
|
+
# typically a Hash (JSON object) or a boolean.
|
8
13
|
module Schema
|
9
14
|
autoload :Application, 'jsi/schema/application'
|
10
15
|
autoload :Validation, 'jsi/schema/validation'
|
16
|
+
|
11
17
|
autoload :Issue, 'jsi/schema/issue'
|
18
|
+
autoload :Ref, 'jsi/schema/ref'
|
12
19
|
|
13
20
|
autoload :SchemaAncestorNode, 'jsi/schema/schema_ancestor_node'
|
14
21
|
|
15
|
-
autoload :Ref, 'jsi/schema/ref'
|
16
|
-
|
17
22
|
autoload :Draft04, 'jsi/schema/draft04'
|
18
23
|
autoload :Draft06, 'jsi/schema/draft06'
|
19
24
|
autoload :Draft07, 'jsi/schema/draft07'
|
@@ -34,7 +39,7 @@ module JSI
|
|
34
39
|
# the contents of a $id keyword whose value is a string, or nil
|
35
40
|
# @return [#to_str, nil]
|
36
41
|
def id
|
37
|
-
if
|
42
|
+
if keyword?('$id') && schema_content['$id'].respond_to?(:to_str)
|
38
43
|
schema_content['$id']
|
39
44
|
else
|
40
45
|
nil
|
@@ -47,7 +52,7 @@ module JSI
|
|
47
52
|
# the contents of an `id` keyword whose value is a string, or nil
|
48
53
|
# @return [#to_str, nil]
|
49
54
|
def id
|
50
|
-
if
|
55
|
+
if keyword?('id') && schema_content['id'].respond_to?(:to_str)
|
51
56
|
schema_content['id']
|
52
57
|
else
|
53
58
|
nil
|
@@ -62,7 +67,7 @@ module JSI
|
|
62
67
|
# @return [Addressable::URI, nil]
|
63
68
|
def id_without_fragment
|
64
69
|
if id
|
65
|
-
id_uri =
|
70
|
+
id_uri = Util.uri(id)
|
66
71
|
if id_uri.merge(fragment: nil).empty?
|
67
72
|
# fragment-only id is just an anchor
|
68
73
|
# e.g. #foo
|
@@ -74,7 +79,7 @@ module JSI
|
|
74
79
|
elsif id_uri.fragment == ''
|
75
80
|
# empty fragment
|
76
81
|
# e.g. http://json-schema.org/draft-07/schema#
|
77
|
-
id_uri.merge(fragment: nil)
|
82
|
+
id_uri.merge(fragment: nil).freeze
|
78
83
|
elsif jsi_schema_base_uri && jsi_schema_base_uri.join(id_uri).merge(fragment: nil) == jsi_schema_base_uri
|
79
84
|
# the id, resolved against the base uri, consists of the base uri plus an anchor fragment.
|
80
85
|
# so there's no non-fragment id.
|
@@ -83,7 +88,7 @@ module JSI
|
|
83
88
|
nil
|
84
89
|
else
|
85
90
|
# e.g. http://localhost:1234/bar#foo
|
86
|
-
id_uri.merge(fragment: nil)
|
91
|
+
id_uri.merge(fragment: nil).freeze
|
87
92
|
end
|
88
93
|
else
|
89
94
|
nil
|
@@ -94,7 +99,7 @@ module JSI
|
|
94
99
|
# @return [String]
|
95
100
|
def anchor
|
96
101
|
if id
|
97
|
-
id_uri =
|
102
|
+
id_uri = Util.uri(id)
|
98
103
|
if id_uri.fragment == ''
|
99
104
|
nil
|
100
105
|
else
|
@@ -128,141 +133,235 @@ module JSI
|
|
128
133
|
end
|
129
134
|
end
|
130
135
|
|
131
|
-
# JSI
|
132
|
-
# extends a JSI::Schema instance and indicates that JSIs which instantiate the schema
|
133
|
-
# are themselves also schemas.
|
136
|
+
# This module extends any JSI Schema that is a meta-schema, i.e. it describes schemas.
|
134
137
|
#
|
135
|
-
#
|
138
|
+
# Examples of a meta-schema include the JSON Schema meta-schemas and
|
136
139
|
# the OpenAPI schema definition which describes "A deterministic version of a JSON Schema object."
|
137
|
-
|
138
|
-
|
140
|
+
#
|
141
|
+
# Meta-schemas include {JSI::Schema} in their
|
142
|
+
# {Schema#jsi_schema_module JSI Schema module}, so for a schema which is an instance of
|
143
|
+
# JSI::Schema::MetaSchema, instances of that schema are instances of {JSI::Schema} and are schemas.
|
144
|
+
#
|
145
|
+
# A schema is indicated as describing other schemas using the {Schema#describes_schema!} method.
|
146
|
+
module MetaSchema
|
147
|
+
# @return [Set<Module>]
|
148
|
+
attr_reader(:schema_implementation_modules)
|
149
|
+
|
150
|
+
# Instantiates the given schema content as a JSI Schema.
|
151
|
+
#
|
152
|
+
# By default, the schema will be registered with the {JSI.schema_registry}.
|
153
|
+
# This can be controlled by params `register` and `schema_registry`.
|
139
154
|
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
# given instance.
|
155
|
+
# By default, the `schema_content` will have any Symbol keys of Hashes replaced with Strings
|
156
|
+
# (recursively through the document). This is controlled by the param `stringify_symbol_keys`.
|
143
157
|
#
|
144
|
-
#
|
158
|
+
# @param schema_content an object to be instantiated as a JSI Schema - typically a Hash
|
159
|
+
# @param uri [#to_str, Addressable::URI] The retrieval URI of the schema document.
|
160
|
+
# If specified, the root schema will be identified by this URI, in addition
|
161
|
+
# to any absolute URI declared with an id keyword, for resolution in the `schema_registry`.
|
145
162
|
#
|
146
|
-
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
163
|
+
# It is rare that this needs to be specified. Most schemas, if they use absolute URIs, will
|
164
|
+
# use the `$id` keyword (`id` in draft 4) to specify this. A different retrieval URI is useful
|
165
|
+
# in unusual cases:
|
166
|
+
#
|
167
|
+
# - A schema in the document uses relative URIs for `$id` or `$ref` without an absolute id in an
|
168
|
+
# ancestor schema - these will be resolved relative to this URI
|
169
|
+
# - Another schema refers with `$ref` to the schema being instantiated by this retrieval URI,
|
170
|
+
# rather than an id declared in the schema - the schema is resolvable by this URI in the
|
171
|
+
# `schema_registry`.
|
172
|
+
# @param register [Boolean] Whether the instantiated schema and any subschemas with absolute URIs
|
173
|
+
# will be registered in the schema registry indicated by param `schema_registry`.
|
174
|
+
# @param schema_registry [SchemaRegistry, nil] The registry this schema will use.
|
175
|
+
#
|
176
|
+
# - The schema and subschemas will be registered here with any declared URI,
|
177
|
+
# unless the `register` param is false.
|
178
|
+
# - References from within the schema (typically from `$ref` keywords) are resolved using this registry.
|
179
|
+
# @param stringify_symbol_keys [Boolean] Whether the schema content will have any Symbol keys of Hashes
|
180
|
+
# replaced with Strings (recursively through the document).
|
181
|
+
# Replacement is done on a copy; the given schema content is not modified.
|
182
|
+
# @param to_immutable (see SchemaSet#new_jsi)
|
183
|
+
# @yield If a block is given, it is evaluated in the context of the schema's JSI schema module
|
184
|
+
# using [Module#module_exec](https://ruby-doc.org/core/Module.html#method-i-module_exec).
|
185
|
+
# @return [JSI::Base subclass + JSI::Schema] a JSI which is a {JSI::Schema} whose content comes from
|
186
|
+
# the given `schema_content` and whose schemas are this schema's inplace applicators.
|
152
187
|
def new_schema(schema_content,
|
153
|
-
uri: nil
|
188
|
+
uri: nil,
|
189
|
+
register: true,
|
190
|
+
schema_registry: JSI.schema_registry,
|
191
|
+
stringify_symbol_keys: true,
|
192
|
+
to_immutable: DEFAULT_CONTENT_TO_IMMUTABLE,
|
193
|
+
&block
|
154
194
|
)
|
155
|
-
schema_jsi = new_jsi(
|
195
|
+
schema_jsi = new_jsi(schema_content,
|
156
196
|
uri: uri,
|
197
|
+
register: register,
|
198
|
+
schema_registry: schema_registry,
|
199
|
+
stringify_symbol_keys: stringify_symbol_keys,
|
200
|
+
to_immutable: to_immutable,
|
157
201
|
)
|
158
|
-
|
202
|
+
|
203
|
+
schema_jsi.jsi_schema_module_exec(&block) if block
|
204
|
+
|
159
205
|
schema_jsi
|
160
206
|
end
|
161
207
|
|
162
|
-
#
|
208
|
+
# Instantiates the given schema content as a JSI Schema, passing all params to
|
209
|
+
# {Schema::MetaSchema#new_schema}, and returns its {Schema#jsi_schema_module JSI Schema Module}.
|
163
210
|
#
|
164
|
-
#
|
165
|
-
|
166
|
-
|
167
|
-
# @return [Module, JSI::SchemaModule] the JSI Schema Module of the schema
|
168
|
-
def new_schema_module(schema_content, **kw)
|
169
|
-
new_schema(schema_content, **kw).jsi_schema_module
|
211
|
+
# @return [JSI::SchemaModule] the JSI Schema Module of the instantiated schema
|
212
|
+
def new_schema_module(schema_content, **kw, &block)
|
213
|
+
new_schema(schema_content, **kw, &block).jsi_schema_module
|
170
214
|
end
|
171
215
|
end
|
172
216
|
|
173
217
|
class << self
|
174
|
-
|
218
|
+
def extended(o)
|
219
|
+
super
|
220
|
+
o.send(:jsi_schema_initialize)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
class << self
|
226
|
+
# An application-wide default meta-schema set by {default_metaschema=}, used by {JSI.new_schema}
|
227
|
+
# to instantiate schemas that do not specify their meta-schema using a `$schema` property.
|
175
228
|
#
|
176
|
-
# @return [nil,
|
229
|
+
# @return [nil, Base + Schema + Schema::MetaSchema]
|
177
230
|
def default_metaschema
|
178
|
-
|
179
|
-
return JSONSchemaOrgDraft07
|
231
|
+
@default_metaschema
|
180
232
|
end
|
181
233
|
|
182
|
-
#
|
234
|
+
# Sets {default_metaschema} to a schema indicated by the given param.
|
235
|
+
#
|
236
|
+
# @param default_metaschema [Schema::MetaSchema, SchemaModule::MetaSchemaModule, #to_str, nil]
|
237
|
+
# Indicates the default meta-schema.
|
238
|
+
# This may be a meta-schema or a meta-schema's schema module (e.g. `JSI::JSONSchemaDraft07`),
|
239
|
+
# or a URI (as would be in a `$schema` keyword).
|
183
240
|
#
|
184
|
-
#
|
185
|
-
# metaschema's schema module (e.g. `JSI::JSONSchemaOrgDraft07`).
|
241
|
+
# `nil` to unset.
|
186
242
|
def default_metaschema=(default_metaschema)
|
187
|
-
|
188
|
-
raise(TypeError, "given default_metaschema does not respond to #new_schema")
|
189
|
-
end
|
190
|
-
@default_metaschema = default_metaschema
|
243
|
+
@default_metaschema = default_metaschema.nil? ? nil : ensure_metaschema(default_metaschema)
|
191
244
|
end
|
192
245
|
|
193
|
-
#
|
246
|
+
# Instantiates the given schema content as a JSI Schema.
|
247
|
+
#
|
248
|
+
# The meta-schema that describes the schema must be indicated:
|
194
249
|
#
|
195
|
-
#
|
250
|
+
# - If the schema object has a `$schema` property, that URI is resolved using the `schema_registry`
|
251
|
+
# param (by default {JSI.schema_registry}), and that meta-schema is used. For example:
|
252
|
+
#
|
253
|
+
# ```ruby
|
254
|
+
# JSI.new_schema({
|
255
|
+
# "$schema" => "http://json-schema.org/draft-07/schema#",
|
256
|
+
# "properties" => ...,
|
257
|
+
# })
|
258
|
+
# ```
|
196
259
|
#
|
197
|
-
# - if the schema object has a `$schema` property, that URI is resolved using the {JSI.schema_registry},
|
198
|
-
# and that metaschema is used.
|
199
260
|
# - if no `$schema` property is present, the `default_metaschema` param is used, if the caller
|
200
|
-
# specifies it.
|
261
|
+
# specifies it. For example:
|
262
|
+
#
|
263
|
+
# ```ruby
|
264
|
+
# JSI.new_schema({"properties" => ...}, default_metaschema: JSI::JSONSchemaDraft07)
|
265
|
+
# ```
|
266
|
+
#
|
201
267
|
# - if no `default_metaschema` param is specified, the application-wide default
|
202
|
-
# {JSI
|
203
|
-
# if the application has set it.
|
268
|
+
# {JSI.default_metaschema JSI.default_metaschema} is used,
|
269
|
+
# if the application has set it. For example:
|
204
270
|
#
|
205
|
-
#
|
271
|
+
# ```ruby
|
272
|
+
# JSI.default_metaschema = JSI::JSONSchemaDraft07
|
273
|
+
# JSI.new_schema({"properties" => ...})
|
274
|
+
# ```
|
206
275
|
#
|
207
|
-
#
|
208
|
-
# passing the `default_metaschema` param is to use `.new_schema` on the metaschema or its module, e.g.
|
209
|
-
# `JSI::JSONSchemaOrgDraft07.new_schema(my_schema_object)`
|
276
|
+
# An ArgumentError is raised if none of these indicates a meta-schema to use.
|
210
277
|
#
|
211
|
-
# if
|
212
|
-
#
|
213
|
-
# Schema#
|
278
|
+
# Note that if you are instantiating a schema known to have no `$schema` property, an alternative to
|
279
|
+
# specifying a `default_metaschema` is to call `new_schema` on the
|
280
|
+
# {Schema::MetaSchema#new_schema meta-schema} or its
|
281
|
+
# {SchemaModule::MetaSchemaModule#new_schema schema module}, e.g.
|
282
|
+
# `JSI::JSONSchemaDraft07.new_schema(my_schema_content)`
|
214
283
|
#
|
215
|
-
# @param
|
216
|
-
#
|
217
|
-
#
|
218
|
-
#
|
219
|
-
# a
|
220
|
-
#
|
221
|
-
# @
|
222
|
-
|
284
|
+
# @param schema_content (see Schema::MetaSchema#new_schema)
|
285
|
+
# @param default_metaschema [Schema::MetaSchema, SchemaModule::MetaSchemaModule, #to_str]
|
286
|
+
# Indicates the meta-schema to use if the given `schema_content` does not have a `$schema` property.
|
287
|
+
# This may be a meta-schema or a meta-schema's schema module (e.g. `JSI::JSONSchemaDraft07`),
|
288
|
+
# or a URI (as would be in a `$schema` keyword).
|
289
|
+
# @param uri (see Schema::MetaSchema#new_schema)
|
290
|
+
# @param register (see Schema::MetaSchema#new_schema)
|
291
|
+
# @param schema_registry (see Schema::MetaSchema#new_schema)
|
292
|
+
# @param stringify_symbol_keys (see Schema::MetaSchema#new_schema)
|
293
|
+
# @param to_immutable (see Schema::DescribesSchema#new_schema)
|
294
|
+
# @yield (see Schema::MetaSchema#new_schema)
|
295
|
+
# @return [JSI::Base subclass + JSI::Schema] a JSI which is a {JSI::Schema} whose content comes from
|
296
|
+
# the given `schema_content` and whose schemas are inplace applicators of the indicated meta-schema
|
297
|
+
def new_schema(schema_content,
|
298
|
+
default_metaschema: nil,
|
299
|
+
# params of Schema::MetaSchema#new_schema have their default values repeated here. delegating in a splat
|
300
|
+
# would remove repetition, but yard doesn't display delegated defaults with its (see X) directive.
|
301
|
+
uri: nil,
|
302
|
+
register: true,
|
303
|
+
schema_registry: JSI.schema_registry,
|
304
|
+
stringify_symbol_keys: true,
|
305
|
+
to_immutable: DEFAULT_CONTENT_TO_IMMUTABLE,
|
306
|
+
&block
|
307
|
+
)
|
308
|
+
new_schema_params = {
|
309
|
+
uri: uri,
|
310
|
+
register: register,
|
311
|
+
schema_registry: schema_registry,
|
312
|
+
stringify_symbol_keys: stringify_symbol_keys,
|
313
|
+
to_immutable: to_immutable,
|
314
|
+
}
|
223
315
|
default_metaschema_new_schema = -> {
|
224
|
-
default_metaschema
|
225
|
-
|
316
|
+
default_metaschema = if default_metaschema
|
317
|
+
Schema.ensure_metaschema(default_metaschema, name: "default_metaschema")
|
318
|
+
elsif self.default_metaschema
|
319
|
+
self.default_metaschema
|
320
|
+
else
|
226
321
|
raise(ArgumentError, [
|
227
|
-
"
|
228
|
-
"
|
229
|
-
"JSI
|
230
|
-
"
|
231
|
-
"
|
322
|
+
"When instantiating a schema with no `$schema` property, you must specify its meta-schema by one of these methods:",
|
323
|
+
"- pass the `default_metaschema` param to this method",
|
324
|
+
" e.g.: JSI.new_schema(..., default_metaschema: JSI::JSONSchemaDraft07)",
|
325
|
+
"- invoke `new_schema` on the appropriate meta-schema or its schema module",
|
326
|
+
" e.g.: JSI::JSONSchemaDraft07.new_schema(...)",
|
327
|
+
"- set JSI.default_metaschema to an application-wide default meta-schema initially",
|
328
|
+
" e.g.: JSI.default_metaschema = JSI::JSONSchemaDraft07",
|
329
|
+
"instantiating schema_content: #{schema_content.pretty_inspect.chomp}",
|
232
330
|
].join("\n"))
|
233
331
|
end
|
234
|
-
|
235
|
-
raise(TypeError, "given default_metaschema does not respond to #new_schema: #{default_metaschema.pretty_inspect.chomp}")
|
236
|
-
end
|
237
|
-
default_metaschema.new_schema(schema_object, **kw)
|
332
|
+
default_metaschema.new_schema(schema_content, **new_schema_params, &block)
|
238
333
|
}
|
239
|
-
if
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
334
|
+
if schema_content.is_a?(Schema)
|
335
|
+
raise(TypeError, [
|
336
|
+
"Given schema_content is already a JSI::Schema. It cannot be instantiated as the content of a schema.",
|
337
|
+
"given: #{schema_content.pretty_inspect.chomp}",
|
338
|
+
].join("\n"))
|
339
|
+
elsif schema_content.is_a?(JSI::Base)
|
340
|
+
raise(TypeError, [
|
341
|
+
"Given schema_content is a JSI::Base. It cannot be instantiated as the content of a schema.",
|
342
|
+
"given: #{schema_content.pretty_inspect.chomp}",
|
343
|
+
].join("\n"))
|
344
|
+
elsif schema_content.respond_to?(:to_hash)
|
345
|
+
id = schema_content['$schema'] || stringify_symbol_keys && schema_content[:'$schema']
|
346
|
+
if id
|
347
|
+
unless id.respond_to?(:to_str)
|
348
|
+
raise(ArgumentError, "given schema_content keyword `$schema` is not a string")
|
248
349
|
end
|
249
|
-
metaschema.
|
350
|
+
metaschema = Schema.ensure_metaschema(id, name: '$schema', schema_registry: schema_registry)
|
351
|
+
metaschema.new_schema(schema_content, **new_schema_params, &block)
|
250
352
|
else
|
251
353
|
default_metaschema_new_schema.call
|
252
354
|
end
|
253
|
-
elsif [true, false].include?(schema_object)
|
254
|
-
default_metaschema_new_schema.call
|
255
355
|
else
|
256
|
-
|
356
|
+
default_metaschema_new_schema.call
|
257
357
|
end
|
258
358
|
end
|
359
|
+
end
|
259
360
|
|
260
|
-
|
261
|
-
alias_method :new, :new_schema
|
262
|
-
|
263
|
-
# @deprecated
|
264
|
-
alias_method :from_object, :new_schema
|
361
|
+
self.default_metaschema = nil
|
265
362
|
|
363
|
+
module Schema
|
364
|
+
class << self
|
266
365
|
# ensure the given object is a JSI Schema
|
267
366
|
#
|
268
367
|
# @param schema [Object] the thing the caller wishes to ensure is a Schema
|
@@ -274,19 +373,25 @@ module JSI
|
|
274
373
|
if schema.is_a?(Schema)
|
275
374
|
schema
|
276
375
|
else
|
277
|
-
if reinstantiate_as
|
376
|
+
if reinstantiate_as && schema.is_a?(JSI::Base)
|
278
377
|
# TODO warn; behavior is undefined and I hate this implementation
|
279
378
|
|
280
|
-
|
379
|
+
result_schema_indicated_schemas = SchemaSet.new(schema.jsi_indicated_schemas + reinstantiate_as)
|
380
|
+
result_schema_applied_schemas = result_schema_indicated_schemas.inplace_applicator_schemas(schema.jsi_node_content)
|
281
381
|
|
282
|
-
result_schema_class = JSI::SchemaClasses.class_for_schemas(
|
382
|
+
result_schema_class = JSI::SchemaClasses.class_for_schemas(result_schema_applied_schemas,
|
383
|
+
includes: SchemaClasses.includes_for(schema.jsi_node_content),
|
384
|
+
mutable: schema.jsi_mutable?,
|
385
|
+
)
|
283
386
|
|
284
|
-
result_schema_class.new(
|
285
|
-
jsi_document: schema.jsi_document,
|
387
|
+
result_schema_class.new(schema.jsi_document,
|
286
388
|
jsi_ptr: schema.jsi_ptr,
|
287
|
-
|
389
|
+
jsi_indicated_schemas: result_schema_indicated_schemas,
|
288
390
|
jsi_schema_base_uri: schema.jsi_schema_base_uri,
|
289
391
|
jsi_schema_resource_ancestors: schema.jsi_schema_resource_ancestors,
|
392
|
+
jsi_schema_registry: schema.jsi_schema_registry,
|
393
|
+
jsi_content_to_immutable: schema.jsi_content_to_immutable,
|
394
|
+
jsi_root_node: schema.jsi_ptr.root? ? nil : schema.jsi_root_node, # bad
|
290
395
|
)
|
291
396
|
else
|
292
397
|
raise(NotASchemaError, [
|
@@ -296,21 +401,62 @@ module JSI
|
|
296
401
|
end
|
297
402
|
end
|
298
403
|
end
|
404
|
+
|
405
|
+
# Ensures the given param identifies a meta-schema and returns that meta-schema.
|
406
|
+
#
|
407
|
+
# @api private
|
408
|
+
# @param metaschema [Schema::MetaSchema, SchemaModule::MetaSchemaModule, #to_str]
|
409
|
+
# @raise [TypeError] if the param does not indicate a meta-schema
|
410
|
+
# @return [Base + Schema + Schema::MetaSchema]
|
411
|
+
def ensure_metaschema(metaschema, name: nil, schema_registry: JSI.schema_registry)
|
412
|
+
if metaschema.respond_to?(:to_str)
|
413
|
+
schema = Schema::Ref.new(metaschema, schema_registry: schema_registry).deref_schema
|
414
|
+
if !schema.describes_schema?
|
415
|
+
raise(TypeError, [name, "URI indicates a schema that is not a meta-schema: #{metaschema.pretty_inspect.chomp}"].compact.join(" "))
|
416
|
+
end
|
417
|
+
schema
|
418
|
+
elsif metaschema.is_a?(SchemaModule::MetaSchemaModule)
|
419
|
+
metaschema.schema
|
420
|
+
elsif metaschema.is_a?(Schema::MetaSchema)
|
421
|
+
metaschema
|
422
|
+
else
|
423
|
+
raise(TypeError, "#{name || "param"} does not indicate a meta-schema: #{metaschema.pretty_inspect.chomp}")
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
|
429
|
+
def initialize(*)
|
430
|
+
super
|
431
|
+
jsi_schema_initialize
|
432
|
+
end
|
433
|
+
else
|
434
|
+
def initialize(*, **)
|
435
|
+
super
|
436
|
+
jsi_schema_initialize
|
437
|
+
end
|
299
438
|
end
|
300
439
|
|
301
440
|
# the underlying JSON data used to instantiate this JSI::Schema.
|
302
|
-
# this is an alias for
|
441
|
+
# this is an alias for {Base#jsi_node_content}, named for clarity in the context of working with
|
303
442
|
# a schema.
|
304
443
|
def schema_content
|
305
444
|
jsi_node_content
|
306
445
|
end
|
307
446
|
|
447
|
+
# does this schema contain the given keyword?
|
448
|
+
# @return [Boolean]
|
449
|
+
def keyword?(keyword)
|
450
|
+
schema_content = jsi_node_content
|
451
|
+
schema_content.respond_to?(:to_hash) && schema_content.key?(keyword)
|
452
|
+
end
|
453
|
+
|
308
454
|
# the URI of this schema, calculated from our `#id`, resolved against our `#jsi_schema_base_uri`
|
309
455
|
# @return [Addressable::URI, nil]
|
310
456
|
def schema_absolute_uri
|
311
457
|
if respond_to?(:id_without_fragment) && id_without_fragment
|
312
458
|
if jsi_schema_base_uri
|
313
|
-
|
459
|
+
jsi_schema_base_uri.join(id_without_fragment).freeze
|
314
460
|
elsif id_without_fragment.absolute?
|
315
461
|
id_without_fragment
|
316
462
|
else
|
@@ -321,7 +467,7 @@ module JSI
|
|
321
467
|
end
|
322
468
|
|
323
469
|
# a nonrelative URI which refers to this schema.
|
324
|
-
# nil if no
|
470
|
+
# `nil` if no ancestor of this schema defines an id.
|
325
471
|
# see {#schema_uris} for all URIs known to refer to this schema.
|
326
472
|
# @return [Addressable::URI, nil]
|
327
473
|
def schema_uri
|
@@ -331,9 +477,11 @@ module JSI
|
|
331
477
|
# nonrelative URIs (that is, absolute, but possibly with a fragment) which refer to this schema
|
332
478
|
# @return [Array<Addressable::URI>]
|
333
479
|
def schema_uris
|
334
|
-
|
480
|
+
@schema_uris_map[]
|
481
|
+
end
|
482
|
+
|
483
|
+
private def schema_uris_compute(**_) # TODO remove **_ eventually (keyword argument compatibility)
|
335
484
|
each_schema_uri.to_a
|
336
|
-
end
|
337
485
|
end
|
338
486
|
|
339
487
|
# see {#schema_uris}
|
@@ -344,22 +492,22 @@ module JSI
|
|
344
492
|
|
345
493
|
yield schema_absolute_uri if schema_absolute_uri
|
346
494
|
|
347
|
-
|
348
|
-
resource.
|
495
|
+
ancestor_schemas = jsi_subschema_resource_ancestors.reverse_each.select do |resource|
|
496
|
+
resource.schema_absolute_uri
|
349
497
|
end
|
350
498
|
|
351
|
-
anchored =
|
352
|
-
|
499
|
+
anchored = respond_to?(:anchor) ? anchor : nil
|
500
|
+
ancestor_schemas.each do |ancestor_schema|
|
353
501
|
if anchored
|
354
|
-
if
|
355
|
-
yield
|
502
|
+
if ancestor_schema.jsi_anchor_subschema(anchor) == self
|
503
|
+
yield(ancestor_schema.schema_absolute_uri.merge(fragment: anchor).freeze)
|
356
504
|
else
|
357
505
|
anchored = false
|
358
506
|
end
|
359
507
|
end
|
360
508
|
|
361
|
-
relative_ptr =
|
362
|
-
yield
|
509
|
+
relative_ptr = jsi_ptr.relative_to(ancestor_schema.jsi_ptr)
|
510
|
+
yield(ancestor_schema.schema_absolute_uri.merge(fragment: relative_ptr.fragment).freeze)
|
363
511
|
end
|
364
512
|
|
365
513
|
nil
|
@@ -368,9 +516,6 @@ module JSI
|
|
368
516
|
# a module which extends all instances of this schema. this may be opened by the application to add
|
369
517
|
# methods to schema instances.
|
370
518
|
#
|
371
|
-
# this module includes accessor methods for object property names this schema
|
372
|
-
# describes (see {#described_object_property_names}). these accessors wrap {Base#[]} and {Base#[]=}.
|
373
|
-
#
|
374
519
|
# some functionality is also defined on the module itself (its singleton class, not for its instances):
|
375
520
|
#
|
376
521
|
# - the module is extended with {JSI::SchemaModule}, which defines .new_jsi to instantiate instances
|
@@ -380,7 +525,7 @@ module JSI
|
|
380
525
|
# as `schema.items.jsi_schema_module`.
|
381
526
|
# - method .schema which returns this schema.
|
382
527
|
#
|
383
|
-
# @return [
|
528
|
+
# @return [SchemaModule]
|
384
529
|
def jsi_schema_module
|
385
530
|
JSI::SchemaClasses.module_for_schema(self)
|
386
531
|
end
|
@@ -395,56 +540,79 @@ module JSI
|
|
395
540
|
jsi_schema_module.module_exec(*a, **kw, &block)
|
396
541
|
end
|
397
542
|
|
398
|
-
#
|
399
|
-
|
400
|
-
|
401
|
-
end
|
402
|
-
|
403
|
-
# instantiates the given instance as a JSI::Base class for schemas matched from this schema to the
|
404
|
-
# instance.
|
543
|
+
# Instantiates a new JSI whose content comes from the given `instance` param.
|
544
|
+
# This schema indicates the schemas of the JSI - its schemas are inplace
|
545
|
+
# applicators of this schema which apply to the given instance.
|
405
546
|
#
|
406
|
-
# @param
|
407
|
-
# @
|
408
|
-
#
|
409
|
-
|
410
|
-
def new_jsi(instance,
|
411
|
-
**kw
|
412
|
-
)
|
547
|
+
# @param (see SchemaSet#new_jsi)
|
548
|
+
# @return [JSI::Base subclass] a JSI whose content comes from the given instance and whose schemas are
|
549
|
+
# inplace applicators of this schema.
|
550
|
+
def new_jsi(instance, **kw)
|
413
551
|
SchemaSet[self].new_jsi(instance, **kw)
|
414
552
|
end
|
415
553
|
|
416
|
-
#
|
554
|
+
# @param keyword schema keyword e.g. "$ref", "$schema"
|
555
|
+
# @return [Schema::Ref]
|
556
|
+
def schema_ref(keyword = "$ref")
|
557
|
+
raise(ArgumentError, "keyword not present: #{keyword}") unless keyword?(keyword)
|
558
|
+
@schema_ref_map[keyword: keyword, value: schema_content[keyword]]
|
559
|
+
end
|
560
|
+
|
561
|
+
# Does this schema itself describe a schema? I.e. is this schema a meta-schema?
|
417
562
|
# @return [Boolean]
|
418
563
|
def describes_schema?
|
419
|
-
|
564
|
+
jsi_schema_module <= JSI::Schema || false
|
420
565
|
end
|
421
566
|
|
422
|
-
#
|
423
|
-
#
|
424
|
-
|
425
|
-
|
426
|
-
return @jsi_schema_instance_modules if instance_variable_defined?(:@jsi_schema_instance_modules)
|
427
|
-
return Set[].freeze
|
567
|
+
# Is this a JSI Schema?
|
568
|
+
# @return [Boolean]
|
569
|
+
def jsi_is_schema?
|
570
|
+
true
|
428
571
|
end
|
429
572
|
|
430
|
-
#
|
573
|
+
# Indicates that this schema describes schemas, i.e. it is a meta-schema.
|
574
|
+
# this schema is extended with {Schema::MetaSchema} and its {#jsi_schema_module} is extended
|
575
|
+
# with {SchemaModule::MetaSchemaModule}, and the JSI Schema Module will include
|
576
|
+
# JSI::Schema and the given modules.
|
431
577
|
#
|
578
|
+
# @param schema_implementation_modules [Enumerable<Module>] modules which implement the functionality of
|
579
|
+
# the schema to extend schemas described by this schema.
|
432
580
|
# @return [void]
|
433
|
-
def
|
434
|
-
|
581
|
+
def describes_schema!(schema_implementation_modules)
|
582
|
+
schema_implementation_modules = Util.ensure_module_set(schema_implementation_modules)
|
583
|
+
|
584
|
+
if describes_schema?
|
585
|
+
# this schema, or one equal to it, has already had describes_schema! called on it.
|
586
|
+
# this is to be avoided, but is not particularly a problem.
|
587
|
+
# it is a bug if it was called different times with different schema_implementation_modules, though.
|
588
|
+
unless jsi_schema_module.schema_implementation_modules == schema_implementation_modules
|
589
|
+
raise(ArgumentError, "this schema already describes a schema with different schema_implementation_modules")
|
590
|
+
end
|
591
|
+
else
|
592
|
+
jsi_schema_module.include(Schema)
|
593
|
+
schema_implementation_modules.each do |mod|
|
594
|
+
jsi_schema_module.include(mod)
|
595
|
+
end
|
596
|
+
jsi_schema_module.extend(SchemaModule::MetaSchemaModule)
|
597
|
+
end
|
598
|
+
|
599
|
+
@schema_implementation_modules = schema_implementation_modules
|
600
|
+
extend(Schema::MetaSchema)
|
601
|
+
|
602
|
+
nil
|
435
603
|
end
|
436
604
|
|
437
605
|
# a resource containing this schema.
|
438
606
|
#
|
439
|
-
#
|
607
|
+
# If any ancestor, or this schema itself, is a schema with an absolute uri (see {#schema_absolute_uri}),
|
440
608
|
# the resource root is the closest schema with an absolute uri.
|
441
609
|
#
|
442
|
-
#
|
443
|
-
#
|
610
|
+
# If no ancestor schema has an absolute uri, the schema_resource_root is the {Base#jsi_root_node document's root node}.
|
611
|
+
# In this case, the resource root may or may not be a schema itself.
|
444
612
|
#
|
445
613
|
# @return [JSI::Base] resource containing this schema
|
446
614
|
def schema_resource_root
|
447
|
-
jsi_subschema_resource_ancestors.
|
615
|
+
jsi_subschema_resource_ancestors.last || jsi_root_node
|
448
616
|
end
|
449
617
|
|
450
618
|
# is this schema the root of a schema resource?
|
@@ -458,73 +626,95 @@ module JSI
|
|
458
626
|
# @param subptr [JSI::Ptr, #to_ary] a relative pointer, or array of tokens, pointing to the subschema
|
459
627
|
# @return [JSI::Schema] the subschema at the location indicated by subptr. self if subptr is empty.
|
460
628
|
def subschema(subptr)
|
461
|
-
subschema_map[Ptr.ary_ptr(subptr)]
|
629
|
+
@subschema_map[subptr: Ptr.ary_ptr(subptr)]
|
462
630
|
end
|
463
631
|
|
464
|
-
private
|
465
|
-
|
466
|
-
def subschema_map
|
467
|
-
jsi_memomap(:subschema) do |subptr|
|
468
|
-
if is_a?(MetaschemaNode::BootstrapSchema)
|
469
|
-
self.class.new(
|
470
|
-
jsi_document,
|
471
|
-
jsi_ptr: jsi_ptr + subptr,
|
472
|
-
jsi_schema_base_uri: jsi_resource_ancestor_uri,
|
473
|
-
)
|
474
|
-
else
|
475
|
-
Schema.ensure_schema(subptr.evaluate(self, as_jsi: true), msg: [
|
632
|
+
private def subschema_compute(subptr: )
|
633
|
+
Schema.ensure_schema(jsi_descendent_node(subptr), msg: [
|
476
634
|
"subschema is not a schema at pointer: #{subptr.pointer}"
|
477
635
|
])
|
478
|
-
end
|
479
|
-
end
|
480
636
|
end
|
481
637
|
|
482
|
-
|
483
|
-
|
484
|
-
# a schema in the same schema resource as this one (see #schema_resource_root) at the given
|
638
|
+
# a schema in the same schema resource as this one (see {#schema_resource_root}) at the given
|
485
639
|
# pointer relative to the root of the schema resource.
|
486
640
|
#
|
487
641
|
# @param ptr [JSI::Ptr, #to_ary] a pointer to a schema from our schema resource root
|
488
642
|
# @return [JSI::Schema] the schema pointed to by ptr
|
489
643
|
def resource_root_subschema(ptr)
|
490
|
-
resource_root_subschema_map[Ptr.ary_ptr(ptr)]
|
644
|
+
@resource_root_subschema_map[ptr: Ptr.ary_ptr(ptr)]
|
491
645
|
end
|
492
646
|
|
493
|
-
private
|
494
|
-
|
495
|
-
|
496
|
-
jsi_memomap(:resource_root_subschema_map) do |ptr|
|
497
|
-
schema = self
|
498
|
-
if schema.is_a?(MetaschemaNode::BootstrapSchema)
|
499
|
-
# BootstrapSchema does not track jsi_schema_resource_ancestors used by #schema_resource_root;
|
500
|
-
# resource_root_subschema is always relative to the document root.
|
501
|
-
# BootstrapSchema also does not implement jsi_root_node or #[]. we instantiate the ptr directly
|
502
|
-
# rather than as a subschema from the root.
|
503
|
-
schema.class.new(
|
504
|
-
schema.jsi_document,
|
505
|
-
jsi_ptr: ptr,
|
506
|
-
jsi_schema_base_uri: nil,
|
507
|
-
)
|
508
|
-
else
|
509
|
-
resource_root = schema.schema_resource_root
|
510
|
-
Schema.ensure_schema(ptr.evaluate(resource_root, as_jsi: true),
|
511
|
-
msg: [
|
512
|
-
"subschema is not a schema at pointer: #{ptr.pointer}"
|
513
|
-
],
|
514
|
-
reinstantiate_as: schema.jsi_schemas.select(&:describes_schema?)
|
647
|
+
private def resource_root_subschema_compute(ptr: )
|
648
|
+
Schema.ensure_schema(schema_resource_root.jsi_descendent_node(ptr),
|
649
|
+
reinstantiate_as: jsi_schemas.select(&:describes_schema?)
|
515
650
|
)
|
516
|
-
|
651
|
+
end
|
652
|
+
|
653
|
+
# a set of inplace applicator schemas of this schema (from $ref, allOf, etc.) which apply to the
|
654
|
+
# given instance.
|
655
|
+
#
|
656
|
+
# the returned set will contain this schema itself, unless this schema contains a $ref keyword.
|
657
|
+
#
|
658
|
+
# @param instance [Object] the instance to check any applicators against
|
659
|
+
# @return [JSI::SchemaSet] matched applicator schemas
|
660
|
+
def inplace_applicator_schemas(instance)
|
661
|
+
SchemaSet.new(each_inplace_applicator_schema(instance))
|
662
|
+
end
|
663
|
+
|
664
|
+
# yields each inplace applicator schema which applies to the given instance.
|
665
|
+
#
|
666
|
+
# @param instance (see #inplace_applicator_schemas)
|
667
|
+
# @param visited_refs [Enumerable<JSI::Schema::Ref>]
|
668
|
+
# @yield [JSI::Schema]
|
669
|
+
# @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
|
670
|
+
def each_inplace_applicator_schema(
|
671
|
+
instance,
|
672
|
+
visited_refs: Util::EMPTY_ARY,
|
673
|
+
&block
|
674
|
+
)
|
675
|
+
return to_enum(__method__, instance, visited_refs: visited_refs) unless block
|
676
|
+
|
677
|
+
catch(:jsi_application_done) do
|
678
|
+
internal_inplace_applicate_keywords(instance, visited_refs, &block)
|
517
679
|
end
|
680
|
+
|
681
|
+
nil
|
518
682
|
end
|
519
683
|
|
520
|
-
|
684
|
+
# a set of child applicator subschemas of this schema which apply to the child of the given instance
|
685
|
+
# on the given token.
|
686
|
+
#
|
687
|
+
# @param token [Object] the array index or object property name for the child instance
|
688
|
+
# @param instance [Object] the instance to check any child applicators against
|
689
|
+
# @return [JSI::SchemaSet] child applicator subschemas of this schema for the given token
|
690
|
+
# of the instance
|
691
|
+
def child_applicator_schemas(token, instance)
|
692
|
+
SchemaSet.new(each_child_applicator_schema(token, instance))
|
693
|
+
end
|
694
|
+
|
695
|
+
# yields each child applicator subschema (from properties, items, etc.) which applies to the child of
|
696
|
+
# the given instance on the given token.
|
697
|
+
#
|
698
|
+
# @param (see #child_applicator_schemas)
|
699
|
+
# @yield [JSI::Schema]
|
700
|
+
# @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
|
701
|
+
def each_child_applicator_schema(token, instance, &block)
|
702
|
+
return to_enum(__method__, token, instance) unless block
|
703
|
+
|
704
|
+
internal_child_applicate_keywords(token, instance, &block)
|
705
|
+
|
706
|
+
nil
|
707
|
+
end
|
521
708
|
|
522
709
|
# any object property names this schema indicates may be present on its instances.
|
523
710
|
# this includes any keys of this schema's "properties" object and any entries of this schema's
|
524
711
|
# array of "required" property keys.
|
525
712
|
# @return [Set]
|
526
713
|
def described_object_property_names
|
527
|
-
|
714
|
+
@described_object_property_names_map[]
|
715
|
+
end
|
716
|
+
|
717
|
+
private def described_object_property_names_compute(**_) # TODO remove **_ eventually (keyword argument compatibility)
|
528
718
|
Set.new.tap do |property_names|
|
529
719
|
if schema_content.respond_to?(:to_hash) && schema_content['properties'].respond_to?(:to_hash)
|
530
720
|
property_names.merge(schema_content['properties'].keys)
|
@@ -533,7 +723,6 @@ module JSI
|
|
533
723
|
property_names.merge(schema_content['required'].to_ary)
|
534
724
|
end
|
535
725
|
end.freeze
|
536
|
-
end
|
537
726
|
end
|
538
727
|
|
539
728
|
# validates the given instance against this schema
|
@@ -541,7 +730,7 @@ module JSI
|
|
541
730
|
# @param instance [Object] the instance to validate against this schema
|
542
731
|
# @return [JSI::Validation::Result]
|
543
732
|
def instance_validate(instance)
|
544
|
-
if instance.is_a?(
|
733
|
+
if instance.is_a?(SchemaAncestorNode)
|
545
734
|
instance_ptr = instance.jsi_ptr
|
546
735
|
instance_document = instance.jsi_document
|
547
736
|
else
|
@@ -555,52 +744,80 @@ module JSI
|
|
555
744
|
# @param instance [Object] the instance to validate against this schema
|
556
745
|
# @return [Boolean]
|
557
746
|
def instance_valid?(instance)
|
558
|
-
if instance.is_a?(
|
747
|
+
if instance.is_a?(SchemaAncestorNode)
|
559
748
|
instance = instance.jsi_node_content
|
560
749
|
end
|
561
750
|
internal_validate_instance(Ptr[], instance, validate_only: true).valid?
|
562
751
|
end
|
563
752
|
|
753
|
+
# validates the given instance against this schema
|
754
|
+
#
|
564
755
|
# @private
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
# @
|
570
|
-
def
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
756
|
+
# @param instance_ptr [JSI::Ptr] a pointer to the instance to validate against the schema, in the instance_document
|
757
|
+
# @param instance_document [#to_hash, #to_ary, Object] document containing the instance instance_ptr pointer points to
|
758
|
+
# @param validate_only [Boolean] whether to return a full schema validation result or a simple, validation-only result
|
759
|
+
# @param visited_refs [Enumerable<JSI::Schema::Ref>]
|
760
|
+
# @return [JSI::Validation::Result]
|
761
|
+
def internal_validate_instance(
|
762
|
+
instance_ptr,
|
763
|
+
instance_document,
|
764
|
+
visited_refs: Util::EMPTY_ARY,
|
765
|
+
validate_only: false
|
766
|
+
)
|
767
|
+
if validate_only
|
768
|
+
result = JSI::Validation::VALID
|
769
|
+
else
|
770
|
+
result = JSI::Validation::FullResult.new
|
771
|
+
end
|
772
|
+
result_builder = result.class::Builder.new(
|
773
|
+
result: result,
|
774
|
+
schema: self,
|
775
|
+
instance_ptr: instance_ptr,
|
776
|
+
instance_document: instance_document,
|
777
|
+
validate_only: validate_only,
|
778
|
+
visited_refs: visited_refs,
|
779
|
+
)
|
588
780
|
|
589
|
-
|
590
|
-
|
591
|
-
|
781
|
+
catch(:jsi_validation_result) do
|
782
|
+
# note: true/false are not valid as schemas in draft 4; they are only values of
|
783
|
+
# additionalProperties / additionalItems. since their behavior is undefined, though,
|
784
|
+
# it's fine for them to behave the same as boolean schemas in later drafts.
|
785
|
+
# I don't care about draft 4 to implement a different structuring for that.
|
786
|
+
if schema_content == true
|
787
|
+
# noop
|
788
|
+
elsif schema_content == false
|
789
|
+
result_builder.validate(false, 'instance is not valid against `false` schema')
|
790
|
+
elsif schema_content.respond_to?(:to_hash)
|
791
|
+
internal_validate_keywords(result_builder)
|
792
|
+
else
|
793
|
+
result_builder.schema_error('schema is not a boolean or a JSON object')
|
794
|
+
end
|
795
|
+
result
|
796
|
+
end.freeze
|
592
797
|
end
|
593
798
|
|
594
799
|
# schema resources which are ancestors of any subschemas below this schema.
|
595
800
|
# this may include this schema if this is a schema resource root.
|
596
|
-
# @private
|
801
|
+
# @api private
|
597
802
|
# @return [Array<JSI::Schema>]
|
598
803
|
def jsi_subschema_resource_ancestors
|
599
804
|
if schema_resource_root?
|
600
|
-
jsi_schema_resource_ancestors
|
805
|
+
jsi_schema_resource_ancestors.dup.push(self).freeze
|
601
806
|
else
|
602
807
|
jsi_schema_resource_ancestors
|
603
808
|
end
|
604
809
|
end
|
810
|
+
|
811
|
+
private
|
812
|
+
|
813
|
+
def jsi_schema_initialize
|
814
|
+
@schema_ref_map = jsi_memomap(key_by: proc { |i| i[:keyword] }) do |keyword: , value: |
|
815
|
+
Schema::Ref.new(value, ref_schema: self)
|
816
|
+
end
|
817
|
+
@schema_uris_map = jsi_memomap(&method(:schema_uris_compute))
|
818
|
+
@subschema_map = jsi_memomap(&method(:subschema_compute))
|
819
|
+
@resource_root_subschema_map = jsi_memomap(&method(:resource_root_subschema_compute))
|
820
|
+
@described_object_property_names_map = jsi_memomap(&method(:described_object_property_names_compute))
|
821
|
+
end
|
605
822
|
end
|
606
823
|
end
|