Skip to content

Commit

Permalink
Merge 2d6a817 into f3564ae
Browse files Browse the repository at this point in the history
  • Loading branch information
notEthan committed Feb 27, 2021
2 parents f3564ae + 2d6a817 commit a8a72e7
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 16 deletions.
1 change: 1 addition & 0 deletions lib/jsi.rb
Expand Up @@ -30,6 +30,7 @@ class Bug < NotImplementedError
autoload :Hashlike, 'jsi/typelike_modules'
autoload :Arraylike, 'jsi/typelike_modules'
autoload :Schema, 'jsi/schema'
autoload :SchemaSet, 'jsi/schema_set'
autoload :Base, 'jsi/base'
autoload :Metaschema, 'jsi/metaschema'
autoload :MetaschemaNode, 'jsi/metaschema_node'
Expand Down
10 changes: 5 additions & 5 deletions lib/jsi/metaschema_node.rb
Expand Up @@ -68,18 +68,18 @@ 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.reference_tokens.inject(Set[root_bootstrap_schema]) do |bootstrap_schemas, tok|
our_bootstrap_schemas = jsi_ptr.reference_tokens.inject(SchemaSet[root_bootstrap_schema]) do |bootstrap_schemas, tok|
subschemas_for_token = bootstrap_schemas.map do |bootstrap_schema|
if instance_for_schemas.respond_to?(:to_ary)
bootstrap_schema.subschemas_for_index(tok)
else
bootstrap_schema.subschemas_for_property_name(tok)
end
end.inject(Set.new, &:|)
end.inject(SchemaSet[], &:|)
instance_for_schemas = instance_for_schemas[tok]
bootstrap_schemas_for_instance = subschemas_for_token.map do |bootstrap_schema|
bootstrap_schema.match_to_instance(instance_for_schemas)
end.inject(Set.new, &:|)
end.inject(SchemaSet[], &:|)
bootstrap_schemas_for_instance
end

Expand All @@ -95,7 +95,7 @@ def initialize(
end
end

@jsi_schemas = our_bootstrap_schemas.map do |bootstrap_schema|
@jsi_schemas = SchemaSet.new(our_bootstrap_schemas) do |bootstrap_schema|
if bootstrap_schema.jsi_ptr == jsi_ptr
self
else
Expand All @@ -104,7 +104,7 @@ def initialize(
jsi_schema_base_uri: bootstrap_schema.jsi_schema_base_uri,
)
end
end.to_set.freeze
end

@jsi_schemas.each do |schema|
extend schema.jsi_schema_module
Expand Down
2 changes: 1 addition & 1 deletion lib/jsi/schema.rb
Expand Up @@ -244,7 +244,7 @@ def jsi_schema_module

# @return [Class subclassing JSI::Base] a JSI class (subclass of JSI::Base) representing this schema.
def jsi_schema_class
JSI.class_for_schemas(Set[self])
JSI.class_for_schemas(SchemaSet[self])
end

# instantiates the given instance as a JSI::Base class for schemas matched from this schema to the
Expand Down
12 changes: 6 additions & 6 deletions lib/jsi/schema/application/child_application.rb
Expand Up @@ -6,10 +6,10 @@ module Schema::Application::ChildApplication
# `properties`, `patternProperties`, and `additionalProperties`.
#
# @param property_name [String] the property name for which to find subschemas
# @return [Set<JSI::Schema>] subschemas of this schema for the given property_name
# @return [JSI::SchemaSet] subschemas of this schema for the given property_name
def subschemas_for_property_name(property_name)
jsi_memoize(__method__, property_name) do |property_name|
Set.new.tap do |subschemas|
SchemaSet.build do |subschemas|
if schema_content.respond_to?(:to_hash)
apply_additional = true
if schema_content.key?('properties') && schema_content['properties'].respond_to?(:to_hash) && schema_content['properties'].key?(property_name)
Expand All @@ -28,18 +28,18 @@ def subschemas_for_property_name(property_name)
subschemas << subschema(['additionalProperties'])
end
end
end.freeze
end
end
end

# returns a set of subschemas of this schema for the given array index, from keywords
# `items` and `additionalItems`.
#
# @param index [Integer] the array index for which to find subschemas
# @return [Set<JSI::Schema>] subschemas of this schema for the given array index
# @return [JSI::SchemaSet] subschemas of this schema for the given array index
def subschemas_for_index(index)
jsi_memoize(__method__, index) do |idx|
Set.new.tap do |subschemas|
SchemaSet.build do |subschemas|
if schema_content.respond_to?(:to_hash)
if schema_content['items'].respond_to?(:to_ary)
if schema_content['items'].each_index.to_a.include?(idx)
Expand All @@ -51,7 +51,7 @@ def subschemas_for_index(index)
subschemas << subschema(['items'])
end
end
end.freeze
end
end
end
end
Expand Down
6 changes: 3 additions & 3 deletions lib/jsi/schema/application/inplace_application.rb
Expand Up @@ -8,9 +8,9 @@ module Schema::Application::InplaceApplication
# the returned set will contain this schema itself, unless this schema contains a $ref keyword.
#
# @param instance [Object] the instance to check any applicators against
# @return [Set<JSI::Schema>] matched applicator schemas
# @return [JSI::SchemaSet] matched applicator schemas
def match_to_instance(instance)
Set.new.tap do |schemas|
SchemaSet.build do |schemas|
if schema_content.respond_to?(:to_hash)
if schema_content['$ref'].respond_to?(:to_str)
ref = Schema::Ref.new(schema_content['$ref'], self)
Expand Down Expand Up @@ -43,7 +43,7 @@ def match_to_instance(instance)
else
schemas << self
end
end.freeze
end
end
end
end
3 changes: 2 additions & 1 deletion lib/jsi/schema_classes.rb
Expand Up @@ -45,7 +45,8 @@ module SchemaClasses
class << self
# see {JSI.class_for_schemas}
def class_for_schemas(schema_objects)
schemas = schema_objects.map { |schema_object| JSI.new_schema(schema_object) }.to_set.freeze
schemas = SchemaSet.new(schema_objects) { |schema_object| JSI.new_schema(schema_object) }

jsi_memoize(:class_for_schemas, schemas) do |schemas|
Class.new(Base).instance_exec(schemas) do |schemas|
define_singleton_method(:jsi_class_schemas) { schemas }
Expand Down
62 changes: 62 additions & 0 deletions lib/jsi/schema_set.rb
@@ -0,0 +1,62 @@
# frozen_string_literal: true

module JSI
# a Set of JSI Schemas. always frozen.
#
# 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
#
# @yield [Set] a Set to which the block may add schemas
# @return [SchemaSet]
def build
mutable_set = Set.new
yield mutable_set
new(mutable_set)
end
end

# initializes a SchemaSet from the given enum and freezes it.
#
# if a block is given, each element of the enum is passed to it, and the result must be a Schema.
# if no block is given, the enum must contain only Schemas.
#
# @param enum [#each] the schemas to be included in the SchemaSet, or items to be passed to the block
# @yieldparam yields each element of enum for preprocessing into a Schema
# @yieldreturn [JSI::Schema]
# @raise [JSI::Schema::NotASchemaError]
def initialize(enum, &block)
super

not_schemas = reject { |s| s.is_a?(Schema) }
if !not_schemas.empty?
raise(Schema::NotASchemaError, [
"JSI::SchemaSet initialized with non-schema objects:",
*not_schemas.map { |ns| ns.pretty_inspect.chomp },
].join("\n"))
end

freeze
end

def inspect
"#{self.class}[#{map(&:inspect).join(", ")}]"
end

def pretty_print(q)
q.text self.class.to_s
q.text '['
q.group_sub {
q.nest(2) {
q.breakable('')
q.seplist(self, nil, :each) { |e|
q.pp e
}
}
}
q.breakable ''
q.text ']'
end
end
end
47 changes: 47 additions & 0 deletions test/schema_set_test.rb
@@ -0,0 +1,47 @@
# frozen_string_literal: true

require_relative 'test_helper'

describe 'JSI::SchemaSet' do
let(:schema_a) { JSI::JSONSchemaOrgDraft06.new_schema({'title' => 'A'}) }
describe 'initialization' do
describe '.new' do
it 'initializes' do
schema_set = JSI::SchemaSet.new([schema_a])
assert_equal(1, schema_set.size)
end
it 'errors given non-schemas' do
err = assert_raises(JSI::Schema::NotASchemaError) { JSI::SchemaSet.new([3]) }
assert_equal("JSI::SchemaSet initialized with non-schema objects:\n3", err.message)
end
end
describe '.build' do
it 'initializes' do
schema_set = JSI::SchemaSet.build do |schemas|
schemas << schema_a
end
assert_equal(1, schema_set.size)
end
it 'errors given non-schemas' do
err = assert_raises(JSI::Schema::NotASchemaError) do
JSI::SchemaSet.build do |schemas|
schemas << 3
end
end
assert_equal("JSI::SchemaSet initialized with non-schema objects:\n3", err.message)
end
end
end
describe '#inspect' do
it 'inspects' do
inspect = JSI::SchemaSet[schema_a].inspect
assert_equal(%q(JSI::SchemaSet[#{<JSI (JSI::JSONSchemaOrgDraft06) Schema> "title" => "A"}]), inspect)
end
end
describe '#pretty_print' do
it 'pretty prints' do
pp = JSI::SchemaSet[schema_a].pretty_inspect
assert_equal(%q(JSI::SchemaSet[#{<JSI (JSI::JSONSchemaOrgDraft06) Schema> "title" => "A"}]), pp.chomp)
end
end
end

0 comments on commit a8a72e7

Please sign in to comment.