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:



217
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
# File 'lib/jsi/base.rb', line 217

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)



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

def jsi_conf
  @jsi_conf
end

#jsi_documentObject (readonly)

document containing the instance of this JSI at our #jsi_ptr



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

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.



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

def jsi_indicated_schemas
  @jsi_indicated_schemas
end

#jsi_ptrJSI::Ptr (readonly)

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



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

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.



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

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.



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

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.



171
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
# File 'lib/jsi/base.rb', line 171

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



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

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)


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

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.

Raises:



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

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.



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

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.



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

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



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

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



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

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.



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

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.



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

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.



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

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.



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

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.



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

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.



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

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.



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

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



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

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.



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

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.



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

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



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

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



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

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.

Yields:

  • (JSI::Base)

    each descendent node, starting with self



325
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
# File 'lib/jsi/base.rb', line 325

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:



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

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:



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

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



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

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.



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

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



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

def jsi_instance
  jsi_node_content
end

#jsi_is_schema?Boolean

Is this a JSI Schema?



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

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.



736
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
# File 'lib/jsi/base.rb', line 736

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?



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

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.



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

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.

Raises:



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

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.



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

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.



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

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



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

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



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

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.



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


#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



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

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



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

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.

Raises:



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

def jsi_valid!
  jsi_validate.valid!
end

#jsi_valid?Boolean

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



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

def jsi_valid?
  jsi_indicated_schemas.instance_valid?(self)
end

#jsi_validateJSI::Validation::Result::Full

validates this JSI's instance against its schemas



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

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.



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

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.



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

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