Class: JSI::MetaschemaNode

Inherits:
Base
  • Object
show all
Defined in:
lib/jsi/metaschema_node.rb

Overview

a MetaschemaNode is a JSI instance representing a node in a document which contains a metaschema. the root of the metaschema is pointed to by metaschema_root_ptr. the schema describing the root of the document is pointed to by root_schema_ptr.

like JSI::Base's normal subclasses, this class represents an instance of a schema set, an instance which may itself be a schema. unlike JSI::Base, the document containing the instance and its schemas is the same, and a schema (the metaschema) may be an instance of itself.

unlike JSI::Base's normal subclasses, the schemas describing the instance are not part of the class. since the metaschema describes itself, attempting to construct a class from the JSI Schema Module of a schema which is itself an instance of that class results in a causality loop. instead, a MetaschemaNode calculates its #jsi_schemas and extends itself with their JSI Schema modules during initialization. the MetaschemaNode of the metaschema is extended with its own JSI Schema Module.

if the MetaschemaNode's schemas include its self, it is extended with JSI::Metaschema.

a MetaschemaNode is extended with JSI::Schema when it represents a schema - this is the case when the metaschema is one of its schemas.

Instance Attribute Summary collapse

Attributes inherited from Base

#jsi_document, #jsi_ptr

Instance Method Summary collapse

Methods inherited from Base

#[]=, #as_json, #dup, #each, inspect, #inspect, #jsi_each_child_node, #jsi_parent_nodes, #jsi_schema_modules, #jsi_select_children_leaf_first, #jsi_select_children_node_first, #jsi_valid?, #jsi_validate, name, #pretty_print, to_s

Methods included from Util::FingerprintHash

#==, #hash

Methods included from Schema::SchemaAncestorNode

#jsi_anchor_subschema, #jsi_anchor_subschemas, #jsi_resource_ancestor_uri

Methods included from PathedNode

#jsi_node_content

Constructor Details

#initialize(jsi_document, jsi_ptr: Ptr[], metaschema_instance_modules:, metaschema_root_ptr: Ptr[], root_schema_ptr: Ptr[], jsi_schema_base_uri: nil) ⇒ MetaschemaNode

Returns a new instance of MetaschemaNode.

Parameters:

  • jsi_document

    the document containing the metaschema

  • jsi_ptr (JSI::Ptr) (defaults to: Ptr[])

    ptr to this MetaschemaNode in jsi_document

  • metaschema_instance_modules (Enumerable<Module>)

    modules which implement the functionality of the schema, to be applied to every schema which is an instance of the metaschema. this must include JSI::Schema directly or indirectly. these are the Schema#jsi_schema_instance_modules of the metaschema.

  • metaschema_root_ptr (JSI::Ptr) (defaults to: Ptr[])

    ptr to the root of the metaschema in the jsi_document

  • root_schema_ptr (JSI::Ptr) (defaults to: Ptr[])

    ptr to the schema describing the root of the jsi_document


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/jsi/metaschema_node.rb', line 34

def initialize(
    jsi_document,
    jsi_ptr: Ptr[],
    metaschema_instance_modules: ,
    metaschema_root_ptr: Ptr[],
    root_schema_ptr: Ptr[],
    jsi_schema_base_uri: nil
)
  jsi_initialize_memos

  self.jsi_document = jsi_document
  self.jsi_ptr = jsi_ptr
  @metaschema_instance_modules = Util.ensure_module_set(metaschema_instance_modules)
  @metaschema_root_ptr = metaschema_root_ptr
  @root_schema_ptr = root_schema_ptr

  if jsi_ptr.root? && jsi_schema_base_uri
    raise(NotImplementedError, "unsupported jsi_schema_base_uri on metaschema document root")
  end
  self.jsi_schema_base_uri = jsi_schema_base_uri

  jsi_node_content = self.jsi_node_content

  if jsi_node_content.respond_to?(:to_hash)
    extend PathedHashNode
  end
  if jsi_node_content.respond_to?(:to_ary)
    extend PathedArrayNode
  end

  instance_for_schemas = jsi_document
  bootstrap_schema_class = JSI::SchemaClasses.bootstrap_schema_class(metaschema_instance_modules)
  root_bootstrap_schema = bootstrap_schema_class.new(
    jsi_document,
    jsi_ptr: root_schema_ptr,
    jsi_schema_base_uri: nil, # supplying jsi_schema_base_uri on root bootstrap schema is not supported
  )
  our_bootstrap_schemas = jsi_ptr.tokens.inject(SchemaSet[root_bootstrap_schema]) do |bootstrap_schemas, tok|
    child_instance_for_schemas = instance_for_schemas[tok]
    bootstrap_schemas_for_instance = SchemaSet.build do |schemas|
      bootstrap_schemas.each do |bootstrap_schema|
        bootstrap_schema.each_child_applicator_schema(tok, instance_for_schemas) do |child_app_schema|
          child_app_schema.each_inplace_applicator_schema(child_instance_for_schemas) do |child_inpl_app_schema|
            schemas << child_inpl_app_schema
          end
        end
      end
    end
    instance_for_schemas = child_instance_for_schemas
    bootstrap_schemas_for_instance
  end

  our_bootstrap_schemas.each do |bootstrap_schema|
    if bootstrap_schema.jsi_ptr == metaschema_root_ptr
      metaschema_instance_modules.each do |metaschema_instance_module|
        extend metaschema_instance_module
      end
    end
    if bootstrap_schema.jsi_ptr == jsi_ptr
      extend Metaschema
      self.jsi_schema_instance_modules = metaschema_instance_modules
    end
  end

  @jsi_schemas = SchemaSet.new(our_bootstrap_schemas) do |bootstrap_schema|
    if bootstrap_schema.jsi_ptr == jsi_ptr
      self
    else
      new_node(
        jsi_ptr: bootstrap_schema.jsi_ptr,
        jsi_schema_base_uri: bootstrap_schema.jsi_schema_base_uri,
      )
    end
  end

  @jsi_schemas.each do |schema|
    extend schema.jsi_schema_module
  end

  # workarounds
  begin # draft 4 boolean schema workaround
    # in draft 4, boolean schemas are not described in the root, but on anyOf schemas on
    # properties/additionalProperties and properties/additionalItems.
    # since these describe schemas, their jsi_schema_instance_modules are the metaschema_instance_modules.
    addtlPropsanyOf = metaschema_root_ptr["properties"]["additionalProperties"]["anyOf"]
    addtlItemsanyOf = metaschema_root_ptr["properties"]["additionalItems"]["anyOf"]

    if !jsi_ptr.root? && [addtlPropsanyOf, addtlItemsanyOf].include?(jsi_ptr.parent)
      self.jsi_schema_instance_modules = metaschema_instance_modules
    end
  end
end

Instance Attribute Details

#jsi_schemasJSI::SchemaSet (readonly)

JSI Schemas describing this MetaschemaNode

Returns:


141
142
143
# File 'lib/jsi/metaschema_node.rb', line 141

def jsi_schemas
  @jsi_schemas
end

#metaschema_instance_modulesSet<Module> (readonly)

Set of modules to apply to schemas which are instances of (described by) the metaschema

Returns:

  • (Set<Module>)

129
130
131
# File 'lib/jsi/metaschema_node.rb', line 129

def metaschema_instance_modules
  @metaschema_instance_modules
end

#metaschema_root_ptrJSI::Ptr (readonly)

ptr to the root of the metaschema in the jsi_document

Returns:


133
134
135
# File 'lib/jsi/metaschema_node.rb', line 133

def metaschema_root_ptr
  @metaschema_root_ptr
end

#root_schema_ptrJSI::Ptr (readonly)

ptr to the schema of the root of the jsi_document

Returns:


137
138
139
# File 'lib/jsi/metaschema_node.rb', line 137

def root_schema_ptr
  @root_schema_ptr
end

Instance Method Details

#[](token, as_jsi: :auto) ⇒ JSI::Base, Object

subscripts to return a child value identified by the given token.

Parameters:

  • token (String, Integer, Object)

    an array index or hash key (JSON object property name) of the instance identifying the child value

  • as_jsi (:auto, true, false) (defaults to: :auto)

    whether to return the result value as a JSI. one of:

    • :auto (default): by default a JSI will be returned when either:

      • the result is a complex value (responds to #to_ary or #to_hash) and is described by some schemas
      • the result is a schema (including true/false schemas)

    a plain value is returned when no schemas are known to describe the instance, or when the value is a simple type (anything unresponsive to #to_ary / #to_hash).

    • true: the result value will always be returned as a JSI. the #jsi_schemas of the result may be empty if no schemas describe the instance.
    • false: the result value will always be the plain instance.

    note that nil is returned (regardless of as_jsi) when there is no value to return because the token is not a hash key or array index of the instance and no default value applies. (one exception is when this JSI's instance is a Hash with a default or default_proc, which has unspecified behavior.)

Returns:

  • (JSI::Base, Object)

    the child value identified by the subscript token


167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/jsi/metaschema_node.rb', line 167

def [](token, as_jsi: :auto)
  if respond_to?(:to_hash)
    token_in_range = jsi_node_content_hash_pubsend(:key?, token)
    value = jsi_node_content_hash_pubsend(:[], token)
  elsif respond_to?(:to_ary)
    token_in_range = jsi_node_content_ary_pubsend(:each_index).include?(token)
    value = jsi_node_content_ary_pubsend(:[], token)
  else
    raise(NoMethodError, "cannot subscript (using token: #{token.inspect}) from content: #{jsi_node_content.pretty_inspect.chomp}")
  end

  begin
    if token_in_range
      value_node = jsi_subinstance_memos[token]

      jsi_subinstance_as_jsi(value, value_node.jsi_schemas, as_jsi) do
        value_node
      end
    else
      # I think I will not support Hash#default/#default_proc in this case.
      nil
    end
  end
end

#jsi_fingerprintObject

an opaque fingerprint of this MetaschemaNode for FingerprintHash


216
217
218
# File 'lib/jsi/metaschema_node.rb', line 216

def jsi_fingerprint
  {class: self.class, jsi_document: jsi_document}.merge(our_initialize_params)
end

#jsi_modified_copy {|Object| ... } ⇒ MetaschemaNode

instantiates a new MetaschemaNode whose instance is a modified copy of this MetaschemaNode's instance

Yields:

  • (Object)

    the node content of the instance. the block should result in a (nondestructively) modified copy of this.

Returns:


196
197
198
# File 'lib/jsi/metaschema_node.rb', line 196

def jsi_modified_copy(&block)
  MetaschemaNode.new(jsi_ptr.modified_document_copy(jsi_document, &block), **our_initialize_params)
end

#jsi_parent_nodeMetaschemaNode

parent MetaschemaNode

Returns:


158
159
160
# File 'lib/jsi/metaschema_node.rb', line 158

def jsi_parent_node
  jsi_ptr.parent.evaluate(jsi_root_node)
end

#jsi_root_nodeMetaschemaNode

document root MetaschemaNode

Returns:


145
146
147
148
149
150
151
152
153
154
# File 'lib/jsi/metaschema_node.rb', line 145

def jsi_root_node
  if jsi_ptr.root?
    self
  else
    new_node(
      jsi_ptr: Ptr[],
      jsi_schema_base_uri: nil,
    )
  end
end