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.
161 162 163 164 165 166 167 168 169 |
# File 'lib/lutaml/uml_repository/repository.rb', line 161 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.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/lutaml/uml_repository/repository.rb', line 188 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.
238 239 240 241 242 243 244 245 246 |
# File 'lib/lutaml/uml_repository/repository.rb', line 238 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.
210 211 212 |
# File 'lib/lutaml/uml_repository/repository.rb', line 210 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.
223 224 225 |
# File 'lib/lutaml/uml_repository/repository.rb', line 223 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 |
# File 'lib/lutaml/uml_repository/repository.rb', line 118 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.
144 145 146 147 148 |
# File 'lib/lutaml/uml_repository/repository.rb', line 144 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.
376 377 378 379 380 381 382 |
# File 'lib/lutaml/uml_repository/repository.rb', line 376 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
671 672 673 |
# File 'lib/lutaml/uml_repository/repository.rb', line 671 def all_classes classes_index end |
#all_diagrams ⇒ Array<Lutaml::Uml::Diagram>
Get all diagrams in the model.
507 508 509 |
# File 'lib/lutaml/uml_repository/repository.rb', line 507 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.
445 446 447 |
# File 'lib/lutaml/uml_repository/repository.rb', line 445 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)
627 628 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 |
# File 'lib/lutaml/uml_repository/repository.rb', line 627 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
477 478 479 |
# File 'lib/lutaml/uml_repository/repository.rb', line 477 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.
404 405 406 |
# File 'lib/lutaml/uml_repository/repository.rb', line 404 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
620 621 622 |
# File 'lib/lutaml/uml_repository/repository.rb', line 620 def classes_index @indexes[:qualified_names]&.values || [] end |
#descendants_of(class_or_qname, max_depth: nil) ⇒ Array
Get all descendant classes.
458 459 460 |
# File 'lib/lutaml/uml_repository/repository.rb', line 458 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.
487 488 489 |
# File 'lib/lutaml/uml_repository/repository.rb', line 487 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
663 664 665 |
# File 'lib/lutaml/uml_repository/repository.rb', line 663 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.
279 280 281 |
# File 'lib/lutaml/uml_repository/repository.rb', line 279 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.
360 361 362 363 364 365 366 367 368 369 370 371 |
# File 'lib/lutaml/uml_repository/repository.rb', line 360 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
343 344 345 346 347 348 |
# File 'lib/lutaml/uml_repository/repository.rb', line 343 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.
390 391 392 |
# File 'lib/lutaml/uml_repository/repository.rb', line 390 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
498 499 500 |
# File 'lib/lutaml/uml_repository/repository.rb', line 498 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.
294 295 296 297 298 299 |
# File 'lib/lutaml/uml_repository/repository.rb', line 294 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.
312 313 314 |
# File 'lib/lutaml/uml_repository/repository.rb', line 312 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.
682 683 684 |
# File 'lib/lutaml/uml_repository/repository.rb', line 682 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
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 |
# File 'lib/lutaml/uml_repository/repository.rb', line 696 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.
326 327 328 |
# File 'lib/lutaml/uml_repository/repository.rb', line 326 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)
614 615 616 |
# File 'lib/lutaml/uml_repository/repository.rb', line 614 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
657 658 659 |
# File 'lib/lutaml/uml_repository/repository.rb', line 657 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.
590 591 592 593 594 |
# File 'lib/lutaml/uml_repository/repository.rb', line 590 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.
606 607 608 |
# File 'lib/lutaml/uml_repository/repository.rb', line 606 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])
524 525 526 527 528 |
# File 'lib/lutaml/uml_repository/repository.rb', line 524 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.
543 544 545 |
# File 'lib/lutaml/uml_repository/repository.rb', line 543 def statistics @statistics end |
#subtypes_of(class_or_qname, recursive: false) ⇒ Array
Get direct child classes (subtypes).
431 432 433 |
# File 'lib/lutaml/uml_repository/repository.rb', line 431 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).
416 417 418 |
# File 'lib/lutaml/uml_repository/repository.rb', line 416 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
565 566 567 568 |
# File 'lib/lutaml/uml_repository/repository.rb', line 565 def validate(verbose: false) Validators::RepositoryValidator.new(@document, @indexes).validate(verbose: verbose) end |