module JSI
class Schema::Ref
def initialize(ref, ref_schema: nil, schema_registry: nil)
raise(ArgumentError, "ref is not a string") unless ref.respond_to?(:to_str)
@ref = ref
@ref_uri = Util.uri(ref)
@ref_schema = ref_schema ? Schema.ensure_schema(ref_schema) : nil
@schema_registry = schema_registry || (ref_schema ? ref_schema.jsi_schema_registry : JSI.schema_registry)
@deref_schema = nil
end
attr_reader :ref
attr_reader :ref_uri
attr_reader :ref_schema
attr_reader(:schema_registry)
def deref_schema
return @deref_schema if @deref_schema
schema_resource_root = nil
check_schema_resource_root = -> {
unless schema_resource_root
raise(Schema::ReferenceError, [
"cannot find schema by ref: #{ref}",
("from: #{ref_schema.pretty_inspect.chomp}" if ref_schema),
].compact.join("\n"))
end
}
ref_uri_nofrag = ref_uri.merge(fragment: nil).freeze
if ref_uri_nofrag.empty?
unless ref_schema
raise(Schema::ReferenceError, [
"cannot find schema by ref: #{ref}",
"with no ref schema",
].join("\n"))
end
schema_resource_root = ref_schema.is_a?(MetaschemaNode::BootstrapSchema) ? nil : ref_schema.schema_resource_root
resolve_fragment_ptr = ref_schema.method(:resource_root_subschema)
else
if ref_uri_nofrag.absolute?
ref_abs_uri = ref_uri_nofrag
elsif ref_schema && ref_schema.jsi_resource_ancestor_uri
ref_abs_uri = ref_schema.jsi_resource_ancestor_uri.join(ref_uri_nofrag).freeze
else
ref_abs_uri = nil
end
if ref_abs_uri
unless schema_registry
raise(Schema::ReferenceError, [
"could not resolve remote ref with no schema_registry specified",
"ref URI: #{ref_uri.to_s}",
("from: #{ref_schema.pretty_inspect.chomp}" if ref_schema),
].compact.join("\n"))
end
schema_resource_root = schema_registry.find(ref_abs_uri)
end
unless schema_resource_root
if ref_schema && ref_schema.jsi_document.respond_to?(:to_hash) && ref_schema.jsi_document['schemas'].respond_to?(:to_hash)
ref_schema.jsi_document['schemas'].each do |k, v|
if Addressable::URI.parse(v['id']) == ref_uri_nofrag
schema_resource_root = ref_schema.resource_root_subschema(['schemas', k])
end
end
end
end
check_schema_resource_root.call
if schema_resource_root.is_a?(Schema)
resolve_fragment_ptr = schema_resource_root.method(:resource_root_subschema)
else
resolve_fragment_ptr = -> (ptr) { schema_resource_root.jsi_descendent_node(ptr) }
end
end
fragment = ref_uri.fragment
if fragment
begin
ptr_from_fragment = Ptr.from_fragment(fragment)
rescue Ptr::PointerSyntaxError
end
end
if ptr_from_fragment
begin
result_schema = resolve_fragment_ptr.call(ptr_from_fragment)
rescue Ptr::ResolutionError
raise(Schema::ReferenceError, [
"could not resolve pointer: #{ptr_from_fragment.pointer.inspect}",
("from: #{ref_schema.pretty_inspect.chomp}" if ref_schema),
("in schema resource root: #{schema_resource_root.pretty_inspect.chomp}" if schema_resource_root),
].compact.join("\n"))
end
elsif fragment.nil?
check_schema_resource_root.call
result_schema = schema_resource_root
else
check_schema_resource_root.call
result_schemas = schema_resource_root.jsi_anchor_subschemas(fragment)
if result_schemas.size == 1
result_schema = result_schemas.first
elsif result_schemas.size == 0
raise(Schema::ReferenceError, [
"could not find schema by fragment: #{fragment.inspect}",
"in schema resource root: #{schema_resource_root.pretty_inspect.chomp}",
].join("\n"))
else
raise(Schema::ReferenceError, [
"found multiple schemas for plain name fragment #{fragment.inspect}:",
*result_schemas.map { |s| s.pretty_inspect.chomp },
].join("\n"))
end
end
Schema.ensure_schema(result_schema, msg: "object identified by uri #{ref} is not a schema:")
return @deref_schema = result_schema
end
def inspect
-%Q(\#<#{self.class.name} #{ref}>)
end
alias_method :to_s, :inspect
def pretty_print(q)
q.text '#<'
q.text self.class.name
q.text ' '
q.text ref
q.text '>'
end
def jsi_fingerprint
{class: self.class, ref: ref, ref_schema: ref_schema}
end
include Util::FingerprintHash
end
end