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)
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 |
# File 'lib/lutaml/uml_repository/repository.rb', line 78 def initialize(document:, indexes: nil, metadata: nil, options: {}) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength @document = document.freeze @indexes = indexes || IndexBuilder.build_all(document) @metadata = # Initialize runtime query services (not serialized to LUR) # These are lightweight wrappers that operate on @document and @indexes unless [:skip_queries] @package_query = Queries::PackageQuery.new(@document, @indexes) @class_query = Queries::ClassQuery.new(@document, @indexes) @inheritance_query = Queries::InheritanceQuery.new( @document, @indexes ) @association_query = Queries::AssociationQuery.new( @document, @indexes ) @diagram_query = Queries::DiagramQuery.new(@document, @indexes) @search_query = Queries::SearchQuery.new(@document, @indexes) end # Initialize statistics calculator and cache result @statistics_calculator = StatisticsCalculator.new(@document, @indexes) @statistics = @statistics_calculator.calculate.freeze # Initialize error handler for helpful error messages @error_handler = ErrorHandler.new(self) freeze end |
Instance Attribute Details
#document ⇒ Lutaml::Uml::Document (readonly)
Returns The underlying UML document.
56 57 58 |
# File 'lib/lutaml/uml_repository/repository.rb', line 56 def document @document end |
#indexes ⇒ Hash (readonly)
Returns The indexes for fast lookups.
59 60 61 |
# File 'lib/lutaml/uml_repository/repository.rb', line 59 def indexes @indexes end |
#metadata ⇒ PackageMetadata? (readonly)
Returns The package metadata (if loaded from LUR).
62 63 64 |
# File 'lib/lutaml/uml_repository/repository.rb', line 62 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.
163 164 165 166 167 168 169 170 171 |
# File 'lib/lutaml/uml_repository/repository.rb', line 163 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.
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/lutaml/uml_repository/repository.rb', line 190 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.
240 241 242 243 244 245 246 247 248 |
# File 'lib/lutaml/uml_repository/repository.rb', line 240 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.
212 213 214 |
# File 'lib/lutaml/uml_repository/repository.rb', line 212 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.
225 226 227 |
# File 'lib/lutaml/uml_repository/repository.rb', line 225 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.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/lutaml/uml_repository/repository.rb', line 118 def self.from_xmi(xmi_path, = {}) # Parse XMI using Lutaml::Parser document = Lutaml::Parser.parse([File.new(xmi_path)]).first # 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.
145 146 147 148 149 150 |
# File 'lib/lutaml/uml_repository/repository.rb', line 145 def self.from_xmi_lazy(xmi_path, = {}) # Parse XMI using Lutaml::Parser document = Lutaml::Parser.parse([File.new(xmi_path)]).first 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.
378 379 380 381 382 383 384 |
# File 'lib/lutaml/uml_repository/repository.rb', line 378 def all_attributes indexes[:qualified_names].flat_map do |_qname, entity| next [] unless entity.respond_to?(:attributes) && entity.attributes entity.attributes end end |
#all_classes ⇒ Array
Get all classes in the repository
673 674 675 |
# File 'lib/lutaml/uml_repository/repository.rb', line 673 def all_classes classes_index end |
#all_diagrams ⇒ Array<Lutaml::Uml::Diagram>
Get all diagrams in the model.
509 510 511 |
# File 'lib/lutaml/uml_repository/repository.rb', line 509 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.
447 448 449 |
# File 'lib/lutaml/uml_repository/repository.rb', line 447 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)
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 |
# File 'lib/lutaml/uml_repository/repository.rb', line 629 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.respond_to?(:associations) && 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
479 480 481 |
# File 'lib/lutaml/uml_repository/repository.rb', line 479 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.
406 407 408 |
# File 'lib/lutaml/uml_repository/repository.rb', line 406 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
622 623 624 |
# File 'lib/lutaml/uml_repository/repository.rb', line 622 def classes_index @indexes[:qualified_names]&.values || [] end |
#descendants_of(class_or_qname, max_depth: nil) ⇒ Array
Get all descendant classes.
460 461 462 |
# File 'lib/lutaml/uml_repository/repository.rb', line 460 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.
489 490 491 |
# File 'lib/lutaml/uml_repository/repository.rb', line 489 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
665 666 667 |
# File 'lib/lutaml/uml_repository/repository.rb', line 665 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.
281 282 283 |
# File 'lib/lutaml/uml_repository/repository.rb', line 281 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.
362 363 364 365 366 367 368 369 370 371 372 373 |
# File 'lib/lutaml/uml_repository/repository.rb', line 362 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
345 346 347 348 349 350 |
# File 'lib/lutaml/uml_repository/repository.rb', line 345 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.
392 393 394 |
# File 'lib/lutaml/uml_repository/repository.rb', line 392 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
500 501 502 |
# File 'lib/lutaml/uml_repository/repository.rb', line 500 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.
296 297 298 299 300 301 |
# File 'lib/lutaml/uml_repository/repository.rb', line 296 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.
314 315 316 |
# File 'lib/lutaml/uml_repository/repository.rb', line 314 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.
684 685 686 |
# File 'lib/lutaml/uml_repository/repository.rb', line 684 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
698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 |
# File 'lib/lutaml/uml_repository/repository.rb', line 698 def marshal_load(data) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength @document = data[:document] @indexes = data[:indexes] @metadata = data[:metadata] # Reinitialize runtime query services @package_query = Queries::PackageQuery.new(@document, @indexes) @class_query = Queries::ClassQuery.new(@document, @indexes) @inheritance_query = Queries::InheritanceQuery.new(@document, @indexes) @association_query = Queries::AssociationQuery.new(@document, @indexes) @diagram_query = Queries::DiagramQuery.new(@document, @indexes) @search_query = Queries::SearchQuery.new(@document, @indexes) # Reinitialize helpers and cache statistics @statistics_calculator = StatisticsCalculator.new(@document, @indexes) @statistics = @statistics_calculator.calculate.freeze @error_handler = ErrorHandler.new(self) freeze end |
#package_tree(path = "ModelRoot", max_depth: nil) ⇒ Hash?
Build a hierarchical tree structure of packages.
328 329 330 |
# File 'lib/lutaml/uml_repository/repository.rb', line 328 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)
616 617 618 |
# File 'lib/lutaml/uml_repository/repository.rb', line 616 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
659 660 661 |
# File 'lib/lutaml/uml_repository/repository.rb', line 659 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.
592 593 594 595 596 |
# File 'lib/lutaml/uml_repository/repository.rb', line 592 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.
608 609 610 |
# File 'lib/lutaml/uml_repository/repository.rb', line 608 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])
526 527 528 529 530 |
# File 'lib/lutaml/uml_repository/repository.rb', line 526 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.
545 546 547 |
# File 'lib/lutaml/uml_repository/repository.rb', line 545 def statistics @statistics end |
#subtypes_of(class_or_qname, recursive: false) ⇒ Array
Get direct child classes (subtypes).
433 434 435 |
# File 'lib/lutaml/uml_repository/repository.rb', line 433 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).
418 419 420 |
# File 'lib/lutaml/uml_repository/repository.rb', line 418 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
567 568 569 570 |
# File 'lib/lutaml/uml_repository/repository.rb', line 567 def validate(verbose: false) Validators::RepositoryValidator.new(@document, @indexes).validate(verbose: verbose) end |