jsi-dev 0.0.8 → 0.0.9
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 +3 -4
- data/CHANGELOG.md +19 -0
- data/LICENSE.md +2 -3
- data/README.md +87 -43
- data/docs/{glossary.md → Glossary.md} +84 -52
- data/jsi.gemspec +1 -1
- data/lib/jsi/base/mutability.rb +48 -0
- data/lib/jsi/base/node.rb +66 -52
- data/lib/jsi/base.rb +592 -176
- data/lib/jsi/jsi_coder.rb +4 -2
- data/lib/jsi/metaschema_node/bootstrap_schema.rb +118 -59
- data/lib/jsi/metaschema_node.rb +244 -154
- data/lib/jsi/ptr.rb +45 -17
- data/lib/jsi/ref.rb +197 -0
- data/lib/jsi/registry.rb +311 -0
- data/lib/jsi/schema/cxt/child_application.rb +35 -0
- data/lib/jsi/schema/cxt/inplace_application.rb +37 -0
- data/lib/jsi/schema/cxt.rb +80 -0
- data/lib/jsi/schema/dialect.rb +137 -0
- data/lib/jsi/schema/draft04.rb +113 -5
- data/lib/jsi/schema/draft06.rb +123 -5
- data/lib/jsi/schema/draft07.rb +157 -5
- data/lib/jsi/schema/draft202012.rb +303 -0
- data/lib/jsi/schema/dynamic_anchor_map.rb +63 -0
- data/lib/jsi/schema/element.rb +69 -0
- data/lib/jsi/schema/elements/anchor.rb +13 -0
- data/lib/jsi/schema/elements/array_validation.rb +82 -0
- data/lib/jsi/schema/elements/comment.rb +10 -0
- data/lib/jsi/schema/{validation → elements}/const.rb +11 -7
- data/lib/jsi/schema/elements/contains.rb +59 -0
- data/lib/jsi/schema/elements/contains_minmax.rb +91 -0
- data/lib/jsi/schema/elements/content_encoding.rb +10 -0
- data/lib/jsi/schema/elements/content_media_type.rb +10 -0
- data/lib/jsi/schema/elements/content_schema.rb +16 -0
- data/lib/jsi/schema/elements/default.rb +11 -0
- data/lib/jsi/schema/elements/definitions.rb +19 -0
- data/lib/jsi/schema/elements/dependencies.rb +99 -0
- data/lib/jsi/schema/elements/dependent_required.rb +49 -0
- data/lib/jsi/schema/elements/dependent_schemas.rb +69 -0
- data/lib/jsi/schema/elements/dynamic_ref.rb +69 -0
- data/lib/jsi/schema/elements/enum.rb +26 -0
- data/lib/jsi/schema/elements/examples.rb +10 -0
- data/lib/jsi/schema/elements/format.rb +10 -0
- data/lib/jsi/schema/elements/id.rb +30 -0
- data/lib/jsi/schema/elements/if_then_else.rb +82 -0
- data/lib/jsi/schema/elements/info_bool.rb +10 -0
- data/lib/jsi/schema/elements/info_string.rb +10 -0
- data/lib/jsi/schema/elements/items.rb +93 -0
- data/lib/jsi/schema/elements/items_prefixed.rb +96 -0
- data/lib/jsi/schema/elements/not.rb +31 -0
- data/lib/jsi/schema/elements/numeric.rb +137 -0
- data/lib/jsi/schema/elements/numeric_draft04.rb +77 -0
- data/lib/jsi/schema/elements/object_validation.rb +55 -0
- data/lib/jsi/schema/elements/pattern.rb +35 -0
- data/lib/jsi/schema/elements/properties.rb +145 -0
- data/lib/jsi/schema/elements/property_names.rb +48 -0
- data/lib/jsi/schema/elements/ref.rb +62 -0
- data/lib/jsi/schema/elements/required.rb +34 -0
- data/lib/jsi/schema/elements/self.rb +24 -0
- data/lib/jsi/schema/elements/some_of.rb +180 -0
- data/lib/jsi/schema/elements/string_validation.rb +57 -0
- data/lib/jsi/schema/elements/type.rb +43 -0
- data/lib/jsi/schema/elements/unevaluated_items.rb +54 -0
- data/lib/jsi/schema/elements/unevaluated_properties.rb +54 -0
- data/lib/jsi/schema/elements/xschema.rb +10 -0
- data/lib/jsi/schema/elements/xvocabulary.rb +10 -0
- data/lib/jsi/schema/elements.rb +101 -0
- data/lib/jsi/schema/issue.rb +3 -4
- data/lib/jsi/schema/schema_ancestor_node.rb +105 -52
- data/lib/jsi/schema/vocabulary.rb +36 -0
- data/lib/jsi/schema.rb +598 -383
- data/lib/jsi/schema_classes.rb +195 -141
- data/lib/jsi/schema_set.rb +85 -128
- data/lib/jsi/set.rb +23 -0
- data/lib/jsi/simple_wrap.rb +14 -17
- data/lib/jsi/struct.rb +57 -0
- data/lib/jsi/uri.rb +40 -0
- data/lib/jsi/util/private/memo_map.rb +9 -13
- data/lib/jsi/util/private.rb +59 -31
- data/lib/jsi/util/typelike.rb +19 -60
- data/lib/jsi/util.rb +53 -34
- data/lib/jsi/validation/error.rb +45 -2
- data/lib/jsi/validation/result.rb +121 -90
- data/lib/jsi/validation.rb +1 -6
- data/lib/jsi/version.rb +1 -1
- data/lib/jsi.rb +170 -36
- data/lib/schemas/json-schema.org/draft/2020-12/schema.rb +62 -0
- data/lib/schemas/json-schema.org/draft-04/schema.rb +60 -109
- data/lib/schemas/json-schema.org/draft-06/schema.rb +53 -108
- data/lib/schemas/json-schema.org/draft-07/schema.rb +63 -127
- data/readme.rb +4 -4
- data/{resources}/schemas/2020-12_strict.json +19 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/applicator.json +48 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/content.json +17 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/core.json +51 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-annotation.json +14 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-assertion.json +14 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/meta-data.json +37 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/unevaluated.json +15 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/meta/validation.json +98 -0
- data/{resources}/schemas/json-schema.org/draft/2020-12/schema.json +58 -0
- metadata +73 -52
- data/lib/jsi/metaschema.rb +0 -6
- data/lib/jsi/schema/application/child_application/contains.rb +0 -25
- data/lib/jsi/schema/application/child_application/draft04.rb +0 -21
- data/lib/jsi/schema/application/child_application/draft06.rb +0 -28
- data/lib/jsi/schema/application/child_application/draft07.rb +0 -28
- data/lib/jsi/schema/application/child_application/items.rb +0 -18
- data/lib/jsi/schema/application/child_application/properties.rb +0 -25
- data/lib/jsi/schema/application/child_application.rb +0 -13
- data/lib/jsi/schema/application/draft04.rb +0 -8
- data/lib/jsi/schema/application/draft06.rb +0 -8
- data/lib/jsi/schema/application/draft07.rb +0 -8
- data/lib/jsi/schema/application/inplace_application/dependencies.rb +0 -28
- data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -25
- data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -26
- data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -32
- data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +0 -20
- data/lib/jsi/schema/application/inplace_application/ref.rb +0 -18
- data/lib/jsi/schema/application/inplace_application/someof.rb +0 -44
- data/lib/jsi/schema/application/inplace_application.rb +0 -14
- data/lib/jsi/schema/application.rb +0 -12
- data/lib/jsi/schema/ref.rb +0 -183
- data/lib/jsi/schema/validation/array.rb +0 -69
- data/lib/jsi/schema/validation/contains.rb +0 -25
- data/lib/jsi/schema/validation/dependencies.rb +0 -49
- data/lib/jsi/schema/validation/draft04/minmax.rb +0 -91
- data/lib/jsi/schema/validation/draft04.rb +0 -110
- data/lib/jsi/schema/validation/draft06.rb +0 -120
- data/lib/jsi/schema/validation/draft07.rb +0 -157
- data/lib/jsi/schema/validation/enum.rb +0 -25
- data/lib/jsi/schema/validation/ifthenelse.rb +0 -46
- data/lib/jsi/schema/validation/items.rb +0 -54
- data/lib/jsi/schema/validation/not.rb +0 -20
- data/lib/jsi/schema/validation/numeric.rb +0 -121
- data/lib/jsi/schema/validation/object.rb +0 -45
- data/lib/jsi/schema/validation/pattern.rb +0 -34
- data/lib/jsi/schema/validation/properties.rb +0 -101
- data/lib/jsi/schema/validation/property_names.rb +0 -32
- data/lib/jsi/schema/validation/ref.rb +0 -40
- data/lib/jsi/schema/validation/required.rb +0 -27
- data/lib/jsi/schema/validation/someof.rb +0 -90
- data/lib/jsi/schema/validation/string.rb +0 -47
- data/lib/jsi/schema/validation/type.rb +0 -49
- data/lib/jsi/schema/validation.rb +0 -49
- data/lib/jsi/schema_registry.rb +0 -190
- data/lib/jsi/util/private/attr_struct.rb +0 -130
data/lib/jsi/schema.rb
CHANGED
|
@@ -4,24 +4,29 @@ module JSI
|
|
|
4
4
|
# JSI::Schema is a module which extends {JSI::Base} instances which represent JSON schemas.
|
|
5
5
|
#
|
|
6
6
|
# This module is included on the {Schema#jsi_schema_module JSI Schema module} of any schema
|
|
7
|
-
#
|
|
8
|
-
# Therefore, any JSI instance described by a schema which is a {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
9
|
# a schema and is extended by this module.
|
|
10
10
|
#
|
|
11
11
|
# The content of an instance which is a JSI::Schema (referred to in this context as schema_content) is
|
|
12
12
|
# typically a Hash (JSON object) or a boolean.
|
|
13
13
|
module Schema
|
|
14
|
-
autoload
|
|
15
|
-
autoload
|
|
14
|
+
autoload(:Element, 'jsi/schema/element')
|
|
15
|
+
autoload(:Vocabulary, 'jsi/schema/vocabulary')
|
|
16
|
+
autoload(:Dialect, 'jsi/schema/dialect')
|
|
17
|
+
autoload(:Cxt, 'jsi/schema/cxt')
|
|
18
|
+
|
|
19
|
+
autoload(:Elements, 'jsi/schema/elements')
|
|
16
20
|
|
|
17
21
|
autoload :Issue, 'jsi/schema/issue'
|
|
18
|
-
autoload
|
|
22
|
+
autoload(:DynamicAnchorMap, 'jsi/schema/dynamic_anchor_map')
|
|
19
23
|
|
|
20
24
|
autoload :SchemaAncestorNode, 'jsi/schema/schema_ancestor_node'
|
|
21
25
|
|
|
22
26
|
autoload :Draft04, 'jsi/schema/draft04'
|
|
23
27
|
autoload :Draft06, 'jsi/schema/draft06'
|
|
24
28
|
autoload :Draft07, 'jsi/schema/draft07'
|
|
29
|
+
autoload(:Draft202012, 'jsi/schema/draft202012')
|
|
25
30
|
|
|
26
31
|
class Error < StandardError
|
|
27
32
|
end
|
|
@@ -30,219 +35,147 @@ module JSI
|
|
|
30
35
|
class NotASchemaError < Error
|
|
31
36
|
end
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
class ReferenceError < StandardError
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# extends any schema which uses the keyword '$id' to identify its canonical URI
|
|
38
|
-
module BigMoneyId
|
|
39
|
-
# the contents of a $id keyword whose value is a string, or nil
|
|
40
|
-
# @return [#to_str, nil]
|
|
41
|
-
def id
|
|
42
|
-
if keyword?('$id') && schema_content['$id'].respond_to?(:to_str)
|
|
43
|
-
schema_content['$id']
|
|
44
|
-
else
|
|
45
|
-
nil
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# extends any schema which uses the keyword 'id' to identify its canonical URI
|
|
51
|
-
module OldId
|
|
52
|
-
# the contents of an `id` keyword whose value is a string, or nil
|
|
53
|
-
# @return [#to_str, nil]
|
|
54
|
-
def id
|
|
55
|
-
if keyword?('id') && schema_content['id'].respond_to?(:to_str)
|
|
56
|
-
schema_content['id']
|
|
57
|
-
else
|
|
58
|
-
nil
|
|
59
|
-
end
|
|
60
|
-
end
|
|
38
|
+
class NotAMetaSchemaError < TypeError
|
|
61
39
|
end
|
|
62
40
|
|
|
63
|
-
#
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
# e.g. #foo
|
|
74
|
-
nil
|
|
75
|
-
elsif id_uri.fragment == nil
|
|
76
|
-
# no fragment
|
|
77
|
-
# e.g. http://localhost:1234/bar
|
|
78
|
-
id_uri
|
|
79
|
-
elsif id_uri.fragment == ''
|
|
80
|
-
# empty fragment
|
|
81
|
-
# e.g. http://json-schema.org/draft-07/schema#
|
|
82
|
-
id_uri.merge(fragment: nil).freeze
|
|
83
|
-
elsif jsi_schema_base_uri && jsi_schema_base_uri.join(id_uri).merge(fragment: nil) == jsi_schema_base_uri
|
|
84
|
-
# the id, resolved against the base uri, consists of the base uri plus an anchor fragment.
|
|
85
|
-
# so there's no non-fragment id.
|
|
86
|
-
# e.g. base uri is http://localhost:1234/bar
|
|
87
|
-
# and id is http://localhost:1234/bar#foo
|
|
88
|
-
nil
|
|
89
|
-
else
|
|
90
|
-
# e.g. http://localhost:1234/bar#foo
|
|
91
|
-
id_uri.merge(fragment: nil).freeze
|
|
92
|
-
end
|
|
93
|
-
else
|
|
94
|
-
nil
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
# an anchor defined by a non-empty fragment in the id uri
|
|
99
|
-
# @return [String]
|
|
100
|
-
def anchor
|
|
101
|
-
if id
|
|
102
|
-
id_uri = Util.uri(id)
|
|
103
|
-
if id_uri.fragment == ''
|
|
104
|
-
nil
|
|
105
|
-
else
|
|
106
|
-
id_uri.fragment
|
|
107
|
-
end
|
|
108
|
-
else
|
|
109
|
-
nil
|
|
110
|
-
end
|
|
41
|
+
# @deprecated alias after v0.8
|
|
42
|
+
# an exception raised when we are unable to resolve a schema reference
|
|
43
|
+
ReferenceError = ResolutionError
|
|
44
|
+
|
|
45
|
+
# A reference to a schema identified by a given URI.
|
|
46
|
+
# {#resolve} will return a Schema, and param `referrer` must be a Schema.
|
|
47
|
+
class Ref < Ref
|
|
48
|
+
# @param ref_schema [Schema] deprecated; use `referrer`
|
|
49
|
+
def initialize(ref, ref_schema: nil, **kw)
|
|
50
|
+
super(ref, referrer: ref_schema, **kw)
|
|
111
51
|
end
|
|
112
|
-
end
|
|
113
52
|
|
|
114
|
-
# @private
|
|
115
|
-
module IntegerAllows0Fraction
|
|
116
|
-
# is `value` an integer?
|
|
117
|
-
# @private
|
|
118
|
-
# @param value
|
|
119
53
|
# @return [Boolean]
|
|
120
|
-
def
|
|
121
|
-
|
|
54
|
+
def resolve_schema?
|
|
55
|
+
true
|
|
122
56
|
end
|
|
123
|
-
end
|
|
124
57
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
# @private
|
|
129
|
-
# @param value
|
|
130
|
-
# @return [Boolean]
|
|
131
|
-
def internal_integer?(value)
|
|
132
|
-
value.is_a?(Integer)
|
|
58
|
+
# @deprecated after v0.8
|
|
59
|
+
def deref_schema
|
|
60
|
+
resolve
|
|
133
61
|
end
|
|
134
62
|
end
|
|
135
63
|
|
|
136
|
-
# This module extends any JSI Schema
|
|
64
|
+
# This module extends any JSI Schema that is a meta-schema, i.e. it describes schemas.
|
|
137
65
|
#
|
|
138
|
-
# Examples of a schema
|
|
66
|
+
# Examples of a meta-schema include the JSON Schema meta-schemas and
|
|
139
67
|
# the OpenAPI schema definition which describes "A deterministic version of a JSON Schema object."
|
|
140
68
|
#
|
|
141
|
-
#
|
|
69
|
+
# Meta-schemas include {JSI::Schema} in their
|
|
142
70
|
# {Schema#jsi_schema_module JSI Schema module}, so for a schema which is an instance of
|
|
143
|
-
#
|
|
71
|
+
# JSI::Schema::MetaSchema, instances of that schema are instances of {JSI::Schema} and are schemas.
|
|
144
72
|
#
|
|
145
73
|
# A schema is indicated as describing other schemas using the {Schema#describes_schema!} method.
|
|
146
|
-
module
|
|
74
|
+
module MetaSchema
|
|
75
|
+
# @return [Schema::Dialect]
|
|
76
|
+
attr_reader(:described_dialect)
|
|
77
|
+
|
|
147
78
|
# Instantiates the given schema content as a JSI Schema.
|
|
148
79
|
#
|
|
149
|
-
# By default, the schema will be registered with the {JSI.
|
|
150
|
-
# This can be controlled by
|
|
80
|
+
# By default, the schema will be registered with the {JSI.registry}.
|
|
81
|
+
# This can be controlled by the `register` param and {Base::Conf#registry `registry`} {Base::Conf configuration}.
|
|
151
82
|
#
|
|
152
83
|
# By default, the `schema_content` will have any Symbol keys of Hashes replaced with Strings
|
|
153
84
|
# (recursively through the document). This is controlled by the param `stringify_symbol_keys`.
|
|
154
85
|
#
|
|
155
|
-
#
|
|
156
|
-
#
|
|
157
|
-
# If specified, the root schema will be identified by this URI, in addition
|
|
158
|
-
# to any absolute URI declared with an id keyword, for resolution in the `schema_registry`.
|
|
159
|
-
#
|
|
160
|
-
# It is rare that this needs to be specified. Most schemas, if they use absolute URIs, will
|
|
161
|
-
# use the `$id` keyword (`id` in draft 4) to specify this. A different retrieval URI is useful
|
|
162
|
-
# in unusual cases:
|
|
86
|
+
# Schemas instantiated with `new_schema` are immutable, their content transformed using
|
|
87
|
+
# the {Base::Conf configured} {Base::Conf#to_immutable `to_immutable`}.
|
|
163
88
|
#
|
|
164
|
-
#
|
|
165
|
-
#
|
|
166
|
-
# - Another schema refers with `$ref` to the schema being instantiated by this retrieval URI,
|
|
167
|
-
# rather than an id declared in the schema - the schema is resolvable by this URI in the
|
|
168
|
-
# `schema_registry`.
|
|
169
|
-
# @param register [Boolean] Whether the instantiated schema and any subschemas with absolute URIs
|
|
170
|
-
# will be registered in the schema registry indicated by param `schema_registry`.
|
|
171
|
-
# @param schema_registry [SchemaRegistry, nil] The registry this schema will use.
|
|
89
|
+
# Parameters are passed to {SchemaSet#new_jsi} and are documented there, but some have
|
|
90
|
+
# different defaults for new_schema.
|
|
172
91
|
#
|
|
173
|
-
#
|
|
174
|
-
#
|
|
175
|
-
#
|
|
176
|
-
# @param stringify_symbol_keys
|
|
177
|
-
#
|
|
178
|
-
#
|
|
179
|
-
#
|
|
180
|
-
# using [Module#module_exec](https://ruby-doc.org/core/Module.html#method-i-module_exec).
|
|
181
|
-
# @return [JSI::Base subclass + JSI::Schema] a JSI which is a {JSI::Schema} whose content comes from
|
|
182
|
-
# the given `schema_content` and whose schemas are this schema's inplace applicators.
|
|
92
|
+
# @param schema_content an object to be instantiated as a JSI Schema - typically a Hash
|
|
93
|
+
# @param base_uri
|
|
94
|
+
# @param register
|
|
95
|
+
# @param stringify_symbol_keys
|
|
96
|
+
# @param conf_kw (see SchemaSet#new_jsi)
|
|
97
|
+
# @return [Base + Schema] A JSI which is a {Schema} whose content comes from
|
|
98
|
+
# the given `schema_content` and whose schemas are this meta-schema's in-place applicators.
|
|
183
99
|
def new_schema(schema_content,
|
|
184
|
-
|
|
100
|
+
base_uri: nil,
|
|
185
101
|
register: true,
|
|
186
|
-
schema_registry: JSI.schema_registry,
|
|
187
102
|
stringify_symbol_keys: true,
|
|
188
|
-
|
|
103
|
+
**conf_kw
|
|
189
104
|
)
|
|
190
|
-
|
|
191
|
-
|
|
105
|
+
raise(BlockGivenError) if block_given?
|
|
106
|
+
new_jsi(schema_content,
|
|
107
|
+
base_uri: base_uri,
|
|
192
108
|
register: register,
|
|
193
|
-
schema_registry: schema_registry,
|
|
194
109
|
stringify_symbol_keys: stringify_symbol_keys,
|
|
110
|
+
**conf_kw,
|
|
111
|
+
mutable: false,
|
|
195
112
|
)
|
|
196
|
-
if block
|
|
197
|
-
schema_jsi.jsi_schema_module_exec(&block)
|
|
198
|
-
end
|
|
199
|
-
schema_jsi
|
|
200
113
|
end
|
|
201
114
|
|
|
202
115
|
# Instantiates the given schema content as a JSI Schema, passing all params to
|
|
203
|
-
# {Schema::
|
|
116
|
+
# {Schema::MetaSchema#new_schema}, and returns its {Schema#jsi_schema_module JSI Schema Module}.
|
|
204
117
|
#
|
|
205
|
-
# @
|
|
118
|
+
# @yield If a block is given, it is evaluated in the context of the schema module
|
|
119
|
+
# using [Module#module_exec](https://ruby-doc.org/core/Module.html#method-i-module_exec).
|
|
120
|
+
# @return [JSI::SchemaModule] the JSI Schema Module of the instantiated schema
|
|
206
121
|
def new_schema_module(schema_content, **kw, &block)
|
|
207
|
-
new_schema(schema_content, **kw
|
|
122
|
+
schema_jsi = new_schema(schema_content, **kw)
|
|
123
|
+
schema_jsi.jsi_schema_module_exec(&block) if block
|
|
124
|
+
schema_jsi.jsi_schema_module
|
|
208
125
|
end
|
|
209
126
|
end
|
|
210
127
|
|
|
211
|
-
|
|
128
|
+
# @private
|
|
129
|
+
module ExtendedInitialize
|
|
212
130
|
def extended(o)
|
|
213
131
|
super
|
|
214
132
|
o.send(:jsi_schema_initialize)
|
|
215
133
|
end
|
|
134
|
+
|
|
135
|
+
def included(m)
|
|
136
|
+
super
|
|
137
|
+
return if m.is_a?(Class)
|
|
138
|
+
|
|
139
|
+
# if a module (m) includes Schema, and an object (o) is extended with m,
|
|
140
|
+
# then o should have #jsi_schema_initialize called, but Schema.extended is not called,
|
|
141
|
+
# so m needs its own .extended method to call jsi_schema_initialize.
|
|
142
|
+
# note: extending m with ExtendedInitialize for .extended, rather than m.define_singleton_method(:extended),
|
|
143
|
+
# avoids possibly clobbering an existing singleton .extended method the module has defined.
|
|
144
|
+
m.extend(ExtendedInitialize)
|
|
145
|
+
end
|
|
216
146
|
end
|
|
147
|
+
|
|
148
|
+
extend(ExtendedInitialize)
|
|
217
149
|
end
|
|
218
150
|
|
|
219
151
|
class << self
|
|
220
|
-
# An application-wide default
|
|
221
|
-
# to instantiate schemas
|
|
152
|
+
# An application-wide default meta-schema set by {default_metaschema=}, used by {JSI.new_schema}
|
|
153
|
+
# to instantiate schemas that do not specify their meta-schema using a `$schema` property.
|
|
222
154
|
#
|
|
223
|
-
# @return [nil, Base + Schema + Schema::
|
|
155
|
+
# @return [nil, Base + Schema + Schema::MetaSchema]
|
|
224
156
|
def default_metaschema
|
|
225
157
|
@default_metaschema
|
|
226
158
|
end
|
|
227
159
|
|
|
228
160
|
# Sets {default_metaschema} to a schema indicated by the given param.
|
|
229
161
|
#
|
|
230
|
-
# @param default_metaschema [Schema::
|
|
231
|
-
# Indicates the default
|
|
232
|
-
# This may be a
|
|
162
|
+
# @param default_metaschema [Schema::MetaSchema, SchemaModule::MetaSchemaModule, #to_str, nil]
|
|
163
|
+
# Indicates the default meta-schema.
|
|
164
|
+
# This may be a meta-schema or a meta-schema's schema module (e.g. `JSI::JSONSchemaDraft07`),
|
|
233
165
|
# or a URI (as would be in a `$schema` keyword).
|
|
234
166
|
#
|
|
235
167
|
# `nil` to unset.
|
|
236
168
|
def default_metaschema=(default_metaschema)
|
|
237
|
-
@default_metaschema = default_metaschema.nil? ? nil :
|
|
169
|
+
@default_metaschema = default_metaschema.nil? ? nil : Schema.ensure_metaschema(default_metaschema)
|
|
238
170
|
end
|
|
239
171
|
|
|
240
172
|
# Instantiates the given schema content as a JSI Schema.
|
|
241
173
|
#
|
|
242
|
-
# The
|
|
174
|
+
# The meta-schema that describes the schema must be indicated:
|
|
243
175
|
#
|
|
244
|
-
# - If the schema object has a `$schema` property, that URI is resolved using the
|
|
245
|
-
#
|
|
176
|
+
# - If the schema object has a `$schema` property, that URI is resolved using the
|
|
177
|
+
# {Base::Conf configured} {Base::Conf#registry `registry`} (by default {JSI.registry JSI.registry}),
|
|
178
|
+
# and that meta-schema is used. For example:
|
|
246
179
|
#
|
|
247
180
|
# ```ruby
|
|
248
181
|
# JSI.new_schema({
|
|
@@ -267,60 +200,65 @@ module JSI
|
|
|
267
200
|
# JSI.new_schema({"properties" => ...})
|
|
268
201
|
# ```
|
|
269
202
|
#
|
|
270
|
-
#
|
|
203
|
+
# An ArgumentError is raised if none of these indicates a meta-schema to use.
|
|
271
204
|
#
|
|
272
205
|
# Note that if you are instantiating a schema known to have no `$schema` property, an alternative to
|
|
273
206
|
# specifying a `default_metaschema` is to call `new_schema` on the
|
|
274
|
-
# {Schema::
|
|
275
|
-
# {SchemaModule::
|
|
276
|
-
# `JSI::JSONSchemaDraft07.new_schema(my_schema_content)`
|
|
207
|
+
# {Schema::MetaSchema#new_schema meta-schema} or its
|
|
208
|
+
# {SchemaModule::MetaSchemaModule#new_schema schema module}, e.g.
|
|
209
|
+
# `JSI::JSONSchemaDraft07.new_schema(my_schema_content)`. This will ignore any `$schema` keyword
|
|
210
|
+
# that may be present.
|
|
211
|
+
#
|
|
212
|
+
# Schemas instantiated with `new_schema` are immutable, their content transformed using
|
|
213
|
+
# the {Base::Conf configured} {Base::Conf#to_immutable `to_immutable`}.
|
|
214
|
+
#
|
|
215
|
+
# Most parameters are passed to {SchemaSet#new_jsi} and are documented there, but some have
|
|
216
|
+
# different defaults for JSI.new_schema.
|
|
277
217
|
#
|
|
278
|
-
# @param schema_content (see Schema::
|
|
279
|
-
# @param default_metaschema [Schema::
|
|
280
|
-
# Indicates the
|
|
281
|
-
# This may be a
|
|
218
|
+
# @param schema_content (see Schema::MetaSchema#new_schema)
|
|
219
|
+
# @param default_metaschema [Schema::MetaSchema, SchemaModule::MetaSchemaModule, #to_str]
|
|
220
|
+
# Indicates the meta-schema to use if the given `schema_content` does not have a `$schema` property.
|
|
221
|
+
# This may be a meta-schema or a meta-schema's schema module (e.g. `JSI::JSONSchemaDraft07`),
|
|
282
222
|
# or a URI (as would be in a `$schema` keyword).
|
|
283
|
-
# @param
|
|
284
|
-
# @param register
|
|
285
|
-
# @param
|
|
286
|
-
# @param
|
|
287
|
-
# @
|
|
288
|
-
#
|
|
289
|
-
# the given `schema_content` and whose schemas are inplace applicators of the indicated metaschema
|
|
223
|
+
# @param base_uri
|
|
224
|
+
# @param register
|
|
225
|
+
# @param stringify_symbol_keys
|
|
226
|
+
# @param conf_kw (see SchemaSet#new_jsi)
|
|
227
|
+
# @return [Base + Schema] A JSI which is a {Schema} whose content comes from
|
|
228
|
+
# the given `schema_content` and whose schemas are in-place applicators of the indicated meta-schema.
|
|
290
229
|
def new_schema(schema_content,
|
|
291
230
|
default_metaschema: nil,
|
|
292
|
-
|
|
293
|
-
# would remove repetition, but yard doesn't display delegated defaults with its (see X) directive.
|
|
294
|
-
uri: nil,
|
|
231
|
+
base_uri: nil,
|
|
295
232
|
register: true,
|
|
296
|
-
schema_registry: JSI.schema_registry,
|
|
297
233
|
stringify_symbol_keys: true,
|
|
298
|
-
|
|
234
|
+
**conf_kw
|
|
299
235
|
)
|
|
236
|
+
raise(BlockGivenError) if block_given?
|
|
300
237
|
new_schema_params = {
|
|
301
|
-
|
|
238
|
+
base_uri: base_uri,
|
|
302
239
|
register: register,
|
|
303
|
-
schema_registry: schema_registry,
|
|
304
240
|
stringify_symbol_keys: stringify_symbol_keys,
|
|
241
|
+
**conf_kw,
|
|
305
242
|
}
|
|
243
|
+
conf = Base::Conf.new(**conf_kw) # some redundancy instantiating this - not passed to MetaSchema#new_schema, just used in this method
|
|
306
244
|
default_metaschema_new_schema = -> {
|
|
307
245
|
default_metaschema = if default_metaschema
|
|
308
|
-
Schema.
|
|
246
|
+
Schema.ensure_metaschema(default_metaschema, name: "default_metaschema", registry: conf.registry)
|
|
309
247
|
elsif self.default_metaschema
|
|
310
248
|
self.default_metaschema
|
|
311
249
|
else
|
|
312
250
|
raise(ArgumentError, [
|
|
313
|
-
"When instantiating a schema with no `$schema` property, you must specify its
|
|
251
|
+
"When instantiating a schema with no `$schema` property, you must specify its meta-schema by one of these methods:",
|
|
314
252
|
"- pass the `default_metaschema` param to this method",
|
|
315
253
|
" e.g.: JSI.new_schema(..., default_metaschema: JSI::JSONSchemaDraft07)",
|
|
316
|
-
"- invoke `new_schema` on the appropriate
|
|
254
|
+
"- invoke `new_schema` on the appropriate meta-schema or its schema module",
|
|
317
255
|
" e.g.: JSI::JSONSchemaDraft07.new_schema(...)",
|
|
318
|
-
"- set JSI.default_metaschema to an application-wide default
|
|
256
|
+
"- set JSI.default_metaschema to an application-wide default meta-schema initially",
|
|
319
257
|
" e.g.: JSI.default_metaschema = JSI::JSONSchemaDraft07",
|
|
320
258
|
"instantiating schema_content: #{schema_content.pretty_inspect.chomp}",
|
|
321
259
|
].join("\n"))
|
|
322
260
|
end
|
|
323
|
-
default_metaschema.new_schema(schema_content, **new_schema_params
|
|
261
|
+
default_metaschema.new_schema(schema_content, **new_schema_params)
|
|
324
262
|
}
|
|
325
263
|
if schema_content.is_a?(Schema)
|
|
326
264
|
raise(TypeError, [
|
|
@@ -338,8 +276,8 @@ module JSI
|
|
|
338
276
|
unless id.respond_to?(:to_str)
|
|
339
277
|
raise(ArgumentError, "given schema_content keyword `$schema` is not a string")
|
|
340
278
|
end
|
|
341
|
-
metaschema = Schema.
|
|
342
|
-
metaschema.new_schema(schema_content, **new_schema_params
|
|
279
|
+
metaschema = Schema.ensure_metaschema(id, name: '$schema', registry: conf.registry)
|
|
280
|
+
metaschema.new_schema(schema_content, **new_schema_params)
|
|
343
281
|
else
|
|
344
282
|
default_metaschema_new_schema.call
|
|
345
283
|
end
|
|
@@ -356,59 +294,67 @@ module JSI
|
|
|
356
294
|
# ensure the given object is a JSI Schema
|
|
357
295
|
#
|
|
358
296
|
# @param schema [Object] the thing the caller wishes to ensure is a Schema
|
|
359
|
-
# @
|
|
360
|
-
# if the schema param is not a schema
|
|
297
|
+
# @yieldreturn [#to_s, #to_ary] first line(s) of the error message, overriding the default
|
|
361
298
|
# @raise [NotASchemaError] if the schema param is not a schema
|
|
362
299
|
# @return [Schema] the given schema
|
|
363
|
-
def ensure_schema(schema,
|
|
300
|
+
def ensure_schema(schema, reinstantiate_as: nil)
|
|
364
301
|
if schema.is_a?(Schema)
|
|
365
302
|
schema
|
|
366
303
|
else
|
|
367
304
|
if reinstantiate_as && schema.is_a?(JSI::Base)
|
|
368
305
|
# TODO warn; behavior is undefined and I hate this implementation
|
|
369
306
|
|
|
370
|
-
|
|
307
|
+
result_schema_indicated_schemas = SchemaSet.new(schema.jsi_indicated_schemas + reinstantiate_as)
|
|
308
|
+
result_schema_applied_schemas = result_schema_indicated_schemas.each_yield_set do |is, y|
|
|
309
|
+
is.each_inplace_applicator_schema(schema.jsi_node_content, &y)
|
|
310
|
+
end
|
|
371
311
|
|
|
372
|
-
result_schema_class = JSI::SchemaClasses.class_for_schemas(
|
|
373
|
-
includes: SchemaClasses.includes_for(schema.jsi_node_content)
|
|
312
|
+
result_schema_class = JSI::SchemaClasses.class_for_schemas(result_schema_applied_schemas,
|
|
313
|
+
includes: SchemaClasses.includes_for(schema.jsi_node_content),
|
|
314
|
+
mutable: schema.jsi_mutable?,
|
|
374
315
|
)
|
|
375
316
|
|
|
376
|
-
result_schema_class.new(
|
|
317
|
+
result_schema_class.new(
|
|
318
|
+
jsi_document: schema.jsi_document,
|
|
377
319
|
jsi_ptr: schema.jsi_ptr,
|
|
378
|
-
jsi_indicated_schemas:
|
|
379
|
-
|
|
320
|
+
jsi_indicated_schemas: result_schema_indicated_schemas,
|
|
321
|
+
jsi_base_uri: schema.jsi_base_uri,
|
|
380
322
|
jsi_schema_resource_ancestors: schema.jsi_schema_resource_ancestors,
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
323
|
+
jsi_schema_dynamic_anchor_map: schema.jsi_schema_dynamic_anchor_map,
|
|
324
|
+
jsi_conf: schema.equal?(schema.jsi_root_node) ? schema.jsi_conf : nil,
|
|
325
|
+
jsi_root_node: schema.equal?(schema.jsi_root_node) ? nil : schema.jsi_root_node, # bad
|
|
326
|
+
).send(:jsi_initialized)
|
|
384
327
|
else
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
328
|
+
msg = []
|
|
329
|
+
msg.concat([*(block_given? ? yield : "indicated object is not a schema:")])
|
|
330
|
+
msg << schema.pretty_inspect.chomp
|
|
331
|
+
if schema.is_a?(Base)
|
|
332
|
+
msg << "its schemas (which should include a Meta-Schema): #{schema.jsi_schemas.pretty_inspect.chomp}"
|
|
333
|
+
end
|
|
334
|
+
raise(NotASchemaError, msg.compact.join("\n"))
|
|
389
335
|
end
|
|
390
336
|
end
|
|
391
337
|
end
|
|
392
338
|
|
|
393
|
-
# Ensures the given param identifies a
|
|
339
|
+
# Ensures the given param identifies a meta-schema and returns that meta-schema.
|
|
394
340
|
#
|
|
395
341
|
# @api private
|
|
396
|
-
# @param
|
|
397
|
-
# @raise [TypeError] if the param does not indicate a schema
|
|
398
|
-
# @return [Base + Schema + Schema::
|
|
399
|
-
def
|
|
400
|
-
if
|
|
401
|
-
schema = Schema::Ref.new(
|
|
342
|
+
# @param metaschema [Schema::MetaSchema, SchemaModule::MetaSchemaModule, #to_str]
|
|
343
|
+
# @raise [TypeError] if the param does not indicate a meta-schema
|
|
344
|
+
# @return [Base + Schema + Schema::MetaSchema]
|
|
345
|
+
def ensure_metaschema(metaschema, name: nil, registry: JSI.registry)
|
|
346
|
+
if metaschema.respond_to?(:to_str)
|
|
347
|
+
schema = Schema::Ref.new(metaschema, registry: registry).resolve
|
|
402
348
|
if !schema.describes_schema?
|
|
403
|
-
raise(
|
|
349
|
+
raise(NotAMetaSchemaError, [name, "URI indicates a schema that is not a meta-schema: #{metaschema.pretty_inspect.chomp}"].compact.join(" "))
|
|
404
350
|
end
|
|
405
351
|
schema
|
|
406
|
-
elsif
|
|
407
|
-
|
|
408
|
-
elsif
|
|
409
|
-
|
|
352
|
+
elsif metaschema.is_a?(SchemaModule::MetaSchemaModule)
|
|
353
|
+
metaschema.schema
|
|
354
|
+
elsif metaschema.is_a?(Schema::MetaSchema)
|
|
355
|
+
metaschema
|
|
410
356
|
else
|
|
411
|
-
raise(
|
|
357
|
+
raise(NotAMetaSchemaError, "#{name || "param"} does not indicate a meta-schema: #{metaschema.pretty_inspect.chomp}")
|
|
412
358
|
end
|
|
413
359
|
end
|
|
414
360
|
end
|
|
@@ -425,6 +371,12 @@ module JSI
|
|
|
425
371
|
end
|
|
426
372
|
end
|
|
427
373
|
|
|
374
|
+
# @!method dialect
|
|
375
|
+
# The dialect of this schema
|
|
376
|
+
# @return [Schema::Dialect]
|
|
377
|
+
# note: defined on a meta-schema's schema module by Schema#describes_schema!
|
|
378
|
+
|
|
379
|
+
|
|
428
380
|
# the underlying JSON data used to instantiate this JSI::Schema.
|
|
429
381
|
# this is an alias for {Base#jsi_node_content}, named for clarity in the context of working with
|
|
430
382
|
# a schema.
|
|
@@ -439,83 +391,93 @@ module JSI
|
|
|
439
391
|
schema_content.respond_to?(:to_hash) && schema_content.key?(keyword)
|
|
440
392
|
end
|
|
441
393
|
|
|
442
|
-
#
|
|
443
|
-
# @return [
|
|
394
|
+
# Does this schema contain the given keyword with the given value?
|
|
395
|
+
# @return [Boolean]
|
|
396
|
+
def keyword_value?(keyword, value)
|
|
397
|
+
keyword?(keyword) && schema_content[keyword] == value
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
# the string contents of an `$id`/`id` keyword, or nil
|
|
401
|
+
# @return [#to_str, nil]
|
|
402
|
+
def id
|
|
403
|
+
dialect_invoke_each(:id).first
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
# @return [Enumerable<String>]
|
|
407
|
+
def anchors
|
|
408
|
+
anchors = Set[]
|
|
409
|
+
anchors.merge(dialect_invoke_each(:anchor))
|
|
410
|
+
anchors.merge(dialect_invoke_each(:dynamicAnchor))
|
|
411
|
+
anchors.freeze
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# the URI of this schema, from an `$id` keyword, resolved against our `#jsi_base_uri`
|
|
415
|
+
# @deprecated after v0.8 - use `#jsi_resource_uri`
|
|
416
|
+
# @return [URI, nil]
|
|
444
417
|
def schema_absolute_uri
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
418
|
+
jsi_resource_uri
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
# @deprecated after v0.8 - use `#jsi_resource_uris`
|
|
422
|
+
# @return [Enumerable<URI>]
|
|
423
|
+
def schema_absolute_uris
|
|
424
|
+
jsi_resource_uris
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
# @yield [URI]
|
|
428
|
+
private def jsi_each_resource_uri_compute
|
|
429
|
+
dialect_invoke_each(:id_without_fragment) do |id_without_fragment|
|
|
430
|
+
if jsi_base_uri
|
|
431
|
+
yield(jsi_base_uri.join(id_without_fragment))
|
|
448
432
|
elsif id_without_fragment.absolute?
|
|
449
|
-
id_without_fragment
|
|
450
|
-
else
|
|
451
|
-
# TODO warn / schema_error
|
|
452
|
-
nil
|
|
433
|
+
yield(id_without_fragment)
|
|
453
434
|
end
|
|
454
435
|
end
|
|
436
|
+
super
|
|
455
437
|
end
|
|
456
438
|
|
|
457
439
|
# a nonrelative URI which refers to this schema.
|
|
458
|
-
# nil if no
|
|
440
|
+
# `nil` if no ancestor of this schema defines an id.
|
|
459
441
|
# see {#schema_uris} for all URIs known to refer to this schema.
|
|
460
|
-
# @return [
|
|
442
|
+
# @return [URI, nil]
|
|
461
443
|
def schema_uri
|
|
462
444
|
schema_uris.first
|
|
463
445
|
end
|
|
464
446
|
|
|
465
447
|
# nonrelative URIs (that is, absolute, but possibly with a fragment) which refer to this schema
|
|
466
|
-
# @return [Array<
|
|
448
|
+
# @return [Array<URI>]
|
|
467
449
|
def schema_uris
|
|
468
|
-
@schema_uris_map[]
|
|
450
|
+
@schema_uris_map[schema_content: schema_content]
|
|
469
451
|
end
|
|
470
452
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
453
|
+
# @yield [URI]
|
|
454
|
+
private def schema_uris_compute(&block)
|
|
455
|
+
jsi_resource_uris.each(&block)
|
|
474
456
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
return to_enum(__method__) unless block_given?
|
|
480
|
-
|
|
481
|
-
yield schema_absolute_uri if schema_absolute_uri
|
|
482
|
-
|
|
483
|
-
parent_schemas = jsi_subschema_resource_ancestors.reverse_each.select do |resource|
|
|
484
|
-
resource.schema_absolute_uri
|
|
485
|
-
end
|
|
486
|
-
|
|
487
|
-
anchored = respond_to?(:anchor) ? anchor : nil
|
|
488
|
-
parent_schemas.each do |parent_schema|
|
|
489
|
-
if anchored
|
|
490
|
-
if parent_schema.jsi_anchor_subschema(anchor) == self
|
|
491
|
-
yield parent_schema.schema_absolute_uri.merge(fragment: anchor).freeze
|
|
492
|
-
else
|
|
493
|
-
anchored = false
|
|
457
|
+
if jsi_resource_root
|
|
458
|
+
anchors.each do |anchor|
|
|
459
|
+
jsi_resource_root.jsi_resource_uris.each do |uri|
|
|
460
|
+
yield(uri.merge(fragment: anchor))
|
|
494
461
|
end
|
|
495
462
|
end
|
|
463
|
+
end
|
|
496
464
|
|
|
497
|
-
|
|
498
|
-
|
|
465
|
+
jsi_subschema_resource_ancestors.reverse_each do |ancestor_schema|
|
|
466
|
+
relative_ptr = jsi_ptr.relative_to(ancestor_schema.jsi_ptr)
|
|
467
|
+
ancestor_schema.jsi_resource_uris.each do |uri|
|
|
468
|
+
yield(uri.merge(fragment: relative_ptr.fragment))
|
|
469
|
+
end
|
|
499
470
|
end
|
|
500
471
|
|
|
501
472
|
nil
|
|
502
473
|
end
|
|
503
474
|
|
|
504
|
-
#
|
|
505
|
-
#
|
|
506
|
-
#
|
|
507
|
-
# some functionality is also defined on the module itself (its singleton class, not for its instances):
|
|
475
|
+
# The {SchemaModule JSI Schema Module} for this schema.
|
|
476
|
+
# JSI instances described by this schema are instances of this module.
|
|
508
477
|
#
|
|
509
|
-
#
|
|
510
|
-
# of this schema (see {#new_jsi}).
|
|
511
|
-
# - properties described by this schema's metaschema are defined as methods to get subschemas' schema
|
|
512
|
-
# modules, so for example `schema.jsi_schema_module.items` returns the same module
|
|
513
|
-
# as `schema.items.jsi_schema_module`.
|
|
514
|
-
# - method .schema which returns this schema.
|
|
515
|
-
#
|
|
516
|
-
# @return [Module + SchemaModule]
|
|
478
|
+
# @return [SchemaModule]
|
|
517
479
|
def jsi_schema_module
|
|
518
|
-
|
|
480
|
+
jsi_schema_module_connection
|
|
519
481
|
end
|
|
520
482
|
|
|
521
483
|
# Evaluates the given block in the context of this schema's JSI schema module.
|
|
@@ -528,79 +490,109 @@ module JSI
|
|
|
528
490
|
jsi_schema_module.module_exec(*a, **kw, &block)
|
|
529
491
|
end
|
|
530
492
|
|
|
493
|
+
# @return [String, nil]
|
|
494
|
+
def jsi_schema_module_name
|
|
495
|
+
# don't hit #jsi_schema_module - avoid creating module, avoid erroring for MSN::BootstrapSchema
|
|
496
|
+
@memos[:schema_module_connection] && @memos[:schema_module_connection].name
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
# @return [String, nil]
|
|
500
|
+
def jsi_schema_module_name_from_ancestor
|
|
501
|
+
is_a?(Base) ? jsi_schema_module.name_from_ancestor : nil
|
|
502
|
+
end
|
|
503
|
+
|
|
531
504
|
# Instantiates a new JSI whose content comes from the given `instance` param.
|
|
532
|
-
# This schema indicates the schemas of the JSI - its schemas are
|
|
505
|
+
# This schema indicates the schemas of the JSI - its schemas are in-place
|
|
533
506
|
# applicators of this schema which apply to the given instance.
|
|
534
507
|
#
|
|
535
|
-
#
|
|
536
|
-
#
|
|
537
|
-
#
|
|
508
|
+
# All parameters are passed to {SchemaSet#new_jsi}.
|
|
509
|
+
#
|
|
510
|
+
# @return [Base] a JSI whose content comes from the given instance and whose schemas are
|
|
511
|
+
# in-place applicators of this schema.
|
|
538
512
|
def new_jsi(instance, **kw)
|
|
513
|
+
raise(BlockGivenError) if block_given?
|
|
539
514
|
SchemaSet[self].new_jsi(instance, **kw)
|
|
540
515
|
end
|
|
541
516
|
|
|
542
|
-
# @param
|
|
517
|
+
# @param ref [#to_str] ref URI
|
|
543
518
|
# @return [Schema::Ref]
|
|
544
|
-
def schema_ref(
|
|
545
|
-
|
|
546
|
-
@schema_ref_map[keyword: keyword, value: schema_content[keyword]]
|
|
519
|
+
def schema_ref(ref = schema_content["$ref"])
|
|
520
|
+
@schema_ref_map[ref]
|
|
547
521
|
end
|
|
548
522
|
|
|
549
|
-
#
|
|
523
|
+
# Does this schema itself describe a schema? I.e. is this schema a meta-schema?
|
|
550
524
|
# @return [Boolean]
|
|
551
525
|
def describes_schema?
|
|
552
|
-
|
|
526
|
+
is_a?(Schema::MetaSchema)
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
# Is this a JSI Schema?
|
|
530
|
+
# @return [Boolean]
|
|
531
|
+
def jsi_is_schema?
|
|
532
|
+
true
|
|
553
533
|
end
|
|
554
534
|
|
|
555
|
-
#
|
|
556
|
-
# this schema is extended with {
|
|
557
|
-
# with {SchemaModule::
|
|
558
|
-
# JSI::Schema
|
|
535
|
+
# Indicates that this schema describes schemas, i.e. it is a meta-schema.
|
|
536
|
+
# this schema is extended with {Schema::MetaSchema} and its {#jsi_schema_module} is extended
|
|
537
|
+
# with {SchemaModule::MetaSchemaModule}, and the JSI Schema Module will include
|
|
538
|
+
# JSI::Schema.
|
|
559
539
|
#
|
|
560
|
-
# @param
|
|
561
|
-
# the schema to extend schemas described by this schema.
|
|
540
|
+
# @param dialect [Schema::Dialect, nil] dialect may be passed, or inferred from `$vocabulary`
|
|
562
541
|
# @return [void]
|
|
563
|
-
def describes_schema!(
|
|
564
|
-
|
|
542
|
+
def describes_schema!(dialect = nil)
|
|
543
|
+
# TODO rm bridge code hax
|
|
544
|
+
dialect = dialect.first::DIALECT if dialect.is_a?(Array) && dialect.size == 1
|
|
545
|
+
|
|
546
|
+
if !dialect
|
|
547
|
+
raise(ArgumentError, "no dialect given and no $vocabulary hash/object") if !schema_content['$vocabulary'].respond_to?(:to_hash)
|
|
548
|
+
dialect = Schema::Dialect.from_xvocabulary(schema_content['$vocabulary'], registry: jsi_registry)
|
|
549
|
+
end
|
|
565
550
|
|
|
566
|
-
if
|
|
567
|
-
|
|
551
|
+
raise(TypeError) if !dialect.is_a?(Schema::Dialect)
|
|
552
|
+
|
|
553
|
+
if jsi_schema_module <= Schema
|
|
554
|
+
# this schema has already had describes_schema! called on it.
|
|
568
555
|
# this is to be avoided, but is not particularly a problem.
|
|
569
|
-
# it is a bug if it was called different times with different
|
|
570
|
-
|
|
571
|
-
raise(ArgumentError, "this schema already describes a schema with different
|
|
556
|
+
# it is a bug if it was called different times with different dialect, though.
|
|
557
|
+
if @described_dialect != dialect
|
|
558
|
+
raise(ArgumentError, "this schema already describes a schema with different dialect")
|
|
572
559
|
end
|
|
573
560
|
else
|
|
574
561
|
jsi_schema_module.include(Schema)
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
jsi_schema_module.extend(SchemaModule::DescribesSchemaModule)
|
|
579
|
-
jsi_schema_module.instance_variable_set(:@schema_implementation_modules, schema_implementation_modules)
|
|
562
|
+
jsi_schema_module.send(:define_method, :dialect) { dialect }
|
|
563
|
+
proc { |metaschema| jsi_schema_module.send(:define_method, :metaschema) { metaschema } }[self]
|
|
564
|
+
jsi_schema_module.extend(SchemaModule::MetaSchemaModule)
|
|
580
565
|
end
|
|
581
566
|
|
|
582
|
-
|
|
567
|
+
@described_dialect = dialect
|
|
568
|
+
extend(Schema::MetaSchema)
|
|
583
569
|
|
|
584
570
|
nil
|
|
585
571
|
end
|
|
586
572
|
|
|
587
573
|
# a resource containing this schema.
|
|
588
574
|
#
|
|
589
|
-
#
|
|
575
|
+
# If any ancestor, or this schema itself, is a schema with an absolute uri (see {#schema_absolute_uri}),
|
|
590
576
|
# the resource root is the closest schema with an absolute uri.
|
|
591
577
|
#
|
|
592
|
-
# If no
|
|
578
|
+
# If no ancestor schema has an absolute uri, the schema_resource_root is the {Base#jsi_root_node document's root node}.
|
|
593
579
|
# In this case, the resource root may or may not be a schema itself.
|
|
594
580
|
#
|
|
581
|
+
# @deprecated after v0.8
|
|
595
582
|
# @return [JSI::Base] resource containing this schema
|
|
596
583
|
def schema_resource_root
|
|
597
|
-
|
|
584
|
+
jsi_resource_root
|
|
598
585
|
end
|
|
599
586
|
|
|
600
587
|
# is this schema the root of a schema resource?
|
|
601
588
|
# @return [Boolean]
|
|
589
|
+
def jsi_is_resource_root?
|
|
590
|
+
super || jsi_resource_uris.any?
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
# @deprecated after v0.8
|
|
602
594
|
def schema_resource_root?
|
|
603
|
-
|
|
595
|
+
jsi_is_resource_root?
|
|
604
596
|
end
|
|
605
597
|
|
|
606
598
|
# a subschema of this Schema
|
|
@@ -608,83 +600,202 @@ module JSI
|
|
|
608
600
|
# @param subptr [JSI::Ptr, #to_ary] a relative pointer, or array of tokens, pointing to the subschema
|
|
609
601
|
# @return [JSI::Schema] the subschema at the location indicated by subptr. self if subptr is empty.
|
|
610
602
|
def subschema(subptr)
|
|
611
|
-
|
|
612
|
-
end
|
|
613
|
-
|
|
614
|
-
private def subschema_compute(subptr: )
|
|
615
|
-
Schema.ensure_schema(jsi_descendent_node(subptr), msg: [
|
|
616
|
-
"subschema is not a schema at pointer: #{subptr.pointer}"
|
|
617
|
-
])
|
|
603
|
+
Schema.ensure_schema(jsi_descendent_node(subptr)) { "subschema is not a schema at pointer: #{Ptr.ary_ptr(subptr).pointer}" }
|
|
618
604
|
end
|
|
619
605
|
|
|
620
|
-
#
|
|
606
|
+
# A schema in the same schema resource as this one (see {Schema::SchemaAncestorNode#jsi_resource_root}) at the given
|
|
621
607
|
# pointer relative to the root of the schema resource.
|
|
622
608
|
#
|
|
623
609
|
# @param ptr [JSI::Ptr, #to_ary] a pointer to a schema from our schema resource root
|
|
624
610
|
# @return [JSI::Schema] the schema pointed to by ptr
|
|
625
611
|
def resource_root_subschema(ptr)
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
private def resource_root_subschema_compute(ptr: )
|
|
630
|
-
Schema.ensure_schema(schema_resource_root.jsi_descendent_node(ptr),
|
|
631
|
-
msg: [
|
|
632
|
-
"subschema is not a schema at pointer: #{ptr.pointer}"
|
|
633
|
-
],
|
|
634
|
-
reinstantiate_as: jsi_schemas.select(&:describes_schema?)
|
|
612
|
+
Schema.ensure_schema(jsi_resource_root.jsi_descendent_node(ptr),
|
|
613
|
+
reinstantiate_as: jsi_conf.reinstantiate_nonschemas && jsi_schemas.select(&:describes_schema?),
|
|
635
614
|
)
|
|
636
615
|
end
|
|
637
616
|
|
|
638
|
-
#
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
# @return [JSI::SchemaSet] matched applicator schemas
|
|
645
|
-
def inplace_applicator_schemas(instance)
|
|
646
|
-
SchemaSet.new(each_inplace_applicator_schema(instance))
|
|
617
|
+
# @yield [Schema]
|
|
618
|
+
def jsi_each_descendent_schema(&block)
|
|
619
|
+
return(to_enum(__method__)) unless block_given?
|
|
620
|
+
|
|
621
|
+
yield(self)
|
|
622
|
+
dialect_invoke_each(:subschema) { |ptr| subschema(ptr).jsi_each_descendent_schema(&block) }
|
|
647
623
|
end
|
|
648
624
|
|
|
649
|
-
# yields each
|
|
650
|
-
#
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
# @yield [JSI::Schema]
|
|
654
|
-
# @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
|
|
655
|
-
def each_inplace_applicator_schema(instance, visited_refs: [], &block)
|
|
656
|
-
return to_enum(__method__, instance, visited_refs: visited_refs) unless block
|
|
625
|
+
# yields each descendent of this node (including itself) within the same resource that is a Schema
|
|
626
|
+
# @yield [Schema]
|
|
627
|
+
def jsi_each_descendent_schema_same_resource(&block)
|
|
628
|
+
return(to_enum(__method__)) unless block_given?
|
|
657
629
|
|
|
658
|
-
|
|
659
|
-
|
|
630
|
+
yield(self)
|
|
631
|
+
dialect_invoke_each(:subschema) do |ptr|
|
|
632
|
+
desc = subschema(ptr)
|
|
633
|
+
if !desc.jsi_is_resource_root?
|
|
634
|
+
desc.jsi_each_descendent_schema_same_resource(&block)
|
|
635
|
+
end
|
|
660
636
|
end
|
|
637
|
+
end
|
|
661
638
|
|
|
662
|
-
|
|
639
|
+
# @yield [Ptr]
|
|
640
|
+
def each_immediate_subschema_ptr
|
|
641
|
+
return(to_enum(__method__)) unless block_given?
|
|
642
|
+
|
|
643
|
+
dialect_invoke_each(:subschema) { |ptr| yield(Ptr.ary_ptr(ptr)) }
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
# @private
|
|
647
|
+
# @yield each in-place applicator schema and params yielded from action :inplace_applicate
|
|
648
|
+
def each_immediate_inplace_applicator_schema(
|
|
649
|
+
instance: ,
|
|
650
|
+
visited_refs: ,
|
|
651
|
+
collect_evaluated: ,
|
|
652
|
+
&block
|
|
653
|
+
)
|
|
654
|
+
# if collect_evaluated, applicators must validate the instance to set `evaluated`
|
|
655
|
+
if collect_evaluated || @inplace_application_requires_instance
|
|
656
|
+
dialect_invoke_each(:inplace_applicate, Cxt::InplaceApplication::WithInstance,
|
|
657
|
+
instance: instance,
|
|
658
|
+
visited_refs: visited_refs,
|
|
659
|
+
collect_evaluated: collect_evaluated,
|
|
660
|
+
&block
|
|
661
|
+
)
|
|
662
|
+
else
|
|
663
|
+
# memoize: if the instance is not used by any in-place applicator present in this schema,
|
|
664
|
+
# the schema can do in-place application once instead of for every instance,
|
|
665
|
+
# for a very substantial performance gain.
|
|
666
|
+
#
|
|
667
|
+
# :inplace_applicate yields (schema, **keywords)
|
|
668
|
+
# so @memos[:immediate_inplace_applicators] is a 2D Array of tuples (schema, keywords)
|
|
669
|
+
@memos[:immediate_inplace_applicators] ||= begin
|
|
670
|
+
immediate_inplace_applicators = []
|
|
671
|
+
dialect_invoke_each(:inplace_applicate, Cxt::InplaceApplication,
|
|
672
|
+
visited_refs: visited_refs,
|
|
673
|
+
) do |s, **kw|
|
|
674
|
+
immediate_inplace_applicators.push([s, kw])
|
|
675
|
+
end
|
|
676
|
+
immediate_inplace_applicators.freeze
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
@memos[:immediate_inplace_applicators].each do |(s, kw)|
|
|
680
|
+
yield(s, **kw)
|
|
681
|
+
end
|
|
682
|
+
nil
|
|
683
|
+
end
|
|
663
684
|
end
|
|
664
685
|
|
|
665
|
-
#
|
|
666
|
-
# on the given token.
|
|
686
|
+
# Yields each in-place applicator schema which applies to the given instance.
|
|
667
687
|
#
|
|
668
|
-
# @param
|
|
669
|
-
# @param
|
|
670
|
-
# @
|
|
671
|
-
#
|
|
672
|
-
def
|
|
673
|
-
|
|
688
|
+
# @param instance [Object] the instance to check any applicators against
|
|
689
|
+
# @param visited_refs [Enumerable<JSI::Schema::Ref>]
|
|
690
|
+
# @yield [JSI::Schema]
|
|
691
|
+
# @return [nil]
|
|
692
|
+
def each_inplace_applicator_schema(
|
|
693
|
+
instance,
|
|
694
|
+
visited_refs: Util::EMPTY_ARY,
|
|
695
|
+
&block
|
|
696
|
+
)
|
|
697
|
+
each_immediate_inplace_applicator_schema(
|
|
698
|
+
instance: instance,
|
|
699
|
+
visited_refs: visited_refs,
|
|
700
|
+
collect_evaluated: false, # child application is not invoked so no evaluated children to collect
|
|
701
|
+
) do |schema, ref: nil, applicate: true|
|
|
702
|
+
if schema.equal?(self) && !ref
|
|
703
|
+
yield(self)
|
|
704
|
+
elsif applicate
|
|
705
|
+
schema.each_inplace_applicator_schema(
|
|
706
|
+
instance,
|
|
707
|
+
visited_refs: Util.add_visited_ref(visited_refs, ref),
|
|
708
|
+
&block
|
|
709
|
+
)
|
|
710
|
+
end
|
|
711
|
+
end
|
|
674
712
|
end
|
|
675
713
|
|
|
676
714
|
# yields each child applicator subschema (from properties, items, etc.) which applies to the child of
|
|
677
715
|
# the given instance on the given token.
|
|
678
716
|
#
|
|
679
|
-
# @param
|
|
717
|
+
# @param token [Object] the array index or object property name for the child instance
|
|
718
|
+
# @param instance [Object] the instance to check any child applicators against
|
|
680
719
|
# @yield [JSI::Schema]
|
|
681
720
|
# @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
|
|
682
721
|
def each_child_applicator_schema(token, instance, &block)
|
|
683
|
-
|
|
722
|
+
dialect_invoke_each(:child_applicate,
|
|
723
|
+
Cxt::ChildApplication,
|
|
724
|
+
instance: instance,
|
|
725
|
+
token: token,
|
|
726
|
+
collect_evaluated: false,
|
|
727
|
+
collect_evaluated_validate: false,
|
|
728
|
+
evaluated: false,
|
|
729
|
+
&block
|
|
730
|
+
)
|
|
731
|
+
end
|
|
684
732
|
|
|
685
|
-
|
|
733
|
+
# For each in-place applicator schema that applies to the given instance, yields each child applicator
|
|
734
|
+
# of that schema that applies to the child of the instance on the given token.
|
|
735
|
+
#
|
|
736
|
+
# This method handles collection of whether the child was evaluated by any applicator
|
|
737
|
+
# when that evaluation is needed by either this schema or the caller (per param `collect_evaluated`).
|
|
738
|
+
# This is relevant to schemas containing `unevaluatedProperties` or `unevaluatedItems`.
|
|
739
|
+
#
|
|
740
|
+
# @param token [Object] array index or hash/object property name
|
|
741
|
+
# @param instance [Object]
|
|
742
|
+
# @param collect_evaluated [Boolean] Does the caller need this method to collect successful child evaluation?
|
|
743
|
+
# Note: this method will still collect child evaluation if this schema needs it; this only needs to be
|
|
744
|
+
# passed true when called by an in-place applicator schema that needs it (i.e. contains `unevaluated*`).
|
|
745
|
+
# @param collect_evaluated_validate [Boolean] See {Base::Conf#application_collect_evaluated_validate}
|
|
746
|
+
# @yield [Schema]
|
|
747
|
+
# @return [Boolean] if `collect_evaluated` is true, whether the child was successfully evaluated
|
|
748
|
+
# by a child applicator schema. if `collect_evaluated` is false, undefined/void.
|
|
749
|
+
def each_inplace_child_applicator_schema(
|
|
750
|
+
token,
|
|
751
|
+
instance,
|
|
752
|
+
visited_refs: Util::EMPTY_ARY,
|
|
753
|
+
collect_evaluated: false,
|
|
754
|
+
collect_evaluated_validate: false,
|
|
755
|
+
&block
|
|
756
|
+
)
|
|
757
|
+
collect_evaluated ||= application_requires_evaluated
|
|
758
|
+
inplace_child_evaluated = false
|
|
759
|
+
applicate_self = false
|
|
760
|
+
|
|
761
|
+
each_immediate_inplace_applicator_schema(
|
|
762
|
+
instance: instance,
|
|
763
|
+
visited_refs: visited_refs,
|
|
764
|
+
collect_evaluated: collect_evaluated,
|
|
765
|
+
) do |schema, ref: nil, applicate: true|
|
|
766
|
+
if schema.equal?(self) && !ref
|
|
767
|
+
applicate_self = true
|
|
768
|
+
elsif applicate || (collect_evaluated && !inplace_child_evaluated)
|
|
769
|
+
schema_evaluated = schema.each_inplace_child_applicator_schema(
|
|
770
|
+
token,
|
|
771
|
+
instance,
|
|
772
|
+
visited_refs: Util.add_visited_ref(visited_refs, ref),
|
|
773
|
+
collect_evaluated: collect_evaluated && !inplace_child_evaluated,
|
|
774
|
+
collect_evaluated_validate: collect_evaluated_validate,
|
|
775
|
+
# the `if` keyword needs to yield to here because it does affect `evaluated`,
|
|
776
|
+
# but it does not applicate itself/its applicators, so does not yield to the given block.
|
|
777
|
+
&(applicate ? block : proc { })
|
|
778
|
+
)
|
|
779
|
+
inplace_child_evaluated ||= collect_evaluated && schema_evaluated && (!collect_evaluated_validate || schema.instance_valid?(instance))
|
|
780
|
+
end
|
|
781
|
+
end
|
|
686
782
|
|
|
687
|
-
|
|
783
|
+
if applicate_self
|
|
784
|
+
child_application = dialect.invoke(:child_applicate, Cxt::ChildApplication.new(
|
|
785
|
+
schema: self,
|
|
786
|
+
abort: false,
|
|
787
|
+
token: token,
|
|
788
|
+
instance: instance,
|
|
789
|
+
collect_evaluated: collect_evaluated,
|
|
790
|
+
collect_evaluated_validate: collect_evaluated_validate,
|
|
791
|
+
evaluated: inplace_child_evaluated,
|
|
792
|
+
block: block,
|
|
793
|
+
))
|
|
794
|
+
|
|
795
|
+
child_application.evaluated
|
|
796
|
+
else
|
|
797
|
+
inplace_child_evaluated
|
|
798
|
+
end
|
|
688
799
|
end
|
|
689
800
|
|
|
690
801
|
# any object property names this schema indicates may be present on its instances.
|
|
@@ -692,24 +803,13 @@ module JSI
|
|
|
692
803
|
# array of "required" property keys.
|
|
693
804
|
# @return [Set]
|
|
694
805
|
def described_object_property_names
|
|
695
|
-
@described_object_property_names_map[]
|
|
806
|
+
@described_object_property_names_map[schema_content: schema_content]
|
|
696
807
|
end
|
|
697
808
|
|
|
698
|
-
|
|
699
|
-
Set.new.tap do |property_names|
|
|
700
|
-
if schema_content.respond_to?(:to_hash) && schema_content['properties'].respond_to?(:to_hash)
|
|
701
|
-
property_names.merge(schema_content['properties'].keys)
|
|
702
|
-
end
|
|
703
|
-
if schema_content.respond_to?(:to_hash) && schema_content['required'].respond_to?(:to_ary)
|
|
704
|
-
property_names.merge(schema_content['required'].to_ary)
|
|
705
|
-
end
|
|
706
|
-
end.freeze
|
|
707
|
-
end
|
|
708
|
-
|
|
709
|
-
# validates the given instance against this schema
|
|
809
|
+
# Validates the given instance against this schema, returning a result with each validation error.
|
|
710
810
|
#
|
|
711
811
|
# @param instance [Object] the instance to validate against this schema
|
|
712
|
-
# @return [JSI::Validation::Result]
|
|
812
|
+
# @return [JSI::Validation::Result::Full]
|
|
713
813
|
def instance_validate(instance)
|
|
714
814
|
if instance.is_a?(SchemaAncestorNode)
|
|
715
815
|
instance_ptr = instance.jsi_ptr
|
|
@@ -731,6 +831,15 @@ module JSI
|
|
|
731
831
|
internal_validate_instance(Ptr[], instance, validate_only: true).valid?
|
|
732
832
|
end
|
|
733
833
|
|
|
834
|
+
# Asserts that the given instance is valid against this schema.
|
|
835
|
+
# {JSI::Invalid} is raised if it is not.
|
|
836
|
+
#
|
|
837
|
+
# @raise [Invalid]
|
|
838
|
+
# @return [nil]
|
|
839
|
+
def instance_valid!(instance)
|
|
840
|
+
instance_validate(instance).valid!
|
|
841
|
+
end
|
|
842
|
+
|
|
734
843
|
# validates the given instance against this schema
|
|
735
844
|
#
|
|
736
845
|
# @private
|
|
@@ -739,54 +848,160 @@ module JSI
|
|
|
739
848
|
# @param validate_only [Boolean] whether to return a full schema validation result or a simple, validation-only result
|
|
740
849
|
# @param visited_refs [Enumerable<JSI::Schema::Ref>]
|
|
741
850
|
# @return [JSI::Validation::Result]
|
|
742
|
-
def internal_validate_instance(
|
|
851
|
+
def internal_validate_instance(
|
|
852
|
+
instance_ptr,
|
|
853
|
+
instance_document,
|
|
854
|
+
visited_refs: Util::EMPTY_ARY,
|
|
855
|
+
validate_only: false
|
|
856
|
+
)
|
|
743
857
|
if validate_only
|
|
744
|
-
result = JSI::Validation::
|
|
858
|
+
result = JSI::Validation::Result::Valid.new
|
|
745
859
|
else
|
|
746
|
-
result = JSI::Validation::
|
|
860
|
+
result = JSI::Validation::Result::Full.new
|
|
747
861
|
end
|
|
748
|
-
result_builder = result.
|
|
862
|
+
result_builder = result.class::Builder.new(
|
|
863
|
+
result: result,
|
|
864
|
+
schema: self,
|
|
865
|
+
abort: false,
|
|
866
|
+
instance_ptr: instance_ptr,
|
|
867
|
+
instance_document: instance_document,
|
|
868
|
+
validate_only: validate_only,
|
|
869
|
+
visited_refs: visited_refs,
|
|
870
|
+
)
|
|
749
871
|
|
|
750
872
|
catch(:jsi_validation_result) do
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
# it's fine for them to behave the same as boolean schemas in later drafts.
|
|
754
|
-
# I don't care about draft 4 to implement a different structuring for that.
|
|
755
|
-
if schema_content == true
|
|
756
|
-
# noop
|
|
757
|
-
elsif schema_content == false
|
|
758
|
-
result_builder.validate(false, 'instance is not valid against `false` schema')
|
|
759
|
-
elsif schema_content.respond_to?(:to_hash)
|
|
760
|
-
internal_validate_keywords(result_builder)
|
|
761
|
-
else
|
|
762
|
-
result_builder.schema_error('schema is not a boolean or a JSON object')
|
|
763
|
-
end
|
|
873
|
+
dialect.invoke(:validate, result_builder)
|
|
874
|
+
|
|
764
875
|
result
|
|
765
876
|
end.freeze
|
|
766
877
|
end
|
|
767
878
|
|
|
879
|
+
# See {Base#jsi_as_child_default_as_jsi}. true for Schema, including boolean schemas.
|
|
880
|
+
def jsi_as_child_default_as_jsi
|
|
881
|
+
true
|
|
882
|
+
end
|
|
883
|
+
|
|
884
|
+
# @param action_name [Symbol]
|
|
885
|
+
# @param cxt_class [Class]
|
|
886
|
+
# @yield
|
|
887
|
+
def dialect_invoke_each(
|
|
888
|
+
action_name,
|
|
889
|
+
cxt_class = Cxt::Block,
|
|
890
|
+
**cxt_param,
|
|
891
|
+
&block
|
|
892
|
+
)
|
|
893
|
+
return(to_enum(__method__, action_name, cxt_class, **cxt_param)) unless block_given?
|
|
894
|
+
|
|
895
|
+
cxt = cxt_class.new(
|
|
896
|
+
schema: self,
|
|
897
|
+
abort: false,
|
|
898
|
+
block: block,
|
|
899
|
+
**cxt_param,
|
|
900
|
+
)
|
|
901
|
+
dialect.invoke(action_name, cxt)
|
|
902
|
+
|
|
903
|
+
nil
|
|
904
|
+
end
|
|
905
|
+
|
|
768
906
|
# schema resources which are ancestors of any subschemas below this schema.
|
|
769
907
|
# this may include this schema if this is a schema resource root.
|
|
770
908
|
# @api private
|
|
771
909
|
# @return [Array<JSI::Schema>]
|
|
772
910
|
def jsi_subschema_resource_ancestors
|
|
773
|
-
if
|
|
911
|
+
if jsi_is_resource_root?
|
|
774
912
|
jsi_schema_resource_ancestors.dup.push(self).freeze
|
|
775
913
|
else
|
|
776
914
|
jsi_schema_resource_ancestors
|
|
777
915
|
end
|
|
778
916
|
end
|
|
779
917
|
|
|
918
|
+
# @private
|
|
919
|
+
def jsi_next_schema_dynamic_anchor_map
|
|
920
|
+
return @memos[:next_schema_dynamic_anchor_map] if @memos.key?(:next_schema_dynamic_anchor_map)
|
|
921
|
+
|
|
922
|
+
if !dialect.elements.any? { |e| e.invokes?(:dynamicAnchor) }
|
|
923
|
+
return @memos[:next_schema_dynamic_anchor_map] = jsi_schema_dynamic_anchor_map
|
|
924
|
+
end
|
|
925
|
+
if !jsi_resource_root.is_a?(Schema)
|
|
926
|
+
# if the resource root is not a schema, then this schema does not add to dynamic_anchor_map.
|
|
927
|
+
# we could treat self as the anchor root, but that complicates DynamicAnchorMap#without_node.
|
|
928
|
+
return @memos[:next_schema_dynamic_anchor_map] = jsi_schema_dynamic_anchor_map
|
|
929
|
+
end
|
|
930
|
+
|
|
931
|
+
map = jsi_schema_dynamic_anchor_map
|
|
932
|
+
|
|
933
|
+
anchor_root = jsi_resource_root
|
|
934
|
+
descendent_schemas = [[anchor_root, Util::EMPTY_ARY]]
|
|
935
|
+
|
|
936
|
+
while !descendent_schemas.empty?
|
|
937
|
+
descendent_schema, ptrs = *descendent_schemas.shift
|
|
938
|
+
|
|
939
|
+
descendent_schema.dialect_invoke_each(:dynamicAnchor) do |anchor|
|
|
940
|
+
next if map.key?(anchor)
|
|
941
|
+
map = map.merge({
|
|
942
|
+
anchor => [anchor_root.jsi_with_schema_dynamic_anchor_map(Schema::DynamicAnchorMap::EMPTY), ptrs].freeze,
|
|
943
|
+
}).freeze
|
|
944
|
+
end
|
|
945
|
+
|
|
946
|
+
descendent_schema.each_immediate_subschema_ptr do |subptr|
|
|
947
|
+
# we want a schema at subptr to
|
|
948
|
+
# - check if it is a schema resource root
|
|
949
|
+
# - check for $dynamicAnchor
|
|
950
|
+
# can't use #subschema here (it would need to pass this method's result to instantiate the subschema);
|
|
951
|
+
# a minimal bootstrap schema is used instead.
|
|
952
|
+
# note: not using dialect.bootstrap_schema. this bootstrap is only used once, skip memoization.
|
|
953
|
+
descendent_subschema = MetaSchemaNode::BootstrapSchema.new(
|
|
954
|
+
dialect: dialect,
|
|
955
|
+
jsi_document: jsi_document,
|
|
956
|
+
jsi_ptr: descendent_schema.jsi_ptr + subptr,
|
|
957
|
+
# note: same as anchor_root.jsi_next_base_uri since we don't cross resource boundaries.
|
|
958
|
+
jsi_base_uri: descendent_schema.jsi_next_base_uri,
|
|
959
|
+
)
|
|
960
|
+
if !descendent_subschema.jsi_is_resource_root?
|
|
961
|
+
descendent_schemas.push([descendent_subschema, ptrs.dup.push(subptr).freeze])
|
|
962
|
+
end
|
|
963
|
+
end
|
|
964
|
+
end
|
|
965
|
+
|
|
966
|
+
@memos[:next_schema_dynamic_anchor_map] = map
|
|
967
|
+
end
|
|
968
|
+
|
|
969
|
+
# @private pending stronger stability of dynamic scope
|
|
970
|
+
def with_dynamic_scope_from(node)
|
|
971
|
+
node = node.jsi_node if node.is_a?(SchemaModule::Connection)
|
|
972
|
+
jsi_with_schema_dynamic_anchor_map(node.jsi_next_schema_dynamic_anchor_map)
|
|
973
|
+
end
|
|
974
|
+
|
|
975
|
+
# Does application require collection of evaluated children?
|
|
976
|
+
# (i.e. does the schema contain `unevaluatedItems` / `unevaluatedProperties`?)
|
|
977
|
+
# @private
|
|
978
|
+
# @return [Boolean]
|
|
979
|
+
attr_reader(:application_requires_evaluated)
|
|
980
|
+
|
|
981
|
+
# @private
|
|
982
|
+
# @return [#to_s, nil]
|
|
983
|
+
def jsi_schema_identifier(required: false)
|
|
984
|
+
name = jsi_schema_module_name_from_ancestor
|
|
985
|
+
return name if name
|
|
986
|
+
return schema_uri || (required ? jsi_ptr.uri : nil) if jsi_schema_dynamic_anchor_map.empty?
|
|
987
|
+
-"#{schema_uri || jsi_ptr.uri}#{jsi_schema_dynamic_anchor_map.anchor_schemas_identifier}"
|
|
988
|
+
end
|
|
989
|
+
|
|
780
990
|
private
|
|
781
991
|
|
|
992
|
+
KEY_BY_NONE = proc { nil }
|
|
993
|
+
|
|
782
994
|
def jsi_schema_initialize
|
|
783
|
-
|
|
784
|
-
|
|
995
|
+
# guard against being called twice on MetaSchemaNode, first from extend(Schema) then extend(jsi_schema_module) that includes Schema.
|
|
996
|
+
# both extends need to initialize for edge case of draft4's boolean schema that is not described by meta-schema.
|
|
997
|
+
instance_variable_defined?(:@jsi_schema_initialized) ? return : (@jsi_schema_initialized = true)
|
|
998
|
+
@schema_ref_map = Hash.new { |h, ref| h[ref] = Schema::Ref.new(ref, referrer: self) }
|
|
999
|
+
@schema_uris_map = jsi_memomap(key_by: KEY_BY_NONE) { to_enum(:schema_uris_compute).to_a.freeze }
|
|
1000
|
+
@described_object_property_names_map = jsi_memomap(key_by: KEY_BY_NONE) do
|
|
1001
|
+
Set.new(dialect_invoke_each(:described_object_property_names)).freeze
|
|
785
1002
|
end
|
|
786
|
-
@
|
|
787
|
-
@
|
|
788
|
-
@resource_root_subschema_map = jsi_memomap(&method(:resource_root_subschema_compute))
|
|
789
|
-
@described_object_property_names_map = jsi_memomap(&method(:described_object_property_names_compute))
|
|
1003
|
+
@application_requires_evaluated = dialect_invoke_each(:application_requires_evaluated).any?
|
|
1004
|
+
@inplace_application_requires_instance = dialect_invoke_each(:inplace_application_requires_instance).any?
|
|
790
1005
|
end
|
|
791
1006
|
end
|
|
792
1007
|
end
|