Class: JSI::Schema::Ref

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

Overview

A JSI::Schema::Ref is a reference to a schema identified by a URI, typically from a $ref keyword of a schema.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ref, ref_schema: nil, schema_registry: nil) ⇒ Ref

Returns a new instance of Ref.

Parameters:

  • ref (String)

    A reference URI - typically the $ref value of the ref_schema

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

    A schema from which the reference originated.

    If the ref URI consists of only a fragment, it is resolved from the ref_schema's JSI::Schema#schema_resource_root. Otherwise the resource is found in the ref_schema's #jsi_schema_registry (and any fragment is resolved from there).

  • schema_registry (SchemaRegistry) (defaults to: nil)

    The registry in which the resource this ref refers to will be found. This should only be specified in the absence of a ref_schema. If neither is specified, JSI.schema_registry is used.

Raises:

  • (ArgumentError)


16
17
18
19
20
21
22
23
# File 'lib/jsi/schema/ref.rb', line 16

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

Instance Attribute Details

#refString (readonly)

Returns:

  • (String)


26
27
28
# File 'lib/jsi/schema/ref.rb', line 26

def ref
  @ref
end

#ref_schemaSchema? (readonly)

Returns:



32
33
34
# File 'lib/jsi/schema/ref.rb', line 32

def ref_schema
  @ref_schema
end

#ref_uriAddressable::URI (readonly)

Returns:

  • (Addressable::URI)


29
30
31
# File 'lib/jsi/schema/ref.rb', line 29

def ref_uri
  @ref_uri
end

#schema_registrySchemaRegistry? (readonly)

Returns:



35
36
37
# File 'lib/jsi/schema/ref.rb', line 35

def schema_registry
  @schema_registry
end

Instance Method Details

#deref_schemaJSI::Schema

finds the schema this ref points to

Returns:

Raises:



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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/jsi/schema/ref.rb', line 41

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

    # 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).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
      # 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 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
      # 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

Returns:

  • (String)


160
161
162
# File 'lib/jsi/schema/ref.rb', line 160

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

#jsi_fingerprintObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

see Util::Private::FingerprintHash



180
181
182
# File 'lib/jsi/schema/ref.rb', line 180

def jsi_fingerprint
  {class: self.class, ref: ref, ref_schema: ref_schema}.freeze
end

#pretty_print(q)

This method returns an undefined value.

pretty-prints a representation of self to the given printer



170
171
172
173
174
175
176
# File 'lib/jsi/schema/ref.rb', line 170

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

#to_sObject



164
165
166
# File 'lib/jsi/schema/ref.rb', line 164

def to_s
  inspect
end