Class: JSI::Base

Inherits:
Object
  • Object
show all
Includes:
Schema::SchemaAncestorNode
Defined in:
lib/jsi/base.rb

Overview

A JSI::Base instance represents a node in a JSON document (its #jsi_document) at a particular location (its #jsi_ptr), described by any number of JSON Schemas (its #jsi_schemas).

JSI::Base is an abstract base class. The subclasses used to instantiate JSIs are dynamically created as needed for a given instance.

These subclasses are generally intended to be ignored by applications using this library - the purpose they serve is to include modules relevant to the instance. The modules these classes include are:

Direct Known Subclasses

MetaSchemaNode

Defined Under Namespace

Modules: ArrayNode, HashNode, Immutable, Mutable, StringNode Classes: ChildNotPresent, Conf, SimpleNodeChildError

Instance Attribute Summary collapse

Attributes included from Schema::SchemaAncestorNode

#jsi_base_uri, #jsi_schema_dynamic_anchor_map, #jsi_schema_resource_ancestors

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Schema::SchemaAncestorNode

#jsi_anchor_subschemas, #jsi_is_resource_root?, #jsi_next_base_uri, #jsi_resource_uri, #jsi_resource_uris, #jsi_schema_base_uri, #jsi_schema_registry

Constructor Details

#initialize(jsi_document:, jsi_ptr: , jsi_indicated_schemas:, jsi_base_uri: nil, jsi_schema_resource_ancestors: Util::EMPTY_ARY, jsi_schema_dynamic_anchor_map: Schema::DynamicAnchorMap::EMPTY, jsi_dynamic_root_map: nil, jsi_conf: nil, jsi_root_node: nil) ⇒ Base

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

initializes a JSI whose instance is in the given document at the given pointer.

this is a private api - users should look elsewhere to instantiate JSIs, in particular:

Parameters:

  • jsi_document (Object)

    the document containing the instance

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

    a pointer pointing to the JSI's instance in the document

  • jsi_base_uri (URI, nil) (defaults to: nil)

    see SchemaSet#new_jsi param base_uri

  • jsi_schema_resource_ancestors (Array<JSI::Base + JSI::Schema>) (defaults to: Util::EMPTY_ARY)
  • jsi_schema_dynamic_anchor_map (Schema::DynamicAnchorMap) (defaults to: Schema::DynamicAnchorMap::EMPTY)
  • jsi_dynamic_root_map (defaults to: nil)

    map of (ptr, dynamic_anchor_map) → (Base root node), shared across dynamic root nodes

  • jsi_conf (Base::Conf) (defaults to: nil)
  • jsi_root_node (JSI::Base, nil) (defaults to: nil)


218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/jsi/base.rb', line 218

def initialize(
    jsi_document: ,
    jsi_ptr: Ptr[],
    jsi_indicated_schemas: ,
    jsi_base_uri: nil,
    jsi_schema_resource_ancestors: Util::EMPTY_ARY,
    jsi_schema_dynamic_anchor_map: Schema::DynamicAnchorMap::EMPTY,
    jsi_dynamic_root_map: nil,
    jsi_conf: nil,
    jsi_root_node: nil
)
  #chkbug fail(Bug, "no #jsi_schemas") unless respond_to?(:jsi_schemas)

  #chkbug fail(Bug) if !jsi_root_node ^ jsi_conf
  @jsi_conf = jsi_conf = jsi_conf || jsi_root_node.jsi_conf
  @jsi_document = jsi_document
  #chkbug fail(Bug) unless jsi_ptr.is_a?(Ptr)
  #chkbug fail(Bug) unless jsi_ptr.resolve_against(jsi_document).equal?(jsi_ptr)
  @jsi_ptr = jsi_ptr
  #chkbug fail(Bug) unless jsi_indicated_schemas.is_a?(SchemaSet)
  @jsi_indicated_schemas = jsi_indicated_schemas
  self.jsi_base_uri = jsi_base_uri
  self.jsi_schema_resource_ancestors = jsi_schema_resource_ancestors
  self.jsi_schema_dynamic_anchor_map = jsi_schema_dynamic_anchor_map
  #chkbug fail(Bug) if jsi_root_node && jsi_dynamic_root_map
  @jsi_dynamic_root_map = jsi_dynamic_root_map || (jsi_root_node ? jsi_root_node.jsi_dynamic_root_map : jsi_memomap(&method(:jsi_dynamic_root_compute)))
  @jsi_root_node = jsi_root_node || self
  @root_rel_ptr = @jsi_ptr.relative_to(@jsi_root_node.jsi_ptr)

  # @memos does not freeze if/when the node freezes
  @memos = {}
  jsi_memomaps_initialize
  jsi_mutability_initialize

  super()

  if jsi_instance.is_a?(JSI::Base)
    raise(TypeError, "a JSI::Base instance must not be another JSI::Base. received: #{jsi_instance.pretty_inspect.chomp}")
  end
end

Instance Attribute Details

#jsi_confBase::Conf (readonly)

Returns:



284
285
286
# File 'lib/jsi/base.rb', line 284

def jsi_conf
  @jsi_conf
end

#jsi_documentObject (readonly)

document containing the instance of this JSI at our #jsi_ptr



268
269
270
# File 'lib/jsi/base.rb', line 268

def jsi_document
  @jsi_document
end

#jsi_indicated_schemasJSI::SchemaSet (readonly)

The schemas indicated as describing this instance, prior to in-place application.

This is different from #jsi_schemas, which are the in-place applicator schemas which describe this instance. for most purposes, #jsi_schemas is more relevant.

jsi_indicated_schemas does not include in-place applicator schemas, such as the subschemas of allOf, whereas #jsi_schemas does.

this does include indicated schemas which do not apply themselves, such as $ref schemas (on json schema drafts up to 7) - these are not included on #jsi_schemas.

Returns:



315
316
317
# File 'lib/jsi/base.rb', line 315

def jsi_indicated_schemas
  @jsi_indicated_schemas
end

#jsi_ptrJSI::Ptr (readonly)

Ptr pointing to this JSI's instance within our #jsi_document

Returns:



272
273
274
# File 'lib/jsi/base.rb', line 272

def jsi_ptr
  @jsi_ptr
end

#jsi_root_nodeJSI::Base (readonly)

The root ancestor of this node. This is typically the document root, though it can be a different resource root when dynamic scope is overridden.

Returns:



289
290
291
# File 'lib/jsi/base.rb', line 289

def jsi_root_node
  @jsi_root_node
end

Class Method Details

.inspectString

A string indicating the schema module name and/or schema URI of each schema the class represents.

Returns:

  • (String)


151
152
153
154
155
156
157
158
159
# File 'lib/jsi/base.rb', line 151

def inspect
  return super unless respond_to?(:jsi_class_schemas)
  schema_names = jsi_class_schemas.map do |schema|
    mod_name = schema.jsi_schema_module_name_from_ancestor
    next "#{mod_name} <#{schema.jsi_resource_uri}>" if mod_name && schema.jsi_resource_uri
    mod_name || "<#{schema.schema_uri || schema.jsi_ptr.uri}>"
  end
  "(#{[superclass, *schema_names, *jsi_class_includes].join(' + ')})"
end

.nameString

A constant name of this class. This is generated from any schema module name or URI of each schema this class represents, or random characters.

this generated name is not too pretty but can be more helpful than an anonymous class, especially in error messages.

Returns:

  • (String)


172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/jsi/base.rb', line 172

def name
  return super if instance_variable_defined?(:@tried_to_name)
  @tried_to_name = true
  return super unless respond_to?(:jsi_class_schemas)
  alnum = proc { |id| (id % 36**4).to_s(36).rjust(4, '0').upcase }
  schema_names = jsi_class_schemas.map do |schema|
    named_ancestor, tokens = schema.jsi_schema_module.send(:named_ancestor_tokens)
    if named_ancestor
      [named_ancestor.jsi_schema_module_connection.name, *tokens].join('_')
    elsif schema.schema_uri
      schema.schema_uri.to_s
    else
      [alnum[schema.jsi_root_node.__id__], *schema.jsi_ptr.tokens].join('_')
    end
  end
  includes_names = jsi_class_includes.map { |m| m.name.sub(/\AJSI::Base::/, '').gsub(Util::RUBY_REJECT_NAME_RE, '_') }
  if schema_names.any?
    parts = schema_names.compact.sort.map { |n| 'X' + n.to_s }
    parts += includes_names
    const_name = Util.const_name_from_parts(parts, join: '__')
    const_name += "__" + alnum[__id__] if SchemaClasses.const_defined?(const_name)
  else
    const_name = (['X' + alnum[__id__]] + includes_names).join('__')
  end
  # collisions are technically possible though vanishingly unlikely
  SchemaClasses.const_set(const_name, self) unless SchemaClasses.const_defined?(const_name)
  super
end

.to_sObject



161
162
163
# File 'lib/jsi/base.rb', line 161

def to_s
  inspect
end

Instance Method Details

#/(ptr) ⇒ JSI::Base

The descendent node at the given Ptr, token array, or pointer string.

Note that, though more convenient to type, using an operator whose meaning may not be intuitive to a reader could impair readability of code.

examples:

my_jsi / ['foo', 'bar']
my_jsi / %w(foo bar)
my_jsi / '/foo/bar'
my_schema / JSI::Ptr['additionalProperties']
my_schema / %w(properties foo items additionalProperties)

Parameters:

Returns:



504
505
506
# File 'lib/jsi/base.rb', line 504

def /(ptr)
  jsi_descendent_node(ptr.respond_to?(:to_str) ? Ptr.from_pointer(ptr) : ptr)
end

#[](token, as_jsi: jsi_child_as_jsi_default, use_default: jsi_child_use_default_default) ⇒ Base, ...

Returns a child or children identified by param token.

Parameters:

  • token (String, Integer, Range, Object)

    Identifies the child or children to return. Typically an array index or hash key (JSON object property name) of the instance.

    For an array instance, this may also be a Range (in which case an Array of children is returned) or a negative index; these behave as Array#[] does.

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

    (default is :auto or conf child_as_jsi)

    Whether to return the child as a JSI. One of:

    • :auto: By default a JSI will be returned when either:

      • the child is an array or hash
      • the child is a schema (including true/false schemas)

    The plain content is returned when it is a simple type.

    • true: the result will always be returned as a JSI.
    • false: the result will always be the node's content.

    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.)

  • use_default (true, false) (defaults to: jsi_child_use_default_default)

    (default is false) Whether to return a schema default value when the token refers to a child that is not in the document. If the token is not an array index or hash key of the instance, and one schema for the child instance specifies a default value, that default is returned.

    if the result with the default value is a JSI (per the as_jsi param), that JSI is not a child of this JSI - this JSI is not modified to fill in the default value. the result is a JSI within a new document containing the filled-in default.

    if the child instance's schemas do not indicate a single default value (that is, if zero or multiple defaults are specified across those schemas), nil is returned.

Returns:

  • (Base, Object, Array, nil)

    the child or children identified by token

Raises:



659
660
661
662
663
# File 'lib/jsi/base.rb', line 659

def [](token, as_jsi: jsi_child_as_jsi_default, use_default: jsi_child_use_default_default)
  raise(BlockGivenError) if block_given?
  # note: overridden by Base::HashNode, Base::ArrayNode
  jsi_simple_node_child_error(token)
end

#[]=(token, value) ⇒ Object

Assigns a child identified by the given token to the given value. If the given value is a JSI node, its content is used; its #jsi_schemas are not.

Parameters:

  • token (String, Integer, Object)

    token identifying the child to assign

  • value (JSI::Base, Object)

    the value to be assigned



696
697
698
699
700
701
702
703
704
705
# File 'lib/jsi/base.rb', line 696

def []=(token, value)
  unless jsi_array? || jsi_hash?
    jsi_simple_node_child_error(token)
  end
  if value.is_a?(Base)
    self[token] = value.jsi_node_content
  else
    jsi_node_content[token] = value
  end
end

#as_json(options = {}) ⇒ Object

A structure coerced to JSONifiable types from the instance content. Calls Util#as_json with the instance and any given options.



1015
1016
1017
# File 'lib/jsi/base.rb', line 1015

def as_json(options = {})
  Util.as_json(jsi_instance, **options)
end

#described_by?(schema) ⇒ Boolean

Is this JSI described by the given schema (or schema module)?

Parameters:

Returns:

  • (Boolean)


711
712
713
714
715
716
717
718
719
# File 'lib/jsi/base.rb', line 711

def described_by?(schema)
  if schema.is_a?(Schema)
    jsi_schemas.include?(schema)
  elsif schema.is_a?(SchemaModule)
    jsi_schemas.include?(schema.schema)
  else
    raise(TypeError, "expected a Schema or Schema Module; got: #{schema.pretty_inspect.chomp}")
  end
end

#dupBase

A JSI whose node content is a duplicate of this JSI's (using its #dup).

Note that immutable JSIs are not made mutable with #dup. The content's #dup may return an unfrozen copy, but instantiating a modified copy of this JSI involves transforming the content to immutable again (using conf to_immutable).

Returns:



933
934
935
# File 'lib/jsi/base.rb', line 933

def dup
  jsi_modified_copy(&:dup)
end

#encode_with(coder) ⇒ Object

Psych/YAML .dump calls this method; dumping a JSI as YAML will dump its instance.

Parameters:

  • coder (Psych::Coder)


1028
1029
1030
1031
# File 'lib/jsi/base.rb', line 1028

def encode_with(coder)
  coder.represent_object(nil, jsi_node_content)
  nil
end

#jmespath_search(expression, **runtime_options) ⇒ Array, ...

queries this JSI using the JMESPath Ruby gem. see https://jmespath.org/ to learn the JMESPath query language.

the JMESPath gem is not a dependency of JSI, so must be installed / added to your Gemfile to use. e.g. gem 'jmespath', '~> 1.5'. note that versions below 1.5 are not compatible with JSI.

Parameters:

  • expression (String)

    a JMESPath expression

  • runtime_options

    passed to JMESPath.search, though no runtime_options are publicly documented or normally used.

Returns:



832
833
834
835
836
# File 'lib/jsi/base.rb', line 832

def jmespath_search(expression, **runtime_options)
  Util.require_jmespath

  JMESPath.search(expression, self, **runtime_options)
end

#jsi_ancestor_nodesArray<JSI::Base>

ancestor JSI instances from this node up to the root. this node itself is always its own first ancestor.

Returns:



468
469
470
471
472
473
474
475
476
477
478
# File 'lib/jsi/base.rb', line 468

def jsi_ancestor_nodes
  ancestors = []
  ancestor = jsi_root_node
  ancestors << ancestor

  @root_rel_ptr.tokens.each do |token|
    ancestor = ancestor.jsi_child_node(token)
    ancestors << ancestor
  end
  ancestors.reverse!.freeze
end

#jsi_array?Boolean

Is the instance an array?

An array is typically an instance of the Array class but may be an object that supports implicit conversion with a #to_ary method.

Returns:

  • (Boolean)


776
777
778
779
# File 'lib/jsi/base.rb', line 776

def jsi_array?
  # note: overridden by Base::ArrayNode
  false
end

#jsi_as_child_default_as_jsiBoolean

When accessing this node as a child (from a parent's #[] or a property reader), should the result by default be a JSI node (this node), or its node content? This default may be overridden using the as_jsi parameter calling the parent's #[]. Configurable using child_as_jsi.

Returns:

  • (Boolean)


670
671
672
673
# File 'lib/jsi/base.rb', line 670

def jsi_as_child_default_as_jsi
  # base default is false, for simple types. overridden by complex types (HashNode, ArrayNode), Schema, and others.
  jsi_conf.child_as_jsi
end

#jsi_child_as_jsi_default:auto, ...

Deprecated.

after v0.8. This is the parent node's preference whether its children are returned as JSIs, but it is better for a child to indicate whether it should be a JSI by overriding #jsi_as_child_default_as_jsi.

The default value for the param as_jsi of #[], controlling whether a child is returned as a JSI instance.

Returns:

  • (:auto, true, false)

    a valid value of the as_jsi param of #[]



679
680
681
# File 'lib/jsi/base.rb', line 679

def jsi_child_as_jsi_default
  :auto
end

#jsi_child_ensure_present(token) ⇒ nil

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (nil)


534
535
536
537
538
539
# File 'lib/jsi/base.rb', line 534

def jsi_child_ensure_present(token)
  if !jsi_child_token_present?(token)
    raise(ChildNotPresent, -"token does not identify a child that is present: #{token.inspect}\nself = #{pretty_inspect.chomp}")
  end
  nil
end

#jsi_child_node(token) ⇒ JSI::Base

Parameters:

  • token

    An array index or Hash/object property name identifying a present child of this node

Returns:

Raises:



568
569
570
# File 'lib/jsi/base.rb', line 568

def jsi_child_node(token)
  @child_node_by_token_map[token: token]
end

#jsi_child_token_present?(token) ⇒ Boolean

Does the given token identify a child of this node?

In other words, is the given token an array index or hash key of the instance?

Always false if this is not a complex node.

Parameters:

  • token (String, Integer)

Returns:

  • (Boolean)


527
528
529
530
# File 'lib/jsi/base.rb', line 527

def jsi_child_token_present?(token)
  # note: overridden by Base::HashNode, Base::ArrayNode
  false
end

#jsi_child_use_default_defaulttrue, false

The default value for the param use_default of #[], controlling whether a schema default value is returned when a token refers to a child that is not in the document. Configurable using child_use_default.

Returns:

  • (true, false)

    a valid value of the use_default param of #[]



687
688
689
# File 'lib/jsi/base.rb', line 687

def jsi_child_use_default_default
  jsi_conf.child_use_default
end

#jsi_descendent_node(ptr) ⇒ JSI::Base

the descendent node at the given pointer

Parameters:

Returns:



484
485
486
487
# File 'lib/jsi/base.rb', line 484

def jsi_descendent_node(ptr)
  tokens = Ptr.ary_ptr(ptr).resolve_against(jsi_node_content).tokens
  tokens.inject(self, &:jsi_child_node)
end

#jsi_each_child_token {|String, Integer| ... } ⇒ nil, Enumerator

yields each token (array index or hash key) identifying a child node. yields nothing if this node is not complex or has no children.

Yields:

  • (String, Integer)

    each child token

Returns:

  • (nil, Enumerator)

    an Enumerator if invoked without a block; otherwise nil



513
514
515
516
517
# File 'lib/jsi/base.rb', line 513

def jsi_each_child_token
  # note: overridden by Base::HashNode, Base::ArrayNode
  return to_enum(__method__) { 0 } unless block_given?
  nil
end

#jsi_each_descendent_node(propertyNames: false) {|JSI::Base| ... } ⇒ nil, Enumerator

yields a JSI of each node at or below this one in this JSI's document.

Parameters:

  • propertyNames (Boolean) (defaults to: false)

    Whether to also yield each object property name (Hash key) of any descendent which is a hash/object. These are described by propertyNames subshemas of that object's schemas. They are not actual descendents of this node. See JSI::Base::HashNode#jsi_each_propertyName.

Yields:

  • (JSI::Base)

    each descendent node, starting with self

Returns:

  • (nil, Enumerator)

    an Enumerator if invoked without a block; otherwise nil



326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/jsi/base.rb', line 326

def jsi_each_descendent_node(propertyNames: false, &block)
  unless block
    return to_enum(__method__, propertyNames: propertyNames) do
      # size
      Util.ycomb do |rec|
        proc do |node|
          if node.respond_to?(:to_hash)
            node.to_hash.inject(1) { |c, (k, child)| c + rec[child] + (propertyNames ? rec[k] : 0) }
          elsif node.respond_to?(:to_ary)
            node.to_ary.inject(1) { |c, child| c + rec[child] }
          else
            1
          end
        end
      end[jsi_node_content]
    end
  end

  yield self

  if propertyNames && is_a?(HashNode)
    jsi_each_propertyName do |propertyName|
      propertyName.jsi_each_descendent_node(propertyNames: propertyNames, &block)
    end
  end

  jsi_each_child_token do |token|
    jsi_child_node(token).jsi_each_descendent_node(propertyNames: propertyNames, &block)
  end

  nil
end

#jsi_each_descendent_schema {|Base + Schema| ... } ⇒ nil, Enumerator

yields each descendent of this node that is a JSI Schema

Yields:

Returns:

  • (nil, Enumerator)

    an Enumerator if invoked without a block; otherwise nil



362
363
364
365
366
367
368
369
# File 'lib/jsi/base.rb', line 362

def jsi_each_descendent_schema(&block)
  return(to_enum(__method__)) unless block_given?

  # note: this never yields self; if self is a Schema, Schema#jsi_each_descendent_schema overrides this method
  jsi_each_child_token do |token|
    jsi_child_node(token).jsi_each_descendent_schema(&block)
  end
end

#jsi_each_descendent_schema_same_resource {|Schema| ... } ⇒ Object

yields each descendent of this node within the same resource that is a Schema

Yields:



373
374
375
376
377
378
379
380
381
382
383
# File 'lib/jsi/base.rb', line 373

def jsi_each_descendent_schema_same_resource(&block)
  return(to_enum(__method__)) unless block_given?

  jsi_each_child_token do |token|
    child = jsi_child_node(token)
    if !child.jsi_is_resource_root?
      # note: if child is a Schema, Schema#jsi_each_descendent_schema_same_resource overrides Base
      child.jsi_each_descendent_schema_same_resource(&block)
    end
  end
end

#jsi_fingerprintObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

see Util::Private::FingerprintHash



1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
# File 'lib/jsi/base.rb', line 1035

def jsi_fingerprint
  {
    class: JSI::Base,
    jsi_schemas: jsi_schemas,
    jsi_document: jsi_document,
    jsi_ptr: jsi_ptr,
    # for instances in documents with schemas:
    jsi_base_uri: jsi_base_uri,
    jsi_root_uri: jsi_conf.root_uri,
    # different dynamic anchor map means dynamic references may resolve to different resources so must not be equal
    jsi_schema_dynamic_anchor_map: jsi_schema_dynamic_anchor_map,
    **jsi_conf.for_fingerprint,
  }.freeze
end

#jsi_hash?Boolean

Is the instance a ruby Hash (JSON object)?

This is typically an instance of the Hash class but may be an object that supports implicit conversion with a #to_hash method.

Returns:

  • (Boolean)


788
789
790
791
# File 'lib/jsi/base.rb', line 788

def jsi_hash?
  # note: overridden by Base::HashNode
  false
end

#jsi_instanceObject

The JSON schema instance this JSI represents - the underlying JSON data used to instantiate this JSI. The same as #jsi_node_content - 'node content' is usually preferable terminology, to avoid ambiguity in the heavily overloaded term 'instance'.



299
300
301
# File 'lib/jsi/base.rb', line 299

def jsi_instance
  jsi_node_content
end

#jsi_is_schema?Boolean

Is this a JSI Schema?

Returns:

  • (Boolean)


723
724
725
# File 'lib/jsi/base.rb', line 723

def jsi_is_schema?
  false
end

#jsi_modified_copy(**conf_kw) {|Object| ... } ⇒ Base

yields the content of this JSI's instance. the block must result in a modified copy of the yielded instance (not modified in place, which would alter this JSI as well) which will be used to instantiate and return a new JSI with the modified content.

the result may have different schemas which describe it than this JSI's schemas, if conditional applicator schemas apply differently to the modified instance.

Yields:

  • (Object)

    this JSI's instance. the block should result in a nondestructively modified copy of this.

Returns:

  • (Base)

    the modified copy of self



737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
# File 'lib/jsi/base.rb', line 737

def jsi_modified_copy(**conf_kw, &block)
    modified_document = @jsi_ptr.modified_document_copy(@jsi_document, &block)

    conf = jsi_conf.merge(**conf_kw)

    modified_document = conf.to_immutable.call(modified_document) if !jsi_mutable? && conf.to_immutable

    root_content = jsi_root_node.jsi_ptr.evaluate(modified_document)

    root_applied_schemas = SchemaSet.build do |y|
      c = y.method(:yield) # TODO drop c, just pass y, when all supported Enumerator::Yielder.method_defined?(:to_proc)
      jsi_root_node.jsi_indicated_schemas.each do |is|
        is.each_inplace_applicator_schema(root_content, &c)
      end
    end

    root_class = JSI::SchemaClasses.class_for_schemas(root_applied_schemas,
      includes: SchemaClasses.includes_for(root_content),
      mutable: jsi_mutable?,
    )
    modified_jsi_root_node = root_class.new(
      jsi_document: modified_document,
      jsi_ptr: jsi_root_node.jsi_ptr,
      jsi_indicated_schemas: jsi_root_node.jsi_indicated_schemas,
      jsi_base_uri: jsi_root_node.jsi_base_uri,
      jsi_schema_dynamic_anchor_map: jsi_root_node.jsi_schema_dynamic_anchor_map,
      jsi_conf: conf,
    ).send(:jsi_initialized)

    modified_jsi_root_node.jsi_descendent_node(@root_rel_ptr)
end

#jsi_mutable?Boolean

Is this JSI mutable?

Returns:

  • (Boolean)


795
796
797
# File 'lib/jsi/base.rb', line 795

def jsi_mutable?
  # note: overridden by Base::Mutable / Immutable
end

#jsi_node_contentObject

the content of this node in our #jsi_document at our #jsi_ptr. the same as #jsi_instance.



292
293
294
# File 'lib/jsi/base.rb', line 292

def jsi_node_content
  # stub method for doc, overridden by Mutable/Immutable
end

#jsi_node_content_child(token) ⇒ Object?

The child of the #jsi_node_content identified by the given token, or nil if the token does not identify an existing child.

In other words, the element of the instance array at the given index, or the value of the instance hash/object for the given key.

Returns:

  • (Object, nil)

Raises:



549
550
551
552
# File 'lib/jsi/base.rb', line 549

def jsi_node_content_child(token)
  # note: overridden by Base::HashNode, Base::ArrayNode
  jsi_simple_node_child_error(token)
end

#jsi_parent_nodeJSI::Base?

the immediate parent of this JSI. nil if there is no parent.

Returns:



461
462
463
# File 'lib/jsi/base.rb', line 461

def jsi_parent_node
  @root_rel_ptr.root? ? nil : jsi_root_node.jsi_descendent_node(@root_rel_ptr.parent)
end

#jsi_parent_nodesArray<JSI::Base>

JSI nodes above this one in the document.

Returns:



448
449
450
451
452
453
454
455
456
# File 'lib/jsi/base.rb', line 448

def jsi_parent_nodes
  parent_nodes = []
  ptr = @root_rel_ptr
  while !ptr.root?
    ptr = ptr.parent
    parent_nodes.push(jsi_root_node.jsi_descendent_node(ptr))
  end
  parent_nodes.freeze
end

#jsi_registryRegistry?

See SchemaSet#new_jsi param registry

Returns:



276
277
278
# File 'lib/jsi/base.rb', line 276

def jsi_registry
  jsi_conf.registry
end

#jsi_resource_rootBase

The nearest ancestor (including this node) that is a resource root.

A resource root is a schema with an absolute URI, or the document's root node (which might not be a schema and might not have an absolute URI).

Returns:



843
844
845
# File 'lib/jsi/base.rb', line 843

def jsi_resource_root
  super || jsi_root_node
end

#jsi_schemasJSI::SchemaSet

The set of schemas that describe this instance. These are the applicator schemas that apply to this instance, the result of in-place application of our #jsi_indicated_schemas.

Returns:



# File 'lib/jsi/base.rb', line 259


#jsi_select_descendents_leaf_first {|JSI::Base| ... } ⇒ JSI::Base

recursively selects descendent nodes of this JSI, returning a modified copy of self containing only descendent nodes for which the given block had a true-ish result.

this method recursively descends child nodes before yielding each node, so leaf nodes are yielded before their parents.

Yields:

  • (JSI::Base)

    each descendent node below self

Returns:

  • (JSI::Base)

    modified copy of self containing only the selected nodes



423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
# File 'lib/jsi/base.rb', line 423

def jsi_select_descendents_leaf_first(&block)
  jsi_modified_copy do |instance|
    if jsi_array? || jsi_hash?
      res = instance.class.new
      jsi_each_child_token do |token|
        v = jsi_child_node(token).jsi_select_descendents_leaf_first(&block)
        if yield(v)
          res_v = v.jsi_node_content
          if jsi_array?
            res << res_v
          else
            res[token] = res_v
          end
        end
      end
      res
    else
      instance
    end
  end
end

#jsi_select_descendents_node_first {|JSI::Base| ... } ⇒ JSI::Base

recursively selects descendent nodes of this JSI, returning a modified copy of self containing only descendent nodes for which the given block had a true-ish result.

this method yields a node before recursively descending to its child nodes, so leaf nodes are yielded last, after their parents. if a node is not selected, its descendents are never recursed.

Yields:

  • (JSI::Base)

    each descendent node below self

Returns:

  • (JSI::Base)

    modified copy of self containing only the selected nodes



393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
# File 'lib/jsi/base.rb', line 393

def jsi_select_descendents_node_first(&block)
  jsi_modified_copy do |instance|
    if jsi_array? || jsi_hash?
      res = instance.class.new
      jsi_each_child_token do |token|
        v = jsi_child_node(token)
        if yield(v)
          res_v = v.jsi_select_descendents_node_first(&block).jsi_node_content
          if jsi_array?
            res << res_v
          else
            res[token] = res_v
          end
        end
      end
      res
    else
      instance
    end
  end
end

#jsi_valid!nil

Asserts that this JSI is valid against its schemas. Error::Invalid is raised if it is not.

Returns:

  • (nil)

Raises:



817
818
819
# File 'lib/jsi/base.rb', line 817

def jsi_valid!
  jsi_validate.valid!
end

#jsi_valid?Boolean

whether this JSI's instance is valid against all of its schemas

Returns:

  • (Boolean)


808
809
810
# File 'lib/jsi/base.rb', line 808

def jsi_valid?
  jsi_indicated_schemas.instance_valid?(self)
end

#jsi_validateJSI::Validation::Result::Full

validates this JSI's instance against its schemas



802
803
804
# File 'lib/jsi/base.rb', line 802

def jsi_validate
  jsi_indicated_schemas.instance_validate(self)
end

#pretty_print(q) ⇒ Object

Prints a string indicating this JSI's schemas, briefly, and its content.

If described by a schema with a named schema module, that is shown. The number of schemas describing this JSI is indicated.

If this JSI is a simple type, the node's content is inspected; if complex, its children are inspected.



943
944
945
946
947
# File 'lib/jsi/base.rb', line 943

def pretty_print(q)
  jsi_pp_object_group(q, jsi_object_group_text) do
      q.pp jsi_instance
  end
end

#to_json(options = {}) ⇒ String

A JSON encoded string of the instance content. Calls Util#to_json with the instance and any given options.

Returns:

  • (String)


1022
1023
1024
# File 'lib/jsi/base.rb', line 1022

def to_json(options = {})
  Util.to_json(jsi_instance, options)
end