Skip to content

Commit

Permalink
Merge e532200 into 8c33c58
Browse files Browse the repository at this point in the history
  • Loading branch information
notEthan committed Apr 17, 2024
2 parents 8c33c58 + e532200 commit 7eb12af
Show file tree
Hide file tree
Showing 13 changed files with 66 additions and 82 deletions.
18 changes: 12 additions & 6 deletions lib/jsi/base.rb
Expand Up @@ -220,7 +220,7 @@ def jsi_each_descendent_node(propertyNames: false, &block)
end

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

nil
Expand All @@ -239,7 +239,7 @@ def jsi_select_descendents_node_first(&block)
if jsi_array? || jsi_hash?
res = instance.class.new
jsi_each_child_token do |token|
v = jsi_child(token, as_jsi: true)
v = jsi_child_node(token)
if yield(v)
res_v = v.jsi_select_descendents_node_first(&block).jsi_node_content
if jsi_array?
Expand Down Expand Up @@ -269,7 +269,7 @@ def jsi_select_descendents_leaf_first(&block)
if jsi_array? || jsi_hash?
res = instance.class.new
jsi_each_child_token do |token|
v = jsi_child(token, as_jsi: true).jsi_select_descendents_leaf_first(&block)
v = jsi_child_node(token).jsi_select_descendents_leaf_first(&block)
if yield(v)
res_v = v.jsi_node_content
if jsi_array?
Expand All @@ -294,7 +294,7 @@ def jsi_parent_nodes

jsi_ptr.tokens.map do |token|
parent.tap do
parent = parent[token, as_jsi: true]
parent = parent.jsi_child_node(token)
end
end.reverse!.freeze
end
Expand All @@ -315,7 +315,7 @@ def jsi_ancestor_nodes
ancestors << ancestor

jsi_ptr.tokens.each do |token|
ancestor = ancestor[token, as_jsi: true]
ancestor = ancestor.jsi_child_node(token)
ancestors << ancestor
end
ancestors.reverse!.freeze
Expand Down Expand Up @@ -408,6 +408,12 @@ def jsi_child(token, as_jsi: )
end
private :jsi_child # internals for #[] but idk, could be public

# @param token (see Base#[])
# @return [JSI::Base]
protected def jsi_child_node(token)
jsi_child(token, as_jsi: true)
end

# A default value for a child of this node identified by the given token, if schemas describing
# that child define a default value.
#
Expand Down Expand Up @@ -437,7 +443,7 @@ def jsi_default_child(token, as_jsi: )
jsi_child_as_jsi(defaults.first, child_applied_schemas, as_jsi) do
jsi_modified_copy do |i|
i.dup.tap { |i_dup| i_dup[token] = defaults.first }
end[token, as_jsi: true]
end.jsi_child_node(token)
end
else
child_content
Expand Down
4 changes: 2 additions & 2 deletions lib/jsi/base/node.rb
Expand Up @@ -98,7 +98,7 @@ def as_json(options = {})
k.is_a?(Symbol) ? k.to_s :
k.respond_to?(:to_str) && (kstr = k.to_str).is_a?(String) ? kstr :
raise(TypeError, "JSON object (Hash) cannot be keyed with: #{k.pretty_inspect.chomp}")
hash[ks] = jsi_child(k, as_jsi: true).as_json(**options)
hash[ks] = jsi_child_node(k).as_json(**options)
end
hash
end
Expand Down Expand Up @@ -263,7 +263,7 @@ def to_ary(**kw)

# See {Base#as_json}
def as_json(options = {})
each_index.map { |i| jsi_child(i, as_jsi: true).as_json(**options) }
each_index.map { |i| jsi_child_node(i).as_json(**options) }
end

include Util::Arraylike
Expand Down
2 changes: 1 addition & 1 deletion lib/jsi/jsi_coder.rb
Expand Up @@ -31,7 +31,7 @@ class JSICoder
# or an array of them. note that it may be preferable to simply use an array schema.
# @param jsi_opt [Hash] keyword arguments to pass to {Schema#new_jsi} when loading
# @param as_json_opt [Hash] keyword arguments to pass to `#as_json` when dumping
def initialize(schema, array: false, jsi_opt: {}, as_json_opt: {})
def initialize(schema, array: false, jsi_opt: Util::EMPTY_HASH, as_json_opt: Util::EMPTY_HASH)
unless schema.respond_to?(:new_jsi)
raise(ArgumentError, "schema param does not respond to #new_jsi: #{schema.inspect}")
end
Expand Down
2 changes: 1 addition & 1 deletion lib/jsi/metaschema_node.rb
Expand Up @@ -229,7 +229,7 @@ def our_initialize_params
jsi_schema_base_uri: jsi_schema_base_uri,
jsi_schema_registry: jsi_schema_registry,
jsi_content_to_immutable: jsi_content_to_immutable,
}
}.freeze
end

# note: not for root node
Expand Down
14 changes: 10 additions & 4 deletions lib/jsi/ptr.rb
Expand Up @@ -167,19 +167,25 @@ def parent
tokens.size == 1 ? EMPTY : Ptr.new(tokens[0...-1].freeze)
end

# whether this pointer contains the other_ptr - that is, whether this pointer is an ancestor
# of `other_ptr`, a descendent pointer. `contains?` is inclusive; a pointer does contain itself.
# whether this pointer is an ancestor of `other_ptr`, a descendent pointer.
# `ancestor_of?` is inclusive; a pointer is an ancestor of itself.
#
# @return [Boolean]
def contains?(other_ptr)
def ancestor_of?(other_ptr)
tokens == other_ptr.tokens[0...tokens.size]
end

# @deprecated
def contains?(other_ptr)
ancestor_of?(other_ptr)
end

# part of this pointer relative to the given ancestor_ptr
# @return [JSI::Ptr]
# @raise [JSI::Ptr::Error] if the given ancestor_ptr is not an ancestor of this pointer
def relative_to(ancestor_ptr)
return self if ancestor_ptr.empty?
unless ancestor_ptr.contains?(self)
unless ancestor_ptr.ancestor_of?(self)
raise(Error, "ancestor_ptr #{ancestor_ptr.inspect} is not ancestor of #{inspect}")
end
ancestor_ptr.tokens.size == tokens.size ? EMPTY : Ptr.new(tokens[ancestor_ptr.tokens.size..-1].freeze)
Expand Down
34 changes: 21 additions & 13 deletions lib/jsi/schema.rb
Expand Up @@ -376,16 +376,17 @@ def ensure_schema(schema, msg: "indicated object is not a schema:", reinstantiat
if reinstantiate_as && schema.is_a?(JSI::Base)
# TODO warn; behavior is undefined and I hate this implementation

result_schema_schemas = schema.jsi_schemas + reinstantiate_as
result_schema_indicated_schemas = SchemaSet.new(schema.jsi_indicated_schemas + reinstantiate_as)
result_schema_applied_schemas = result_schema_indicated_schemas.inplace_applicator_schemas(schema.jsi_node_content)

result_schema_class = JSI::SchemaClasses.class_for_schemas(result_schema_schemas,
result_schema_class = JSI::SchemaClasses.class_for_schemas(result_schema_applied_schemas,
includes: SchemaClasses.includes_for(schema.jsi_node_content),
mutable: schema.jsi_mutable?,
)

result_schema_class.new(schema.jsi_document,
jsi_ptr: schema.jsi_ptr,
jsi_indicated_schemas: schema.jsi_indicated_schemas,
jsi_indicated_schemas: result_schema_indicated_schemas,
jsi_schema_base_uri: schema.jsi_schema_base_uri,
jsi_schema_resource_ancestors: schema.jsi_schema_resource_ancestors,
jsi_schema_registry: schema.jsi_schema_registry,
Expand Down Expand Up @@ -466,7 +467,7 @@ def schema_absolute_uri
end

# a nonrelative URI which refers to this schema.
# nil if no parent of this schema defines an id.
# `nil` if no ancestor of this schema defines an id.
# see {#schema_uris} for all URIs known to refer to this schema.
# @return [Addressable::URI, nil]
def schema_uri
Expand All @@ -491,22 +492,22 @@ def each_schema_uri

yield schema_absolute_uri if schema_absolute_uri

parent_schemas = jsi_subschema_resource_ancestors.reverse_each.select do |resource|
ancestor_schemas = jsi_subschema_resource_ancestors.reverse_each.select do |resource|
resource.schema_absolute_uri
end

anchored = respond_to?(:anchor) ? anchor : nil
parent_schemas.each do |parent_schema|
ancestor_schemas.each do |ancestor_schema|
if anchored
if parent_schema.jsi_anchor_subschema(anchor) == self
yield parent_schema.schema_absolute_uri.merge(fragment: anchor).freeze
if ancestor_schema.jsi_anchor_subschema(anchor) == self
yield(ancestor_schema.schema_absolute_uri.merge(fragment: anchor).freeze)
else
anchored = false
end
end

relative_ptr = jsi_ptr.relative_to(parent_schema.jsi_ptr)
yield parent_schema.schema_absolute_uri.merge(fragment: relative_ptr.fragment).freeze
relative_ptr = jsi_ptr.relative_to(ancestor_schema.jsi_ptr)
yield(ancestor_schema.schema_absolute_uri.merge(fragment: relative_ptr.fragment).freeze)
end

nil
Expand Down Expand Up @@ -603,10 +604,10 @@ def describes_schema!(schema_implementation_modules)

# a resource containing this schema.
#
# if any parent, or this schema itself, is a schema with an absolute uri (see {#schema_absolute_uri}),
# If any ancestor, or this schema itself, is a schema with an absolute uri (see {#schema_absolute_uri}),
# the resource root is the closest schema with an absolute uri.
#
# If no parent schema has an absolute uri, the schema_resource_root is the {Base#jsi_root_node document's root node}.
# If no ancestor schema has an absolute uri, the schema_resource_root is the {Base#jsi_root_node document's root node}.
# In this case, the resource root may or may not be a schema itself.
#
# @return [JSI::Base] resource containing this schema
Expand Down Expand Up @@ -767,7 +768,14 @@ def internal_validate_instance(
else
result = JSI::Validation::FullResult.new
end
result_builder = result.builder(self, instance_ptr, instance_document, validate_only, visited_refs)
result_builder = result.class::Builder.new(
result: result,
schema: self,
instance_ptr: instance_ptr,
instance_document: instance_document,
validate_only: validate_only,
visited_refs: visited_refs,
)

catch(:jsi_validation_result) do
# note: true/false are not valid as schemas in draft 4; they are only values of
Expand Down
6 changes: 3 additions & 3 deletions lib/jsi/schema/schema_ancestor_node.rb
Expand Up @@ -18,7 +18,7 @@ def initialize(*, **)

# the base URI used to resolve the ids of schemas at or below this JSI.
# this is always an absolute URI (with no fragment).
# this may be the absolute schema URI of a parent schema or the URI from which the document was retrieved.
# This may be the absolute schema URI of an ancestor schema or the URI from which the document was retrieved.
# @api private
# @return [Addressable::URI, nil]
attr_reader :jsi_schema_base_uri
Expand All @@ -34,7 +34,7 @@ def initialize(*, **)

# the URI of the resource containing this node.
# this is always an absolute URI (with no fragment).
# if this node is a schema with an id, this is its absolute URI; otherwise a parent resource's URI,
# If this node is a schema with an id, this is its absolute URI; otherwise an ancestor resource's URI,
# or nil if not contained by a resource with a URI.
# @return [Addressable::URI, nil]
def jsi_resource_ancestor_uri
Expand Down Expand Up @@ -97,7 +97,7 @@ def jsi_schema_resource_ancestors=(jsi_schema_resource_ancestors)
#chkbug end
#chkbug if anc.jsi_ptr == jsi_ptr
#chkbug raise(Bug, "ancestor is self")
#chkbug elsif !anc.jsi_ptr.contains?(jsi_ptr)
#chkbug elsif !anc.jsi_ptr.ancestor_of?(jsi_ptr)
#chkbug raise(Bug, "ancestor does not contain self")
#chkbug end
#chkbug last_anc_ptr = anc.jsi_ptr
Expand Down
15 changes: 7 additions & 8 deletions lib/jsi/schema_set.rb
Expand Up @@ -6,14 +6,12 @@ module JSI
# any schema instance is described by a set of schemas.
class SchemaSet < ::Set
class << self
# builds a SchemaSet from a mutable Set which is added to by the given block
# Builds a SchemaSet, yielding a yielder to be called with each schema of the SchemaSet.
#
# @yield [Set] a Set to which the block may add schemas
# @yield [Enumerator::Yielder]
# @return [SchemaSet]
def build
mutable_set = Set.new
yield mutable_set
new(mutable_set)
def build(&block)
new(Enumerator.new(&block))
end

# ensures the given param becomes a SchemaSet. returns the param if it is already SchemaSet, otherwise
Expand Down Expand Up @@ -192,8 +190,9 @@ def each_child_applicator_schema(token, instance, &block)
# @param instance [Object] the instance to validate against our schemas
# @return [JSI::Validation::Result]
def instance_validate(instance)
results = map { |schema| schema.instance_validate(instance) }
results.inject(Validation::FullResult.new, &:merge).freeze
inject(Validation::FullResult.new) do |result, schema|
result.merge(schema.instance_validate(instance))
end.freeze
end

# whether the given instance is valid against our schemas
Expand Down
2 changes: 1 addition & 1 deletion lib/jsi/simple_wrap.rb
Expand Up @@ -15,7 +15,7 @@ def internal_validate_keywords(result_builder)
end

simple_wrap_metaschema = JSI.new_metaschema(nil, schema_implementation_modules: [simple_wrap_implementation])
SimpleWrap = simple_wrap_metaschema.new_schema_module({})
SimpleWrap = simple_wrap_metaschema.new_schema_module(Util::EMPTY_HASH)

# SimpleWrap is a JSI schema module which recursively wraps nested structures
module SimpleWrap
Expand Down
21 changes: 2 additions & 19 deletions lib/jsi/util/private.rb
Expand Up @@ -12,6 +12,8 @@ module Util::Private

EMPTY_ARY = [].freeze

EMPTY_HASH = {}.freeze

EMPTY_SET = Set[].freeze

CLASSES_ALWAYS_FROZEN = Set[TrueClass, FalseClass, NilClass, Integer, Float, BigDecimal, Rational, Symbol].freeze
Expand Down Expand Up @@ -179,24 +181,5 @@ def freeze
super
end
end

module Virtual
class InstantiationError < StandardError
end

# this virtual class is not intended to be instantiated except by its subclasses, which override #initialize
def initialize
# :nocov:
raise(InstantiationError, "cannot instantiate virtual class #{self.class}")
# :nocov:
end

# virtual_method is used to indicate that the method calling it must be implemented on the (non-virtual) subclass
def virtual_method
# :nocov:
raise(Bug, "class #{self.class} must implement #{caller_locations.first.label}")
# :nocov:
end
end
end
end
4 changes: 4 additions & 0 deletions lib/jsi/validation/error.rb
Expand Up @@ -29,6 +29,10 @@ module Validation
# document containing the instance at instance_ptr
# @return [Object]
class Error
def initialize(attributes = {})
super
freeze
end
end
end
end
24 changes: 1 addition & 23 deletions lib/jsi/validation/result.rb
Expand Up @@ -3,10 +3,7 @@
module JSI
module Validation
# a result of validating an instance against schemas which describe it.
# virtual base class.
class Result
include Util::Virtual

Builder = Util::AttrStruct[*%w(
result
schema
Expand All @@ -24,7 +21,6 @@ def instance
end

def schema_issue(*_)
virtual_method
end

def schema_error(message, keyword = nil)
Expand Down Expand Up @@ -74,23 +70,10 @@ def merge_schema_issues(other_result)
end

class Result
def builder(schema, instance_ptr, instance_document, validate_only, visited_refs)
self.class::Builder.new(
result: self,
schema: schema,
instance_ptr: instance_ptr,
instance_document: instance_document,
validate_only: validate_only,
visited_refs: visited_refs,
)
end

# is the instance valid against its schemas?
# @return [Boolean]
def valid?
# :nocov:
virtual_method
# :nocov:
#dbg raise(NotImplementedError)
end

include Util::FingerprintHash
Expand Down Expand Up @@ -144,7 +127,6 @@ def valid?
end

def freeze
@validation_errors.each(&:freeze)
@schema_issues.each(&:freeze)
@validation_errors.freeze
@schema_issues.freeze
Expand All @@ -160,10 +142,6 @@ def merge(result)
self
end

def +(result)
FullResult.new.merge(self).merge(result)
end

# see {Util::Private::FingerprintHash}
# @api private
def jsi_fingerprint
Expand Down

0 comments on commit 7eb12af

Please sign in to comment.