Class: JSI::Schema::Ref

Inherits:
Object
  • Object
show all
Includes:
Util::FingerprintHash
Defined in:
lib/jsi/schema/ref.rb

Overview

JSI::Schema::Ref is a reference to another schema (the result of #deref_schema), resolved using a ref URI from a ref schema (the ref URI typically the contents of the ref_schema's "$ref" keyword)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ref, ref_schema = nil) ⇒ Ref

Returns a new instance of Ref.

Parameters:

  • ref (String)

    a reference URI

  • ref_schema (JSI::Schema) (defaults to: nil)

    a schema from which the reference originated

Raises:

  • (ArgumentError)


9
10
11
12
13
14
# File 'lib/jsi/schema/ref.rb', line 9

def initialize(ref, ref_schema = nil)
  raise(ArgumentError, "ref is not a string") unless ref.respond_to?(:to_str)
  @ref = ref
  @ref_uri = Addressable::URI.parse(ref)
  @ref_schema = ref_schema ? Schema.ensure_schema(ref_schema) : nil
end

Instance Attribute Details

#refObject (readonly)

Returns the value of attribute ref.



16
17
18
# File 'lib/jsi/schema/ref.rb', line 16

def ref
  @ref
end

#ref_schemaObject (readonly)

Returns the value of attribute ref_schema.



20
21
22
# File 'lib/jsi/schema/ref.rb', line 20

def ref_schema
  @ref_schema
end

#ref_uriObject (readonly)

Returns the value of attribute ref_uri.



18
19
20
# File 'lib/jsi/schema/ref.rb', line 18

def ref_uri
  @ref_uri
end

Instance Method Details

#deref_schemaJSI::Schema

finds the schema this ref points to

Returns:

Raises:



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/jsi/schema/ref.rb', line 26

def deref_schema
  return @deref_schema if instance_variable_defined?(:@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)

  if ref_uri_nofrag.empty?
    unless ref_schema
      raise(Schema::ReferenceError, [
        "cannot find schema by ref: #{ref}",
        "with no ref schema",
      ].join("\n"))
    end

    # the URI only consists of a fragment (or is empty).
    # for a fragment pointer, resolve using Schema#resource_root_subschema on the ref_schema.
    # for a fragment anchor, bootstrap does not support anchors; otherwise use the ref_schema's schema_resource_root.
    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
    # find the schema_resource_root from the non-fragment URI. we will resolve any fragment, either pointer or anchor, from there.

    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)
    else
      ref_abs_uri = nil
    end
    if ref_abs_uri
      schema_resource_root = JSI.schema_registry.find(ref_abs_uri)
    end

    unless schema_resource_root
      # HAX for how google does refs and ids
      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_key do |k|
          if Addressable::URI.parse(ref_schema.jsi_document['schemas'][k]['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
      # Note: Schema#resource_root_subschema will reinstantiate nonschemas as schemas.
      # not implemented for remote refs when the schema_resource_root is not a schema.
      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

    # find an anchor that resembles the fragment
    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

#inspectString Also known as: to_s

Returns:

  • (String)


138
139
140
# File 'lib/jsi/schema/ref.rb', line 138

def inspect
  %Q(\#<#{self.class.name} #{ref}>)
end

#pretty_print(q) ⇒ void

This method returns an undefined value.

pretty-prints a representation of self to the given printer



146
147
148
149
150
151
152
# File 'lib/jsi/schema/ref.rb', line 146

def pretty_print(q)
  q.text '#<'
  q.text self.class.name
  q.text ' '
  q.text ref
  q.text '>'
end