class Schematrix::DocumentStore
Loads and caches schema documents, and resolves locator URIs to the node they point at. Each file is read and parsed at most once. Fragments that are plain names (no leading slash) resolve through the documentโs $anchor index instead of the JSON pointer walk. Remote http(s) locators never touch the network: they resolve through the configured schema directories, which mirror the URI as host/path below the directory, optionally carrying one of the known file extensions.
Constants
- REMOTE_EXTENSIONS
-
File extensions probed for a mapped remote URI, in order: the exact mirrored path first, then common schema file extensions.
- SCHEMA_CLASSES
-
Ruby classes a parsed document may have to be a schema at all.
- SUPPORTED_DIALECTS
-
Dialects this tool targets: the 2020-12 core metaschema, http and https spellings, with or without a trailing empty fragment. A document naming any other dialect raises
UnsupportedDialectErrorat load time; a document without a header is read as 2020-12.
Public Class Methods
Source
# File lib/schematrix/document_store.rb, line 53 def initialize(schema_dirs: []) @schema_dirs = schema_dirs.map { |dir| File.expand_path(dir) } @documents = {} @anchors = {} @dynamic_anchors = {} @ignored = {} end
schema_dirs lists the directories remote URIs resolve through, in search order. An empty list means remote references fail loudly instead of resolving.
Public Instance Methods
Source
# File lib/schematrix/document_store.rb, line 98 def canonicalize(uri) return uri unless anchor_name?(uri) pointer = anchor_pointers(document(uri), uri)[uri.fragment] if pointer.nil? raise UnresolvableLocatorError, "No $anchor named #{uri.fragment} in #{uri}" end rewritten = uri.dup rewritten.fragment = pointer rewritten end
Rewrites plain-name fragments (no leading slash) to the JSON pointer of the schema carrying that $anchor, so every consumer of the locator (class names, edges, cycle guards) sees the pointer form. Pointer fragments pass through unchanged. Raises UnresolvableLocatorError when the document names no such anchor.
Source
# File lib/schematrix/document_store.rb, line 87 def document_key(uri) key = uri.dup key.fragment = nil key.to_s end
Identity of the document a locator points into: the URI with the fragment stripped. Every locator into the same document shares one cache key this way, and documents on different hosts (or schemes) never collide on a shared path.
# File lib/schematrix/document_store.rb, line 116 def last_dynamic_anchor_locator(uri, name) anchors = @dynamic_anchors[document_key(uri)] ||= begin index = {} each_node(document(uri)) do |node, path| anchor = node["$dynamicAnchor"] index[anchor] = path if anchor end index end segments = anchors[name] return nil if segments.nil? locator = uri.dup locator.fragment = "/#{segments.map { |token| escape(token) }.join("/")}" locator end
The locator of the last schema carrying a $dynamicAnchor with the given name, in document order. nil when the document names no such anchor. This is the fallback resolution for $dynamicRef (the static approximation lives in Visitor).
Source
# File lib/schematrix/document_store.rb, line 66 def node_at(uri) uri = canonicalize(uri) walk_pointer(document(uri), uri) end
Returns the schema node the given URI points at: the document is loaded (and validated) on first access, the $anchor form of the fragment is rewritten to its pointer, and the JSON pointer is walked. Raises UnresolvableLocatorError when the pointer names nothing.
# File lib/schematrix/document_store.rb, line 74 def note_ignored(key, keyword, locator) seen = (@ignored[key] ||= Set.new) return unless seen.add?(keyword) Schematrix.logger&.debug( "Ignoring #{keyword} at #{locator}: no codegen representation", ) end
Registry hook for keywords with no codegen representation: each one is logged once per document, at debug level. The key identifies a document (see document_key).