jsi 0.6.0 → 0.8.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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -1
  3. data/CHANGELOG.md +33 -0
  4. data/LICENSE.md +1 -1
  5. data/README.md +29 -23
  6. data/jsi.gemspec +29 -0
  7. data/lib/jsi/base/mutability.rb +44 -0
  8. data/lib/jsi/base/node.rb +348 -0
  9. data/lib/jsi/base.rb +497 -339
  10. data/lib/jsi/jsi_coder.rb +19 -17
  11. data/lib/jsi/metaschema_node/bootstrap_schema.rb +61 -26
  12. data/lib/jsi/metaschema_node.rb +161 -133
  13. data/lib/jsi/ptr.rb +80 -47
  14. data/lib/jsi/schema/application/child_application/contains.rb +11 -2
  15. data/lib/jsi/schema/application/child_application/draft04.rb +0 -1
  16. data/lib/jsi/schema/application/child_application/draft06.rb +0 -1
  17. data/lib/jsi/schema/application/child_application/draft07.rb +0 -1
  18. data/lib/jsi/schema/application/child_application/items.rb +3 -3
  19. data/lib/jsi/schema/application/child_application/properties.rb +3 -3
  20. data/lib/jsi/schema/application/child_application.rb +0 -27
  21. data/lib/jsi/schema/application/inplace_application/dependencies.rb +1 -1
  22. data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -1
  23. data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -1
  24. data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -1
  25. data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +3 -3
  26. data/lib/jsi/schema/application/inplace_application/ref.rb +2 -2
  27. data/lib/jsi/schema/application/inplace_application/someof.rb +26 -11
  28. data/lib/jsi/schema/application/inplace_application.rb +0 -32
  29. data/lib/jsi/schema/draft04.rb +0 -1
  30. data/lib/jsi/schema/draft06.rb +0 -1
  31. data/lib/jsi/schema/draft07.rb +0 -1
  32. data/lib/jsi/schema/ref.rb +46 -19
  33. data/lib/jsi/schema/schema_ancestor_node.rb +69 -66
  34. data/lib/jsi/schema/validation/array.rb +3 -3
  35. data/lib/jsi/schema/validation/const.rb +1 -1
  36. data/lib/jsi/schema/validation/contains.rb +2 -2
  37. data/lib/jsi/schema/validation/dependencies.rb +1 -1
  38. data/lib/jsi/schema/validation/draft04/minmax.rb +8 -6
  39. data/lib/jsi/schema/validation/draft04.rb +0 -2
  40. data/lib/jsi/schema/validation/draft06.rb +0 -2
  41. data/lib/jsi/schema/validation/draft07.rb +0 -2
  42. data/lib/jsi/schema/validation/enum.rb +1 -1
  43. data/lib/jsi/schema/validation/ifthenelse.rb +5 -5
  44. data/lib/jsi/schema/validation/items.rb +7 -7
  45. data/lib/jsi/schema/validation/not.rb +1 -1
  46. data/lib/jsi/schema/validation/numeric.rb +5 -5
  47. data/lib/jsi/schema/validation/object.rb +2 -2
  48. data/lib/jsi/schema/validation/pattern.rb +2 -2
  49. data/lib/jsi/schema/validation/properties.rb +7 -7
  50. data/lib/jsi/schema/validation/property_names.rb +1 -1
  51. data/lib/jsi/schema/validation/ref.rb +2 -2
  52. data/lib/jsi/schema/validation/required.rb +1 -1
  53. data/lib/jsi/schema/validation/someof.rb +3 -3
  54. data/lib/jsi/schema/validation/string.rb +2 -2
  55. data/lib/jsi/schema/validation/type.rb +1 -1
  56. data/lib/jsi/schema/validation.rb +1 -3
  57. data/lib/jsi/schema.rb +443 -226
  58. data/lib/jsi/schema_classes.rb +241 -147
  59. data/lib/jsi/schema_registry.rb +78 -19
  60. data/lib/jsi/schema_set.rb +114 -28
  61. data/lib/jsi/simple_wrap.rb +18 -4
  62. data/lib/jsi/util/private/attr_struct.rb +141 -0
  63. data/lib/jsi/util/private/memo_map.rb +75 -0
  64. data/lib/jsi/util/private.rb +185 -0
  65. data/lib/jsi/{typelike_modules.rb → util/typelike.rb} +79 -105
  66. data/lib/jsi/util.rb +157 -153
  67. data/lib/jsi/validation/error.rb +4 -0
  68. data/lib/jsi/validation/result.rb +18 -32
  69. data/lib/jsi/version.rb +1 -1
  70. data/lib/jsi.rb +65 -39
  71. data/lib/schemas/json-schema.org/draft-04/schema.rb +160 -3
  72. data/lib/schemas/json-schema.org/draft-06/schema.rb +162 -3
  73. data/lib/schemas/json-schema.org/draft-07/schema.rb +189 -3
  74. metadata +27 -11
  75. data/lib/jsi/metaschema.rb +0 -7
  76. data/lib/jsi/pathed_node.rb +0 -116
  77. data/lib/jsi/schema/validation/core.rb +0 -39
  78. data/lib/jsi/util/attr_struct.rb +0 -106
data/lib/jsi/jsi_coder.rb CHANGED
@@ -29,12 +29,16 @@ module JSI
29
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
- def initialize(schema, array: false)
32
+ # @param jsi_opt [Hash] keyword arguments to pass to {Schema#new_jsi} when loading
33
+ # @param as_json_opt [Hash] keyword arguments to pass to `#as_json` when dumping
34
+ def initialize(schema, array: false, jsi_opt: Util::EMPTY_HASH, as_json_opt: Util::EMPTY_HASH)
33
35
  unless schema.respond_to?(:new_jsi)
34
36
  raise(ArgumentError, "schema param does not respond to #new_jsi: #{schema.inspect}")
35
37
  end
36
38
  @schema = schema
37
39
  @array = array
40
+ @jsi_opt = jsi_opt
41
+ @as_json_opt = as_json_opt
38
42
  end
39
43
 
40
44
  # loads the database column to JSI instances of our schema
@@ -44,15 +48,15 @@ module JSI
44
48
  # instance(s), or nil if data is nil
45
49
  def load(data)
46
50
  return nil if data.nil?
47
- object = if @array
51
+
52
+ if @array
48
53
  unless data.respond_to?(:to_ary)
49
54
  raise TypeError, "expected array-like column data; got: #{data.class}: #{data.inspect}"
50
55
  end
51
- data.map { |el| load_object(el) }
56
+ data.to_ary.map { |el| load_object(el) }
52
57
  else
53
58
  load_object(data)
54
59
  end
55
- object
56
60
  end
57
61
 
58
62
  # dumps the object for the database
@@ -61,32 +65,30 @@ module JSI
61
65
  # @return [Object, Array, nil] the schema instance(s) of the JSI(s), or nil if object is nil
62
66
  def dump(object)
63
67
  return nil if object.nil?
64
- jsonifiable = begin
65
- if @array
66
- unless object.respond_to?(:to_ary)
67
- raise(TypeError, "expected array-like attribute; got: #{object.class}: #{object.inspect}")
68
- end
69
- object.map do |el|
70
- dump_object(el)
71
- end
72
- else
73
- dump_object(object)
68
+
69
+ if @array
70
+ unless object.respond_to?(:to_ary)
71
+ raise(TypeError, "expected array-like attribute; got: #{object.class}: #{object.inspect}")
74
72
  end
73
+ object.to_ary.map do |el|
74
+ dump_object(el)
75
+ end
76
+ else
77
+ dump_object(object)
75
78
  end
76
- jsonifiable
77
79
  end
78
80
 
79
81
  private
80
82
  # @param data [Object]
81
83
  # @return [JSI::Base]
82
84
  def load_object(data)
83
- @schema.new_jsi(data)
85
+ @schema.new_jsi(data, **@jsi_opt)
84
86
  end
85
87
 
86
88
  # @param object [JSI::Base, Object]
87
89
  # @return [Object]
88
90
  def dump_object(object)
89
- JSI::Typelike.as_json(object)
91
+ JSI::Util.as_json(object, **@as_json_opt)
90
92
  end
91
93
  end
92
94
  end
@@ -1,32 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSI
4
- # @private
5
- # internal class to bootstrap a metaschema. represents a schema without the complexity of JSI::Base. the
4
+ # internal class to bootstrap a meta-schema. represents a schema without the complexity of JSI::Base. the
6
5
  # schema is represented but schemas describing the schema are not.
7
6
  #
8
7
  # this class is to only be instantiated on nodes in the document that are known to be schemas.
9
8
  # Schema#subschema and Schema#resource_root_subschema are the intended mechanisms to instantiate subschemas
10
9
  # and resolve references. #[] and #jsi_root_node are not implemented.
11
10
  #
12
- # metaschema instance modules are attached to generated subclasses of BootstrapSchema by
11
+ # schema implementation modules are included on generated subclasses of BootstrapSchema by
13
12
  # {SchemaClasses.bootstrap_schema_class}. that subclass is instantiated with a document and
14
13
  # pointer, representing a schema.
15
- class MetaschemaNode::BootstrapSchema
16
- include Util::Memoize
14
+ #
15
+ # BootstrapSchema does not support mutation; its document must be immutable.
16
+ #
17
+ # @api private
18
+ class MetaSchemaNode::BootstrapSchema
17
19
  include Util::FingerprintHash
18
20
  include Schema::SchemaAncestorNode
21
+ include Schema
19
22
 
20
23
  class << self
21
24
  def inspect
22
- if self == MetaschemaNode::BootstrapSchema
23
- name
25
+ if self == MetaSchemaNode::BootstrapSchema
26
+ name.freeze
24
27
  else
25
- "#{name || MetaschemaNode::BootstrapSchema.name} (#{metaschema_instance_modules.map(&:inspect).join(', ')})"
28
+ -"#{name || MetaSchemaNode::BootstrapSchema.name} (#{schema_implementation_modules.map(&:inspect).join(', ')})"
26
29
  end
27
30
  end
28
31
 
29
- alias_method :to_s, :inspect
32
+ def to_s
33
+ inspect
34
+ end
30
35
  end
31
36
 
32
37
  # @param jsi_ptr [JSI::Ptr] pointer to the schema in the document
@@ -36,15 +41,17 @@ module JSI
36
41
  jsi_ptr: Ptr[],
37
42
  jsi_schema_base_uri: nil
38
43
  )
39
- unless respond_to?(:metaschema_instance_modules)
40
- raise(TypeError, "cannot instantiate #{self.class.inspect} which has no method #metaschema_instance_modules")
41
- end
42
-
43
- jsi_initialize_memos
44
+ raise(Bug, "no #schema_implementation_modules") unless respond_to?(:schema_implementation_modules)
44
45
 
45
46
  self.jsi_ptr = jsi_ptr
46
47
  self.jsi_document = jsi_document
47
48
  self.jsi_schema_base_uri = jsi_schema_base_uri
49
+ self.jsi_schema_resource_ancestors = Util::EMPTY_ARY
50
+
51
+ @jsi_node_content = jsi_ptr.evaluate(jsi_document)
52
+ #chkbug raise(Bug, 'BootstrapSchema instance must be frozen') unless jsi_node_content.frozen?
53
+
54
+ super()
48
55
  end
49
56
 
50
57
  # document containing the schema content
@@ -53,13 +60,37 @@ module JSI
53
60
  # JSI::Ptr pointing to this schema within the document
54
61
  attr_reader :jsi_ptr
55
62
 
56
- def jsi_node_content
57
- jsi_ptr.evaluate(jsi_document)
63
+ attr_reader(:jsi_node_content)
64
+
65
+ # overrides {Schema#subschema}
66
+ def subschema(subptr)
67
+ self.class.new(
68
+ jsi_document,
69
+ jsi_ptr: jsi_ptr + subptr,
70
+ jsi_schema_base_uri: jsi_resource_ancestor_uri,
71
+ )
72
+ end
73
+
74
+ # overrides {Schema#resource_root_subschema}
75
+ def resource_root_subschema(ptr)
76
+ # BootstrapSchema does not track jsi_schema_resource_ancestors used by Schema#schema_resource_root;
77
+ # resource_root_subschema is always relative to the document root.
78
+ # BootstrapSchema also does not implement jsi_root_node or #[]. we instantiate the ptr directly
79
+ # rather than as a subschema from the root.
80
+ self.class.new(
81
+ jsi_document,
82
+ jsi_ptr: Ptr.ary_ptr(ptr),
83
+ jsi_schema_base_uri: nil,
84
+ )
58
85
  end
59
86
 
60
87
  # @return [String]
61
88
  def inspect
62
- "\#<#{jsi_object_group_text.join(' ')} #{schema_content.inspect}>"
89
+ -"\#<#{jsi_object_group_text.join(' ')} #{schema_content.inspect}>"
90
+ end
91
+
92
+ def to_s
93
+ inspect
63
94
  end
64
95
 
65
96
  # pretty-prints a representation of self to the given printer
@@ -67,11 +98,9 @@ module JSI
67
98
  def pretty_print(q)
68
99
  q.text '#<'
69
100
  q.text jsi_object_group_text.join(' ')
70
- q.group_sub {
71
- q.nest(2) {
101
+ q.group(2) {
72
102
  q.breakable ' '
73
103
  q.pp schema_content
74
- }
75
104
  }
76
105
  q.breakable ''
77
106
  q.text '>'
@@ -81,20 +110,26 @@ module JSI
81
110
  # @return [Array<String>]
82
111
  def jsi_object_group_text
83
112
  [
84
- self.class.name || MetaschemaNode::BootstrapSchema.name,
85
- "(#{metaschema_instance_modules.map(&:inspect).join(', ')})",
113
+ self.class.name || MetaSchemaNode::BootstrapSchema.name,
114
+ -"(#{schema_implementation_modules.map(&:inspect).join(', ')})",
86
115
  jsi_ptr.uri,
87
- ]
116
+ ].freeze
88
117
  end
89
118
 
90
- # @private
119
+ # see {Util::Private::FingerprintHash}
120
+ # @api private
91
121
  def jsi_fingerprint
92
122
  {
93
123
  class: self.class,
94
124
  jsi_ptr: @jsi_ptr,
95
125
  jsi_document: @jsi_document,
96
- metaschema_instance_modules: metaschema_instance_modules,
97
- }
126
+ }.freeze
127
+ end
128
+
129
+ private
130
+
131
+ def jsi_memomap_class
132
+ Util::MemoMap::Immutable
98
133
  end
99
134
  end
100
135
  end
@@ -1,134 +1,140 @@
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.
4
+ # A MetaSchemaNode is a JSI instance representing a node in a document that contains a meta-schema.
5
+ # The root of the meta-schema is pointed to by metaschema_root_ptr.
6
6
  # the schema describing the root of the document is pointed to by root_schema_ptr.
7
7
  #
8
8
  # like JSI::Base's normal subclasses, this class represents an instance of a schema set, an instance
9
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.
10
+ # is the same, and a schema (the meta-schema) may be an instance of itself.
11
11
  #
12
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
13
+ # Since the meta-schema describes itself, attempting to construct a class from the JSI Schema Module of a
14
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
15
+ # instead, a MetaSchemaNode calculates its {#jsi_schemas} and extends itself with their JSI Schema
16
16
  # modules during initialization.
17
- # the MetaschemaNode of the metaschema is extended with its own JSI Schema Module.
17
+ # The MetaSchemaNode of the meta-schema is extended with its own JSI Schema Module.
18
18
  #
19
- # if the MetaschemaNode's schemas include its self, it is extended with JSI::Metaschema.
19
+ # if the MetaSchemaNode's schemas include its self, it is extended with {JSI::Schema::MetaSchema}.
20
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
21
+ # a MetaSchemaNode is extended with JSI::Schema when it represents a schema - this is the case when
22
+ # the meta-schema is one of its schemas.
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 metaschema_instance_modules [Enumerable<Module>] modules which implement the functionality of the
29
- # schema, to be applied to every schema which is an instance of the metaschema. this must include
30
- # JSI::Schema directly or indirectly. these are the {Schema#jsi_schema_instance_modules} of the
31
- # metaschema.
32
- # @param metaschema_root_ptr [JSI::Ptr] ptr to the root of the metaschema in the jsi_document
26
+ include(Base::Immutable)
27
+
28
+ # @param jsi_document the document containing the meta-schema.
29
+ # this must be frozen recursively; MetaSchemaNode does support mutation.
30
+ # @param jsi_ptr [JSI::Ptr] ptr to this MetaSchemaNode in jsi_document
31
+ # @param schema_implementation_modules [Enumerable<Module>] modules which implement the functionality
32
+ # of the schema. These are included on the {Schema#jsi_schema_module} of the meta-schema.
33
+ # They extend any schema described by the meta-schema, including those in the document containing
34
+ # the meta-schema, and the meta-schema itself.
35
+ # see {Schema#describes_schema!} param `schema_implementation_modules`.
36
+ # @param metaschema_root_ptr [JSI::Ptr] ptr to the root of the meta-schema in the jsi_document
33
37
  # @param root_schema_ptr [JSI::Ptr] ptr to the schema describing the root of the jsi_document
34
38
  def initialize(
35
39
  jsi_document,
36
40
  jsi_ptr: Ptr[],
37
- metaschema_instance_modules: ,
41
+ schema_implementation_modules: ,
38
42
  metaschema_root_ptr: Ptr[],
39
43
  root_schema_ptr: Ptr[],
40
- jsi_schema_base_uri: nil
44
+ jsi_schema_base_uri: nil,
45
+ jsi_schema_registry: nil,
46
+ jsi_content_to_immutable: DEFAULT_CONTENT_TO_IMMUTABLE,
47
+ jsi_root_node: nil
41
48
  )
42
- jsi_initialize_memos
49
+ super(jsi_document,
50
+ jsi_ptr: jsi_ptr,
51
+ jsi_indicated_schemas: SchemaSet[],
52
+ jsi_schema_base_uri: jsi_schema_base_uri,
53
+ jsi_schema_registry: jsi_schema_registry,
54
+ jsi_content_to_immutable: jsi_content_to_immutable,
55
+ jsi_root_node: jsi_root_node,
56
+ )
43
57
 
44
- self.jsi_document = jsi_document
45
- self.jsi_ptr = jsi_ptr
46
- @metaschema_instance_modules = Util.ensure_module_set(metaschema_instance_modules)
58
+ @schema_implementation_modules = schema_implementation_modules = Util.ensure_module_set(schema_implementation_modules)
47
59
  @metaschema_root_ptr = metaschema_root_ptr
48
60
  @root_schema_ptr = root_schema_ptr
49
61
 
50
62
  if jsi_ptr.root? && jsi_schema_base_uri
51
- raise(NotImplementedError, "unsupported jsi_schema_base_uri on metaschema document root")
63
+ raise(NotImplementedError, "unsupported jsi_schema_base_uri on meta-schema document root")
52
64
  end
53
- self.jsi_schema_base_uri = jsi_schema_base_uri
54
65
 
55
- jsi_node_content = self.jsi_node_content
66
+ #chkbug raise(Bug, 'MetaSchemaNode instance must be frozen') unless jsi_node_content.frozen?
56
67
 
57
- if jsi_node_content.respond_to?(:to_hash)
58
- extend PathedHashNode
59
- end
60
- if jsi_node_content.respond_to?(:to_ary)
61
- extend PathedArrayNode
62
- end
68
+ extends = Set[]
63
69
 
64
70
  instance_for_schemas = jsi_document
65
- bootstrap_schema_class = JSI::SchemaClasses.bootstrap_schema_class(metaschema_instance_modules)
71
+ bootstrap_schema_class = JSI::SchemaClasses.bootstrap_schema_class(schema_implementation_modules)
66
72
  root_bootstrap_schema = bootstrap_schema_class.new(
67
73
  jsi_document,
68
74
  jsi_ptr: root_schema_ptr,
69
75
  jsi_schema_base_uri: nil, # supplying jsi_schema_base_uri on root bootstrap schema is not supported
70
76
  )
71
- our_bootstrap_schemas = jsi_ptr.tokens.inject(SchemaSet[root_bootstrap_schema]) do |bootstrap_schemas, tok|
72
- child_instance_for_schemas = instance_for_schemas[tok]
73
- bootstrap_schemas_for_instance = SchemaSet.build do |schemas|
74
- bootstrap_schemas.each do |bootstrap_schema|
75
- bootstrap_schema.each_child_applicator_schema(tok, instance_for_schemas) do |child_app_schema|
76
- child_app_schema.each_inplace_applicator_schema(child_instance_for_schemas) do |child_inpl_app_schema|
77
- schemas << child_inpl_app_schema
78
- end
79
- end
80
- end
81
- end
82
- instance_for_schemas = child_instance_for_schemas
83
- bootstrap_schemas_for_instance
77
+ our_bootstrap_indicated_schemas = jsi_ptr.tokens.inject(SchemaSet[root_bootstrap_schema]) do |bootstrap_indicated_schemas, tok|
78
+ bootstrap_schemas = bootstrap_indicated_schemas.inplace_applicator_schemas(instance_for_schemas)
79
+ child_indicated_schemas = bootstrap_schemas.child_applicator_schemas(tok, instance_for_schemas)
80
+ instance_for_schemas = instance_for_schemas[tok]
81
+ child_indicated_schemas
84
82
  end
83
+ @indicated_schemas_map = jsi_memomap { bootstrap_schemas_to_msn(our_bootstrap_indicated_schemas) }
84
+
85
+ our_bootstrap_schemas = our_bootstrap_indicated_schemas.inplace_applicator_schemas(instance_for_schemas)
85
86
 
87
+ describes_self = false
86
88
  our_bootstrap_schemas.each do |bootstrap_schema|
87
89
  if bootstrap_schema.jsi_ptr == metaschema_root_ptr
88
- metaschema_instance_modules.each do |metaschema_instance_module|
89
- extend metaschema_instance_module
90
+ # this is described by the meta-schema, i.e. this is a schema
91
+ extend Schema
92
+ schema_implementation_modules.each do |schema_implementation_module|
93
+ extend schema_implementation_module
90
94
  end
95
+ extends += schema_implementation_modules
91
96
  end
92
97
  if bootstrap_schema.jsi_ptr == jsi_ptr
93
- extend Metaschema
94
- self.jsi_schema_instance_modules = metaschema_instance_modules
98
+ # this is the meta-schema (it is described by itself)
99
+ describes_self = true
95
100
  end
96
101
  end
97
102
 
98
- @jsi_schemas = SchemaSet.new(our_bootstrap_schemas) do |bootstrap_schema|
99
- if bootstrap_schema.jsi_ptr == jsi_ptr
100
- self
101
- else
102
- new_node(
103
- jsi_ptr: bootstrap_schema.jsi_ptr,
104
- jsi_schema_base_uri: bootstrap_schema.jsi_schema_base_uri,
105
- )
106
- end
103
+ @jsi_schemas = bootstrap_schemas_to_msn(our_bootstrap_schemas)
104
+
105
+ # note: jsi_schemas must already be set for jsi_schema_module to be used/extended
106
+ if describes_self
107
+ describes_schema!(schema_implementation_modules)
107
108
  end
108
109
 
109
- @jsi_schemas.each do |schema|
110
- extend schema.jsi_schema_module
110
+ extends_for_instance = JSI::SchemaClasses.includes_for(jsi_node_content)
111
+ extends.merge(extends_for_instance)
112
+ extends.freeze
113
+
114
+ conflicting_modules = Set[self.class] + extends + @jsi_schemas.map(&:jsi_schema_module)
115
+ reader_modules = @jsi_schemas.map do |schema|
116
+ JSI::SchemaClasses.schema_property_reader_module(schema, conflicting_modules: conflicting_modules)
111
117
  end
112
118
 
113
- # workarounds
114
- begin # draft 4 boolean schema workaround
115
- # in draft 4, boolean schemas are not described in the root, but on anyOf schemas on
116
- # properties/additionalProperties and properties/additionalItems.
117
- # since these describe schemas, their jsi_schema_instance_modules are the metaschema_instance_modules.
118
- addtlPropsanyOf = metaschema_root_ptr["properties"]["additionalProperties"]["anyOf"]
119
- addtlItemsanyOf = metaschema_root_ptr["properties"]["additionalItems"]["anyOf"]
119
+ readers = reader_modules.map(&:jsi_property_readers).inject(Set[], &:merge).freeze
120
+ define_singleton_method(:jsi_property_readers) { readers }
120
121
 
121
- if !jsi_ptr.root? && [addtlPropsanyOf, addtlItemsanyOf].include?(jsi_ptr.parent)
122
- self.jsi_schema_instance_modules = metaschema_instance_modules
123
- end
122
+ reader_modules.each { |reader_module| extend reader_module }
123
+
124
+ extends_for_instance.each do |m|
125
+ extend m
126
+ end
127
+
128
+ @jsi_schemas.each do |schema|
129
+ extend schema.jsi_schema_module
124
130
  end
125
131
  end
126
132
 
127
- # Set of modules to apply to schemas which are instances of (described by) the metaschema
133
+ # Set of modules to apply to schemas that are instances of (described by) the meta-schema
128
134
  # @return [Set<Module>]
129
- attr_reader :metaschema_instance_modules
135
+ attr_reader :schema_implementation_modules
130
136
 
131
- # ptr to the root of the metaschema in the jsi_document
137
+ # ptr to the root of the meta-schema in the jsi_document
132
138
  # @return [JSI::Ptr]
133
139
  attr_reader :metaschema_root_ptr
134
140
 
@@ -136,110 +142,132 @@ module JSI
136
142
  # @return [JSI::Ptr]
137
143
  attr_reader :root_schema_ptr
138
144
 
139
- # JSI Schemas describing this MetaschemaNode
145
+ # JSI Schemas describing this MetaSchemaNode
140
146
  # @return [JSI::SchemaSet]
141
147
  attr_reader :jsi_schemas
142
148
 
143
- # document root MetaschemaNode
144
- # @return [MetaschemaNode]
145
- def jsi_root_node
146
- if jsi_ptr.root?
147
- self
148
- else
149
- new_node(
150
- jsi_ptr: Ptr[],
151
- jsi_schema_base_uri: nil,
152
- )
153
- end
149
+ # See {Base#jsi_indicated_schemas}
150
+ # @return [JSI::SchemaSet]
151
+ def jsi_indicated_schemas
152
+ @indicated_schemas_map[]
154
153
  end
155
154
 
156
- # parent MetaschemaNode
157
- # @return [MetaschemaNode]
158
- def jsi_parent_node
159
- jsi_ptr.parent.evaluate(jsi_root_node)
160
- end
155
+ # see {Base#jsi_child}
156
+ def jsi_child(token, as_jsi: )
157
+ child_node = @root_descendent_node_map[ptr: jsi_ptr[token]]
161
158
 
162
- # subscripts to return a child value identified by the given token.
163
- #
164
- # @param token (see JSI::Base#[])
165
- # @param as_jsi (see JSI::Base#[])
166
- # @return (see JSI::Base#[])
167
- def [](token, as_jsi: :auto)
168
- if respond_to?(:to_hash)
169
- token_in_range = jsi_node_content_hash_pubsend(:key?, token)
170
- value = jsi_node_content_hash_pubsend(:[], token)
171
- elsif respond_to?(:to_ary)
172
- token_in_range = jsi_node_content_ary_pubsend(:each_index).include?(token)
173
- value = jsi_node_content_ary_pubsend(:[], token)
174
- else
175
- raise(NoMethodError, "cannot subscript (using token: #{token.inspect}) from content: #{jsi_node_content.pretty_inspect.chomp}")
159
+ jsi_child_as_jsi(child_node.jsi_node_content, child_node.jsi_schemas, as_jsi) do
160
+ child_node
176
161
  end
162
+ end
163
+ private :jsi_child
177
164
 
178
- begin
179
- if token_in_range
180
- value_node = jsi_subinstance_memos[token]
181
-
182
- jsi_subinstance_as_jsi(value, value_node.jsi_schemas, as_jsi) do
183
- value_node
184
- end
185
- else
186
- # I think I will not support Hash#default/#default_proc in this case.
187
- nil
188
- end
189
- end
165
+ # See {Base#jsi_default_child}
166
+ def jsi_default_child(token, as_jsi: )
167
+ jsi_node_content_child(token)
190
168
  end
169
+ private :jsi_default_child # internals for #[] but idk, could be public
191
170
 
192
- # instantiates a new MetaschemaNode whose instance is a modified copy of this MetaschemaNode's instance
171
+ # instantiates a new MetaSchemaNode whose instance is a modified copy of this MetaSchemaNode's instance
193
172
  # @yield [Object] the node content of the instance. the block should result
194
173
  # in a (nondestructively) modified copy of this.
195
- # @return [MetaschemaNode] modified copy of self
174
+ # @return [MetaSchemaNode] modified copy of self
196
175
  def jsi_modified_copy(&block)
197
- MetaschemaNode.new(jsi_ptr.modified_document_copy(jsi_document, &block), **our_initialize_params)
176
+ if jsi_ptr.root?
177
+ modified_document = jsi_ptr.modified_document_copy(jsi_document, &block)
178
+ modified_document = jsi_content_to_immutable.call(modified_document) if jsi_content_to_immutable
179
+ MetaSchemaNode.new(modified_document, **our_initialize_params)
180
+ else
181
+ modified_jsi_root_node = jsi_root_node.jsi_modified_copy do |root|
182
+ jsi_ptr.modified_document_copy(root, &block)
183
+ end
184
+ modified_jsi_root_node.jsi_descendent_node(jsi_ptr)
185
+ end
198
186
  end
199
187
 
200
188
  # @private
201
189
  # @return [Array<String>]
202
190
  def jsi_object_group_text
203
191
  if jsi_schemas && jsi_schemas.any?
204
- class_n_schemas = "#{self.class} (#{jsi_schemas.map { |s| s.jsi_ptr.uri }.join(' ')})"
192
+ class_n_schemas = -"#{self.class} (#{jsi_schemas.map { |s| s.jsi_schema_module.name_from_ancestor || s.jsi_ptr.uri }.join(' ')})"
205
193
  else
206
194
  class_n_schemas = self.class.to_s
207
195
  end
208
196
  [
209
197
  class_n_schemas,
210
- is_a?(Metaschema) ? "Metaschema" : is_a?(Schema) ? "Schema" : nil,
211
- *(jsi_node_content.respond_to?(:jsi_object_group_text) ? jsi_node_content.jsi_object_group_text : []),
212
- ].compact
198
+ is_a?(Schema::MetaSchema) ? "Meta-Schema" : is_a?(Schema) ? "Schema" : nil,
199
+ *(jsi_node_content.respond_to?(:jsi_object_group_text) ? jsi_node_content.jsi_object_group_text : nil),
200
+ ].compact.freeze
213
201
  end
214
202
 
215
- # an opaque fingerprint of this MetaschemaNode for FingerprintHash
203
+ # see {Util::Private::FingerprintHash}
204
+ # @api private
216
205
  def jsi_fingerprint
217
- {class: self.class, jsi_document: jsi_document}.merge(our_initialize_params)
206
+ {class: self.class, jsi_document: jsi_document}.merge(our_initialize_params).freeze
218
207
  end
219
208
 
209
+ protected
210
+
211
+ attr_reader :root_descendent_node_map
212
+
220
213
  private
221
214
 
215
+ def jsi_memomaps_initialize
216
+ if jsi_ptr.root?
217
+ @root_descendent_node_map = jsi_memomap(&method(:jsi_root_descendent_node_compute))
218
+ else
219
+ @root_descendent_node_map = @jsi_root_node.root_descendent_node_map
220
+ end
221
+ end
222
+
223
+ # note: does not include jsi_root_node
222
224
  def our_initialize_params
223
225
  {
224
226
  jsi_ptr: jsi_ptr,
225
- metaschema_instance_modules: metaschema_instance_modules,
227
+ schema_implementation_modules: schema_implementation_modules,
226
228
  metaschema_root_ptr: metaschema_root_ptr,
227
229
  root_schema_ptr: root_schema_ptr,
228
230
  jsi_schema_base_uri: jsi_schema_base_uri,
229
- }
231
+ jsi_schema_registry: jsi_schema_registry,
232
+ jsi_content_to_immutable: jsi_content_to_immutable,
233
+ }.freeze
230
234
  end
231
235
 
232
- def new_node(params)
233
- MetaschemaNode.new(jsi_document, **our_initialize_params.merge(params))
236
+ # note: not for root node
237
+ def new_node(**params)
238
+ MetaSchemaNode.new(jsi_document, jsi_root_node: jsi_root_node, **our_initialize_params, **params)
234
239
  end
235
240
 
236
- def jsi_subinstance_memos
237
- jsi_memomap(:subinstance) do |token|
241
+ def jsi_root_descendent_node_compute(ptr: )
242
+ #chkbug raise(Bug) unless jsi_ptr.root?
243
+ if ptr.root?
244
+ self
245
+ else
238
246
  new_node(
239
- jsi_ptr: jsi_ptr[token],
247
+ jsi_ptr: ptr,
240
248
  jsi_schema_base_uri: jsi_resource_ancestor_uri,
241
249
  )
242
250
  end
243
251
  end
252
+
253
+ # @param bootstrap_schemas [Enumerable<BootstrapSchema>]
254
+ # @return [SchemaSet<MetaSchemaNode>]
255
+ def bootstrap_schemas_to_msn(bootstrap_schemas)
256
+ SchemaSet.new(bootstrap_schemas) do |bootstrap_schema|
257
+ if bootstrap_schema.jsi_ptr == jsi_ptr
258
+ self
259
+ elsif bootstrap_schema.jsi_ptr.root?
260
+ @jsi_root_node
261
+ else
262
+ new_node(
263
+ jsi_ptr: bootstrap_schema.jsi_ptr,
264
+ jsi_schema_base_uri: bootstrap_schema.jsi_schema_base_uri,
265
+ )
266
+ end
267
+ end
268
+ end
244
269
  end
270
+
271
+ # @deprecated after v0.7.0, alias of {MetaSchemaNode}
272
+ MetaschemaNode = MetaSchemaNode
245
273
  end