jsi 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -2
  3. data/CHANGELOG.md +9 -0
  4. data/LICENSE.md +2 -3
  5. data/README.md +68 -31
  6. data/docs/Glossary.md +313 -0
  7. data/jsi.gemspec +1 -0
  8. data/lib/jsi/base/mutability.rb +4 -0
  9. data/lib/jsi/base/node.rb +63 -24
  10. data/lib/jsi/base.rb +556 -173
  11. data/lib/jsi/metaschema_node/bootstrap_schema.rb +106 -56
  12. data/lib/jsi/metaschema_node.rb +227 -160
  13. data/lib/jsi/ptr.rb +32 -15
  14. data/lib/jsi/ref.rb +197 -0
  15. data/lib/jsi/registry.rb +311 -0
  16. data/lib/jsi/schema/cxt/child_application.rb +35 -0
  17. data/lib/jsi/schema/cxt/inplace_application.rb +37 -0
  18. data/lib/jsi/schema/cxt.rb +80 -0
  19. data/lib/jsi/schema/dialect.rb +137 -0
  20. data/lib/jsi/schema/draft04.rb +113 -5
  21. data/lib/jsi/schema/draft06.rb +123 -5
  22. data/lib/jsi/schema/draft07.rb +157 -5
  23. data/lib/jsi/schema/draft202012.rb +303 -0
  24. data/lib/jsi/schema/dynamic_anchor_map.rb +63 -0
  25. data/lib/jsi/schema/element.rb +69 -0
  26. data/lib/jsi/schema/elements/anchor.rb +13 -0
  27. data/lib/jsi/schema/elements/array_validation.rb +82 -0
  28. data/lib/jsi/schema/elements/comment.rb +10 -0
  29. data/lib/jsi/schema/{validation → elements}/const.rb +11 -7
  30. data/lib/jsi/schema/elements/contains.rb +59 -0
  31. data/lib/jsi/schema/elements/contains_minmax.rb +91 -0
  32. data/lib/jsi/schema/elements/content_encoding.rb +10 -0
  33. data/lib/jsi/schema/elements/content_media_type.rb +10 -0
  34. data/lib/jsi/schema/elements/content_schema.rb +16 -0
  35. data/lib/jsi/schema/elements/default.rb +11 -0
  36. data/lib/jsi/schema/elements/definitions.rb +19 -0
  37. data/lib/jsi/schema/elements/dependencies.rb +99 -0
  38. data/lib/jsi/schema/elements/dependent_required.rb +49 -0
  39. data/lib/jsi/schema/elements/dependent_schemas.rb +69 -0
  40. data/lib/jsi/schema/elements/dynamic_ref.rb +69 -0
  41. data/lib/jsi/schema/elements/enum.rb +26 -0
  42. data/lib/jsi/schema/elements/examples.rb +10 -0
  43. data/lib/jsi/schema/elements/format.rb +10 -0
  44. data/lib/jsi/schema/elements/id.rb +30 -0
  45. data/lib/jsi/schema/elements/if_then_else.rb +82 -0
  46. data/lib/jsi/schema/elements/info_bool.rb +10 -0
  47. data/lib/jsi/schema/elements/info_string.rb +10 -0
  48. data/lib/jsi/schema/elements/items.rb +93 -0
  49. data/lib/jsi/schema/elements/items_prefixed.rb +96 -0
  50. data/lib/jsi/schema/elements/not.rb +31 -0
  51. data/lib/jsi/schema/elements/numeric.rb +137 -0
  52. data/lib/jsi/schema/elements/numeric_draft04.rb +77 -0
  53. data/lib/jsi/schema/elements/object_validation.rb +55 -0
  54. data/lib/jsi/schema/elements/pattern.rb +35 -0
  55. data/lib/jsi/schema/elements/properties.rb +145 -0
  56. data/lib/jsi/schema/elements/property_names.rb +48 -0
  57. data/lib/jsi/schema/elements/ref.rb +62 -0
  58. data/lib/jsi/schema/elements/required.rb +34 -0
  59. data/lib/jsi/schema/elements/self.rb +24 -0
  60. data/lib/jsi/schema/elements/some_of.rb +180 -0
  61. data/lib/jsi/schema/elements/string_validation.rb +57 -0
  62. data/lib/jsi/schema/elements/type.rb +43 -0
  63. data/lib/jsi/schema/elements/unevaluated_items.rb +54 -0
  64. data/lib/jsi/schema/elements/unevaluated_properties.rb +54 -0
  65. data/lib/jsi/schema/elements/xschema.rb +10 -0
  66. data/lib/jsi/schema/elements/xvocabulary.rb +10 -0
  67. data/lib/jsi/schema/elements.rb +101 -0
  68. data/lib/jsi/schema/issue.rb +3 -4
  69. data/lib/jsi/schema/schema_ancestor_node.rb +103 -50
  70. data/lib/jsi/schema/vocabulary.rb +36 -0
  71. data/lib/jsi/schema.rb +520 -338
  72. data/lib/jsi/schema_classes.rb +168 -124
  73. data/lib/jsi/schema_set.rb +67 -126
  74. data/lib/jsi/set.rb +23 -0
  75. data/lib/jsi/simple_wrap.rb +13 -16
  76. data/lib/jsi/struct.rb +57 -0
  77. data/lib/jsi/uri.rb +40 -0
  78. data/lib/jsi/util/private/memo_map.rb +9 -13
  79. data/lib/jsi/util/private.rb +57 -12
  80. data/lib/jsi/util/typelike.rb +19 -64
  81. data/lib/jsi/util.rb +52 -34
  82. data/lib/jsi/validation/error.rb +41 -2
  83. data/lib/jsi/validation/result.rb +118 -71
  84. data/lib/jsi/validation.rb +1 -6
  85. data/lib/jsi/version.rb +1 -1
  86. data/lib/jsi.rb +158 -41
  87. data/lib/schemas/json-schema.org/draft/2020-12/schema.rb +67 -0
  88. data/lib/schemas/json-schema.org/draft-04/schema.rb +63 -106
  89. data/lib/schemas/json-schema.org/draft-06/schema.rb +56 -105
  90. data/lib/schemas/json-schema.org/draft-07/schema.rb +67 -124
  91. data/readme.rb +3 -3
  92. data/{resources}/schemas/2020-12_strict.json +19 -0
  93. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/applicator.json +48 -0
  94. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/content.json +17 -0
  95. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/core.json +51 -0
  96. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-annotation.json +14 -0
  97. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-assertion.json +14 -0
  98. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/meta-data.json +37 -0
  99. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/unevaluated.json +15 -0
  100. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/validation.json +98 -0
  101. data/{resources}/schemas/json-schema.org/draft/2020-12/schema.json +58 -0
  102. metadata +70 -48
  103. data/lib/jsi/schema/application/child_application/contains.rb +0 -25
  104. data/lib/jsi/schema/application/child_application/draft04.rb +0 -21
  105. data/lib/jsi/schema/application/child_application/draft06.rb +0 -28
  106. data/lib/jsi/schema/application/child_application/draft07.rb +0 -28
  107. data/lib/jsi/schema/application/child_application/items.rb +0 -18
  108. data/lib/jsi/schema/application/child_application/properties.rb +0 -25
  109. data/lib/jsi/schema/application/child_application.rb +0 -13
  110. data/lib/jsi/schema/application/draft04.rb +0 -8
  111. data/lib/jsi/schema/application/draft06.rb +0 -8
  112. data/lib/jsi/schema/application/draft07.rb +0 -8
  113. data/lib/jsi/schema/application/inplace_application/dependencies.rb +0 -28
  114. data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -25
  115. data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -26
  116. data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -32
  117. data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +0 -20
  118. data/lib/jsi/schema/application/inplace_application/ref.rb +0 -18
  119. data/lib/jsi/schema/application/inplace_application/someof.rb +0 -44
  120. data/lib/jsi/schema/application/inplace_application.rb +0 -14
  121. data/lib/jsi/schema/application.rb +0 -12
  122. data/lib/jsi/schema/ref.rb +0 -186
  123. data/lib/jsi/schema/validation/array.rb +0 -69
  124. data/lib/jsi/schema/validation/contains.rb +0 -25
  125. data/lib/jsi/schema/validation/dependencies.rb +0 -49
  126. data/lib/jsi/schema/validation/draft04/minmax.rb +0 -93
  127. data/lib/jsi/schema/validation/draft04.rb +0 -110
  128. data/lib/jsi/schema/validation/draft06.rb +0 -120
  129. data/lib/jsi/schema/validation/draft07.rb +0 -157
  130. data/lib/jsi/schema/validation/enum.rb +0 -25
  131. data/lib/jsi/schema/validation/ifthenelse.rb +0 -46
  132. data/lib/jsi/schema/validation/items.rb +0 -54
  133. data/lib/jsi/schema/validation/not.rb +0 -20
  134. data/lib/jsi/schema/validation/numeric.rb +0 -121
  135. data/lib/jsi/schema/validation/object.rb +0 -45
  136. data/lib/jsi/schema/validation/pattern.rb +0 -34
  137. data/lib/jsi/schema/validation/properties.rb +0 -101
  138. data/lib/jsi/schema/validation/property_names.rb +0 -32
  139. data/lib/jsi/schema/validation/ref.rb +0 -40
  140. data/lib/jsi/schema/validation/required.rb +0 -27
  141. data/lib/jsi/schema/validation/someof.rb +0 -90
  142. data/lib/jsi/schema/validation/string.rb +0 -47
  143. data/lib/jsi/schema/validation/type.rb +0 -49
  144. data/lib/jsi/schema/validation.rb +0 -49
  145. data/lib/jsi/schema_registry.rb +0 -200
  146. data/lib/jsi/util/private/attr_struct.rb +0 -141
@@ -1,117 +1,189 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSI
4
- # A MetaSchemaNode is a JSI instance representing a node in a document that contains a meta-schema.
5
- # The root of the meta-schema is pointed to by metaschema_root_ptr.
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
- # like JSI::Base's normal subclasses, this class represents an instance of a schema set, an instance
9
- # which may itself be a schema. unlike JSI::Base, the document containing the instance and its schemas
10
- # is the same, and a schema (the meta-schema) may be an instance of itself.
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
- # unlike JSI::Base's normal subclasses, the schemas describing the instance are not part of the class.
13
- # Since the meta-schema describes itself, attempting to construct a class from the JSI Schema Module of a
14
- # schema which is itself an instance of that class results in a causality loop.
15
- # instead, a MetaSchemaNode calculates its {#jsi_schemas} and extends itself with their JSI Schema
16
- # modules during initialization.
17
- # The MetaSchemaNode of the meta-schema is extended with its own JSI Schema Module.
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
- # if the MetaSchemaNode's schemas include its self, it is extended with {JSI::Schema::MetaSchema}.
20
- #
21
- # a MetaSchemaNode is extended with JSI::Schema when it represents a schema - this is the case when
22
- # the meta-schema is one of its schemas.
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
23
  class MetaSchemaNode < Base
24
24
  autoload :BootstrapSchema, 'jsi/metaschema_node/bootstrap_schema'
25
25
 
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
+
26
35
  include(Base::Immutable)
27
36
 
28
- # @param jsi_document the document containing the meta-schema.
29
- # this must be frozen recursively; MetaSchemaNode does support mutation.
30
- # @param jsi_ptr [JSI::Ptr] ptr to this MetaSchemaNode in jsi_document
31
- # @param schema_implementation_modules [Enumerable<Module>] modules which implement the functionality
32
- # of the schema. These are included on the {Schema#jsi_schema_module} of the meta-schema.
33
- # They extend any schema described by the meta-schema, including those in the document containing
34
- # the meta-schema, and the meta-schema itself.
35
- # see {Schema#describes_schema!} param `schema_implementation_modules`.
36
- # @param metaschema_root_ptr [JSI::Ptr] ptr to the root of the meta-schema in the jsi_document
37
- # @param root_schema_ptr [JSI::Ptr] ptr to the schema describing the root of the jsi_document
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
+ class Conf end
46
+ Conf::ATTRS = Base::Conf::ATTRS.merge(conf_attrs)
47
+
48
+ # {Base::Conf} with additional configuration for MetaSchemaNode.
49
+ #
50
+ # @!attribute dialect
51
+ # @return [Schema::Dialect]
52
+ # @!attribute metaschema_root_ref
53
+ # URI reference to the root of the meta-schema.
54
+ #
55
+ # Default: `"#"` resolves to the root of the `jsi_document`.
56
+ # @return [Addressable::URI]
57
+ # @!attribute root_schema_ref
58
+ # URI reference to the schema describing the root of the jsi_document.
59
+ # When schemas of the meta-schema are in multiple documents, this describes the roots of all instantiated documents.
60
+ #
61
+ # Default: `metaschema_root_ref` value, i.e. the document root is a schema.
62
+ # @return [Addressable::URI]
63
+ # @!attribute bootstrap_registry
64
+ # Default: nil
65
+ # @return [Registry, nil]
66
+ class Conf < Base::Conf
67
+ def initialize(
68
+ dialect: ,
69
+ metaschema_root_ref: '#',
70
+ root_schema_ref: metaschema_root_ref,
71
+ registry: nil, # overrides Base::Conf default value JSI.registry
72
+ is_metaschema: DEFAULT_IS_METASCHEMA,
73
+ **kw
74
+ )
75
+ super(
76
+ dialect: dialect,
77
+ metaschema_root_ref: Util.uri(metaschema_root_ref, nnil: true),
78
+ root_schema_ref: Util.uri(root_schema_ref, nnil: true),
79
+ registry: registry,
80
+ is_metaschema: is_metaschema,
81
+ **kw,
82
+ )
83
+ end
84
+ end
85
+
86
+ # See {JSI.new_metaschema_node} to instantiate.
87
+ #
88
+ # Note: when instantiating MetaSchemaNode directly, the caller must invoke #jsi_initialize_finish.
89
+ # @api private
38
90
  def initialize(
39
- jsi_document,
40
- jsi_ptr: Ptr[],
41
- schema_implementation_modules: ,
42
- metaschema_root_ptr: Ptr[],
43
- root_schema_ptr: Ptr[],
44
- jsi_schema_base_uri: nil,
45
- jsi_schema_registry: nil,
46
- jsi_content_to_immutable: DEFAULT_CONTENT_TO_IMMUTABLE,
47
- jsi_root_node: nil
91
+ **kw
48
92
  )
49
- super(jsi_document,
50
- jsi_ptr: jsi_ptr,
93
+ super(
51
94
  jsi_indicated_schemas: SchemaSet[],
52
- jsi_schema_base_uri: jsi_schema_base_uri,
53
- jsi_schema_registry: jsi_schema_registry,
54
- jsi_content_to_immutable: jsi_content_to_immutable,
55
- jsi_root_node: jsi_root_node,
95
+ **kw,
56
96
  )
57
97
 
58
- @schema_implementation_modules = schema_implementation_modules = Util.ensure_module_set(schema_implementation_modules)
59
- @metaschema_root_ptr = metaschema_root_ptr
60
- @root_schema_ptr = root_schema_ptr
98
+ @initialize_finish_started = false
99
+ @initialize_finished = false
100
+ @to_initialize_finish = []
61
101
 
62
- if jsi_ptr.root? && jsi_schema_base_uri
63
- raise(NotImplementedError, "unsupported jsi_schema_base_uri on meta-schema document root")
102
+ if jsi_ptr.root? && jsi_base_uri
103
+ raise(NotImplementedError, "unsupported jsi_base_uri on meta-schema document root")
64
104
  end
65
105
 
66
- #chkbug raise(Bug, 'MetaSchemaNode instance must be frozen') unless jsi_node_content.frozen?
106
+ #chkbug fail(Bug, 'MetaSchemaNode instance must be frozen') unless jsi_node_content.frozen?
107
+
108
+ bootstrap_schema_from_ref = proc do |ref_uri|
109
+ ref_uri_nofrag = ref_uri.merge(fragment: nil)
110
+
111
+ if ref_uri_nofrag.empty?
112
+ ptr = Ptr.from_fragment(ref_uri.fragment).resolve_against(jsi_document) # anchor not supported
113
+ if jsi_conf.root_schema_ref == jsi_conf.metaschema_root_ref
114
+ # root is a schema
115
+ jsi_conf.dialect.bootstrap_schema(
116
+ jsi_document: jsi_document,
117
+ jsi_base_uri: nil, # not supported
118
+ jsi_registry: jsi_conf.bootstrap_registry,
119
+ ).resource_root_subschema(ptr)
120
+ else
121
+ jsi_conf.dialect.bootstrap_schema(
122
+ jsi_document: jsi_document,
123
+ jsi_ptr: ptr,
124
+ jsi_base_uri: nil, # not supported
125
+ jsi_registry: jsi_conf.bootstrap_registry,
126
+ )
127
+ end
128
+ else
129
+ # if not fragment-only, ref must be registered in the bootstrap_registry
130
+ ref = Schema::Ref.new(ref_uri, registry: jsi_conf.bootstrap_registry)
131
+ ref.resolve
132
+ end
133
+ end
67
134
 
68
- extends = Set[]
135
+ @bootstrap_metaschema = bootstrap_schema_from_ref[jsi_conf.metaschema_root_ref]
69
136
 
70
137
  instance_for_schemas = jsi_document
71
- bootstrap_schema_class = JSI::SchemaClasses.bootstrap_schema_class(schema_implementation_modules)
72
- root_bootstrap_schema = bootstrap_schema_class.new(
73
- jsi_document,
74
- jsi_ptr: root_schema_ptr,
75
- jsi_schema_base_uri: nil, # supplying jsi_schema_base_uri on root bootstrap schema is not supported
76
- )
138
+ root_bootstrap_schema = bootstrap_schema_from_ref[jsi_conf.root_schema_ref]
77
139
  our_bootstrap_indicated_schemas = jsi_ptr.tokens.inject(SchemaSet[root_bootstrap_schema]) do |bootstrap_indicated_schemas, tok|
78
- bootstrap_schemas = bootstrap_indicated_schemas.inplace_applicator_schemas(instance_for_schemas)
79
- child_indicated_schemas = bootstrap_schemas.child_applicator_schemas(tok, instance_for_schemas)
140
+ child_indicated_schemas = bootstrap_indicated_schemas.each_yield_set do |is, y|
141
+ is.each_inplace_child_applicator_schema(tok, instance_for_schemas,
142
+ collect_evaluated_validate: jsi_conf.application_collect_evaluated_validate,
143
+ &y
144
+ )
145
+ end
80
146
  instance_for_schemas = instance_for_schemas[tok]
81
147
  child_indicated_schemas
82
148
  end
83
- @indicated_schemas_map = jsi_memomap { bootstrap_schemas_to_msn(our_bootstrap_indicated_schemas) }
149
+ @indicated_schemas_map = jsi_memomap do
150
+ SchemaSet.new(our_bootstrap_indicated_schemas) { |s| bootstrap_schema_to_msn(s) }
151
+ end
84
152
 
85
- our_bootstrap_schemas = our_bootstrap_indicated_schemas.inplace_applicator_schemas(instance_for_schemas)
153
+ @bootstrap_schemas = our_bootstrap_indicated_schemas.each_yield_set do |is, y|
154
+ is.each_inplace_applicator_schema(instance_for_schemas, &y) # note: instance_for_schemas == jsi_node_content now
155
+ end
156
+
157
+ @jsi_schemas = @bootstrap_schemas
86
158
 
87
- describes_self = false
88
- our_bootstrap_schemas.each do |bootstrap_schema|
89
- if bootstrap_schema.jsi_ptr == metaschema_root_ptr
159
+ @bootstrap_schemas.each do |bootstrap_schema|
160
+ if instance_exec(bootstrap_schema, &jsi_conf.is_metaschema)
90
161
  # this is described by the meta-schema, i.e. this is a schema
91
- extend Schema
92
- schema_implementation_modules.each do |schema_implementation_module|
93
- extend schema_implementation_module
162
+ define_singleton_method(:dialect) { jsi_conf.dialect }
163
+ extend(Schema)
164
+
165
+ if jsi_registry && jsi_resource_uris.any? { |uri| !jsi_registry.registered?(uri) }
166
+ jsi_registry.register_immediate(self)
94
167
  end
95
- extends += schema_implementation_modules
96
- end
97
- if bootstrap_schema.jsi_ptr == jsi_ptr
98
- # this is the meta-schema (it is described by itself)
99
- describes_self = true
100
168
  end
101
169
  end
170
+ end
171
+
172
+ # @api private
173
+ def jsi_initialize_finish
174
+ return self if @initialize_finish_started
175
+ @initialize_finish_started = true
102
176
 
103
- @jsi_schemas = bootstrap_schemas_to_msn(our_bootstrap_schemas)
177
+ @jsi_schemas = SchemaSet.new(@bootstrap_schemas) { |s| bootstrap_schema_to_msn(s) }
104
178
 
105
179
  # note: jsi_schemas must already be set for jsi_schema_module to be used/extended
106
- if describes_self
107
- describes_schema!(schema_implementation_modules)
180
+ if instance_exec(self, &jsi_conf.is_metaschema)
181
+ describes_schema!(jsi_conf.dialect)
108
182
  end
109
183
 
110
184
  extends_for_instance = JSI::SchemaClasses.includes_for(jsi_node_content)
111
- extends.merge(extends_for_instance)
112
- extends.freeze
113
185
 
114
- conflicting_modules = Set[self.class] + extends + @jsi_schemas.map(&:jsi_schema_module)
186
+ conflicting_modules = Set[self.class] + extends_for_instance + @jsi_schemas.map(&:jsi_schema_module)
115
187
  reader_modules = @jsi_schemas.map do |schema|
116
188
  JSI::SchemaClasses.schema_property_reader_module(schema, conflicting_modules: conflicting_modules)
117
189
  end
@@ -128,19 +200,15 @@ module JSI
128
200
  @jsi_schemas.each do |schema|
129
201
  extend schema.jsi_schema_module
130
202
  end
131
- end
132
-
133
- # Set of modules to apply to schemas that are instances of (described by) the meta-schema
134
- # @return [Set<Module>]
135
- attr_reader :schema_implementation_modules
136
203
 
137
- # ptr to the root of the meta-schema in the jsi_document
138
- # @return [JSI::Ptr]
139
- attr_reader :metaschema_root_ptr
204
+ @initialize_finished = true
205
+ while !@to_initialize_finish.empty?
206
+ node = @to_initialize_finish.shift
207
+ node.jsi_initialize_finish
208
+ end
140
209
 
141
- # ptr to the schema of the root of the jsi_document
142
- # @return [JSI::Ptr]
143
- attr_reader :root_schema_ptr
210
+ jsi_initialized
211
+ end
144
212
 
145
213
  # JSI Schemas describing this MetaSchemaNode
146
214
  # @return [JSI::SchemaSet]
@@ -152,15 +220,10 @@ module JSI
152
220
  @indicated_schemas_map[]
153
221
  end
154
222
 
155
- # see {Base#jsi_child}
156
- def jsi_child(token, as_jsi: )
157
- child_node = @root_descendent_node_map[ptr: jsi_ptr[token]]
158
-
159
- jsi_child_as_jsi(child_node.jsi_node_content, child_node.jsi_schemas, as_jsi) do
160
- child_node
161
- end
223
+ # see {Base#jsi_child_node}
224
+ def jsi_child_node(token)
225
+ root_descendent_node(jsi_ptr[token])
162
226
  end
163
- private :jsi_child
164
227
 
165
228
  # See {Base#jsi_default_child}
166
229
  def jsi_default_child(token, as_jsi: )
@@ -168,42 +231,24 @@ module JSI
168
231
  end
169
232
  private :jsi_default_child # internals for #[] but idk, could be public
170
233
 
171
- # instantiates a new MetaSchemaNode whose instance is a modified copy of this MetaSchemaNode's instance
172
- # @yield [Object] the node content of the instance. the block should result
173
- # in a (nondestructively) modified copy of this.
174
- # @return [MetaSchemaNode] modified copy of self
175
- def jsi_modified_copy(&block)
176
- if jsi_ptr.root?
177
- modified_document = jsi_ptr.modified_document_copy(jsi_document, &block)
178
- modified_document = jsi_content_to_immutable.call(modified_document) if jsi_content_to_immutable
179
- MetaSchemaNode.new(modified_document, **our_initialize_params)
180
- else
181
- modified_jsi_root_node = jsi_root_node.jsi_modified_copy do |root|
182
- jsi_ptr.modified_document_copy(root, &block)
183
- end
184
- modified_jsi_root_node.jsi_descendent_node(jsi_ptr)
185
- end
234
+ # @raise [NotImplementedError] not implemented for MetaSchemaNode
235
+ def jsi_modified_copy(**)
236
+ # this had an implementation previously (see git history). but for 2020-12, support for multiple
237
+ # mutually-descriptive schemas as well as dynamic scope makes instantiating a modified copy that
238
+ # preserves self-descriptive/mutually-descriptive relationships generally infeasible.
239
+ raise(NotImplementedError)
186
240
  end
187
241
 
188
- # @private
189
- # @return [Array<String>]
190
- def jsi_object_group_text
191
- if jsi_schemas && jsi_schemas.any?
192
- class_n_schemas = -"#{self.class} (#{jsi_schemas.map { |s| s.jsi_schema_module.name_from_ancestor || s.jsi_ptr.uri }.join(' ')})"
193
- else
194
- class_n_schemas = self.class.to_s
195
- end
196
- [
197
- class_n_schemas,
198
- is_a?(Schema::MetaSchema) ? "Meta-Schema" : is_a?(Schema) ? "Schema" : nil,
199
- *(jsi_node_content.respond_to?(:jsi_object_group_text) ? jsi_node_content.jsi_object_group_text : nil),
200
- ].compact.freeze
242
+ protected def jsi_dynamic_root_instantiate(**kw)
243
+ # self is a resource root being instantiated with overridden dynamic scope
244
+ #chkbug fail unless jsi_ptr.root? # jsi_schema_resource_ancestors not tracked
245
+ to_initialize_finish(MetaSchemaNode.new(**kw))
201
246
  end
202
247
 
203
248
  # see {Util::Private::FingerprintHash}
204
249
  # @api private
205
250
  def jsi_fingerprint
206
- {class: self.class, jsi_document: jsi_document}.merge(our_initialize_params).freeze
251
+ jsi_fingerprint_no_schemas
207
252
  end
208
253
 
209
254
  protected
@@ -213,61 +258,83 @@ module JSI
213
258
  private
214
259
 
215
260
  def jsi_memomaps_initialize
216
- if jsi_ptr.root?
261
+ if equal?(@jsi_root_node)
217
262
  @root_descendent_node_map = jsi_memomap(&method(:jsi_root_descendent_node_compute))
218
263
  else
219
264
  @root_descendent_node_map = @jsi_root_node.root_descendent_node_map
220
265
  end
221
266
  end
222
267
 
223
- # note: does not include jsi_root_node
224
- def our_initialize_params
225
- {
226
- jsi_ptr: jsi_ptr,
227
- schema_implementation_modules: schema_implementation_modules,
228
- metaschema_root_ptr: metaschema_root_ptr,
229
- root_schema_ptr: root_schema_ptr,
230
- jsi_schema_base_uri: jsi_schema_base_uri,
231
- jsi_schema_registry: jsi_schema_registry,
232
- jsi_content_to_immutable: jsi_content_to_immutable,
233
- }.freeze
234
- end
235
-
236
- # note: not for root node
237
- def new_node(**params)
238
- MetaSchemaNode.new(jsi_document, jsi_root_node: jsi_root_node, **our_initialize_params, **params)
239
- end
240
-
241
268
  def jsi_root_descendent_node_compute(ptr: )
242
- #chkbug raise(Bug) unless jsi_ptr.root?
269
+ # note: self is jsi_root_node
270
+ #chkbug fail(Bug) unless equal?(jsi_root_node)
243
271
  if ptr.root?
244
272
  self
245
273
  else
246
- new_node(
274
+ MetaSchemaNode.new(
275
+ jsi_document: jsi_document,
247
276
  jsi_ptr: ptr,
248
- jsi_schema_base_uri: jsi_resource_ancestor_uri,
277
+ jsi_base_uri: jsi_next_base_uri,
278
+ jsi_schema_resource_ancestors: is_a?(Schema) ? jsi_subschema_resource_ancestors : Util::EMPTY_ARY,
279
+ # since MSN only supports document root as jsi_resource_root, and jsi_next_schema_dynamic_anchor_map
280
+ # is only passed to descendents that are resource roots, that is not used here.
281
+ jsi_schema_dynamic_anchor_map: jsi_schema_dynamic_anchor_map,
282
+ jsi_root_node: jsi_root_node,
249
283
  )
250
284
  end
251
285
  end
252
286
 
253
- # @param bootstrap_schemas [Enumerable<BootstrapSchema>]
254
- # @return [SchemaSet<MetaSchemaNode>]
255
- def bootstrap_schemas_to_msn(bootstrap_schemas)
256
- SchemaSet.new(bootstrap_schemas) do |bootstrap_schema|
257
- if bootstrap_schema.jsi_ptr == jsi_ptr
258
- self
259
- elsif bootstrap_schema.jsi_ptr.root?
260
- @jsi_root_node
287
+ # @param ptr [Ptr]
288
+ # @return [MetaSchemaNode]
289
+ protected def root_descendent_node(ptr)
290
+ to_initialize_finish(@root_descendent_node_map[
291
+ ptr: ptr.resolve_against(jsi_document),
292
+ ])
293
+ end
294
+
295
+ def to_initialize_finish(node)
296
+ if @initialize_finished
297
+ node.jsi_initialize_finish
298
+ else
299
+ @to_initialize_finish.push(node)
300
+ end
301
+
302
+ node
303
+ end
304
+
305
+ # @param bootstrap_schema [MetaSchemaNode::BootstrapSchema]
306
+ # @return [MetaSchemaNode]
307
+ def bootstrap_schema_to_msn(bootstrap_schema)
308
+ dynamic_anchor_map = Schema::DynamicAnchorMap::EMPTY
309
+ bootstrap_schema.jsi_schema_dynamic_anchor_map.each do |anchor, (bootstrap_anchor_root, anchor_ptrs)|
310
+ msn_anchor_root = bootstrap_schema_to_msn(bootstrap_anchor_root)
311
+ dynamic_anchor_map = dynamic_anchor_map.merge({
312
+ anchor => [msn_anchor_root, anchor_ptrs].freeze,
313
+ }).freeze
314
+ end
315
+
316
+ if bootstrap_schema.jsi_document.equal?(jsi_document)
317
+ root_descendent_node(bootstrap_schema.jsi_ptr).jsi_with_schema_dynamic_anchor_map(dynamic_anchor_map)
318
+ else
319
+ jsi_registry || raise(ResolutionError, "no jsi_registry")
320
+ bootstrap_resource = bootstrap_schema.jsi_resource_root
321
+ resource_uri = bootstrap_resource.jsi_resource_uri || raise(ResolutionError, "no URI: #{bootstrap_resource}")
322
+ if jsi_registry.registered?(resource_uri)
323
+ resource = jsi_registry.find(resource_uri)
324
+ relative_ptr = bootstrap_schema.jsi_ptr.relative_to(bootstrap_resource.jsi_ptr)
325
+ resource.jsi_descendent_node(relative_ptr).jsi_with_schema_dynamic_anchor_map(dynamic_anchor_map)
261
326
  else
262
- new_node(
263
- jsi_ptr: bootstrap_schema.jsi_ptr,
264
- jsi_schema_base_uri: bootstrap_schema.jsi_schema_base_uri,
265
- )
327
+ root = to_initialize_finish(MetaSchemaNode.new(
328
+ jsi_document: bootstrap_schema.jsi_document,
329
+ jsi_ptr: Ptr[],
330
+ jsi_base_uri: nil,
331
+ # this seems like the best dynamic_anchor_map to pass
332
+ jsi_schema_dynamic_anchor_map: dynamic_anchor_map.without_node(nil, document: bootstrap_schema.jsi_document, ptr: Ptr[], registry: jsi_registry),
333
+ jsi_conf: jsi_conf,
334
+ ))
335
+ root.jsi_descendent_node(bootstrap_schema.jsi_ptr).jsi_with_schema_dynamic_anchor_map(dynamic_anchor_map)
266
336
  end
267
337
  end
268
338
  end
269
339
  end
270
-
271
- # @deprecated after v0.7.0, alias of {MetaSchemaNode}
272
- MetaschemaNode = MetaSchemaNode
273
340
  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 < Error
16
+ class ResolutionError < JSI::ResolutionError
17
17
  end
18
18
 
19
19
  POS_INT_RE = /\A[1-9]\d*\z/
@@ -61,9 +61,9 @@ module JSI
61
61
  # => JSI::Ptr["foo bar"]
62
62
  #
63
63
  # Note: A fragment does not include a leading '#'. The string "#/foo" is a URI containing the
64
- # fragment "/foo", which should be parsed by `Addressable::URI` before passing to this method, e.g.:
64
+ # fragment "/foo", which should be parsed by `JSI::URI` before passing to this method, e.g.:
65
65
  #
66
- # JSI::Ptr.from_fragment(Addressable::URI.parse("#/foo").fragment)
66
+ # JSI::Ptr.from_fragment(JSI::URI["#/foo"].fragment)
67
67
  # => JSI::Ptr["foo"]
68
68
  #
69
69
  # @param fragment [String] a fragment containing a pointer
@@ -71,7 +71,7 @@ module JSI
71
71
  # @raise [JSI::Ptr::PointerSyntaxError] when the fragment does not contain a pointer with
72
72
  # valid pointer syntax
73
73
  def self.from_fragment(fragment)
74
- from_pointer(Addressable::URI.unescape(fragment))
74
+ from_pointer(URI.unescape(fragment))
75
75
  end
76
76
 
77
77
  # parse a pointer string and instantiate as a JSI::Ptr
@@ -129,6 +129,24 @@ module JSI
129
129
  res
130
130
  end
131
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
+
132
150
  # the pointer string representation of this pointer
133
151
  # @return [String]
134
152
  def pointer
@@ -138,13 +156,13 @@ module JSI
138
156
  # the fragment string representation of this pointer
139
157
  # @return [String]
140
158
  def fragment
141
- Addressable::URI.escape(pointer).freeze
159
+ URI.escape(pointer).freeze
142
160
  end
143
161
 
144
162
  # a URI consisting of a fragment containing this pointer's fragment string representation
145
- # @return [Addressable::URI]
163
+ # @return [URI]
146
164
  def uri
147
- Addressable::URI.new(fragment: fragment).freeze
165
+ URI.new(fragment: fragment).freeze
148
166
  end
149
167
 
150
168
  # whether this pointer is empty, i.e. it has no tokens
@@ -175,11 +193,6 @@ module JSI
175
193
  tokens == other_ptr.tokens[0...tokens.size]
176
194
  end
177
195
 
178
- # @deprecated
179
- def contains?(other_ptr)
180
- ancestor_of?(other_ptr)
181
- end
182
-
183
196
  # part of this pointer relative to the given ancestor_ptr
184
197
  # @return [JSI::Ptr]
185
198
  # @raise [JSI::Ptr::Error] if the given ancestor_ptr is not an ancestor of this pointer
@@ -196,6 +209,7 @@ module JSI
196
209
  # @return [JSI::Ptr]
197
210
  def +(ptr)
198
211
  if ptr.is_a?(Ptr)
212
+ return(ptr) if tokens.empty?
199
213
  ptr_tokens = ptr.tokens
200
214
  elsif ptr.respond_to?(:to_ary)
201
215
  ptr_tokens = ptr
@@ -210,10 +224,12 @@ module JSI
210
224
  # @return [JSI::Ptr]
211
225
  # @raise [ArgumentError] if n is not between 0 and the size of our tokens
212
226
  def take(n)
227
+ return(EMPTY) if n == 0
228
+ return(self) if n == tokens.size
213
229
  unless n.is_a?(Integer) && n >= 0 && n <= tokens.size
214
230
  raise(ArgumentError, "n not in range (0..#{tokens.size}): #{n.inspect}")
215
231
  end
216
- n == tokens.size ? self : Ptr.new(tokens.take(n).freeze)
232
+ Ptr.new(tokens.take(n).freeze)
217
233
  end
218
234
 
219
235
  # appends the given token to this pointer's tokens and returns the result
@@ -249,13 +265,13 @@ module JSI
249
265
  cdr = tokens.size == 1 ? EMPTY : Ptr.new(tokens[1..-1].freeze)
250
266
  token, document_child = node_subscript_token_child(document, car)
251
267
  modified_document_child = cdr.modified_document_copy(document_child, &block)
252
- if modified_document_child.object_id == document_child.object_id
268
+ if modified_document_child.equal?(document_child)
253
269
  document
254
270
  else
255
271
  modified_document = document.respond_to?(:[]=) ? document.dup :
256
272
  document.respond_to?(:to_hash) ? document.to_hash.dup :
257
273
  document.respond_to?(:to_ary) ? document.to_ary.dup :
258
- raise(Bug) # not possible; node_subscript_token_child would have raised
274
+ fail(Bug) # not possible; node_subscript_token_child would have raised
259
275
  modified_document[token] = modified_document_child
260
276
  modified_document
261
277
  end
@@ -284,6 +300,7 @@ module JSI
284
300
  private
285
301
 
286
302
  def node_subscript_token_child(value, token, *a, **kw)
303
+ token = token.jsi_node_content if token.is_a?(Schema::SchemaAncestorNode)
287
304
  if value.respond_to?(:to_ary)
288
305
  if token.is_a?(String) && (token == '0' || token =~ POS_INT_RE)
289
306
  token = token.to_i