Class: Synthra::Registry
- Inherits:
-
Object
- Object
- Synthra::Registry
- Defined in:
- lib/synthra/registry.rb
Overview
Registry for storing and managing multiple schemas
The Registry provides a central store for schema definitions, enabling:
- Loading multiple DSL files
- Cross-schema references (Ref(User.id))
- Schema lookup by name
- Enumeration of all schemas
When generating data with cross-schema references, pass the registry to the generate method to enable reference resolution.
Instance Method Summary collapse
-
#clear ⇒ void
Clear all registered schemas.
-
#each {|name, schema| ... } ⇒ Enumerator, void
Iterate over all schemas.
-
#extract_schema_nodes(parse_result) ⇒ Array<SchemaNode>
private
Extract schema nodes from parse result Handles both legacy (Array) and Simulyra (ProgramNode) syntax.
-
#initialize ⇒ Registry
constructor
Create a new empty registry.
-
#load_dir(dir, validate: Synthra.configuration.validate_paths_on_load) ⇒ Array<Schema>
Load schemas from all DSL files in a directory.
-
#load_file(path, validate: Synthra.configuration.validate_paths_on_load) ⇒ Array<Schema>
Load schemas from a DSL file.
- #load_file_unsafe(path) ⇒ Object private
-
#load_string(source, validate: Synthra.configuration.validate_paths_on_load) ⇒ Array<Schema>
Load schemas from a DSL string.
-
#names ⇒ Array<String>
Get all schema names.
-
#register_schema(name, schema) ⇒ Schema
Register an already-built Schema object under a name.
-
#schema(name) ⇒ Schema
Get a schema by name.
-
#schema?(name) ⇒ Boolean
Check if a schema exists.
-
#schemas ⇒ Hash<String, Schema>
Get all registered schemas as a hash.
-
#size ⇒ Integer
(also: #count, #length)
Get the number of registered schemas.
-
#unregister(name) ⇒ Schema?
Unregister a schema by name.
-
#validate_paths ⇒ Array<Errors::PathError>
Validate all copy() paths in the registry.
-
#validate_paths! ⇒ true
Validate all copy() paths, raising on first error.
Constructor Details
#initialize ⇒ Registry
Create a new empty registry
68 69 70 71 72 73 74 |
# File 'lib/synthra/registry.rb', line 68 def initialize @schemas = {} @mutex = Mutex.new @ast_cache = {} @cache_mutex = Mutex.new @max_cache_size = Synthra.configuration.registry_cache_size end |
Instance Method Details
#clear ⇒ void
This method returns an undefined value.
Clear all registered schemas
Removes all schemas from the registry.
418 419 420 421 422 423 424 425 |
# File 'lib/synthra/registry.rb', line 418 def clear @mutex.synchronize do @schemas.clear @cache_mutex.synchronize do @ast_cache.clear end end end |
#each {|name, schema| ... } ⇒ Enumerator, void
Iterate over all schemas
Yields each schema name and schema object to the block.
473 474 475 476 477 |
# File 'lib/synthra/registry.rb', line 473 def each(&block) @mutex.synchronize do @schemas.each(&block) end end |
#extract_schema_nodes(parse_result) ⇒ Array<SchemaNode> (private)
Extract schema nodes from parse result Handles both legacy (Array) and Simulyra (ProgramNode) syntax
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/synthra/registry.rb', line 291 def extract_schema_nodes(parse_result) if parse_result.is_a?(Parser::AST::ProgramNode) # Simulyra DSL syntax - combine all schemas all_schemas = [] all_schemas.concat(parse_result.schemas) if parse_result.schemas all_schemas.concat(parse_result.legacy_schemas) if parse_result.legacy_schemas all_schemas elsif parse_result.is_a?(Array) # Legacy syntax - already an array of schema nodes parse_result else # Single node - wrap in array [parse_result].compact end end |
#load_dir(dir, validate: Synthra.configuration.validate_paths_on_load) ⇒ Array<Schema>
Load schemas from all DSL files in a directory
Loads and parses all .dsl files in the specified directory, adding all schemas defined in them to the registry. Existing schemas with the same name will be overwritten.
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/synthra/registry.rb', line 207 def load_dir(dir, validate: Synthra.configuration.validate_paths_on_load) schemas = @mutex.synchronize do # Validate and expand directory path dir = File.(dir) raise Errno::ENOENT, dir unless Dir.exist?(dir) # Find all .dsl files pattern = File.join(dir, "*.dsl") files = Dir.glob(pattern) # Load each file all_schemas = [] files.each do |file| # Call load_file_unsafe to avoid double synchronization (we're already in @mutex) all_schemas.concat(load_file_unsafe(file)) end all_schemas end # Validate paths outside mutex to avoid deadlock validate_paths! if validate schemas end |
#load_file(path, validate: Synthra.configuration.validate_paths_on_load) ⇒ Array<Schema>
Load schemas from a DSL file
Reads and parses the DSL file, adding all schemas defined in it to the registry. Existing schemas with the same name will be overwritten.
106 107 108 109 110 111 112 113 114 115 |
# File 'lib/synthra/registry.rb', line 106 def load_file(path, validate: Synthra.configuration.validate_paths_on_load) schemas = @mutex.synchronize do load_file_unsafe(path) end # Validate paths outside mutex to avoid deadlock validate_paths! if validate schemas end |
#load_file_unsafe(path) ⇒ Object (private)
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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/synthra/registry.rb', line 119 def load_file_unsafe(path) # Check cache first cache_key = File.(path) mtime = File.mtime(path) if File.exist?(path) cached = @cache_mutex.synchronize do cache_entry = @ast_cache[cache_key] if cache_entry && cache_entry[:mtime] == mtime # Update access time for LRU tracking cache_entry[:accessed_at] = Time.now cache_entry[:ast_nodes] else nil end end if cached # Use cached AST. Normalize through extract_schema_nodes so the new Simulyra # (ProgramNode) syntax works here too — load_string already does this; load_file # used to blindly #map the raw parse result and broke on every v2 .dsl file. extract_schema_nodes(cached).map do |node| schema = node.to_schema @schemas[schema.name] = schema schema end else # Parse and cache content = File.read(path) parser = Parser::Parser.new ast_nodes = parser.parse(content) # Cache the AST with LRU eviction @cache_mutex.synchronize do # Limit cache size using LRU (Least Recently Used) eviction if @ast_cache.size >= @max_cache_size # Remove least recently used entry oldest_key = @ast_cache.min_by { |_, v| v[:accessed_at] || Time.at(0) }[0] @ast_cache.delete(oldest_key) end @ast_cache[cache_key] = { ast_nodes: ast_nodes, mtime: mtime, accessed_at: Time.now } end extract_schema_nodes(ast_nodes).map do |node| schema = node.to_schema @schemas[schema.name] = schema schema end end end |
#load_string(source, validate: Synthra.configuration.validate_paths_on_load) ⇒ Array<Schema>
Load schemas from a DSL string
Parses the DSL source code and adds all schemas to the registry. Useful for testing or when DSL content is dynamically generated.
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/synthra/registry.rb', line 262 def load_string(source, validate: Synthra.configuration.validate_paths_on_load) schemas = @mutex.synchronize do parser = Parser::Parser.new parse_result = parser.parse(source) # Handle both legacy (array of SchemaNode) and Simulyra (ProgramNode) syntax ast_nodes = extract_schema_nodes(parse_result) ast_nodes.map do |node| schema = node.to_schema @schemas[schema.name] = schema schema end end # Validate paths outside mutex to avoid deadlock validate_paths! if validate schemas end |
#names ⇒ Array<String>
Get all schema names
488 489 490 491 492 |
# File 'lib/synthra/registry.rb', line 488 def names @mutex.synchronize do @schemas.keys end end |
#register_schema(name, schema) ⇒ Schema
Register an already-built Schema object under a name.
Counterpart to load_string/load_file for callers that construct a Schema directly (OpenAPI import, ActiveRecord inference, the CLI/API server). Was referenced in those paths but never defined, so every one of them raised NoMethodError.
348 349 350 351 352 |
# File 'lib/synthra/registry.rb', line 348 def register_schema(name, schema) @mutex.synchronize do @schemas[name.to_s] = schema end end |
#schema(name) ⇒ Schema
Get a schema by name
Retrieves a schema from the registry. Raises KeyError if not found.
330 331 332 333 334 335 336 |
# File 'lib/synthra/registry.rb', line 330 def schema(name) @mutex.synchronize do @schemas.fetch(name.to_s) do raise KeyError, "Schema not found: #{name}" end end end |
#schema?(name) ⇒ Boolean
Check if a schema exists
383 384 385 386 387 |
# File 'lib/synthra/registry.rb', line 383 def schema?(name) @mutex.synchronize do @schemas.key?(name.to_s) end end |
#schemas ⇒ Hash<String, Schema>
Get all registered schemas as a hash
Returns a copy of the internal schemas hash to prevent modification.
366 367 368 369 370 |
# File 'lib/synthra/registry.rb', line 366 def schemas @mutex.synchronize do @schemas.dup end end |
#size ⇒ Integer Also known as: count, length
Get the number of registered schemas
437 438 439 440 441 |
# File 'lib/synthra/registry.rb', line 437 def size @mutex.synchronize do @schemas.size end end |
#unregister(name) ⇒ Schema?
Unregister a schema by name
Removes a single schema from the registry.
400 401 402 403 404 |
# File 'lib/synthra/registry.rb', line 400 def unregister(name) @mutex.synchronize do @schemas.delete(name.to_s) end end |
#validate_paths ⇒ Array<Errors::PathError>
Validate all copy() paths in the registry
Checks that all copy() type references point to valid paths in the schema structure. This catches typos and invalid references at load time rather than at generation time.
508 509 510 511 512 513 |
# File 'lib/synthra/registry.rb', line 508 def validate_paths # Don't hold mutex during validation - the validator caches schemas # to avoid deadlock validator = Validator::PathValidator.new(self) validator.validate end |
#validate_paths! ⇒ true
Validate all copy() paths, raising on first error
526 527 528 529 530 531 |
# File 'lib/synthra/registry.rb', line 526 def validate_paths! # Don't hold mutex during validation - the validator caches schemas # to avoid deadlock validator = Validator::PathValidator.new(self) validator.validate! end |