Skip to content

Commit

Permalink
Merge remote-tracking branches 'origin/string_node', 'origin/new_meta…
Browse files Browse the repository at this point in the history
…schema', 'origin/msn_indicated', 'origin/default_metaschema', 'origin/deep_to_frozen', 'origin/as_to_json', 'origin/ary_subscript_range', 'origin/schema_module_connect', 'origin/name_metaschema_subschema_modules', 'origin/ivar_assign', 'origin/doc' and 'origin/misc' into HEAD
  • Loading branch information
notEthan committed Jul 5, 2023
12 parents bedd58b + b090ab1 + 192b31c + 20cf202 + e098c58 + 8f3c4f5 + 0e3ac6f + 262f241 + b94c12b + 35a9a31 + 494fe13 + e617aed commit 0968285
Show file tree
Hide file tree
Showing 32 changed files with 960 additions and 252 deletions.
1 change: 1 addition & 0 deletions Rakefile.rb
Expand Up @@ -77,6 +77,7 @@ def ruby(cmd_args, &block)
.github/**/*
.gitignore
.gitmodules
bin/c
Gemfile
Rakefile.rb
test/**/*
Expand Down
23 changes: 22 additions & 1 deletion lib/jsi.rb
Expand Up @@ -66,11 +66,32 @@ def self.new_schema(schema_content,
# Instantiates the given schema content as a JSI Schema, passing all params to
# {JSI.new_schema}, and returns its {Schema#jsi_schema_module JSI Schema Module}.
#
# @return [Module + JSI::SchemaModule]
# @return (see JSI::Schema::DescribesSchema#new_schema_module)
def self.new_schema_module(schema_content, **kw)
JSI::Schema.new_schema(schema_content, **kw).jsi_schema_module
end

# Instantiates the given document as a JSI Metaschema.
#
# @param metaschema_document an object to be instantiated as a JSI Metaschema
# @param schema_implementation_modules (see MetaschemaNode#initialize)
# @return [JSI::MetaschemaNode + JSI::DescribesSchema + JSI::Schema]
def self.new_metaschema(metaschema_document,
schema_implementation_modules:
)
MetaschemaNode.new(metaschema_document,
schema_implementation_modules: schema_implementation_modules,
)
end

# Instantiates the given document as a JSI Metaschema, passing all params to
# {new_metaschema}, and returns its {Schema#jsi_schema_module JSI Schema Module}.
#
# @return [Module + JSI::SchemaModule::DescribesSchemaModule + JSI::SchemaModule]
def self.new_metaschema_module(metaschema_document, **kw)
new_metaschema(metaschema_document, **kw).jsi_schema_module
end

# `JSI.schema_registry` is the {JSI::SchemaRegistry} in which schemas are registered.
#
# @return [JSI::SchemaRegistry]
Expand Down
39 changes: 24 additions & 15 deletions lib/jsi/base.rb
@@ -1,18 +1,18 @@
# frozen_string_literal: true

module JSI
# JSI::Base is the base class of every JSI instance of a JSON schema.
# 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}).
#
# instances are described by a set of one or more JSON schemas. JSI dynamically creates a subclass of
# JSI::Base for each set of JSON schemas which describe an instance that is to be instantiated.
# JSI::Base is an abstract base class. The subclasses used to instantiate JSIs are dynamically created as
# needed for a given instance.
#
# a JSI instance of such a subclass represents a JSON schema instance described by that set of schemas.
# 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:
#
# this subclass includes the JSI Schema Module of each schema it represents.
#
# the method {Base#jsi_schemas} is defined to indicate the schemas the class represents.
#
# the JSI::Base class itself is not intended to be instantiated.
# - the {Schema#jsi_schema_module} of each schema which describes the instance
# - {Base::HashNode} or {Base::ArrayNode}, if the instance is a hash/object or array
# - Modules defining accessor methods for property names described by the schemas
class Base
autoload :ArrayNode, 'jsi/base/node'
autoload :HashNode, 'jsi/base/node'
Expand Down Expand Up @@ -160,7 +160,9 @@ def initialize(jsi_document,
end

# @!method jsi_schemas
# the set of schemas which describe this instance
# The set of schemas that describe this instance.
# These are the applicator schemas that apply to this instance, the result of inplace application
# of our {#jsi_indicated_schemas}.
# @return [JSI::SchemaSet]
# note: defined on subclasses by JSI::SchemaClasses.class_for_schemas

Expand Down Expand Up @@ -621,10 +623,10 @@ def jsi_object_group_text
if jsi_node_content.respond_to?(:jsi_object_group_text)
content_txt = jsi_node_content.jsi_object_group_text
else
content_txt = [jsi_node_content.class.to_s]
content_txt = jsi_node_content.class.to_s
end
else
content_txt = []
content_txt = nil
end

[
Expand All @@ -636,8 +638,14 @@ def jsi_object_group_text

# a jsonifiable representation of the instance
# @return [Object]
def as_json(*opt)
Util.as_json(jsi_instance, *opt)
def as_json(*a, **kw)
Util.as_json(jsi_instance, *a, **kw)
end

# A JSON encoded string of the instance content
# @return [String]
def to_json(*a, **kw)
Util.to_json(jsi_instance, *a, **kw)
end

# see {Util::Private::FingerprintHash}
Expand All @@ -656,7 +664,8 @@ def jsi_fingerprint
private

def jsi_indicated_schemas=(jsi_indicated_schemas)
@jsi_indicated_schemas = SchemaSet.ensure_schema_set(jsi_indicated_schemas)
#chkbug raise(Bug) unless jsi_indicated_schemas.is_a?(SchemaSet)
@jsi_indicated_schemas = jsi_indicated_schemas
end

def jsi_child_node_map
Expand Down
38 changes: 35 additions & 3 deletions lib/jsi/base/node.rb
Expand Up @@ -23,11 +23,11 @@ def to_a(**kw)

# a jsonifiable representation of the node content
# @return [Object]
def as_json(*opt)
def as_json(*a, **kw)
# include Enumerable (above) means, if ActiveSupport is loaded, its undesirable #as_json is included
# https://github.com/rails/rails/blob/v7.0.0/activesupport/lib/active_support/core_ext/object/json.rb#L139-L143
# although Base#as_json does clobber activesupport's, I want as_json defined correctly on the module too.
Util.as_json(jsi_node_content, *opt)
Util.as_json(jsi_node_content, *a, **kw)
end
end

Expand Down Expand Up @@ -221,9 +221,41 @@ def [](token, as_jsi: :auto, use_default: true)
end
end
end
elsif token.is_a?(Range)
type_err = proc do
raise(TypeError, [
"given range does not contain Integers",
"range: #{token.inspect}",
].join("\n"))
end

start_idx = token.begin
if start_idx.is_a?(Integer)
start_idx += size if start_idx < 0
return Util::EMPTY_ARY if start_idx == size
return nil if start_idx < 0 || start_idx > size
elsif start_idx.nil?
start_idx = 0
else
type_err.call
end

end_idx = token.end
if end_idx.is_a?(Integer)
end_idx += size if end_idx < 0
end_idx += 1 unless token.exclude_end?
end_idx = size if end_idx > size
return Util::EMPTY_ARY if start_idx >= end_idx
elsif end_idx.nil?
end_idx = size
else
type_err.call
end

(start_idx...end_idx).map { |i| jsi_child(i, as_jsi: as_jsi) }.freeze
else
raise(TypeError, [
"expected `token` param to be an Integer",
"expected `token` param to be an Integer or Range",
"token: #{token.inspect}",
].join("\n"))
end
Expand Down
6 changes: 4 additions & 2 deletions lib/jsi/jsi_coder.rb
Expand Up @@ -29,12 +29,14 @@ class JSICoder
# will instantiate column data using the JSI schemas represented.
# @param array [Boolean] whether the dumped data represent one instance of the schema,
# or an array of them. note that it may be preferable to simply use an array schema.
def initialize(schema, array: false)
# @param jsi_opt [Hash] keyword arguments to pass to {Schema#new_jsi} when loading
def initialize(schema, array: false, jsi_opt: {})
unless schema.respond_to?(:new_jsi)
raise(ArgumentError, "schema param does not respond to #new_jsi: #{schema.inspect}")
end
@schema = schema
@array = array
@jsi_opt = jsi_opt
end

# loads the database column to JSI instances of our schema
Expand Down Expand Up @@ -78,7 +80,7 @@ def dump(object)
# @param data [Object]
# @return [JSI::Base]
def load_object(data)
@schema.new_jsi(data)
@schema.new_jsi(data, **@jsi_opt)
end

# @param object [JSI::Base, Object]
Expand Down
46 changes: 30 additions & 16 deletions lib/jsi/metaschema_node.rb
Expand Up @@ -67,12 +67,14 @@ def initialize(
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|
bootstrap_indicated_schemas = jsi_ptr.tokens.inject(SchemaSet[root_bootstrap_schema]) do |bootstrap_indicated_schemas, tok|
bootstrap_schemas = bootstrap_indicated_schemas.inplace_applicator_schemas(instance_for_schemas)
child_indicated_schemas = bootstrap_schemas.child_applicator_schemas(tok, instance_for_schemas)
child_schemas = child_indicated_schemas.inplace_applicator_schemas(instance_for_schemas[tok])
instance_for_schemas = instance_for_schemas[tok]
child_schemas
child_indicated_schemas
end
@indicated_schemas_map = jsi_memomap { bootstrap_schemas_to_msn(bootstrap_indicated_schemas) }
our_bootstrap_schemas = bootstrap_indicated_schemas.inplace_applicator_schemas(instance_for_schemas)

our_bootstrap_schemas.each do |bootstrap_schema|
if bootstrap_schema.jsi_ptr == metaschema_root_ptr
Expand All @@ -90,18 +92,7 @@ def initialize(
end
end

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

# note: jsi_schemas must already be set for jsi_schema_module to be used/extended
if is_a?(Metaschema)
Expand Down Expand Up @@ -143,6 +134,12 @@ def initialize(
# @return [JSI::Ptr]
attr_reader :root_schema_ptr

# See {Base#jsi_indicated_schemas}
# @return [JSI::SchemaSet]
def jsi_indicated_schemas
@indicated_schemas_map[]
end

# JSI Schemas describing this MetaschemaNode
# @return [JSI::SchemaSet]
attr_reader :jsi_schemas
Expand Down Expand Up @@ -184,7 +181,7 @@ def jsi_object_group_text
[
class_n_schemas,
is_a?(Metaschema) ? "Metaschema" : is_a?(Schema) ? "Schema" : nil,
*(jsi_node_content.respond_to?(:jsi_object_group_text) ? jsi_node_content.jsi_object_group_text : []),
*(jsi_node_content.respond_to?(:jsi_object_group_text) ? jsi_node_content.jsi_object_group_text : nil),
].compact
end

Expand Down Expand Up @@ -220,5 +217,22 @@ def jsi_child_node_map
)
end
end

# @param bootstrap_schemas [Enumerable<BootstrapSchema>]
# @return [SchemaSet<MetaschemaNode>]
def bootstrap_schemas_to_msn(bootstrap_schemas)
SchemaSet.new(bootstrap_schemas) do |bootstrap_schema|
if bootstrap_schema.jsi_ptr.root?
@jsi_root_node
elsif 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
end
end
end
23 changes: 23 additions & 0 deletions lib/jsi/metaschema_node/bootstrap_schema.rb
Expand Up @@ -47,6 +47,7 @@ def initialize(
self.jsi_ptr = jsi_ptr
self.jsi_document = jsi_document
self.jsi_schema_base_uri = jsi_schema_base_uri
self.jsi_schema_resource_ancestors = Util::EMPTY_ARY
end

# document containing the schema content
Expand All @@ -59,6 +60,28 @@ def jsi_node_content
jsi_ptr.evaluate(jsi_document)
end

# overrides {Schema#subschema}
def subschema(subptr)
self.class.new(
jsi_document,
jsi_ptr: jsi_ptr + subptr,
jsi_schema_base_uri: jsi_resource_ancestor_uri,
)
end

# overrides {Schema#resource_root_subschema}
def resource_root_subschema(ptr)
# BootstrapSchema does not track jsi_schema_resource_ancestors used by Schema#schema_resource_root;
# resource_root_subschema is always relative to the document root.
# BootstrapSchema also does not implement jsi_root_node or #[]. we instantiate the ptr directly
# rather than as a subschema from the root.
self.class.new(
jsi_document,
jsi_ptr: Ptr.ary_ptr(ptr),
jsi_schema_base_uri: nil,
)
end

# @return [String]
def inspect
"\#<#{jsi_object_group_text.join(' ')} #{schema_content.inspect}>"
Expand Down

0 comments on commit 0968285

Please sign in to comment.