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/metaschema_node.rb
CHANGED
|
@@ -1,111 +1,195 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module JSI
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# the schema describing the root of the document is pointed to by root_schema_ptr.
|
|
4
|
+
# A MetaSchemaNode is a JSI instance representing a node in a document that contains a meta-schema,
|
|
5
|
+
# or contains a schema describing a meta-schema (e.g. a meta-schema vocabulary schema).
|
|
7
6
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
7
|
+
# A meta-schema typically has the unique property that it is an instance of itself.
|
|
8
|
+
# It may also be an instance of a number of other schemas, each of which
|
|
9
|
+
# may be an instance of the meta-schema, itself, and/or other schemas.
|
|
11
10
|
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# the
|
|
11
|
+
# This is not a configuration of schemas/instances that normal JSI::Base instantiation can accommodate.
|
|
12
|
+
# MetaSchemaNode instead bootstraps each node on initialization, computing and instantiating
|
|
13
|
+
# the schemas that describe it (other MetaSchemaNode instances) and their schema modules.
|
|
14
|
+
# This results in a node that is a meta-schema being an instance of its own schema module
|
|
15
|
+
# (as well as JSI::Schema and JSI::Schema::MetaSchema), and a node that is a schema being an instance of
|
|
16
|
+
# the meta-schema's schema module (and thereby JSI::Schema).
|
|
18
17
|
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# the
|
|
23
|
-
|
|
18
|
+
# The meta-schema may be anywhere in a document, though it is rare to put it anywhere but at the root.
|
|
19
|
+
# The root of the meta-schema is referenced by the {MetaSchemaNode::Conf configured}
|
|
20
|
+
# {MetaSchemaNode::Conf#metaschema_root_ref `metaschema_root_ref`}.
|
|
21
|
+
# The schema describing the root of the document is referenced by the configured
|
|
22
|
+
# {MetaSchemaNode::Conf#root_schema_ref `root_schema_ref`}.
|
|
23
|
+
class MetaSchemaNode < Base
|
|
24
24
|
autoload :BootstrapSchema, 'jsi/metaschema_node/bootstrap_schema'
|
|
25
25
|
|
|
26
|
-
# @
|
|
27
|
-
#
|
|
28
|
-
# @param
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
# @private - experimental
|
|
27
|
+
# `self` is a MetaSchemaNode
|
|
28
|
+
# @param node [MetaSchemaNode, MetaSchemaNode::BootstrapSchema]
|
|
29
|
+
# @return [Boolean] is `node` a meta-schema?
|
|
30
|
+
DEFAULT_IS_METASCHEMA = proc do |node|
|
|
31
|
+
node.jsi_document.equal?(@bootstrap_metaschema.jsi_document) && node.jsi_ptr == @bootstrap_metaschema.jsi_ptr
|
|
32
|
+
end
|
|
33
|
+
private_constant(:DEFAULT_IS_METASCHEMA)
|
|
34
|
+
|
|
35
|
+
include(Base::Immutable)
|
|
36
|
+
|
|
37
|
+
conf_attrs = {
|
|
38
|
+
dialect: {fingerprint: true },
|
|
39
|
+
metaschema_root_ref: {fingerprint: true },
|
|
40
|
+
root_schema_ref: {fingerprint: true },
|
|
41
|
+
bootstrap_registry: {fingerprint: true },
|
|
42
|
+
is_metaschema: {fingerprint: true },
|
|
43
|
+
}.freeze
|
|
44
|
+
Conf = Base::Conf.subclass(*conf_attrs.keys)
|
|
45
|
+
Conf::ATTRS = Base::Conf::ATTRS.merge(conf_attrs)
|
|
46
|
+
|
|
47
|
+
# {Base::Conf} with additional configuration for MetaSchemaNode.
|
|
48
|
+
#
|
|
49
|
+
# @!attribute dialect
|
|
50
|
+
# @return [Schema::Dialect]
|
|
51
|
+
# @!attribute metaschema_root_ref
|
|
52
|
+
# URI reference to the root of the meta-schema.
|
|
53
|
+
#
|
|
54
|
+
# Default: `"#"` resolves to the root of the `jsi_document`.
|
|
55
|
+
# @return [Addressable::URI]
|
|
56
|
+
# @!attribute root_schema_ref
|
|
57
|
+
# URI reference to the schema describing the root of the jsi_document.
|
|
58
|
+
# When schemas of the meta-schema are in multiple documents, this describes the roots of all instantiated documents.
|
|
59
|
+
#
|
|
60
|
+
# Default: `metaschema_root_ref` value, i.e. the document root is a schema.
|
|
61
|
+
# @return [Addressable::URI]
|
|
62
|
+
# @!attribute bootstrap_registry
|
|
63
|
+
# Default: nil
|
|
64
|
+
# @return [Registry, nil]
|
|
65
|
+
class Conf < Base::Conf
|
|
66
|
+
def initialize(
|
|
67
|
+
dialect: ,
|
|
68
|
+
metaschema_root_ref: '#',
|
|
69
|
+
root_schema_ref: metaschema_root_ref,
|
|
70
|
+
registry: nil, # overrides Base::Conf default value JSI.registry
|
|
71
|
+
is_metaschema: DEFAULT_IS_METASCHEMA,
|
|
72
|
+
**kw
|
|
73
|
+
)
|
|
74
|
+
super(
|
|
75
|
+
dialect: dialect,
|
|
76
|
+
metaschema_root_ref: Util.uri(metaschema_root_ref, nnil: true),
|
|
77
|
+
root_schema_ref: Util.uri(root_schema_ref, nnil: true),
|
|
78
|
+
registry: registry,
|
|
79
|
+
is_metaschema: is_metaschema,
|
|
80
|
+
**kw,
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# See {JSI.new_metaschema_node} to instantiate.
|
|
86
|
+
#
|
|
87
|
+
# Note: when instantiating MetaSchemaNode directly, the caller must invoke #jsi_initialize_finish.
|
|
88
|
+
# @api private
|
|
89
|
+
# @param jsi_ptr [JSI::Ptr] ptr to this MetaSchemaNode in jsi_document
|
|
35
90
|
def initialize(
|
|
36
|
-
jsi_document,
|
|
37
91
|
jsi_ptr: Ptr[],
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
root_schema_ptr: Ptr[],
|
|
41
|
-
jsi_schema_base_uri: nil,
|
|
42
|
-
jsi_schema_registry: nil,
|
|
43
|
-
jsi_root_node: nil
|
|
92
|
+
jsi_root_node: nil,
|
|
93
|
+
**kw
|
|
44
94
|
)
|
|
45
|
-
super(
|
|
95
|
+
super(
|
|
46
96
|
jsi_ptr: jsi_ptr,
|
|
47
97
|
jsi_indicated_schemas: SchemaSet[],
|
|
48
|
-
|
|
49
|
-
|
|
98
|
+
# MSN doesn't track schema_resource_ancestors through descendents, but the root is included when appropriate
|
|
99
|
+
jsi_schema_resource_ancestors: jsi_ptr.root? || !jsi_root_node.is_a?(Schema) ? Util::EMPTY_ARY : [jsi_root_node].freeze,
|
|
50
100
|
jsi_root_node: jsi_root_node,
|
|
101
|
+
**kw,
|
|
51
102
|
)
|
|
52
103
|
|
|
53
|
-
@
|
|
54
|
-
@
|
|
55
|
-
@
|
|
104
|
+
@initialize_finish_started = false
|
|
105
|
+
@initialize_finished = false
|
|
106
|
+
@to_initialize_finish = []
|
|
56
107
|
|
|
57
|
-
if jsi_ptr.root? &&
|
|
58
|
-
raise(NotImplementedError, "unsupported
|
|
108
|
+
if jsi_ptr.root? && jsi_base_uri
|
|
109
|
+
raise(NotImplementedError, "unsupported jsi_base_uri on meta-schema document root")
|
|
59
110
|
end
|
|
60
111
|
|
|
61
|
-
|
|
112
|
+
#chkbug fail(Bug, 'MetaSchemaNode instance must be frozen') unless jsi_node_content.frozen?
|
|
113
|
+
|
|
114
|
+
bootstrap_schema_from_ref = proc do |ref_uri|
|
|
115
|
+
ref_uri_nofrag = ref_uri.merge(fragment: nil)
|
|
116
|
+
|
|
117
|
+
if ref_uri_nofrag.empty?
|
|
118
|
+
ptr = Ptr.from_fragment(ref_uri.fragment).resolve_against(jsi_document) # anchor not supported
|
|
119
|
+
if jsi_conf.root_schema_ref == jsi_conf.metaschema_root_ref
|
|
120
|
+
# root is a schema
|
|
121
|
+
jsi_conf.dialect.bootstrap_schema(
|
|
122
|
+
jsi_document: jsi_document,
|
|
123
|
+
jsi_base_uri: nil, # not supported
|
|
124
|
+
jsi_registry: jsi_conf.bootstrap_registry,
|
|
125
|
+
).resource_root_subschema(ptr)
|
|
126
|
+
else
|
|
127
|
+
jsi_conf.dialect.bootstrap_schema(
|
|
128
|
+
jsi_document: jsi_document,
|
|
129
|
+
jsi_ptr: ptr,
|
|
130
|
+
jsi_base_uri: nil, # not supported
|
|
131
|
+
jsi_registry: jsi_conf.bootstrap_registry,
|
|
132
|
+
)
|
|
133
|
+
end
|
|
134
|
+
else
|
|
135
|
+
# if not fragment-only, ref must be registered in the bootstrap_registry
|
|
136
|
+
ref = Schema::Ref.new(ref_uri, registry: jsi_conf.bootstrap_registry)
|
|
137
|
+
ref.resolve
|
|
138
|
+
end
|
|
139
|
+
end
|
|
62
140
|
|
|
63
|
-
|
|
141
|
+
@bootstrap_metaschema = bootstrap_schema_from_ref[jsi_conf.metaschema_root_ref]
|
|
64
142
|
|
|
65
143
|
instance_for_schemas = jsi_document
|
|
66
|
-
|
|
67
|
-
root_bootstrap_schema = bootstrap_schema_class.new(
|
|
68
|
-
jsi_document,
|
|
69
|
-
jsi_ptr: root_schema_ptr,
|
|
70
|
-
jsi_schema_base_uri: nil, # supplying jsi_schema_base_uri on root bootstrap schema is not supported
|
|
71
|
-
)
|
|
144
|
+
root_bootstrap_schema = bootstrap_schema_from_ref[jsi_conf.root_schema_ref]
|
|
72
145
|
our_bootstrap_indicated_schemas = jsi_ptr.tokens.inject(SchemaSet[root_bootstrap_schema]) do |bootstrap_indicated_schemas, tok|
|
|
73
|
-
|
|
74
|
-
|
|
146
|
+
child_indicated_schemas = bootstrap_indicated_schemas.each_yield_set do |is, y|
|
|
147
|
+
is.each_inplace_child_applicator_schema(tok, instance_for_schemas,
|
|
148
|
+
collect_evaluated_validate: jsi_conf.application_collect_evaluated_validate,
|
|
149
|
+
&y
|
|
150
|
+
)
|
|
151
|
+
end
|
|
75
152
|
instance_for_schemas = instance_for_schemas[tok]
|
|
76
153
|
child_indicated_schemas
|
|
77
154
|
end
|
|
78
|
-
@indicated_schemas_map = jsi_memomap
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
155
|
+
@indicated_schemas_map = jsi_memomap do
|
|
156
|
+
SchemaSet.new(our_bootstrap_indicated_schemas) { |s| bootstrap_schema_to_msn(s) }
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
@bootstrap_schemas = our_bootstrap_indicated_schemas.each_yield_set do |is, y|
|
|
160
|
+
is.each_inplace_applicator_schema(instance_for_schemas, &y) # note: instance_for_schemas == jsi_node_content now
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
@jsi_schemas = @bootstrap_schemas
|
|
164
|
+
|
|
165
|
+
@bootstrap_schemas.each do |bootstrap_schema|
|
|
166
|
+
if instance_exec(bootstrap_schema, &jsi_conf.is_metaschema)
|
|
167
|
+
# this is described by the meta-schema, i.e. this is a schema
|
|
168
|
+
define_singleton_method(:dialect) { jsi_conf.dialect }
|
|
169
|
+
extend(Schema)
|
|
170
|
+
|
|
171
|
+
if jsi_registry && jsi_resource_uris.any? { |uri| !jsi_registry.registered?(uri) }
|
|
172
|
+
jsi_registry.register_immediate(self)
|
|
87
173
|
end
|
|
88
|
-
extends += schema_implementation_modules
|
|
89
|
-
end
|
|
90
|
-
if bootstrap_schema.jsi_ptr == jsi_ptr
|
|
91
|
-
# this is the metaschema (it is described by itself)
|
|
92
|
-
extend Metaschema
|
|
93
|
-
extends << Metaschema
|
|
94
174
|
end
|
|
95
175
|
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# @api private
|
|
179
|
+
def jsi_initialize_finish
|
|
180
|
+
return self if @initialize_finish_started
|
|
181
|
+
@initialize_finish_started = true
|
|
96
182
|
|
|
97
|
-
@jsi_schemas =
|
|
183
|
+
@jsi_schemas = SchemaSet.new(@bootstrap_schemas) { |s| bootstrap_schema_to_msn(s) }
|
|
98
184
|
|
|
99
185
|
# note: jsi_schemas must already be set for jsi_schema_module to be used/extended
|
|
100
|
-
if
|
|
101
|
-
describes_schema!(
|
|
186
|
+
if instance_exec(self, &jsi_conf.is_metaschema)
|
|
187
|
+
describes_schema!(jsi_conf.dialect)
|
|
102
188
|
end
|
|
103
189
|
|
|
104
190
|
extends_for_instance = JSI::SchemaClasses.includes_for(jsi_node_content)
|
|
105
|
-
extends.merge(extends_for_instance)
|
|
106
|
-
extends.freeze
|
|
107
191
|
|
|
108
|
-
conflicting_modules = Set[self.class] +
|
|
192
|
+
conflicting_modules = Set[self.class] + extends_for_instance + @jsi_schemas.map(&:jsi_schema_module)
|
|
109
193
|
reader_modules = @jsi_schemas.map do |schema|
|
|
110
194
|
JSI::SchemaClasses.schema_property_reader_module(schema, conflicting_modules: conflicting_modules)
|
|
111
195
|
end
|
|
@@ -122,21 +206,17 @@ module JSI
|
|
|
122
206
|
@jsi_schemas.each do |schema|
|
|
123
207
|
extend schema.jsi_schema_module
|
|
124
208
|
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# Set of modules to apply to schemas which are instances of (described by) the metaschema
|
|
128
|
-
# @return [Set<Module>]
|
|
129
|
-
attr_reader :schema_implementation_modules
|
|
130
209
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
210
|
+
@initialize_finished = true
|
|
211
|
+
while !@to_initialize_finish.empty?
|
|
212
|
+
node = @to_initialize_finish.shift
|
|
213
|
+
node.jsi_initialize_finish
|
|
214
|
+
end
|
|
134
215
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
attr_reader :root_schema_ptr
|
|
216
|
+
jsi_initialized
|
|
217
|
+
end
|
|
138
218
|
|
|
139
|
-
# JSI Schemas describing this
|
|
219
|
+
# JSI Schemas describing this MetaSchemaNode
|
|
140
220
|
# @return [JSI::SchemaSet]
|
|
141
221
|
attr_reader :jsi_schemas
|
|
142
222
|
|
|
@@ -146,15 +226,10 @@ module JSI
|
|
|
146
226
|
@indicated_schemas_map[]
|
|
147
227
|
end
|
|
148
228
|
|
|
149
|
-
# see {Base#
|
|
150
|
-
def
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
jsi_child_as_jsi(jsi_node_content_child(token), child_node.jsi_schemas, as_jsi) do
|
|
154
|
-
child_node
|
|
155
|
-
end
|
|
229
|
+
# see {Base#jsi_child_node}
|
|
230
|
+
def jsi_child_node(token)
|
|
231
|
+
root_descendent_node(jsi_ptr[token])
|
|
156
232
|
end
|
|
157
|
-
private :jsi_child
|
|
158
233
|
|
|
159
234
|
# See {Base#jsi_default_child}
|
|
160
235
|
def jsi_default_child(token, as_jsi: )
|
|
@@ -162,41 +237,31 @@ module JSI
|
|
|
162
237
|
end
|
|
163
238
|
private :jsi_default_child # internals for #[] but idk, could be public
|
|
164
239
|
|
|
165
|
-
#
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
modified_document = jsi_ptr.modified_document_copy(jsi_document, &block)
|
|
172
|
-
MetaschemaNode.new(modified_document, **our_initialize_params)
|
|
173
|
-
else
|
|
174
|
-
modified_jsi_root_node = jsi_root_node.jsi_modified_copy do |root|
|
|
175
|
-
jsi_ptr.modified_document_copy(root, &block)
|
|
176
|
-
end
|
|
177
|
-
modified_jsi_root_node.jsi_descendent_node(jsi_ptr)
|
|
178
|
-
end
|
|
240
|
+
# @raise [NotImplementedError] not implemented for MetaSchemaNode
|
|
241
|
+
def jsi_modified_copy(**)
|
|
242
|
+
# this had an implementation previously (see git history). but for 2020-12, support for multiple
|
|
243
|
+
# mutually-descriptive schemas as well as dynamic scope makes instantiating a modified copy that
|
|
244
|
+
# preserves self-descriptive/mutually-descriptive relationships generally infeasible.
|
|
245
|
+
raise(NotImplementedError)
|
|
179
246
|
end
|
|
180
247
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
class_n_schemas = -"#{self.class} (#{jsi_schemas.map { |s| s.jsi_schema_module.name_from_ancestor || s.jsi_ptr.uri }.join(' ')})"
|
|
186
|
-
else
|
|
187
|
-
class_n_schemas = self.class.to_s
|
|
188
|
-
end
|
|
189
|
-
[
|
|
190
|
-
class_n_schemas,
|
|
191
|
-
is_a?(Metaschema) ? "Metaschema" : is_a?(Schema) ? "Schema" : nil,
|
|
192
|
-
*(jsi_node_content.respond_to?(:jsi_object_group_text) ? jsi_node_content.jsi_object_group_text : nil),
|
|
193
|
-
].compact
|
|
248
|
+
protected def jsi_dynamic_root_instantiate(**kw)
|
|
249
|
+
# self is a resource root being instantiated with overridden dynamic scope
|
|
250
|
+
#chkbug fail unless jsi_ptr.root? # jsi_schema_resource_ancestors not tracked
|
|
251
|
+
to_initialize_finish(MetaSchemaNode.new(**kw))
|
|
194
252
|
end
|
|
195
253
|
|
|
196
254
|
# see {Util::Private::FingerprintHash}
|
|
197
255
|
# @api private
|
|
198
256
|
def jsi_fingerprint
|
|
199
|
-
{
|
|
257
|
+
{
|
|
258
|
+
class: self.class,
|
|
259
|
+
jsi_document: jsi_document,
|
|
260
|
+
jsi_ptr: jsi_ptr,
|
|
261
|
+
jsi_base_uri: jsi_base_uri,
|
|
262
|
+
jsi_schema_dynamic_anchor_map: jsi_schema_dynamic_anchor_map,
|
|
263
|
+
**jsi_conf.for_fingerprint,
|
|
264
|
+
}.freeze
|
|
200
265
|
end
|
|
201
266
|
|
|
202
267
|
protected
|
|
@@ -206,55 +271,80 @@ module JSI
|
|
|
206
271
|
private
|
|
207
272
|
|
|
208
273
|
def jsi_memomaps_initialize
|
|
209
|
-
if
|
|
210
|
-
@root_descendent_node_map = jsi_memomap(
|
|
274
|
+
if equal?(@jsi_root_node)
|
|
275
|
+
@root_descendent_node_map = jsi_memomap(&method(:jsi_root_descendent_node_compute))
|
|
211
276
|
else
|
|
212
277
|
@root_descendent_node_map = @jsi_root_node.root_descendent_node_map
|
|
213
278
|
end
|
|
214
279
|
end
|
|
215
280
|
|
|
216
|
-
# note: does not include jsi_root_node
|
|
217
|
-
def our_initialize_params
|
|
218
|
-
{
|
|
219
|
-
jsi_ptr: jsi_ptr,
|
|
220
|
-
schema_implementation_modules: schema_implementation_modules,
|
|
221
|
-
metaschema_root_ptr: metaschema_root_ptr,
|
|
222
|
-
root_schema_ptr: root_schema_ptr,
|
|
223
|
-
jsi_schema_base_uri: jsi_schema_base_uri,
|
|
224
|
-
jsi_schema_registry: jsi_schema_registry,
|
|
225
|
-
}
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
# note: not for root node
|
|
229
|
-
def new_node(**params)
|
|
230
|
-
MetaschemaNode.new(jsi_document, jsi_root_node: jsi_root_node, **our_initialize_params, **params)
|
|
231
|
-
end
|
|
232
|
-
|
|
233
281
|
def jsi_root_descendent_node_compute(ptr: )
|
|
234
|
-
#
|
|
282
|
+
# note: self is jsi_root_node
|
|
283
|
+
#chkbug fail(Bug) unless equal?(jsi_root_node)
|
|
235
284
|
if ptr.root?
|
|
236
285
|
self
|
|
237
286
|
else
|
|
238
|
-
|
|
287
|
+
MetaSchemaNode.new(
|
|
288
|
+
jsi_document: jsi_document,
|
|
239
289
|
jsi_ptr: ptr,
|
|
240
|
-
|
|
290
|
+
jsi_base_uri: jsi_next_base_uri,
|
|
291
|
+
# since MSN only supports document root as jsi_resource_root, and jsi_next_schema_dynamic_anchor_map
|
|
292
|
+
# is only passed to descendents that are resource roots, that is not used here.
|
|
293
|
+
jsi_schema_dynamic_anchor_map: jsi_schema_dynamic_anchor_map,
|
|
294
|
+
jsi_root_node: jsi_root_node,
|
|
241
295
|
)
|
|
242
296
|
end
|
|
243
297
|
end
|
|
244
298
|
|
|
245
|
-
# @param
|
|
246
|
-
# @return [
|
|
247
|
-
def
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
299
|
+
# @param ptr [Ptr]
|
|
300
|
+
# @return [MetaSchemaNode]
|
|
301
|
+
protected def root_descendent_node(ptr)
|
|
302
|
+
to_initialize_finish(@root_descendent_node_map[
|
|
303
|
+
ptr: ptr.resolve_against(jsi_document),
|
|
304
|
+
])
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def to_initialize_finish(node)
|
|
308
|
+
if @initialize_finished
|
|
309
|
+
node.jsi_initialize_finish
|
|
310
|
+
else
|
|
311
|
+
@to_initialize_finish.push(node)
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
node
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# @param bootstrap_schema [MetaSchemaNode::BootstrapSchema]
|
|
318
|
+
# @return [MetaSchemaNode]
|
|
319
|
+
def bootstrap_schema_to_msn(bootstrap_schema)
|
|
320
|
+
dynamic_anchor_map = Schema::DynamicAnchorMap::EMPTY
|
|
321
|
+
bootstrap_schema.jsi_schema_dynamic_anchor_map.each do |anchor, (bootstrap_anchor_root, anchor_ptrs)|
|
|
322
|
+
msn_anchor_root = bootstrap_schema_to_msn(bootstrap_anchor_root)
|
|
323
|
+
dynamic_anchor_map = dynamic_anchor_map.merge({
|
|
324
|
+
anchor => [msn_anchor_root, anchor_ptrs].freeze,
|
|
325
|
+
}).freeze
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
if bootstrap_schema.jsi_document.equal?(jsi_document)
|
|
329
|
+
root_descendent_node(bootstrap_schema.jsi_ptr).jsi_with_schema_dynamic_anchor_map(dynamic_anchor_map)
|
|
330
|
+
else
|
|
331
|
+
jsi_registry || raise(ResolutionError, "no jsi_registry")
|
|
332
|
+
bootstrap_resource = bootstrap_schema.jsi_resource_root
|
|
333
|
+
resource_uri = bootstrap_resource.jsi_resource_uri || raise(ResolutionError, "no URI: #{bootstrap_resource}")
|
|
334
|
+
if jsi_registry.registered?(resource_uri)
|
|
335
|
+
resource = jsi_registry.find(resource_uri)
|
|
336
|
+
relative_ptr = bootstrap_schema.jsi_ptr.relative_to(bootstrap_resource.jsi_ptr)
|
|
337
|
+
resource.jsi_descendent_node(relative_ptr).jsi_with_schema_dynamic_anchor_map(dynamic_anchor_map)
|
|
253
338
|
else
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
339
|
+
root = to_initialize_finish(MetaSchemaNode.new(
|
|
340
|
+
jsi_document: bootstrap_schema.jsi_document,
|
|
341
|
+
jsi_ptr: Ptr[],
|
|
342
|
+
jsi_base_uri: nil,
|
|
343
|
+
# this seems like the best dynamic_anchor_map to pass
|
|
344
|
+
jsi_schema_dynamic_anchor_map: dynamic_anchor_map.without_node(nil, document: bootstrap_schema.jsi_document, ptr: Ptr[], registry: jsi_registry),
|
|
345
|
+
jsi_conf: jsi_conf,
|
|
346
|
+
))
|
|
347
|
+
root.jsi_descendent_node(bootstrap_schema.jsi_ptr).jsi_with_schema_dynamic_anchor_map(dynamic_anchor_map)
|
|
258
348
|
end
|
|
259
349
|
end
|
|
260
350
|
end
|
data/lib/jsi/ptr.rb
CHANGED
|
@@ -13,7 +13,7 @@ module JSI
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
# raised when a pointer refers to a path in a document that could not be resolved
|
|
16
|
-
class ResolutionError <
|
|
16
|
+
class ResolutionError < JSI::ResolutionError
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
POS_INT_RE = /\A[1-9]\d*\z/
|
|
@@ -25,6 +25,8 @@ module JSI
|
|
|
25
25
|
def self.ary_ptr(ary_ptr)
|
|
26
26
|
if ary_ptr.is_a?(Ptr)
|
|
27
27
|
ary_ptr
|
|
28
|
+
elsif ary_ptr == Util::EMPTY_ARY
|
|
29
|
+
EMPTY
|
|
28
30
|
else
|
|
29
31
|
new(ary_ptr)
|
|
30
32
|
end
|
|
@@ -59,9 +61,9 @@ module JSI
|
|
|
59
61
|
# => JSI::Ptr["foo bar"]
|
|
60
62
|
#
|
|
61
63
|
# Note: A fragment does not include a leading '#'. The string "#/foo" is a URI containing the
|
|
62
|
-
# fragment "/foo", which should be parsed by `
|
|
64
|
+
# fragment "/foo", which should be parsed by `JSI::URI` before passing to this method, e.g.:
|
|
63
65
|
#
|
|
64
|
-
# JSI::Ptr.from_fragment(
|
|
66
|
+
# JSI::Ptr.from_fragment(JSI::URI["#/foo"].fragment)
|
|
65
67
|
# => JSI::Ptr["foo"]
|
|
66
68
|
#
|
|
67
69
|
# @param fragment [String] a fragment containing a pointer
|
|
@@ -69,7 +71,7 @@ module JSI
|
|
|
69
71
|
# @raise [JSI::Ptr::PointerSyntaxError] when the fragment does not contain a pointer with
|
|
70
72
|
# valid pointer syntax
|
|
71
73
|
def self.from_fragment(fragment)
|
|
72
|
-
from_pointer(
|
|
74
|
+
from_pointer(URI.unescape(fragment))
|
|
73
75
|
end
|
|
74
76
|
|
|
75
77
|
# parse a pointer string and instantiate as a JSI::Ptr
|
|
@@ -127,6 +129,24 @@ module JSI
|
|
|
127
129
|
res
|
|
128
130
|
end
|
|
129
131
|
|
|
132
|
+
# Resolves each token of this pointer in `document`, in particular resolving strings indicating
|
|
133
|
+
# array indices to integers.
|
|
134
|
+
# @param document [Object]
|
|
135
|
+
# @return [Ptr]
|
|
136
|
+
def resolve_against(document)
|
|
137
|
+
return(self) if tokens.empty?
|
|
138
|
+
node = document
|
|
139
|
+
resolved_tokens = nil
|
|
140
|
+
tokens.each_with_index do |token, i|
|
|
141
|
+
resolved_token, node = node_subscript_token_child(node, token)
|
|
142
|
+
next if resolved_token.equal?(token)
|
|
143
|
+
resolved_tokens ||= tokens.dup
|
|
144
|
+
resolved_tokens[i] = resolved_token
|
|
145
|
+
end
|
|
146
|
+
return(self) if !resolved_tokens
|
|
147
|
+
Ptr.new(resolved_tokens.freeze)
|
|
148
|
+
end
|
|
149
|
+
|
|
130
150
|
# the pointer string representation of this pointer
|
|
131
151
|
# @return [String]
|
|
132
152
|
def pointer
|
|
@@ -136,13 +156,13 @@ module JSI
|
|
|
136
156
|
# the fragment string representation of this pointer
|
|
137
157
|
# @return [String]
|
|
138
158
|
def fragment
|
|
139
|
-
|
|
159
|
+
URI.escape(pointer).freeze
|
|
140
160
|
end
|
|
141
161
|
|
|
142
162
|
# a URI consisting of a fragment containing this pointer's fragment string representation
|
|
143
|
-
# @return [
|
|
163
|
+
# @return [URI]
|
|
144
164
|
def uri
|
|
145
|
-
|
|
165
|
+
URI.new(fragment: fragment).freeze
|
|
146
166
|
end
|
|
147
167
|
|
|
148
168
|
# whether this pointer is empty, i.e. it has no tokens
|
|
@@ -165,10 +185,11 @@ module JSI
|
|
|
165
185
|
tokens.size == 1 ? EMPTY : Ptr.new(tokens[0...-1].freeze)
|
|
166
186
|
end
|
|
167
187
|
|
|
168
|
-
# whether this pointer
|
|
169
|
-
#
|
|
188
|
+
# whether this pointer is an ancestor of `other_ptr`, a descendent pointer.
|
|
189
|
+
# `ancestor_of?` is inclusive; a pointer is an ancestor of itself.
|
|
190
|
+
#
|
|
170
191
|
# @return [Boolean]
|
|
171
|
-
def
|
|
192
|
+
def ancestor_of?(other_ptr)
|
|
172
193
|
tokens == other_ptr.tokens[0...tokens.size]
|
|
173
194
|
end
|
|
174
195
|
|
|
@@ -176,7 +197,8 @@ module JSI
|
|
|
176
197
|
# @return [JSI::Ptr]
|
|
177
198
|
# @raise [JSI::Ptr::Error] if the given ancestor_ptr is not an ancestor of this pointer
|
|
178
199
|
def relative_to(ancestor_ptr)
|
|
179
|
-
|
|
200
|
+
return self if ancestor_ptr.empty?
|
|
201
|
+
unless ancestor_ptr.ancestor_of?(self)
|
|
180
202
|
raise(Error, "ancestor_ptr #{ancestor_ptr.inspect} is not ancestor of #{inspect}")
|
|
181
203
|
end
|
|
182
204
|
ancestor_ptr.tokens.size == tokens.size ? EMPTY : Ptr.new(tokens[ancestor_ptr.tokens.size..-1].freeze)
|
|
@@ -187,6 +209,7 @@ module JSI
|
|
|
187
209
|
# @return [JSI::Ptr]
|
|
188
210
|
def +(ptr)
|
|
189
211
|
if ptr.is_a?(Ptr)
|
|
212
|
+
return(ptr) if tokens.empty?
|
|
190
213
|
ptr_tokens = ptr.tokens
|
|
191
214
|
elsif ptr.respond_to?(:to_ary)
|
|
192
215
|
ptr_tokens = ptr
|
|
@@ -201,10 +224,12 @@ module JSI
|
|
|
201
224
|
# @return [JSI::Ptr]
|
|
202
225
|
# @raise [ArgumentError] if n is not between 0 and the size of our tokens
|
|
203
226
|
def take(n)
|
|
227
|
+
return(EMPTY) if n == 0
|
|
228
|
+
return(self) if n == tokens.size
|
|
204
229
|
unless n.is_a?(Integer) && n >= 0 && n <= tokens.size
|
|
205
230
|
raise(ArgumentError, "n not in range (0..#{tokens.size}): #{n.inspect}")
|
|
206
231
|
end
|
|
207
|
-
|
|
232
|
+
Ptr.new(tokens.take(n).freeze)
|
|
208
233
|
end
|
|
209
234
|
|
|
210
235
|
# appends the given token to this pointer's tokens and returns the result
|
|
@@ -237,16 +262,16 @@ module JSI
|
|
|
237
262
|
Util.modified_copy(document, &block)
|
|
238
263
|
else
|
|
239
264
|
car = tokens[0]
|
|
240
|
-
cdr = Ptr.new(tokens[1..-1].freeze)
|
|
265
|
+
cdr = tokens.size == 1 ? EMPTY : Ptr.new(tokens[1..-1].freeze)
|
|
241
266
|
token, document_child = node_subscript_token_child(document, car)
|
|
242
267
|
modified_document_child = cdr.modified_document_copy(document_child, &block)
|
|
243
|
-
if modified_document_child.
|
|
268
|
+
if modified_document_child.equal?(document_child)
|
|
244
269
|
document
|
|
245
270
|
else
|
|
246
271
|
modified_document = document.respond_to?(:[]=) ? document.dup :
|
|
247
272
|
document.respond_to?(:to_hash) ? document.to_hash.dup :
|
|
248
273
|
document.respond_to?(:to_ary) ? document.to_ary.dup :
|
|
249
|
-
|
|
274
|
+
fail(Bug) # not possible; node_subscript_token_child would have raised
|
|
250
275
|
modified_document[token] = modified_document_child
|
|
251
276
|
modified_document
|
|
252
277
|
end
|
|
@@ -259,12 +284,14 @@ module JSI
|
|
|
259
284
|
-"#{self.class.name}[#{tokens.map(&:inspect).join(", ")}]"
|
|
260
285
|
end
|
|
261
286
|
|
|
262
|
-
|
|
287
|
+
def to_s
|
|
288
|
+
inspect
|
|
289
|
+
end
|
|
263
290
|
|
|
264
291
|
# see {Util::Private::FingerprintHash}
|
|
265
292
|
# @api private
|
|
266
293
|
def jsi_fingerprint
|
|
267
|
-
{class: Ptr, tokens: tokens}
|
|
294
|
+
{class: Ptr, tokens: tokens}.freeze
|
|
268
295
|
end
|
|
269
296
|
include Util::FingerprintHash::Immutable
|
|
270
297
|
|
|
@@ -273,6 +300,7 @@ module JSI
|
|
|
273
300
|
private
|
|
274
301
|
|
|
275
302
|
def node_subscript_token_child(value, token, *a, **kw)
|
|
303
|
+
token = token.jsi_node_content if token.is_a?(Schema::SchemaAncestorNode)
|
|
276
304
|
if value.respond_to?(:to_ary)
|
|
277
305
|
if token.is_a?(String) && (token == '0' || token =~ POS_INT_RE)
|
|
278
306
|
token = token.to_i
|