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/child_param_default_methods', 'origin/deep_to_frozen', 'origin/ary_subscript_range', 'origin/msn_root_descendent_node', 'origin/msn_default_child', 'origin/schema_module_connect', 'origin/memomap_immutable', 'origin/ruby_names', 'origin/ivar_assign', 'origin/doc', 'origin/misc' and 'origin/dependabot/github_actions/coverallsapp/github-action-2.2.1' into HEAD
  • Loading branch information
notEthan committed Aug 19, 2023
Show file tree
Hide file tree
Showing 38 changed files with 852 additions and 263 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Expand Up @@ -37,7 +37,7 @@ jobs:
- run: bundle exec rake test

- name: Report to Coveralls
uses: coverallsapp/github-action@v2.1.2
uses: coverallsapp/github-action@v2.2.1
with:
github-token: ${{ secrets.github_token }}
flag-name: "test ruby: ${{ matrix.ruby-version }} os: ${{ matrix.runs-on }}"
Expand All @@ -49,7 +49,7 @@ jobs:
steps:

- name: Report completion to Coveralls
uses: coverallsapp/github-action@v2.1.2
uses: coverallsapp/github-action@v2.2.1
with:
github-token: ${{ secrets.github_token }}
parallel-finished: true
1 change: 1 addition & 0 deletions Gemfile
Expand Up @@ -14,6 +14,7 @@ group(:dev) do
gem 'byebug'
end
end
platform(:jruby) { gem 'ruby-debug' }
end

group(:test) do
Expand Down
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
65 changes: 44 additions & 21 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 @@ -88,7 +88,7 @@ def schema_classes_const_name
end
end
if !schema_names.any?(&:nil?) && !schema_names.empty?
schema_names.sort.map { |n| 'X' + n.to_s.gsub(/[^\w]/, '_') }.join('')
Util.const_name_from_parts(schema_names.sort.map { |part| 'X' + part })
end
end
end
Expand Down Expand Up @@ -154,13 +154,17 @@ def initialize(jsi_document,
self.jsi_schema_base_uri = jsi_schema_base_uri
self.jsi_schema_resource_ancestors = jsi_schema_resource_ancestors

jsi_memomaps_initialize

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

# @!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 @@ -380,7 +384,7 @@ def jsi_node_content_child(token)
# @param token (see Base#[])
# @param as_jsi (see Base#[])
# @return [JSI::Base, Object]
def jsi_child(token, as_jsi: :auto)
def jsi_child(token, as_jsi: )
child_content = jsi_node_content_child(token)

child_indicated_schemas = jsi_schemas.child_applicator_schemas(token, jsi_node_content)
Expand Down Expand Up @@ -408,7 +412,7 @@ def jsi_child(token, as_jsi: :auto)
# @param token (see Base#[])
# @param as_jsi (see Base#[])
# @return [JSI::Base, nil]
def jsi_default_child(token, as_jsi: :auto)
def jsi_default_child(token, as_jsi: )
child_content = jsi_node_content_child(token)

child_indicated_schemas = jsi_schemas.child_applicator_schemas(token, jsi_node_content)
Expand Down Expand Up @@ -437,9 +441,10 @@ def jsi_default_child(token, as_jsi: :auto)
#
# @param token [String, Integer, Object] an array index or hash key (JSON object property name)
# of the instance identifying the child value
# @param as_jsi [:auto, true, false] whether to return the result value as a JSI. one of:
# @param as_jsi [:auto, true, false] (default is `:auto`)
# Whether to return the child as a JSI. One of:
#
# - :auto (default): by default a JSI will be returned when either:
# - `:auto`: By default a JSI will be returned when either:
#
# - the result is a complex value (responds to #to_ary or #to_hash)
# - the result is a schema (including true/false schemas)
Expand All @@ -454,8 +459,9 @@ def jsi_default_child(token, as_jsi: :auto)
# 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.)
# @param use_default [true, false] whether to return a schema default value when the token is not in
# range. if the token is not an array index or hash key of the instance, and one schema for the child
# @param use_default [true, false] (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
Expand All @@ -467,11 +473,24 @@ def jsi_default_child(token, as_jsi: :auto)
# (one exception is when this JSI's instance is a Hash with a default or default_proc, which has
# unspecified behavior.)
# @return [JSI::Base, Object, nil] the child value identified by the subscript token
def [](token, as_jsi: :auto, use_default: true)
def [](token, as_jsi: jsi_child_as_jsi_default, use_default: jsi_child_use_default_default)
# note: overridden by Base::HashNode, Base::ArrayNode
jsi_simple_node_child_error(token)
end

# The default value for the param `as_jsi` of {#[]}, controlling whether a child is returned as a JSI instance.
# @return [:auto, true, false] a valid value of the `as_jsi` param of {#[]}
def jsi_child_as_jsi_default
:auto
end

# 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.
# @return [true, false] a valid value of the `use_default` param of {#[]}
def jsi_child_use_default_default
false
end

# assigns the subscript of the instance identified by the given token to the given value.
# if the value is a JSI, its instance is assigned instead of the JSI value itself.
#
Expand Down Expand Up @@ -621,10 +640,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 Down Expand Up @@ -655,8 +674,12 @@ def jsi_fingerprint

private

def jsi_memomaps_initialize
end

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 @@ -84,7 +84,7 @@ def jsi_node_content_child(token)
end

# See {Base#[]}
def [](token, as_jsi: :auto, use_default: true)
def [](token, as_jsi: jsi_child_as_jsi_default, use_default: jsi_child_use_default_default)
if jsi_node_content_hash_pubsend(:key?, token)
jsi_child(token, as_jsi: as_jsi)
else
Expand Down Expand Up @@ -201,7 +201,7 @@ def jsi_node_content_child(token)
end

# See {Base#[]}
def [](token, as_jsi: :auto, use_default: true)
def [](token, as_jsi: jsi_child_as_jsi_default, use_default: jsi_child_use_default_default)
size = jsi_node_content_ary_pubsend(:size)
if token.is_a?(Integer)
if token < 0
Expand All @@ -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

0 comments on commit 8d03555

Please sign in to comment.