Class: Lutaml::UmlRepository::Repository
- Inherits:
-
Object
- Object
- Lutaml::UmlRepository::Repository
- Includes:
- Deprecated
- Defined in:
- lib/lutaml/uml_repository/repository.rb,
lib/lutaml/uml_repository/repository/loader.rb,
lib/lutaml/uml_repository/repository/deprecated.rb
Overview
Repository provides a fully indexed, queryable in-memory representation of a UML model.
It wraps the existing [‘Lutaml::Uml::Document`](../../uml/document.rb) model and adds:
-
Package hierarchy with path-based navigation
-
Class index with qualified name lookups
-
Type resolution for attribute data types
-
Association tracking including ownership and navigability
-
Diagram metadata for visualization
-
Search capabilities across all model elements
Repository can be built from XMI files or loaded from pre-serialized LUR (LutaML UML Repository) packages for instant loading.
Direct Known Subclasses
Defined Under Namespace
Modules: Deprecated, Loader
Instance Attribute Summary collapse
-
#document ⇒ Lutaml::Uml::Document
readonly
The underlying UML document.
-
#indexes ⇒ Hash
readonly
The indexes for fast lookups.
-
#metadata ⇒ PackageMetadata?
readonly
The package metadata (if loaded from LUR).
Class Method Summary collapse
-
.from_file(path) ⇒ Repository
Auto-detect file type and load appropriately.
-
.from_file_cached(xmi_path, lur_path: nil) ⇒ Repository
Smart caching - use LUR if newer than XMI, otherwise rebuild.
-
.from_file_lazy(path) ⇒ LazyRepository
Auto-detect file type and load with lazy loading.
-
.from_package(lur_path) ⇒ Repository
Load a Repository from a LUR package file.
-
.from_package_lazy(lur_path) ⇒ LazyRepository
Load a Repository from a LUR package file with lazy loading.
-
.from_xmi(xmi_path, options = {}) ⇒ Repository
Build a Repository from an XMI file.
-
.from_xmi_lazy(xmi_path, _options = {}) ⇒ LazyRepository
Build a Repository from an XMI file with lazy index loading.
Instance Method Summary collapse
-
#all_attributes ⇒ Array<Lutaml::Uml::Attribute>
Get all attributes across all classes in the repository.
-
#all_classes ⇒ Array
Get all classes in the repository.
-
#all_diagrams ⇒ Array<Lutaml::Uml::Diagram>
Get all diagrams in the model.
-
#ancestors_of(class_or_qname) ⇒ Array
Get all ancestor classes up to the root.
-
#associations_index ⇒ Array<Lutaml::Uml::Association>
Get all associations as an array Collects from both document-level (XMI) and class-level (QEA/EA).
-
#associations_of(class_or_qname, options = {}) ⇒ Array<Lutaml::Uml::Association>
Get associations involving a class.
-
#classes_in_package(package_path, recursive: false) ⇒ Array
Get classes in a specific package.
-
#classes_index ⇒ Array
Get all classes (including datatypes and enums) as an array.
-
#descendants_of(class_or_qname, max_depth: nil) ⇒ Array
Get all descendant classes.
-
#diagrams_in_package(package_path) ⇒ Array<Lutaml::Uml::Diagram>
Get diagrams in a specific package.
-
#diagrams_index ⇒ Array<Lutaml::Uml::Diagram>
Get all diagrams as an array.
-
#export_to_package(output_path, options = {}) ⇒ void
Export this repository to a LUR package file.
-
#find_attribute(qualified_name) ⇒ Lutaml::Uml::Attribute?
Find an attribute by its qualified name.
-
#find_class(qualified_name, raise_on_error: false) ⇒ Lutaml::Uml::Class, ...
Find a class by its qualified name.
-
#find_classes_by_stereotype(stereotype) ⇒ Array
Find all classes with a specific stereotype.
-
#find_diagram(diagram_name) ⇒ Lutaml::Uml::Diagram?
Find a diagram by its name.
-
#find_package(path, raise_on_error: false) ⇒ Lutaml::Uml::Package, ...
Find a package by its path.
-
#initialize(document:, indexes: nil, metadata: nil, options: {}) ⇒ Repository
constructor
Initialize a new Repository.
-
#list_packages(path = "ModelRoot", recursive: false) ⇒ Array<Lutaml::Uml::Package>
List packages at a specific path.
-
#marshal_dump ⇒ Hash
private
Custom marshaling to exclude runtime-only query objects.
-
#marshal_load(data) ⇒ void
private
Restore from marshaled state.
-
#package_tree(path = "ModelRoot", max_depth: nil) ⇒ Hash?
Build a hierarchical tree structure of packages.
-
#packages_index ⇒ Array<Lutaml::Uml::Package>
Get all packages as an array (excluding root Document).
-
#qualified_name_for(obj) ⇒ Object
Get qualified name(key) by the object from @indexes.
-
#query {|QueryDSL::QueryBuilder| ... } ⇒ QueryDSL::QueryBuilder
Build a query using the Query DSL.
-
#query! {|QueryDSL::QueryBuilder| ... } ⇒ Array
Build and execute a query using the Query DSL.
-
#search(query, types: %i[class attribute association],, fields: [:name]) ⇒ Hash
Search for model elements by query string.
-
#statistics ⇒ Hash
Get comprehensive statistics about the repository.
-
#subtypes_of(class_or_qname, recursive: false) ⇒ Array
Get direct child classes (subtypes).
-
#supertype_of(class_or_qname) ⇒ Lutaml::Uml::Class?
Get the direct parent class (supertype).
-
#validate(verbose: false) ⇒ Validators::ValidationResult
Validate the repository for consistency and integrity.
Methods included from Deprecated
#export, #find_associations, #find_children, #find_diagrams, #search_classes
Constructor Details
#initialize(document:, indexes: nil, metadata: nil, options: {}) ⇒ Repository
Initialize a new Repository.
This is typically not called directly. Use [‘from_xmi`](#from_xmi) instead.
(if loaded from LUR)
60 61 62 63 64 65 66 67 |
# File 'lib/lutaml/uml_repository/repository.rb', line 60 def initialize(document:, indexes: nil, metadata: nil, options: {}) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength @document = document.freeze @indexes = indexes || IndexBuilder.build_all(document) @metadata = init_services(skip_queries: [:skip_queries]) freeze end |
Instance Attribute Details
#document ⇒ Lutaml::Uml::Document (readonly)
Returns The underlying UML document.
38 39 40 |
# File 'lib/lutaml/uml_repository/repository.rb', line 38 def document @document end |
#indexes ⇒ Hash (readonly)
Returns The indexes for fast lookups.
41 42 43 |
# File 'lib/lutaml/uml_repository/repository.rb', line 41 def indexes @indexes end |
#metadata ⇒ PackageMetadata? (readonly)
Returns The package metadata (if loaded from LUR).
44 45 46 |
# File 'lib/lutaml/uml_repository/repository.rb', line 44 def @metadata end |
Class Method Details
.from_file(path) ⇒ Repository
Auto-detect file type and load appropriately.
Detects whether the file is an XMI file (.xmi) or a LUR package (.lur) and loads it using the appropriate method.
122 123 124 125 126 127 128 129 130 |
# File 'lib/lutaml/uml_repository/repository.rb', line 122 def self.from_file(path) case File.extname(path).downcase when ".xmi" then from_xmi(path) when ".lur" then from_package(path) else raise ArgumentError, "Unknown file type: #{path}. Expected .xmi or .lur" end end |
.from_file_cached(xmi_path, lur_path: nil) ⇒ Repository
Smart caching - use LUR if newer than XMI, otherwise rebuild.
This method implements intelligent caching by checking if a LUR package exists and is newer than the source XMI file. If so, it loads from the cache. Otherwise, it builds from XMI and creates/updates the cache.
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/lutaml/uml_repository/repository.rb', line 149 def self.from_file_cached(xmi_path, lur_path: nil) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity lur_path ||= xmi_path.sub(/\.xmi$/i, ".lur") if File.exist?(lur_path) && File.mtime(lur_path) >= File.mtime(xmi_path) puts "Using cached LUR package: #{lur_path}" if $VERBOSE from_package(lur_path) else puts "Building repository from XMI..." if $VERBOSE repo = from_xmi(xmi_path) puts "Caching as LUR package: #{lur_path}" if $VERBOSE repo.export_to_package(lur_path) repo end end |
.from_file_lazy(path) ⇒ LazyRepository
Auto-detect file type and load with lazy loading.
Detects whether the file is an XMI file (.xmi) or a LUR package (.lur) and loads it using the appropriate lazy loading method.
199 200 201 202 203 204 205 206 207 |
# File 'lib/lutaml/uml_repository/repository.rb', line 199 def self.from_file_lazy(path) case File.extname(path).downcase when ".xmi" then from_xmi_lazy(path) when ".lur" then from_package_lazy(path) else raise ArgumentError, "Unknown file type: #{path}. Expected .xmi or .lur" end end |
.from_package(lur_path) ⇒ Repository
Load a Repository from a LUR package file.
171 172 173 |
# File 'lib/lutaml/uml_repository/repository.rb', line 171 def self.from_package(lur_path) PackageLoader.load(lur_path) end |
.from_package_lazy(lur_path) ⇒ LazyRepository
Load a Repository from a LUR package file with lazy loading.
This method loads the document without building indexes, deferring index creation until first access. Useful for very large models.
184 185 186 |
# File 'lib/lutaml/uml_repository/repository.rb', line 184 def self.from_package_lazy(lur_path) PackageLoader.load_document_only(lur_path) end |
.from_xmi(xmi_path, options = {}) ⇒ Repository
Build a Repository from an XMI file.
79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/lutaml/uml_repository/repository.rb', line 79 def self.from_xmi(xmi_path, = {}) document = Lutaml::Xmi::Parsers::Xml.parse(File.new(xmi_path)) # Build indexes indexes = IndexBuilder.build_all(document) # Optionally validate (placeholder for future validation logic) # if options[:validate] # validate_model(document, indexes) # end new(document: document, indexes: indexes, options: ) end |
.from_xmi_lazy(xmi_path, _options = {}) ⇒ LazyRepository
Build a Repository from an XMI file with lazy index loading.
This method creates a LazyRepository that builds indexes on-demand rather than upfront. Useful for very large models (1000+ classes) to reduce initial load time and memory usage.
105 106 107 108 109 |
# File 'lib/lutaml/uml_repository/repository.rb', line 105 def self.from_xmi_lazy(xmi_path, = {}) document = Lutaml::Xmi::Parsers::Xml.parse(File.new(xmi_path)) LazyRepository.new(document: document, lazy: true) end |
Instance Method Details
#all_attributes ⇒ Array<Lutaml::Uml::Attribute>
Get all attributes across all classes in the repository.
337 338 339 340 341 342 343 |
# File 'lib/lutaml/uml_repository/repository.rb', line 337 def all_attributes indexes[:qualified_names].flat_map do |_qname, entity| next [] unless entity.is_a?(Lutaml::Uml::Classifier) && entity.attributes entity.attributes end end |
#all_classes ⇒ Array
Get all classes in the repository
632 633 634 |
# File 'lib/lutaml/uml_repository/repository.rb', line 632 def all_classes classes_index end |
#all_diagrams ⇒ Array<Lutaml::Uml::Diagram>
Get all diagrams in the model.
468 469 470 |
# File 'lib/lutaml/uml_repository/repository.rb', line 468 def all_diagrams diagram_query.all end |
#ancestors_of(class_or_qname) ⇒ Array
Get all ancestor classes up to the root.
Returns ancestors in order from immediate parent to root.
406 407 408 |
# File 'lib/lutaml/uml_repository/repository.rb', line 406 def ancestors_of(class_or_qname) inheritance_query.ancestors(class_or_qname) end |
#associations_index ⇒ Array<Lutaml::Uml::Association>
Get all associations as an array Collects from both document-level (XMI) and class-level (QEA/EA)
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 |
# File 'lib/lutaml/uml_repository/repository.rb', line 588 def associations_index # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity # Use cached index if available (built by IndexBuilder) return @indexes[:associations].values if @indexes[:associations] # Fallback for edge cases: collect from document and classes seen = Set.new associations = [] (@document.associations || []).each do |assoc| if assoc.xmi_id && !seen.include?(assoc.xmi_id) seen << assoc.xmi_id associations << assoc end end classes_index.each do |klass| next unless (klass.is_a?(Lutaml::Uml::Class) || klass.is_a?(Lutaml::Uml::DataType)) && klass.associations klass.associations.each do |assoc| if assoc.xmi_id && !seen.include?(assoc.xmi_id) seen << assoc.xmi_id associations << assoc end end end associations end |
#associations_of(class_or_qname, options = {}) ⇒ Array<Lutaml::Uml::Association>
Get associations involving a class.
Return only navigable associations
438 439 440 |
# File 'lib/lutaml/uml_repository/repository.rb', line 438 def associations_of(class_or_qname, = {}) association_query.find_for_class(class_or_qname, ) end |
#classes_in_package(package_path, recursive: false) ⇒ Array
Get classes in a specific package.
365 366 367 |
# File 'lib/lutaml/uml_repository/repository.rb', line 365 def classes_in_package(package_path, recursive: false) class_query.in_package(package_path, recursive: recursive) end |
#classes_index ⇒ Array
Get all classes (including datatypes and enums) as an array
581 582 583 |
# File 'lib/lutaml/uml_repository/repository.rb', line 581 def classes_index @indexes[:qualified_names]&.values || [] end |
#descendants_of(class_or_qname, max_depth: nil) ⇒ Array
Get all descendant classes.
419 420 421 |
# File 'lib/lutaml/uml_repository/repository.rb', line 419 def descendants_of(class_or_qname, max_depth: nil) inheritance_query.descendants(class_or_qname, max_depth: max_depth) end |
#diagrams_in_package(package_path) ⇒ Array<Lutaml::Uml::Diagram>
Get diagrams in a specific package.
448 449 450 |
# File 'lib/lutaml/uml_repository/repository.rb', line 448 def diagrams_in_package(package_path) diagram_query.in_package(package_path) end |
#diagrams_index ⇒ Array<Lutaml::Uml::Diagram>
Get all diagrams as an array
624 625 626 |
# File 'lib/lutaml/uml_repository/repository.rb', line 624 def diagrams_index all_diagrams end |
#export_to_package(output_path, options = {}) ⇒ void
This method returns an undefined value.
Export this repository to a LUR package file.
240 241 242 |
# File 'lib/lutaml/uml_repository/repository.rb', line 240 def export_to_package(output_path, = {}) PackageExporter.new(self, ).export(output_path) end |
#find_attribute(qualified_name) ⇒ Lutaml::Uml::Attribute?
Find an attribute by its qualified name.
The qualified name format is “PackagePath::ClassName::attributeName”. Splits off the last segment as the attribute name, finds the containing class, then returns the matching attribute.
321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/lutaml/uml_repository/repository.rb', line 321 def find_attribute(qualified_name) class_qname, _, attr_name = qualified_name.rpartition("::") return nil if class_qname.empty? klass = class_query.find_by_qname(class_qname) return nil unless klass attrs = klass.attributes return nil unless attrs attrs.find { |a| a.name == attr_name } end |
#find_class(qualified_name, raise_on_error: false) ⇒ Lutaml::Uml::Class, ...
Find a class by its qualified name.
Lutaml::Uml::Enum, nil]
The class object, or nil if not found
304 305 306 307 308 309 |
# File 'lib/lutaml/uml_repository/repository.rb', line 304 def find_class(qualified_name, raise_on_error: false) result = class_query.find_by_qname(qualified_name) return result if result || !raise_on_error @error_handler.class_not_found_error(qualified_name) end |
#find_classes_by_stereotype(stereotype) ⇒ Array
Find all classes with a specific stereotype.
351 352 353 |
# File 'lib/lutaml/uml_repository/repository.rb', line 351 def find_classes_by_stereotype(stereotype) class_query.find_by_stereotype(stereotype) end |
#find_diagram(diagram_name) ⇒ Lutaml::Uml::Diagram?
Find a diagram by its name.
or nil if not found
459 460 461 |
# File 'lib/lutaml/uml_repository/repository.rb', line 459 def find_diagram(diagram_name) diagram_query.find_by_name(diagram_name) end |
#find_package(path, raise_on_error: false) ⇒ Lutaml::Uml::Package, ...
Find a package by its path.
255 256 257 258 259 260 |
# File 'lib/lutaml/uml_repository/repository.rb', line 255 def find_package(path, raise_on_error: false) result = package_query.find_by_path(path) return result if result || !raise_on_error @error_handler.package_not_found_error(path) end |
#list_packages(path = "ModelRoot", recursive: false) ⇒ Array<Lutaml::Uml::Package>
List packages at a specific path.
273 274 275 |
# File 'lib/lutaml/uml_repository/repository.rb', line 273 def list_packages(path = "ModelRoot", recursive: false) package_query.list(path, recursive: recursive) end |
#marshal_dump ⇒ Hash
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.
Custom marshaling to exclude runtime-only query objects
Only serializes the core data (document, indexes, and metadata), not the derived query service objects. This keeps serialized size minimal.
643 644 645 |
# File 'lib/lutaml/uml_repository/repository.rb', line 643 def marshal_dump { document: @document, indexes: @indexes, metadata: @metadata } end |
#marshal_load(data) ⇒ void
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.
This method returns an undefined value.
Restore from marshaled state
Reconstructs the repository from serialized document, indexes, and metadata, reinitializing all query services.
and :metadata
657 658 659 660 661 662 663 664 |
# File 'lib/lutaml/uml_repository/repository.rb', line 657 def marshal_load(data) @document = data[:document] @indexes = data[:indexes] @metadata = data[:metadata] init_services freeze end |
#package_tree(path = "ModelRoot", max_depth: nil) ⇒ Hash?
Build a hierarchical tree structure of packages.
287 288 289 |
# File 'lib/lutaml/uml_repository/repository.rb', line 287 def package_tree(path = "ModelRoot", max_depth: nil) package_query.tree(path, max_depth: max_depth) end |
#packages_index ⇒ Array<Lutaml::Uml::Package>
Get all packages as an array (excluding root Document)
575 576 577 |
# File 'lib/lutaml/uml_repository/repository.rb', line 575 def packages_index (@indexes[:package_paths]&.values || []).grep(Lutaml::Uml::Package) end |
#qualified_name_for(obj) ⇒ Object
Get qualified name(key) by the object from @indexes
618 619 620 |
# File 'lib/lutaml/uml_repository/repository.rb', line 618 def qualified_name_for(obj) @indexes[:qualified_names].key(obj) end |
#query {|QueryDSL::QueryBuilder| ... } ⇒ QueryDSL::QueryBuilder
Build a query using the Query DSL
Provides a fluent interface for building complex queries with method chaining, lazy evaluation, and composable filters.
551 552 553 554 555 |
# File 'lib/lutaml/uml_repository/repository.rb', line 551 def query(&block) builder = QueryDSL::QueryBuilder.new(self) block&.call(builder) builder end |
#query! {|QueryDSL::QueryBuilder| ... } ⇒ Array
Build and execute a query using the Query DSL
Same as [‘query`](#query) but executes immediately and returns results.
567 568 569 |
# File 'lib/lutaml/uml_repository/repository.rb', line 567 def query!(&) query(&).execute end |
#search(query, types: %i[class attribute association],, fields: [:name]) ⇒ Hash
Search for model elements by query string.
(:name, :documentation)
(default: [:name])
485 486 487 488 489 |
# File 'lib/lutaml/uml_repository/repository.rb', line 485 def search( query, types: %i[class attribute association], fields: [:name] ) search_query.search(query, types: types, fields: fields) end |
#statistics ⇒ Hash
Get comprehensive statistics about the repository.
Returns detailed metrics including package depths, class complexity, attribute distributions, and model quality metrics. Statistics are calculated once during initialization and cached.
504 505 506 |
# File 'lib/lutaml/uml_repository/repository.rb', line 504 def statistics @statistics end |
#subtypes_of(class_or_qname, recursive: false) ⇒ Array
Get direct child classes (subtypes).
392 393 394 |
# File 'lib/lutaml/uml_repository/repository.rb', line 392 def subtypes_of(class_or_qname, recursive: false) inheritance_query.subtypes(class_or_qname, recursive: recursive) end |
#supertype_of(class_or_qname) ⇒ Lutaml::Uml::Class?
Get the direct parent class (supertype).
377 378 379 |
# File 'lib/lutaml/uml_repository/repository.rb', line 377 def supertype_of(class_or_qname) inheritance_query.supertype(class_or_qname) end |
#validate(verbose: false) ⇒ Validators::ValidationResult
Validate the repository for consistency and integrity.
Performs comprehensive validation including:
-
Type reference validation
-
Generalization reference validation
-
Circular inheritance detection
-
Association reference validation
-
Multiplicity validation
526 527 528 529 |
# File 'lib/lutaml/uml_repository/repository.rb', line 526 def validate(verbose: false) Validators::RepositoryValidator.new(@document, @indexes).validate(verbose: verbose) end |