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)
77 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 |
# File 'lib/lutaml/uml_repository/repository.rb', line 77 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.
55 56 57 |
# File 'lib/lutaml/uml_repository/repository.rb', line 55 def document @document end |
#indexes ⇒ Hash (readonly)
Returns The indexes for fast lookups.
58 59 60 |
# File 'lib/lutaml/uml_repository/repository.rb', line 58 def indexes @indexes end |
#metadata ⇒ PackageMetadata? (readonly)
Returns The package metadata (if loaded from LUR).
61 62 63 |
# File 'lib/lutaml/uml_repository/repository.rb', line 61 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.
162 163 164 165 166 167 168 169 170 |
# File 'lib/lutaml/uml_repository/repository.rb', line 162 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.
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/lutaml/uml_repository/repository.rb', line 189 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.
239 240 241 242 243 244 245 246 247 |
# File 'lib/lutaml/uml_repository/repository.rb', line 239 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.
211 212 213 |
# File 'lib/lutaml/uml_repository/repository.rb', line 211 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.
224 225 226 |
# File 'lib/lutaml/uml_repository/repository.rb', line 224 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.
117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/lutaml/uml_repository/repository.rb', line 117 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.
144 145 146 147 148 149 |
# File 'lib/lutaml/uml_repository/repository.rb', line 144 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.
377 378 379 380 381 382 383 |
# File 'lib/lutaml/uml_repository/repository.rb', line 377 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
672 673 674 |
# File 'lib/lutaml/uml_repository/repository.rb', line 672 def all_classes classes_index end |
#all_diagrams ⇒ Array<Lutaml::Uml::Diagram>
Get all diagrams in the model.
508 509 510 |
# File 'lib/lutaml/uml_repository/repository.rb', line 508 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.
446 447 448 |
# File 'lib/lutaml/uml_repository/repository.rb', line 446 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)
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 655 |
# File 'lib/lutaml/uml_repository/repository.rb', line 628 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
478 479 480 |
# File 'lib/lutaml/uml_repository/repository.rb', line 478 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.
405 406 407 |
# File 'lib/lutaml/uml_repository/repository.rb', line 405 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
621 622 623 |
# File 'lib/lutaml/uml_repository/repository.rb', line 621 def classes_index @indexes[:qualified_names]&.values || [] end |
#descendants_of(class_or_qname, max_depth: nil) ⇒ Array
Get all descendant classes.
459 460 461 |
# File 'lib/lutaml/uml_repository/repository.rb', line 459 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.
488 489 490 |
# File 'lib/lutaml/uml_repository/repository.rb', line 488 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
664 665 666 |
# File 'lib/lutaml/uml_repository/repository.rb', line 664 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.
280 281 282 |
# File 'lib/lutaml/uml_repository/repository.rb', line 280 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.
361 362 363 364 365 366 367 368 369 370 371 372 |
# File 'lib/lutaml/uml_repository/repository.rb', line 361 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
344 345 346 347 348 349 |
# File 'lib/lutaml/uml_repository/repository.rb', line 344 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.
391 392 393 |
# File 'lib/lutaml/uml_repository/repository.rb', line 391 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
499 500 501 |
# File 'lib/lutaml/uml_repository/repository.rb', line 499 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.
295 296 297 298 299 300 |
# File 'lib/lutaml/uml_repository/repository.rb', line 295 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.
313 314 315 |
# File 'lib/lutaml/uml_repository/repository.rb', line 313 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.
683 684 685 |
# File 'lib/lutaml/uml_repository/repository.rb', line 683 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
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 |
# File 'lib/lutaml/uml_repository/repository.rb', line 697 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.
327 328 329 |
# File 'lib/lutaml/uml_repository/repository.rb', line 327 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)
615 616 617 |
# File 'lib/lutaml/uml_repository/repository.rb', line 615 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
658 659 660 |
# File 'lib/lutaml/uml_repository/repository.rb', line 658 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.
591 592 593 594 595 |
# File 'lib/lutaml/uml_repository/repository.rb', line 591 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.
607 608 609 |
# File 'lib/lutaml/uml_repository/repository.rb', line 607 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])
525 526 527 528 529 |
# File 'lib/lutaml/uml_repository/repository.rb', line 525 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.
544 545 546 |
# File 'lib/lutaml/uml_repository/repository.rb', line 544 def statistics @statistics end |
#subtypes_of(class_or_qname, recursive: false) ⇒ Array
Get direct child classes (subtypes).
432 433 434 |
# File 'lib/lutaml/uml_repository/repository.rb', line 432 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).
417 418 419 |
# File 'lib/lutaml/uml_repository/repository.rb', line 417 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
566 567 568 569 |
# File 'lib/lutaml/uml_repository/repository.rb', line 566 def validate(verbose: false) Validators::RepositoryValidator.new(@document, @indexes).validate(verbose: verbose) end |