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.
Files changed (148) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -4
  3. data/CHANGELOG.md +19 -0
  4. data/LICENSE.md +2 -3
  5. data/README.md +87 -43
  6. data/docs/{glossary.md → Glossary.md} +84 -52
  7. data/jsi.gemspec +1 -1
  8. data/lib/jsi/base/mutability.rb +48 -0
  9. data/lib/jsi/base/node.rb +66 -52
  10. data/lib/jsi/base.rb +592 -176
  11. data/lib/jsi/jsi_coder.rb +4 -2
  12. data/lib/jsi/metaschema_node/bootstrap_schema.rb +118 -59
  13. data/lib/jsi/metaschema_node.rb +244 -154
  14. data/lib/jsi/ptr.rb +45 -17
  15. data/lib/jsi/ref.rb +197 -0
  16. data/lib/jsi/registry.rb +311 -0
  17. data/lib/jsi/schema/cxt/child_application.rb +35 -0
  18. data/lib/jsi/schema/cxt/inplace_application.rb +37 -0
  19. data/lib/jsi/schema/cxt.rb +80 -0
  20. data/lib/jsi/schema/dialect.rb +137 -0
  21. data/lib/jsi/schema/draft04.rb +113 -5
  22. data/lib/jsi/schema/draft06.rb +123 -5
  23. data/lib/jsi/schema/draft07.rb +157 -5
  24. data/lib/jsi/schema/draft202012.rb +303 -0
  25. data/lib/jsi/schema/dynamic_anchor_map.rb +63 -0
  26. data/lib/jsi/schema/element.rb +69 -0
  27. data/lib/jsi/schema/elements/anchor.rb +13 -0
  28. data/lib/jsi/schema/elements/array_validation.rb +82 -0
  29. data/lib/jsi/schema/elements/comment.rb +10 -0
  30. data/lib/jsi/schema/{validation → elements}/const.rb +11 -7
  31. data/lib/jsi/schema/elements/contains.rb +59 -0
  32. data/lib/jsi/schema/elements/contains_minmax.rb +91 -0
  33. data/lib/jsi/schema/elements/content_encoding.rb +10 -0
  34. data/lib/jsi/schema/elements/content_media_type.rb +10 -0
  35. data/lib/jsi/schema/elements/content_schema.rb +16 -0
  36. data/lib/jsi/schema/elements/default.rb +11 -0
  37. data/lib/jsi/schema/elements/definitions.rb +19 -0
  38. data/lib/jsi/schema/elements/dependencies.rb +99 -0
  39. data/lib/jsi/schema/elements/dependent_required.rb +49 -0
  40. data/lib/jsi/schema/elements/dependent_schemas.rb +69 -0
  41. data/lib/jsi/schema/elements/dynamic_ref.rb +69 -0
  42. data/lib/jsi/schema/elements/enum.rb +26 -0
  43. data/lib/jsi/schema/elements/examples.rb +10 -0
  44. data/lib/jsi/schema/elements/format.rb +10 -0
  45. data/lib/jsi/schema/elements/id.rb +30 -0
  46. data/lib/jsi/schema/elements/if_then_else.rb +82 -0
  47. data/lib/jsi/schema/elements/info_bool.rb +10 -0
  48. data/lib/jsi/schema/elements/info_string.rb +10 -0
  49. data/lib/jsi/schema/elements/items.rb +93 -0
  50. data/lib/jsi/schema/elements/items_prefixed.rb +96 -0
  51. data/lib/jsi/schema/elements/not.rb +31 -0
  52. data/lib/jsi/schema/elements/numeric.rb +137 -0
  53. data/lib/jsi/schema/elements/numeric_draft04.rb +77 -0
  54. data/lib/jsi/schema/elements/object_validation.rb +55 -0
  55. data/lib/jsi/schema/elements/pattern.rb +35 -0
  56. data/lib/jsi/schema/elements/properties.rb +145 -0
  57. data/lib/jsi/schema/elements/property_names.rb +48 -0
  58. data/lib/jsi/schema/elements/ref.rb +62 -0
  59. data/lib/jsi/schema/elements/required.rb +34 -0
  60. data/lib/jsi/schema/elements/self.rb +24 -0
  61. data/lib/jsi/schema/elements/some_of.rb +180 -0
  62. data/lib/jsi/schema/elements/string_validation.rb +57 -0
  63. data/lib/jsi/schema/elements/type.rb +43 -0
  64. data/lib/jsi/schema/elements/unevaluated_items.rb +54 -0
  65. data/lib/jsi/schema/elements/unevaluated_properties.rb +54 -0
  66. data/lib/jsi/schema/elements/xschema.rb +10 -0
  67. data/lib/jsi/schema/elements/xvocabulary.rb +10 -0
  68. data/lib/jsi/schema/elements.rb +101 -0
  69. data/lib/jsi/schema/issue.rb +3 -4
  70. data/lib/jsi/schema/schema_ancestor_node.rb +105 -52
  71. data/lib/jsi/schema/vocabulary.rb +36 -0
  72. data/lib/jsi/schema.rb +598 -383
  73. data/lib/jsi/schema_classes.rb +195 -141
  74. data/lib/jsi/schema_set.rb +85 -128
  75. data/lib/jsi/set.rb +23 -0
  76. data/lib/jsi/simple_wrap.rb +14 -17
  77. data/lib/jsi/struct.rb +57 -0
  78. data/lib/jsi/uri.rb +40 -0
  79. data/lib/jsi/util/private/memo_map.rb +9 -13
  80. data/lib/jsi/util/private.rb +59 -31
  81. data/lib/jsi/util/typelike.rb +19 -60
  82. data/lib/jsi/util.rb +53 -34
  83. data/lib/jsi/validation/error.rb +45 -2
  84. data/lib/jsi/validation/result.rb +121 -90
  85. data/lib/jsi/validation.rb +1 -6
  86. data/lib/jsi/version.rb +1 -1
  87. data/lib/jsi.rb +170 -36
  88. data/lib/schemas/json-schema.org/draft/2020-12/schema.rb +62 -0
  89. data/lib/schemas/json-schema.org/draft-04/schema.rb +60 -109
  90. data/lib/schemas/json-schema.org/draft-06/schema.rb +53 -108
  91. data/lib/schemas/json-schema.org/draft-07/schema.rb +63 -127
  92. data/readme.rb +4 -4
  93. data/{resources}/schemas/2020-12_strict.json +19 -0
  94. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/applicator.json +48 -0
  95. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/content.json +17 -0
  96. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/core.json +51 -0
  97. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-annotation.json +14 -0
  98. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-assertion.json +14 -0
  99. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/meta-data.json +37 -0
  100. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/unevaluated.json +15 -0
  101. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/validation.json +98 -0
  102. data/{resources}/schemas/json-schema.org/draft/2020-12/schema.json +58 -0
  103. metadata +73 -52
  104. data/lib/jsi/metaschema.rb +0 -6
  105. data/lib/jsi/schema/application/child_application/contains.rb +0 -25
  106. data/lib/jsi/schema/application/child_application/draft04.rb +0 -21
  107. data/lib/jsi/schema/application/child_application/draft06.rb +0 -28
  108. data/lib/jsi/schema/application/child_application/draft07.rb +0 -28
  109. data/lib/jsi/schema/application/child_application/items.rb +0 -18
  110. data/lib/jsi/schema/application/child_application/properties.rb +0 -25
  111. data/lib/jsi/schema/application/child_application.rb +0 -13
  112. data/lib/jsi/schema/application/draft04.rb +0 -8
  113. data/lib/jsi/schema/application/draft06.rb +0 -8
  114. data/lib/jsi/schema/application/draft07.rb +0 -8
  115. data/lib/jsi/schema/application/inplace_application/dependencies.rb +0 -28
  116. data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -25
  117. data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -26
  118. data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -32
  119. data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +0 -20
  120. data/lib/jsi/schema/application/inplace_application/ref.rb +0 -18
  121. data/lib/jsi/schema/application/inplace_application/someof.rb +0 -44
  122. data/lib/jsi/schema/application/inplace_application.rb +0 -14
  123. data/lib/jsi/schema/application.rb +0 -12
  124. data/lib/jsi/schema/ref.rb +0 -183
  125. data/lib/jsi/schema/validation/array.rb +0 -69
  126. data/lib/jsi/schema/validation/contains.rb +0 -25
  127. data/lib/jsi/schema/validation/dependencies.rb +0 -49
  128. data/lib/jsi/schema/validation/draft04/minmax.rb +0 -91
  129. data/lib/jsi/schema/validation/draft04.rb +0 -110
  130. data/lib/jsi/schema/validation/draft06.rb +0 -120
  131. data/lib/jsi/schema/validation/draft07.rb +0 -157
  132. data/lib/jsi/schema/validation/enum.rb +0 -25
  133. data/lib/jsi/schema/validation/ifthenelse.rb +0 -46
  134. data/lib/jsi/schema/validation/items.rb +0 -54
  135. data/lib/jsi/schema/validation/not.rb +0 -20
  136. data/lib/jsi/schema/validation/numeric.rb +0 -121
  137. data/lib/jsi/schema/validation/object.rb +0 -45
  138. data/lib/jsi/schema/validation/pattern.rb +0 -34
  139. data/lib/jsi/schema/validation/properties.rb +0 -101
  140. data/lib/jsi/schema/validation/property_names.rb +0 -32
  141. data/lib/jsi/schema/validation/ref.rb +0 -40
  142. data/lib/jsi/schema/validation/required.rb +0 -27
  143. data/lib/jsi/schema/validation/someof.rb +0 -90
  144. data/lib/jsi/schema/validation/string.rb +0 -47
  145. data/lib/jsi/schema/validation/type.rb +0 -49
  146. data/lib/jsi/schema/validation.rb +0 -49
  147. data/lib/jsi/schema_registry.rb +0 -190
  148. data/lib/jsi/util/private/attr_struct.rb +0 -130
@@ -1,111 +1,195 @@
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 which contains a metaschema.
5
- # the root of the metaschema 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 metaschema) 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 metaschema 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 metaschema 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::Metaschema.
20
- #
21
- # a MetaschemaNode is extended with JSI::Schema when it represents a schema - this is the case when
22
- # the metaschema is one of its schemas.
23
- class MetaschemaNode < Base
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
- # @param jsi_document the document containing the metaschema
27
- # @param jsi_ptr [JSI::Ptr] ptr to this MetaschemaNode in jsi_document
28
- # @param schema_implementation_modules [Enumerable<Module>] modules which implement the functionality
29
- # of the schema. these are included on the {Schema#jsi_schema_module} of the metaschema.
30
- # they extend any schema described by the metaschema, including those in the document containing
31
- # the metaschema, and the metaschema itself.
32
- # see {Schema#describes_schema!} param `schema_implementation_modules`.
33
- # @param metaschema_root_ptr [JSI::Ptr] ptr to the root of the metaschema in the jsi_document
34
- # @param root_schema_ptr [JSI::Ptr] ptr to the schema describing the root of the jsi_document
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
- schema_implementation_modules: ,
39
- metaschema_root_ptr: Ptr[],
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(jsi_document,
95
+ super(
46
96
  jsi_ptr: jsi_ptr,
47
97
  jsi_indicated_schemas: SchemaSet[],
48
- jsi_schema_base_uri: jsi_schema_base_uri,
49
- jsi_schema_registry: jsi_schema_registry,
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
- @schema_implementation_modules = Util.ensure_module_set(schema_implementation_modules)
54
- @metaschema_root_ptr = metaschema_root_ptr
55
- @root_schema_ptr = root_schema_ptr
104
+ @initialize_finish_started = false
105
+ @initialize_finished = false
106
+ @to_initialize_finish = []
56
107
 
57
- if jsi_ptr.root? && jsi_schema_base_uri
58
- raise(NotImplementedError, "unsupported jsi_schema_base_uri on metaschema document root")
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
- jsi_node_content = self.jsi_node_content
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
- extends = Set[]
141
+ @bootstrap_metaschema = bootstrap_schema_from_ref[jsi_conf.metaschema_root_ref]
64
142
 
65
143
  instance_for_schemas = jsi_document
66
- bootstrap_schema_class = JSI::SchemaClasses.bootstrap_schema_class(schema_implementation_modules)
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
- bootstrap_schemas = bootstrap_indicated_schemas.inplace_applicator_schemas(instance_for_schemas)
74
- child_indicated_schemas = bootstrap_schemas.child_applicator_schemas(tok, instance_for_schemas)
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 { bootstrap_schemas_to_msn(our_bootstrap_indicated_schemas) }
79
-
80
- our_bootstrap_schemas = our_bootstrap_indicated_schemas.inplace_applicator_schemas(instance_for_schemas)
81
- our_bootstrap_schemas.each do |bootstrap_schema|
82
- if bootstrap_schema.jsi_ptr == metaschema_root_ptr
83
- # this is described by the metaschema, i.e. this is a schema
84
- extend Schema
85
- schema_implementation_modules.each do |schema_implementation_module|
86
- extend schema_implementation_module
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 = bootstrap_schemas_to_msn(our_bootstrap_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 is_a?(Metaschema)
101
- describes_schema!(schema_implementation_modules)
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] + extends + @jsi_schemas.map(&:jsi_schema_module)
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
- # ptr to the root of the metaschema in the jsi_document
132
- # @return [JSI::Ptr]
133
- attr_reader :metaschema_root_ptr
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
- # ptr to the schema of the root of the jsi_document
136
- # @return [JSI::Ptr]
137
- attr_reader :root_schema_ptr
216
+ jsi_initialized
217
+ end
138
218
 
139
- # JSI Schemas describing this MetaschemaNode
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#jsi_child}
150
- def jsi_child(token, as_jsi: )
151
- child_node = @root_descendent_node_map[ptr: jsi_ptr[token]]
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
- # instantiates a new MetaschemaNode whose instance is a modified copy of this MetaschemaNode's instance
166
- # @yield [Object] the node content of the instance. the block should result
167
- # in a (nondestructively) modified copy of this.
168
- # @return [MetaschemaNode] modified copy of self
169
- def jsi_modified_copy(&block)
170
- if jsi_ptr.root?
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
- # @private
182
- # @return [Array<String>]
183
- def jsi_object_group_text
184
- if jsi_schemas && jsi_schemas.any?
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
- {class: self.class, jsi_document: jsi_document}.merge(our_initialize_params)
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 jsi_ptr.root?
210
- @root_descendent_node_map = jsi_memomap(key_by: proc { |i| i[:ptr] }, &method(:jsi_root_descendent_node_compute))
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
- #chkbug raise(Bug) unless jsi_ptr.root?
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
- new_node(
287
+ MetaSchemaNode.new(
288
+ jsi_document: jsi_document,
239
289
  jsi_ptr: ptr,
240
- jsi_schema_base_uri: jsi_resource_ancestor_uri,
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 bootstrap_schemas [Enumerable<BootstrapSchema>]
246
- # @return [SchemaSet<MetaschemaNode>]
247
- def bootstrap_schemas_to_msn(bootstrap_schemas)
248
- SchemaSet.new(bootstrap_schemas) do |bootstrap_schema|
249
- if bootstrap_schema.jsi_ptr == jsi_ptr
250
- self
251
- elsif bootstrap_schema.jsi_ptr.root?
252
- @jsi_root_node
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
- new_node(
255
- jsi_ptr: bootstrap_schema.jsi_ptr,
256
- jsi_schema_base_uri: bootstrap_schema.jsi_schema_base_uri,
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 < Error
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 `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.:
63
65
  #
64
- # JSI::Ptr.from_fragment(Addressable::URI.parse("#/foo").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(Addressable::URI.unescape(fragment))
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
- Addressable::URI.escape(pointer).freeze
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 [Addressable::URI]
163
+ # @return [URI]
144
164
  def uri
145
- Addressable::URI.new(fragment: fragment).freeze
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 contains the other_ptr - that is, whether this pointer is an ancestor
169
- # of `other_ptr`, a descendent pointer. `contains?` is inclusive; a pointer does contain itself.
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 contains?(other_ptr)
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
- unless ancestor_ptr.contains?(self)
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
- n == tokens.size ? self : Ptr.new(tokens.take(n).freeze)
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.object_id == document_child.object_id
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
- raise(Bug) # not possible; node_subscript_token_child would have raised
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
- alias_method :to_s, :inspect
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