jsi 0.4.0 → 0.7.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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +33 -0
  4. data/LICENSE.md +1 -1
  5. data/README.md +114 -42
  6. data/jsi.gemspec +14 -12
  7. data/lib/jsi/base/node.rb +183 -0
  8. data/lib/jsi/base.rb +388 -220
  9. data/lib/jsi/jsi_coder.rb +8 -7
  10. data/lib/jsi/metaschema.rb +0 -1
  11. data/lib/jsi/metaschema_node/bootstrap_schema.rb +101 -0
  12. data/lib/jsi/metaschema_node.rb +159 -135
  13. data/lib/jsi/ptr.rb +303 -0
  14. data/lib/jsi/schema/application/child_application/contains.rb +25 -0
  15. data/lib/jsi/schema/application/child_application/draft04.rb +22 -0
  16. data/lib/jsi/schema/application/child_application/draft06.rb +29 -0
  17. data/lib/jsi/schema/application/child_application/draft07.rb +29 -0
  18. data/lib/jsi/schema/application/child_application/items.rb +18 -0
  19. data/lib/jsi/schema/application/child_application/properties.rb +25 -0
  20. data/lib/jsi/schema/application/child_application.rb +38 -0
  21. data/lib/jsi/schema/application/draft04.rb +8 -0
  22. data/lib/jsi/schema/application/draft06.rb +8 -0
  23. data/lib/jsi/schema/application/draft07.rb +8 -0
  24. data/lib/jsi/schema/application/inplace_application/dependencies.rb +28 -0
  25. data/lib/jsi/schema/application/inplace_application/draft04.rb +26 -0
  26. data/lib/jsi/schema/application/inplace_application/draft06.rb +27 -0
  27. data/lib/jsi/schema/application/inplace_application/draft07.rb +33 -0
  28. data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +20 -0
  29. data/lib/jsi/schema/application/inplace_application/ref.rb +18 -0
  30. data/lib/jsi/schema/application/inplace_application/someof.rb +44 -0
  31. data/lib/jsi/schema/application/inplace_application.rb +41 -0
  32. data/lib/jsi/schema/application.rb +12 -0
  33. data/lib/jsi/schema/draft04.rb +14 -0
  34. data/lib/jsi/schema/draft06.rb +14 -0
  35. data/lib/jsi/schema/draft07.rb +14 -0
  36. data/lib/jsi/schema/issue.rb +36 -0
  37. data/lib/jsi/schema/ref.rb +160 -0
  38. data/lib/jsi/schema/schema_ancestor_node.rb +113 -0
  39. data/lib/jsi/schema/validation/array.rb +69 -0
  40. data/lib/jsi/schema/validation/const.rb +20 -0
  41. data/lib/jsi/schema/validation/contains.rb +25 -0
  42. data/lib/jsi/schema/validation/core.rb +39 -0
  43. data/lib/jsi/schema/validation/dependencies.rb +49 -0
  44. data/lib/jsi/schema/validation/draft04/minmax.rb +91 -0
  45. data/lib/jsi/schema/validation/draft04.rb +112 -0
  46. data/lib/jsi/schema/validation/draft06.rb +122 -0
  47. data/lib/jsi/schema/validation/draft07.rb +159 -0
  48. data/lib/jsi/schema/validation/enum.rb +25 -0
  49. data/lib/jsi/schema/validation/ifthenelse.rb +46 -0
  50. data/lib/jsi/schema/validation/items.rb +54 -0
  51. data/lib/jsi/schema/validation/not.rb +20 -0
  52. data/lib/jsi/schema/validation/numeric.rb +121 -0
  53. data/lib/jsi/schema/validation/object.rb +45 -0
  54. data/lib/jsi/schema/validation/pattern.rb +34 -0
  55. data/lib/jsi/schema/validation/properties.rb +101 -0
  56. data/lib/jsi/schema/validation/property_names.rb +32 -0
  57. data/lib/jsi/schema/validation/ref.rb +40 -0
  58. data/lib/jsi/schema/validation/required.rb +27 -0
  59. data/lib/jsi/schema/validation/someof.rb +90 -0
  60. data/lib/jsi/schema/validation/string.rb +47 -0
  61. data/lib/jsi/schema/validation/type.rb +49 -0
  62. data/lib/jsi/schema/validation.rb +51 -0
  63. data/lib/jsi/schema.rb +508 -149
  64. data/lib/jsi/schema_classes.rb +199 -59
  65. data/lib/jsi/schema_registry.rb +151 -0
  66. data/lib/jsi/schema_set.rb +181 -0
  67. data/lib/jsi/simple_wrap.rb +23 -4
  68. data/lib/jsi/util/private/attr_struct.rb +127 -0
  69. data/lib/jsi/util/private.rb +204 -0
  70. data/lib/jsi/util/typelike.rb +229 -0
  71. data/lib/jsi/util.rb +89 -53
  72. data/lib/jsi/validation/error.rb +34 -0
  73. data/lib/jsi/validation/result.rb +210 -0
  74. data/lib/jsi/validation.rb +15 -0
  75. data/lib/jsi/version.rb +3 -1
  76. data/lib/jsi.rb +44 -14
  77. data/lib/schemas/json-schema.org/draft-04/schema.rb +10 -3
  78. data/lib/schemas/json-schema.org/draft-06/schema.rb +10 -3
  79. data/lib/schemas/json-schema.org/draft-07/schema.rb +14 -0
  80. data/readme.rb +138 -0
  81. data/{resources}/schemas/json-schema.org/draft-04/schema.json +149 -0
  82. data/{resources}/schemas/json-schema.org/draft-06/schema.json +154 -0
  83. data/{resources}/schemas/json-schema.org/draft-07/schema.json +168 -0
  84. metadata +75 -122
  85. data/.simplecov +0 -3
  86. data/Rakefile.rb +0 -9
  87. data/lib/jsi/base/to_rb.rb +0 -128
  88. data/lib/jsi/json/node.rb +0 -203
  89. data/lib/jsi/json/pointer.rb +0 -419
  90. data/lib/jsi/json-schema-fragments.rb +0 -61
  91. data/lib/jsi/json.rb +0 -10
  92. data/lib/jsi/pathed_node.rb +0 -118
  93. data/lib/jsi/typelike_modules.rb +0 -240
  94. data/resources/icons/AGPL-3.0.png +0 -0
  95. data/test/base_array_test.rb +0 -323
  96. data/test/base_hash_test.rb +0 -337
  97. data/test/base_test.rb +0 -486
  98. data/test/jsi_coder_test.rb +0 -85
  99. data/test/jsi_json_arraynode_test.rb +0 -150
  100. data/test/jsi_json_hashnode_test.rb +0 -132
  101. data/test/jsi_json_node_test.rb +0 -257
  102. data/test/jsi_json_pointer_test.rb +0 -102
  103. data/test/jsi_test.rb +0 -11
  104. data/test/jsi_typelike_as_json_test.rb +0 -53
  105. data/test/metaschema_node_test.rb +0 -19
  106. data/test/schema_module_test.rb +0 -21
  107. data/test/schema_test.rb +0 -208
  108. data/test/spreedly_openapi_test.rb +0 -8
  109. data/test/test_helper.rb +0 -97
  110. data/test/util_test.rb +0 -62
data/lib/jsi/jsi_coder.rb CHANGED
@@ -11,7 +11,7 @@ module JSI
11
11
  # `preferences_json` which is an actual json column, and `preferences_txt`
12
12
  # which is a string:
13
13
  #
14
- # Preferences = JSI.class_for_schema(preferences_json_schema)
14
+ # Preferences = JSI.new_schema_module(preferences_json_schema)
15
15
  # class Foo < ActiveRecord::Base
16
16
  # # as a single serializer, loads a Preferences instance from a json column
17
17
  # serialize 'preferences_json', JSI::JSICoder.new(Preferences)
@@ -25,13 +25,13 @@ module JSI
25
25
  # (represented as one json object) or an array of them (represented as a json
26
26
  # array of json objects), indicated by the keyword argument `array`.
27
27
  class JSICoder
28
- # @param schema [JSI::Schema, JSI::SchemaModule, Class < JSI::Base] a schema, a JSI schema class, or
29
- # a JSI schema module. #load will instantiate column data using the JSI schema represented.
28
+ # @param schema [#new_jsi] a Schema, SchemaSet, or JSI schema module. #load
29
+ # will instantiate column data using the JSI schemas represented.
30
30
  # @param array [Boolean] whether the dumped data represent one instance of the schema,
31
31
  # or an array of them. note that it may be preferable to simply use an array schema.
32
32
  def initialize(schema, array: false)
33
33
  unless schema.respond_to?(:new_jsi)
34
- raise(ArgumentError, "not a JSI schema, class, or module: #{schema.inspect}")
34
+ raise(ArgumentError, "schema param does not respond to #new_jsi: #{schema.inspect}")
35
35
  end
36
36
  @schema = schema
37
37
  @array = array
@@ -48,13 +48,14 @@ module JSI
48
48
  unless data.respond_to?(:to_ary)
49
49
  raise TypeError, "expected array-like column data; got: #{data.class}: #{data.inspect}"
50
50
  end
51
- data.map { |el| load_object(el) }
51
+ data.to_ary.map { |el| load_object(el) }
52
52
  else
53
53
  load_object(data)
54
54
  end
55
55
  object
56
56
  end
57
57
 
58
+ # dumps the object for the database
58
59
  # @param object [JSI::Base, Array<JSI::Base>, nil] the JSI or array of JSIs containing
59
60
  # the schema instance(s)
60
61
  # @return [Object, Array, nil] the schema instance(s) of the JSI(s), or nil if object is nil
@@ -65,7 +66,7 @@ module JSI
65
66
  unless object.respond_to?(:to_ary)
66
67
  raise(TypeError, "expected array-like attribute; got: #{object.class}: #{object.inspect}")
67
68
  end
68
- object.map do |el|
69
+ object.to_ary.map do |el|
69
70
  dump_object(el)
70
71
  end
71
72
  else
@@ -85,7 +86,7 @@ module JSI
85
86
  # @param object [JSI::Base, Object]
86
87
  # @return [Object]
87
88
  def dump_object(object)
88
- JSI::Typelike.as_json(object)
89
+ JSI::Util.as_json(object)
89
90
  end
90
91
  end
91
92
  end
@@ -2,6 +2,5 @@
2
2
 
3
3
  module JSI
4
4
  module Metaschema
5
- include JSI::Schema::DescribesSchema
6
5
  end
7
6
  end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ # internal class to bootstrap a metaschema. represents a schema without the complexity of JSI::Base. the
5
+ # schema is represented but schemas describing the schema are not.
6
+ #
7
+ # this class is to only be instantiated on nodes in the document that are known to be schemas.
8
+ # Schema#subschema and Schema#resource_root_subschema are the intended mechanisms to instantiate subschemas
9
+ # and resolve references. #[] and #jsi_root_node are not implemented.
10
+ #
11
+ # schema implementation modules are attached to generated subclasses of BootstrapSchema by
12
+ # {SchemaClasses.bootstrap_schema_class}. that subclass is instantiated with a document and
13
+ # pointer, representing a schema.
14
+ #
15
+ # @api private
16
+ class MetaschemaNode::BootstrapSchema
17
+ include Util::Memoize
18
+ include Util::FingerprintHash
19
+ include Schema::SchemaAncestorNode
20
+
21
+ class << self
22
+ def inspect
23
+ if self == MetaschemaNode::BootstrapSchema
24
+ name
25
+ else
26
+ "#{name || MetaschemaNode::BootstrapSchema.name} (#{schema_implementation_modules.map(&:inspect).join(', ')})"
27
+ end
28
+ end
29
+
30
+ alias_method :to_s, :inspect
31
+ end
32
+
33
+ # @param jsi_ptr [JSI::Ptr] pointer to the schema in the document
34
+ # @param jsi_document [#to_hash, #to_ary, Boolean, Object] document containing the schema
35
+ def initialize(
36
+ jsi_document,
37
+ jsi_ptr: Ptr[],
38
+ jsi_schema_base_uri: nil
39
+ )
40
+ raise(Bug, "no #schema_implementation_modules") unless respond_to?(:schema_implementation_modules)
41
+
42
+ jsi_initialize_memos
43
+
44
+ self.jsi_ptr = jsi_ptr
45
+ self.jsi_document = jsi_document
46
+ self.jsi_schema_base_uri = jsi_schema_base_uri
47
+ end
48
+
49
+ # document containing the schema content
50
+ attr_reader :jsi_document
51
+
52
+ # JSI::Ptr pointing to this schema within the document
53
+ attr_reader :jsi_ptr
54
+
55
+ def jsi_node_content
56
+ jsi_ptr.evaluate(jsi_document)
57
+ end
58
+
59
+ # @return [String]
60
+ def inspect
61
+ "\#<#{jsi_object_group_text.join(' ')} #{schema_content.inspect}>"
62
+ end
63
+
64
+ alias_method :to_s, :inspect
65
+
66
+ # pretty-prints a representation of self to the given printer
67
+ # @return [void]
68
+ def pretty_print(q)
69
+ q.text '#<'
70
+ q.text jsi_object_group_text.join(' ')
71
+ q.group_sub {
72
+ q.nest(2) {
73
+ q.breakable ' '
74
+ q.pp schema_content
75
+ }
76
+ }
77
+ q.breakable ''
78
+ q.text '>'
79
+ end
80
+
81
+ # @private
82
+ # @return [Array<String>]
83
+ def jsi_object_group_text
84
+ [
85
+ self.class.name || MetaschemaNode::BootstrapSchema.name,
86
+ "(#{schema_implementation_modules.map(&:inspect).join(', ')})",
87
+ jsi_ptr.uri,
88
+ ]
89
+ end
90
+
91
+ # @private
92
+ def jsi_fingerprint
93
+ {
94
+ class: self.class,
95
+ jsi_ptr: @jsi_ptr,
96
+ jsi_document: @jsi_document,
97
+ schema_implementation_modules: schema_implementation_modules,
98
+ }
99
+ end
100
+ end
101
+ end
@@ -1,218 +1,242 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSI
4
- # a MetaschemaNode is a PathedNode whose node_document contains a metaschema.
5
- # as with any PathedNode the node_ptr points to the content of a node.
4
+ # a MetaschemaNode is a JSI instance representing a node in a document which contains a metaschema.
6
5
  # the root of the metaschema is pointed to by metaschema_root_ptr.
7
- # the schema of the root of the document is pointed to by root_schema_ptr.
6
+ # the schema describing the root of the document is pointed to by root_schema_ptr.
8
7
  #
9
- # like JSI::Base, this class represents an instance of a schema, an instance
10
- # which may itself be a schema. unlike JSI::Base, the document containing the
11
- # schema and the instance is the same, and a schema may be an instance of itself.
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.
12
11
  #
13
- # the document containing the metaschema, its subschemas, and instances of those
14
- # subschemas is the node_document.
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.
15
18
  #
16
- # the schema instance is the content in the document pointed to by the MetaschemaNode's node_ptr.
17
- #
18
- # unlike with JSI::Base, the schema is not part of the class, since a metaschema
19
- # needs the ability to have its schema be the instance itself.
20
- #
21
- # if the MetaschemaNode's schema is its self, it will be extended with JSI::Metaschema.
19
+ # if the MetaschemaNode's schemas include its self, it is extended with JSI::Metaschema.
22
20
  #
23
21
  # a MetaschemaNode is extended with JSI::Schema when it represents a schema - this is the case when
24
- # its schema is the metaschema.
25
- class MetaschemaNode
26
- include PathedNode
27
- include Util::Memoize
28
-
29
- # not every MetaschemaNode is actually an Enumerable, but it's better to include Enumerable on
30
- # the class than to conditionally extend the instance.
31
- include Enumerable
32
-
33
- # @param node_document the document containing the metaschema
34
- # @param node_ptr [JSI::JSON::Pointer] ptr to this MetaschemaNode in node_document
35
- # @param metaschema_root_ptr [JSI::JSON::Pointer] ptr to the root of the metaschema in node_document
36
- # @param root_schema_ptr [JSI::JSON::Pointer] ptr to the schema of the root of the node_document
37
- def initialize(node_document, node_ptr: JSI::JSON::Pointer[], metaschema_root_ptr: JSI::JSON::Pointer[], root_schema_ptr: JSI::JSON::Pointer[])
38
- @node_document = node_document
39
- @node_ptr = node_ptr
22
+ # the metaschema is one of its schemas.
23
+ class MetaschemaNode < Base
24
+ autoload :BootstrapSchema, 'jsi/metaschema_node/bootstrap_schema'
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
35
+ def initialize(
36
+ jsi_document,
37
+ 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_root_node: nil
43
+ )
44
+ jsi_initialize_memos
45
+
46
+ self.jsi_document = jsi_document
47
+ self.jsi_ptr = jsi_ptr
48
+ @schema_implementation_modules = Util.ensure_module_set(schema_implementation_modules)
40
49
  @metaschema_root_ptr = metaschema_root_ptr
41
50
  @root_schema_ptr = root_schema_ptr
51
+ raise(Bug, 'jsi_root_node') if jsi_ptr.root? ^ !jsi_root_node
52
+ @jsi_root_node = jsi_ptr.root? ? self : jsi_root_node
53
+
54
+ if jsi_ptr.root? && jsi_schema_base_uri
55
+ raise(NotImplementedError, "unsupported jsi_schema_base_uri on metaschema document root")
56
+ end
57
+ self.jsi_schema_base_uri = jsi_schema_base_uri
58
+
59
+ jsi_node_content = self.jsi_node_content
42
60
 
43
- node_content = self.node_content
61
+ if jsi_node_content.respond_to?(:to_hash)
62
+ extend HashNode
63
+ end
64
+ if jsi_node_content.respond_to?(:to_ary)
65
+ extend ArrayNode
66
+ end
44
67
 
45
- if node_content.respond_to?(:to_hash)
46
- extend PathedHashNode
47
- elsif node_content.respond_to?(:to_ary)
48
- extend PathedArrayNode
68
+ instance_for_schemas = jsi_document
69
+ bootstrap_schema_class = JSI::SchemaClasses.bootstrap_schema_class(schema_implementation_modules)
70
+ root_bootstrap_schema = bootstrap_schema_class.new(
71
+ jsi_document,
72
+ jsi_ptr: root_schema_ptr,
73
+ jsi_schema_base_uri: nil, # supplying jsi_schema_base_uri on root bootstrap schema is not supported
74
+ )
75
+ our_bootstrap_schemas = jsi_ptr.tokens.inject(SchemaSet[root_bootstrap_schema]) do |bootstrap_schemas, tok|
76
+ child_indicated_schemas = bootstrap_schemas.child_applicator_schemas(tok, instance_for_schemas)
77
+ child_schemas = child_indicated_schemas.inplace_applicator_schemas(instance_for_schemas[tok])
78
+ instance_for_schemas = instance_for_schemas[tok]
79
+ child_schemas
49
80
  end
50
81
 
51
- instance_for_schema = node_document
52
- schema_ptrs = node_ptr.reference_tokens.inject(Set.new << root_schema_ptr) do |ptrs, tok|
53
- if instance_for_schema.respond_to?(:to_ary)
54
- subschema_ptrs_for_token = ptrs.map do |ptr|
55
- ptr.schema_subschema_ptrs_for_index(node_document, tok)
56
- end.inject(Set.new, &:|)
57
- else
58
- subschema_ptrs_for_token = ptrs.map do |ptr|
59
- ptr.schema_subschema_ptrs_for_property_name(node_document, tok)
60
- end.inject(Set.new, &:|)
82
+ our_bootstrap_schemas.each do |bootstrap_schema|
83
+ if bootstrap_schema.jsi_ptr == metaschema_root_ptr
84
+ # this is described by the metaschema, i.e. this is a schema
85
+ schema_implementation_modules.each do |schema_implementation_module|
86
+ extend schema_implementation_module
87
+ end
88
+ end
89
+ if bootstrap_schema.jsi_ptr == jsi_ptr
90
+ # this is the metaschema (it is described by itself)
91
+ extend Metaschema
61
92
  end
62
- instance_for_schema = instance_for_schema[tok]
63
- ptrs_for_instance = subschema_ptrs_for_token.map do |ptr|
64
- ptr.schema_match_ptrs_to_instance(node_document, instance_for_schema)
65
- end.inject(Set.new, &:|)
66
- ptrs_for_instance
67
93
  end
68
94
 
69
- @jsi_schemas = schema_ptrs.map do |schema_ptr|
70
- if schema_ptr == node_ptr
95
+ @jsi_schemas = SchemaSet.new(our_bootstrap_schemas) do |bootstrap_schema|
96
+ if bootstrap_schema.jsi_ptr == jsi_ptr
71
97
  self
98
+ elsif bootstrap_schema.jsi_ptr.root?
99
+ @jsi_root_node
72
100
  else
73
- new_node(node_ptr: schema_ptr)
101
+ new_node(
102
+ jsi_ptr: bootstrap_schema.jsi_ptr,
103
+ jsi_schema_base_uri: bootstrap_schema.jsi_schema_base_uri,
104
+ jsi_root_node: @jsi_root_node,
105
+ )
74
106
  end
75
- end.to_set
107
+ end
108
+
109
+ # note: jsi_schemas must already be set for jsi_schema_module to be used/extended
110
+ if is_a?(Metaschema)
111
+ describes_schema!(schema_implementation_modules)
112
+ end
76
113
 
77
114
  @jsi_schemas.each do |schema|
78
- if schema.node_ptr == metaschema_root_ptr
79
- extend JSI::Schema
80
- end
81
- if schema.node_ptr == node_ptr
82
- extend Metaschema
83
- end
84
- extend(JSI::SchemaClasses.accessor_module_for_schema(schema, conflicting_modules: [Metaschema, Schema, MetaschemaNode, PathedArrayNode, PathedHashNode]))
115
+ extend schema.jsi_schema_module
85
116
  end
86
117
 
87
118
  # workarounds
88
119
  begin # draft 4 boolean schema workaround
89
120
  # in draft 4, boolean schemas are not described in the root, but on anyOf schemas on
90
121
  # properties/additionalProperties and properties/additionalItems.
91
- # we need to extend those as DescribesSchema.
122
+ # these still describe schemas, despite not being described by the metaschema.
92
123
  addtlPropsanyOf = metaschema_root_ptr["properties"]["additionalProperties"]["anyOf"]
93
124
  addtlItemsanyOf = metaschema_root_ptr["properties"]["additionalItems"]["anyOf"]
94
125
 
95
- if !node_ptr.root? && [addtlPropsanyOf, addtlItemsanyOf].include?(node_ptr.parent)
96
- extend JSI::Schema::DescribesSchema
126
+ if !jsi_ptr.root? && [addtlPropsanyOf, addtlItemsanyOf].include?(jsi_ptr.parent)
127
+ describes_schema!(schema_implementation_modules)
97
128
  end
98
129
  end
99
130
  end
100
131
 
101
- # document containing the metaschema. see PathedNode#node_document.
102
- attr_reader :node_document
103
- # ptr to this metaschema node. see PathedNode#node_ptr.
104
- attr_reader :node_ptr
105
- # ptr to the root of the metaschema in the node_document
132
+ # Set of modules to apply to schemas which are instances of (described by) the metaschema
133
+ # @return [Set<Module>]
134
+ attr_reader :schema_implementation_modules
135
+
136
+ # ptr to the root of the metaschema in the jsi_document
137
+ # @return [JSI::Ptr]
106
138
  attr_reader :metaschema_root_ptr
107
- # ptr to the schema of the root of the node_document
108
- attr_reader :root_schema_ptr
109
- # JSI::Schemas describing this MetaschemaNode
110
- attr_reader :jsi_schemas
111
139
 
112
- # @return [MetaschemaNode] document root MetaschemaNode
113
- def document_root_node
114
- new_node(node_ptr: JSI::JSON::Pointer[])
115
- end
140
+ # ptr to the schema of the root of the jsi_document
141
+ # @return [JSI::Ptr]
142
+ attr_reader :root_schema_ptr
116
143
 
117
- # @return [MetaschemaNode] parent MetaschemaNode
118
- def parent_node
119
- new_node(node_ptr: node_ptr.parent)
120
- end
144
+ # JSI Schemas describing this MetaschemaNode
145
+ # @return [JSI::SchemaSet]
146
+ attr_reader :jsi_schemas
121
147
 
122
- # @param token [String, Integer, Object] the token to subscript
123
- # @return [MetaschemaNode, Object] the node content's subscript value at the given token.
124
- # if there is a subschema defined for that token on this MetaschemaNode's schema,
125
- # returns that value as a MetaschemaNode instantiation of that subschema.
126
- def [](token)
148
+ # subscripts to return a child value identified by the given token.
149
+ #
150
+ # @param token (see JSI::Base#[])
151
+ # @param as_jsi (see JSI::Base#[])
152
+ # @return (see JSI::Base#[])
153
+ def [](token, as_jsi: :auto)
127
154
  if respond_to?(:to_hash)
128
- token_in_range = node_content_hash_pubsend(:key?, token)
129
- value = node_content_hash_pubsend(:[], token)
155
+ token_in_range = jsi_node_content_hash_pubsend(:key?, token)
156
+ value = jsi_node_content_hash_pubsend(:[], token)
130
157
  elsif respond_to?(:to_ary)
131
- token_in_range = node_content_ary_pubsend(:each_index).include?(token)
132
- value = node_content_ary_pubsend(:[], token)
158
+ token_in_range = jsi_node_content_ary_pubsend(:each_index).include?(token)
159
+ value = jsi_node_content_ary_pubsend(:[], token)
133
160
  else
134
- raise(NoMethodError, "cannot subcript (using token: #{token.inspect}) from content: #{node_content.pretty_inspect.chomp}")
161
+ raise(NoMethodError, "cannot subscript (using token: #{token.inspect}) from content: #{jsi_node_content.pretty_inspect.chomp}")
135
162
  end
136
163
 
137
- result = jsi_memoize(:[], token, value, token_in_range) do |token, value, token_in_range|
164
+ begin
138
165
  if token_in_range
139
- value_node = new_node(node_ptr: node_ptr[token])
166
+ value_node = jsi_subinstance_memos[token: token]
140
167
 
141
- if value_node.is_a?(Schema) || value.respond_to?(:to_hash) || value.respond_to?(:to_ary)
168
+ jsi_subinstance_as_jsi(value, value_node.jsi_schemas, as_jsi) do
142
169
  value_node
143
- else
144
- value
145
170
  end
146
171
  else
147
172
  # I think I will not support Hash#default/#default_proc in this case.
148
173
  nil
149
174
  end
150
175
  end
151
- result
152
- end
153
-
154
- # if this MetaschemaNode is a $ref then the $ref is followed. otherwise this MetaschemaNode is returned.
155
- # @return [MetaschemaNode]
156
- def deref(&block)
157
- node_ptr_deref do |deref_ptr|
158
- return new_node(node_ptr: deref_ptr).tap(&(block || Util::NOOP))
159
- end
160
- return self
161
176
  end
162
177
 
178
+ # instantiates a new MetaschemaNode whose instance is a modified copy of this MetaschemaNode's instance
163
179
  # @yield [Object] the node content of the instance. the block should result
164
180
  # in a (nondestructively) modified copy of this.
165
181
  # @return [MetaschemaNode] modified copy of self
166
- def modified_copy(&block)
167
- MetaschemaNode.new(node_ptr.modified_document_copy(node_document, &block), our_initialize_params)
168
- end
169
-
170
- # @return [String]
171
- def inspect
172
- "\#<#{object_group_text.join(' ')} #{node_content.inspect}>"
173
- end
174
-
175
- def pretty_print(q)
176
- q.text '#<'
177
- q.text object_group_text.join(' ')
178
- q.group_sub {
179
- q.nest(2) {
180
- q.breakable ' '
181
- q.pp node_content
182
- }
183
- }
184
- q.breakable ''
185
- q.text '>'
182
+ def jsi_modified_copy(&block)
183
+ if jsi_ptr.root?
184
+ modified_document = jsi_ptr.modified_document_copy(jsi_document, &block)
185
+ MetaschemaNode.new(modified_document, **our_initialize_params)
186
+ else
187
+ modified_jsi_root_node = jsi_root_node.jsi_modified_copy do |root|
188
+ jsi_ptr.modified_document_copy(root, &block)
189
+ end
190
+ modified_jsi_root_node.jsi_descendent_node(jsi_ptr)
191
+ end
186
192
  end
187
193
 
194
+ # @private
188
195
  # @return [Array<String>]
189
- def object_group_text
190
- if jsi_schemas.any?
191
- class_n_schemas = "#{self.class} (#{jsi_schemas.map { |s| s.node_ptr.uri }.join(' ')})"
196
+ def jsi_object_group_text
197
+ if jsi_schemas && jsi_schemas.any?
198
+ class_n_schemas = "#{self.class} (#{jsi_schemas.map { |s| s.jsi_ptr.uri }.join(' ')})"
192
199
  else
193
200
  class_n_schemas = self.class.to_s
194
201
  end
195
202
  [
196
203
  class_n_schemas,
197
204
  is_a?(Metaschema) ? "Metaschema" : is_a?(Schema) ? "Schema" : nil,
198
- *(node_content.respond_to?(:object_group_text) ? node_content.object_group_text : []),
205
+ *(jsi_node_content.respond_to?(:jsi_object_group_text) ? jsi_node_content.jsi_object_group_text : []),
199
206
  ].compact
200
207
  end
201
208
 
202
- # @return [Object] an opaque fingerprint of this MetaschemaNode for FingerprintHash
209
+ # an opaque fingerprint of this MetaschemaNode for FingerprintHash
203
210
  def jsi_fingerprint
204
- {class: self.class, node_document: node_document}.merge(our_initialize_params)
211
+ {class: self.class, jsi_document: jsi_document}.merge(our_initialize_params)
205
212
  end
206
- include Util::FingerprintHash
207
213
 
208
214
  private
209
215
 
216
+ # note: does not include jsi_root_node
210
217
  def our_initialize_params
211
- {node_ptr: node_ptr, metaschema_root_ptr: metaschema_root_ptr, root_schema_ptr: root_schema_ptr}
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
+ }
212
225
  end
213
226
 
227
+ # note: not for root node
214
228
  def new_node(params)
215
- MetaschemaNode.new(node_document, our_initialize_params.merge(params))
229
+ MetaschemaNode.new(jsi_document, **our_initialize_params.merge(params))
230
+ end
231
+
232
+ def jsi_subinstance_memos
233
+ jsi_memomap(:subinstance) do |token: |
234
+ new_node(
235
+ jsi_ptr: jsi_ptr[token],
236
+ jsi_schema_base_uri: jsi_resource_ancestor_uri,
237
+ jsi_root_node: jsi_root_node,
238
+ )
239
+ end
216
240
  end
217
241
  end
218
242
  end