Class: Schemurai::Internal::SchemaGraph

Inherits:
Object
  • Object
show all
Defined in:
lib/schemurai/schema_graph.rb

Defined Under Namespace

Classes: Resource

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(schemas: {}, dialect: Dialect.resolve) ⇒ SchemaGraph

Returns a new instance of SchemaGraph.



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/schemurai/schema_graph.rb', line 84

def initialize(schemas: {}, dialect: Dialect.resolve)
  SchemaDomain.validate_registry!(schemas)
  @external_schemas = schemas.dup
  @indexed_external_schemas = nil
  @compiled_roots = {}.compare_by_identity
  @meta_schema_roots = {}
  @resources = {}
  @uri_registry = {}
  @nodes = []
  @nodes_by_document_location = nil
  @resolved_refs = nil
  @dynamic_anchors = {}
  # External schemas are indexed lazily, so their references are not yet
  # available to compile_node. Track conservatively from the caller.
  # Once required, dynamic scope tracking must remain enabled because all
  # evaluators sharing this graph may reach the dynamic reference.
  @dynamic_scope = !schemas.empty?
  @default_dialect = dialect
end

Instance Attribute Details

#nodes ⇒ Object (readonly)

Returns the value of attribute nodes.



82
83
84
# File 'lib/schemurai/schema_graph.rb', line 82

def nodes
  @nodes
end

#resources ⇒ Object (readonly)

Returns the value of attribute resources.



82
83
84
# File 'lib/schemurai/schema_graph.rb', line 82

def resources
  @resources
end

#uri_registry ⇒ Object (readonly)

Returns the value of attribute uri_registry.



82
83
84
# File 'lib/schemurai/schema_graph.rb', line 82

def uri_registry
  @uri_registry
end

Instance Method Details

#compile(schema, base_uri: nil, dialect: @default_dialect) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/schemurai/schema_graph.rb', line 104

def compile(schema, base_uri: nil, dialect: @default_dialect)
  SchemaDomain.validate!(schema)
  roots = (@compiled_roots[schema] ||= {})
  base_uri = base_uri.to_s
  cache_key = [base_uri, dialect]
  root = roots[cache_key]
  return root if root

  roots[cache_key] = compile_atomically do |changes|
    root_dialect = dialect_for(schema, dialect, changes)
    compile_document(schema, base_uri, root_dialect, changes)
  end
end

#dynamic_anchor(resource, name) ⇒ Object



143
144
145
# File 'lib/schemurai/schema_graph.rb', line 143

def dynamic_anchor(resource, name)
  @dynamic_anchors.dig(resource, name)
end

#dynamic_scope? ⇒ Boolean

Returns:

  • (Boolean)


147
148
149
# File 'lib/schemurai/schema_graph.rb', line 147

def dynamic_scope?
  @dynamic_scope
end

#make_shareable ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/schemurai/schema_graph.rb', line 151

def make_shareable
  return self if Ractor.shareable?(self)

  @external_schemas.each_key do |uri|
    index_external(strip_fragment(uri.to_s), @default_dialect)
  end

  index = 0
  while index < nodes.length
    node = nodes[index]
    schema = node.schema
    if schema.is_a?(Hash)
      ["$ref", "$recursiveRef", "$dynamicRef"].each do |keyword|
        resolve(node, schema[keyword]) if schema.key?(keyword)
      end
    end
    index += 1
  end

  @resolved_refs ||= {}
  Ractor.make_shareable(self)
end

#meta_schema_root(schema) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
# File 'lib/schemurai/schema_graph.rb', line 118

def meta_schema_root(schema)
  SchemaDomain.validate!(schema)
  uri = if schema.is_a?(Hash) && schema.key?("$schema")
    schema["$schema"].to_s
  else
    @default_dialect.uri
  end
  root = (@meta_schema_roots[uri] ||= compile({"$ref" => uri}))
  resolve(root, uri)
  root
end

#node_at(uri) ⇒ Object



253
254
255
# File 'lib/schemurai/schema_graph.rb', line 253

def node_at(uri)
  uri_registry[uri.to_s]
end

#resolve(node, reference) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/schemurai/schema_graph.rb', line 130

def resolve(node, reference)
  if (resolved = @resolved_refs&.[](node))&.key?(reference)
    return resolved[reference]
  end

  if Ractor.shareable?(self)
    raise ResolutionError, "reference #{reference.inspect} was not resolved before sharing"
  end

  target = resolve_uncached(node, reference)
  ((@resolved_refs ||= {})[node] ||= {})[reference] = target
end