Class: Lutaml::UmlRepository::Repository
- Inherits:
-
Object
- Object
- Lutaml::UmlRepository::Repository
- Defined in:
- lib/lutaml/uml_repository/repository.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
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_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(output_path, options = {}) ⇒ Object deprecated Deprecated.
-
#export_to_package(output_path, options = {}) ⇒ void
Export this repository to a LUR package file.
-
#find_associations(class_or_qname, options = {}) ⇒ Object
deprecated
Deprecated.
Use #associations_of
-
#find_children(class_or_qname, recursive: false) ⇒ Object
deprecated
Deprecated.
Use #subtypes_of
-
#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_diagrams(package_path) ⇒ Object deprecated Deprecated.
-
#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(pattern, types: %i[class attribute association],, fields: [:name]) ⇒ Hash
Search for model elements by regex pattern.
-
#search_classes(query_string) ⇒ Object
deprecated
Deprecated.
Use #search with types filter
-
#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.
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)
74 75 76 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 |
# File 'lib/lutaml/uml_repository/repository.rb', line 74 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.
52 53 54 |
# File 'lib/lutaml/uml_repository/repository.rb', line 52 def document @document end |
#indexes ⇒ Hash (readonly)
Returns The indexes for fast lookups.
55 56 57 |
# File 'lib/lutaml/uml_repository/repository.rb', line 55 def indexes @indexes end |
#metadata ⇒ PackageMetadata? (readonly)
Returns The package metadata (if loaded from LUR).
58 59 60 |
# File 'lib/lutaml/uml_repository/repository.rb', line 58 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.
159 160 161 162 163 164 165 166 167 |
# File 'lib/lutaml/uml_repository/repository.rb', line 159 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.
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/lutaml/uml_repository/repository.rb', line 186 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.
236 237 238 239 240 241 242 243 244 |
# File 'lib/lutaml/uml_repository/repository.rb', line 236 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.
208 209 210 |
# File 'lib/lutaml/uml_repository/repository.rb', line 208 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.
221 222 223 |
# File 'lib/lutaml/uml_repository/repository.rb', line 221 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.
114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/lutaml/uml_repository/repository.rb', line 114 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.
141 142 143 144 145 146 |
# File 'lib/lutaml/uml_repository/repository.rb', line 141 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_classes ⇒ Array
Get all classes in the repository
655 656 657 |
# File 'lib/lutaml/uml_repository/repository.rb', line 655 def all_classes classes_index end |
#all_diagrams ⇒ Array<Lutaml::Uml::Diagram>
Get all diagrams in the model.
471 472 473 |
# File 'lib/lutaml/uml_repository/repository.rb', line 471 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.
409 410 411 |
# File 'lib/lutaml/uml_repository/repository.rb', line 409 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)
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 |
# File 'lib/lutaml/uml_repository/repository.rb', line 615 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 associations = [] # Document-level associations (XMI format) associations.concat(@document.associations) if @document.associations # Class-level associations (QEA/EA format) classes_index.each do |klass| next unless klass.respond_to?(:associations) && klass.associations klass.associations.each do |assoc| # Avoid duplicates - check xmi_id next if associations.any? { |a| a.xmi_id == assoc.xmi_id } associations << assoc end end associations end |
#associations_of(class_or_qname, options = {}) ⇒ Array<Lutaml::Uml::Association>
Get associations involving a class.
Return only navigable associations
441 442 443 |
# File 'lib/lutaml/uml_repository/repository.rb', line 441 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.
368 369 370 |
# File 'lib/lutaml/uml_repository/repository.rb', line 368 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
608 609 610 |
# File 'lib/lutaml/uml_repository/repository.rb', line 608 def classes_index @indexes[:qualified_names]&.values || [] end |
#descendants_of(class_or_qname, max_depth: nil) ⇒ Array
Get all descendant classes.
422 423 424 |
# File 'lib/lutaml/uml_repository/repository.rb', line 422 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.
451 452 453 |
# File 'lib/lutaml/uml_repository/repository.rb', line 451 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
647 648 649 |
# File 'lib/lutaml/uml_repository/repository.rb', line 647 def diagrams_index all_diagrams end |
#export(output_path, options = {}) ⇒ Object
DEPRECATED: Use export_to_package instead
685 686 687 |
# File 'lib/lutaml/uml_repository/repository.rb', line 685 def export(output_path, = {}) export_to_package(output_path, ) end |
#export_to_package(output_path, options = {}) ⇒ void
This method returns an undefined value.
Export this repository to a LUR package file.
277 278 279 |
# File 'lib/lutaml/uml_repository/repository.rb', line 277 def export_to_package(output_path, = {}) PackageExporter.new(self, ).export(output_path) end |
#find_associations(class_or_qname, options = {}) ⇒ Object
Use #associations_of
DEPRECATED: Use associations_of instead
673 674 675 |
# File 'lib/lutaml/uml_repository/repository.rb', line 673 def find_associations(class_or_qname, = {}) associations_of(class_or_qname, ) end |
#find_children(class_or_qname, recursive: false) ⇒ Object
Use #subtypes_of
DEPRECATED: Use subtypes_of instead
667 668 669 |
# File 'lib/lutaml/uml_repository/repository.rb', line 667 def find_children(class_or_qname, recursive: false) subtypes_of(class_or_qname, recursive: recursive) 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
341 342 343 344 345 346 |
# File 'lib/lutaml/uml_repository/repository.rb', line 341 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.
354 355 356 |
# File 'lib/lutaml/uml_repository/repository.rb', line 354 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
462 463 464 |
# File 'lib/lutaml/uml_repository/repository.rb', line 462 def find_diagram(diagram_name) diagram_query.find_by_name(diagram_name) end |
#find_diagrams(package_path) ⇒ Object
DEPRECATED: Use diagrams_in_package or all_diagrams instead
679 680 681 |
# File 'lib/lutaml/uml_repository/repository.rb', line 679 def find_diagrams(package_path) diagrams_in_package(package_path) end |
#find_package(path, raise_on_error: false) ⇒ Lutaml::Uml::Package, ...
Find a package by its path.
292 293 294 295 296 297 |
# File 'lib/lutaml/uml_repository/repository.rb', line 292 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.
310 311 312 |
# File 'lib/lutaml/uml_repository/repository.rb', line 310 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.
696 697 698 |
# File 'lib/lutaml/uml_repository/repository.rb', line 696 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
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 |
# File 'lib/lutaml/uml_repository/repository.rb', line 710 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.
324 325 326 |
# File 'lib/lutaml/uml_repository/repository.rb', line 324 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)
602 603 604 |
# File 'lib/lutaml/uml_repository/repository.rb', line 602 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
641 642 643 |
# File 'lib/lutaml/uml_repository/repository.rb', line 641 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.
578 579 580 581 582 |
# File 'lib/lutaml/uml_repository/repository.rb', line 578 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.
594 595 596 |
# File 'lib/lutaml/uml_repository/repository.rb', line 594 def query!(&block) query(&block).execute end |
#search(pattern, types: %i[class attribute association],, fields: [:name]) ⇒ Hash
Search for model elements by regex pattern.
Similar to search but treats query as a regex pattern. Returns SearchResult objects for consistency with regular search.
(:name, :documentation)
(default: [:name])
488 489 490 491 492 |
# File 'lib/lutaml/uml_repository/repository.rb', line 488 def search( query, types: %i[class attribute association], fields: [:name] ) search_query.search(query, types: types, fields: fields) end |
#search_classes(query_string) ⇒ Object
Use #search with types filter
DEPRECATED: Use search with types: [:class] instead
661 662 663 |
# File 'lib/lutaml/uml_repository/repository.rb', line 661 def search_classes(query_string) search(query_string, types: [:class])[:classes] 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.
531 532 533 |
# File 'lib/lutaml/uml_repository/repository.rb', line 531 def statistics @statistics end |
#subtypes_of(class_or_qname, recursive: false) ⇒ Array
Get direct child classes (subtypes).
395 396 397 |
# File 'lib/lutaml/uml_repository/repository.rb', line 395 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).
380 381 382 |
# File 'lib/lutaml/uml_repository/repository.rb', line 380 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
553 554 555 556 |
# File 'lib/lutaml/uml_repository/repository.rb', line 553 def validate(verbose: false) Validators::RepositoryValidator.new(@document, @indexes).validate(verbose: verbose) end |