jsi 0.4.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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