Class: Lutaml::UmlRepository::Repository

Inherits:
Object
  • Object
show all
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.

Examples:

Building from XMI

repo = Lutaml::Xmi::Repository.from_xmi('model.xmi')
klass = repo.find_class("ModelRoot::i-UR::urf::Building")

Navigating package hierarchy

packages = repo.list_packages("ModelRoot::i-UR", recursive: true)
tree = repo.package_tree("ModelRoot", max_depth: 2)

Querying inheritance

parent = repo.supertype_of("ModelRoot::Child")
descendants = repo.descendants_of("ModelRoot::Parent", max_depth: 2)

Direct Known Subclasses

LazyRepository, RepositoryEnhanced

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

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)

Examples:

indexes = IndexBuilder.build_all(document)
repo = Repository.new(document: document, indexes: indexes)

Parameters:

  • document (Lutaml::Uml::Document)

    The UML document to wrap

  • indexes (Hash, nil) (defaults to: nil)

    Pre-built indexes, or nil to build them automatically

  • metadata (PackageMetadata, nil) (defaults to: nil)

    Package metadata



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 options[: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

#documentLutaml::Uml::Document (readonly)

Returns The underlying UML document.

Returns:



52
53
54
# File 'lib/lutaml/uml_repository/repository.rb', line 52

def document
  @document
end

#indexesHash (readonly)

Returns The indexes for fast lookups.

Returns:

  • (Hash)

    The indexes for fast lookups



55
56
57
# File 'lib/lutaml/uml_repository/repository.rb', line 55

def indexes
  @indexes
end

#metadataPackageMetadata? (readonly)

Returns The package metadata (if loaded from LUR).

Returns:



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.

Examples:

repo = Repository.from_file('model.xmi')
repo = Repository.from_file('model.lur')

Parameters:

  • path (String)

    Path to the file (.xmi or .lur)

Returns:

  • (Repository)

    A new or loaded repository instance

Raises:

  • (ArgumentError)

    If the file type is unknown



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.

Examples:

Using default cache path

repo = Repository.from_file_cached('model.xmi')
# Creates/uses model.lur

Using custom cache path

repo = Repository.from_file_cached('model.xmi',
                                       lur_path: 'cache/model.lur')

Parameters:

  • xmi_path (String)

    Path to the XMI file

  • lur_path (String, nil) (defaults to: nil)

    Path to the LUR package (default: XMI path with .lur extension)

Returns:



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.

Examples:

repo = Repository.from_file_lazy('large-model.xmi')
repo = Repository.from_file_lazy('large-model.lur')

Parameters:

  • path (String)

    Path to the file (.xmi or .lur)

Returns:

Raises:

  • (ArgumentError)

    If the file type is unknown



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.

Examples:

repo = Repository.from_package("model.lur")

Parameters:

  • lur_path (String)

    Path to the .lur package file

Returns:



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.

Examples:

repo = Repository.from_package_lazy("large-model.lur")

Parameters:

  • lur_path (String)

    Path to the .lur package file

Returns:



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.

Examples:

repo = Repository.from_xmi('model.xmi')
repo = Repository.from_xmi('model.xmi', validate: true)

Parameters:

  • xmi_path (String)

    Path to the XMI file

  • options (Hash) (defaults to: {})

    Options for parsing

Options Hash (options):

  • :validate (Boolean) — default: false

    Validate model consistency after building indexes

Returns:



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, options = {})
  # 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: 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.

Examples:

repo = Repository.from_xmi_lazy('large-model.xmi')
# Only document loaded, indexes built on first access

Parameters:

  • xmi_path (String)

    Path to the XMI file

  • options (Hash)

    Options for parsing

Returns:



141
142
143
144
145
146
# File 'lib/lutaml/uml_repository/repository.rb', line 141

def self.from_xmi_lazy(xmi_path, _options = {})
  # 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_attributesArray<Lutaml::Uml::Attribute>

Get all attributes across all classes in the repository.

Returns:

  • (Array<Lutaml::Uml::Attribute>)

    All attribute objects



374
375
376
377
378
379
380
# File 'lib/lutaml/uml_repository/repository.rb', line 374

def all_attributes
  indexes[:qualified_names].flat_map do |_qname, entity|
    next [] unless entity.respond_to?(:attributes) && entity.attributes

    entity.attributes
  end
end

#all_classesArray

Get all classes in the repository

Examples:

all = repo.all_classes

Returns:

  • (Array)

    All class objects (classes, datatypes, enums)



689
690
691
# File 'lib/lutaml/uml_repository/repository.rb', line 689

def all_classes
  classes_index
end

#all_diagramsArray<Lutaml::Uml::Diagram>

Get all diagrams in the model.

Examples:

all_diagrams = repo.all_diagrams

Returns:



505
506
507
# File 'lib/lutaml/uml_repository/repository.rb', line 505

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.

Examples:

ancestors = repo.ancestors_of("ModelRoot::GrandChild")

Parameters:

  • class_or_qname (Lutaml::Uml::Class, String)

    The class object or qualified name string

Returns:

  • (Array)

    Array of ancestor class objects, ordered from nearest to furthest



443
444
445
# File 'lib/lutaml/uml_repository/repository.rb', line 443

def ancestors_of(class_or_qname)
  inheritance_query.ancestors(class_or_qname)
end

#associations_indexArray<Lutaml::Uml::Association>

Get all associations as an array Collects from both document-level (XMI) and class-level (QEA/EA)

Returns:



649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
# File 'lib/lutaml/uml_repository/repository.rb', line 649

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

Examples:

all_assocs = repo.associations_of("ModelRoot::Building")
outgoing = repo.associations_of(
"ModelRoot::Building", direction: :source)

Parameters:

  • class_or_qname (Lutaml::Uml::Class, String)

    The class object or qualified name string

  • options (Hash) (defaults to: {})

    Query options

Options Hash (options):

  • :direction (Symbol) — default: :both

    Filter by direction: :source, :target, or :both

  • :owned_only (Boolean)

    Return only owned associations

  • :navigable_only (Boolean)

Returns:



475
476
477
# File 'lib/lutaml/uml_repository/repository.rb', line 475

def associations_of(class_or_qname, options = {})
  association_query.find_for_class(class_or_qname, options)
end

#classes_in_package(package_path, recursive: false) ⇒ Array

Get classes in a specific package.

Examples:

classes = repo.classes_in_package("ModelRoot::i-UR::urf")
all_classes = repo.classes_in_package(
"ModelRoot::i-UR", recursive: true)

Parameters:

  • package_path (String)

    The package path

  • recursive (Boolean) (defaults to: false)

    Whether to include classes from nested packages (default: false)

Returns:

  • (Array)

    Array of class objects in the package



402
403
404
# File 'lib/lutaml/uml_repository/repository.rb', line 402

def classes_in_package(package_path, recursive: false)
  class_query.in_package(package_path, recursive: recursive)
end

#classes_indexArray

Get all classes (including datatypes and enums) as an array

Returns:

  • (Array)

    All classifiers



642
643
644
# File 'lib/lutaml/uml_repository/repository.rb', line 642

def classes_index
  @indexes[:qualified_names]&.values || []
end

#descendants_of(class_or_qname, max_depth: nil) ⇒ Array

Get all descendant classes.

Examples:

descendants = repo.descendants_of("ModelRoot::Parent", max_depth: 2)

Parameters:

  • class_or_qname (Lutaml::Uml::Class, String)

    The class object or qualified name string

  • max_depth (Integer, nil) (defaults to: nil)

    Maximum depth to traverse (nil for unlimited)

Returns:

  • (Array)

    Array of descendant class objects



456
457
458
# File 'lib/lutaml/uml_repository/repository.rb', line 456

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.

Examples:

diagrams = repo.diagrams_in_package("ModelRoot::i-UR::urf")

Parameters:

  • package_path (String)

    The package path or package ID

Returns:



485
486
487
# File 'lib/lutaml/uml_repository/repository.rb', line 485

def diagrams_in_package(package_path)
  diagram_query.in_package(package_path)
end

#diagrams_indexArray<Lutaml::Uml::Diagram>

Get all diagrams as an array

Returns:



681
682
683
# File 'lib/lutaml/uml_repository/repository.rb', line 681

def diagrams_index
  all_diagrams
end

#export(output_path, options = {}) ⇒ Object

Deprecated.

DEPRECATED: Use export_to_package instead



719
720
721
# File 'lib/lutaml/uml_repository/repository.rb', line 719

def export(output_path, options = {})
  export_to_package(output_path, options)
end

#export_to_package(output_path, options = {}) ⇒ void

This method returns an undefined value.

Export this repository to a LUR package file.

Examples:

Export with defaults

repo.export_to_package("model.lur")

Export with PackageMetadata

 = PackageMetadata.new(
  name: "Urban Model",
  version: "2.0",
  publisher: "City Planning"
)
repo.export_to_package("model.lur", metadata: )

Export with custom options (backward compatible)

repo.export_to_package("model.lur",
  name: "My Model",
  version: "2.0",
  serialization_format: :yaml
)

Parameters:

  • output_path (String)

    Path for the output .lur file

  • options (Hash) (defaults to: {})

    Export options

Options Hash (options):

  • :metadata (PackageMetadata, Hash)

    Package metadata

  • :name (String) — default: "UML Model"

    Package name (deprecated, use :metadata)

  • :version (String) — default: "1.0"

    Package version (deprecated, use :metadata)

  • :include_xmi (Boolean) — default: false

    Include source XMI

  • :serialization_format (Symbol) — default: :marshal

    Format to use (:marshal or :yaml)

  • :compression_level (Integer) — default: 6

    ZIP compression level



277
278
279
# File 'lib/lutaml/uml_repository/repository.rb', line 277

def export_to_package(output_path, options = {})
  PackageExporter.new(self, options).export(output_path)
end

#find_associations(class_or_qname, options = {}) ⇒ Object

Deprecated.

DEPRECATED: Use associations_of instead



707
708
709
# File 'lib/lutaml/uml_repository/repository.rb', line 707

def find_associations(class_or_qname, options = {})
  associations_of(class_or_qname, options)
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.

Examples:

attr = repo.find_attribute("ModelRoot::Core::Building::name")

Parameters:

  • qualified_name (String)

    Qualified name of the attribute

Returns:

  • (Lutaml::Uml::Attribute, nil)

    The attribute or nil



358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/lutaml/uml_repository/repository.rb', line 358

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_children(class_or_qname, recursive: false) ⇒ Object

Deprecated.

DEPRECATED: Use subtypes_of instead



701
702
703
# File 'lib/lutaml/uml_repository/repository.rb', line 701

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

Examples:

klass = repo.find_class("ModelRoot::i-UR::urf::Building")
klass = repo.find_class("ModelRoot::Typo", raise_on_error: true)

Parameters:

  • qualified_name (String)

    The qualified name (e.g., “ModelRoot::i-UR::urf::Building”)

  • raise_on_error (Boolean) (defaults to: false)

    Whether to raise an error if not found (default: false)

Returns:

Raises:

  • (NameError)

    If class not found and raise_on_error is true



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.

Examples:

feature_types = repo.find_classes_by_stereotype("featureType")

Parameters:

  • stereotype (String)

    The stereotype to search for

Returns:

  • (Array)

    Array of class objects with the stereotype



388
389
390
# File 'lib/lutaml/uml_repository/repository.rb', line 388

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

Examples:

diagram = repo.find_diagram("Class Diagram 1")

Parameters:

  • diagram_name (String)

    The diagram name

Returns:



496
497
498
# File 'lib/lutaml/uml_repository/repository.rb', line 496

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



713
714
715
# File 'lib/lutaml/uml_repository/repository.rb', line 713

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.

Examples:

package = repo.find_package("ModelRoot::i-UR::urf")
package = repo.find_package("ModelRoot::typo", raise_on_error: true)

Parameters:

  • path (String)

    The package path (e.g., “ModelRoot::i-UR::urf”)

  • raise_on_error (Boolean) (defaults to: false)

    Whether to raise an error if not found (default: false)

Returns:

Raises:

  • (NameError)

    If package not found and raise_on_error is true



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.

Examples:

Non-recursive listing

packages = repo.list_packages("ModelRoot::i-UR", recursive: false)

Recursive listing

packages = repo.list_packages("ModelRoot", recursive: true)

Parameters:

  • path (String) (defaults to: "ModelRoot")

    The parent package path (default: “ModelRoot”)

  • recursive (Boolean) (defaults to: false)

    Whether to include nested packages recursively (default: false)

Returns:



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_dumpHash

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.

Returns:

  • (Hash)

    Serializable state (document, indexes, and metadata)



730
731
732
# File 'lib/lutaml/uml_repository/repository.rb', line 730

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

Parameters:

  • data (Hash)

    Serialized state with :document, :indexes,



744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
# File 'lib/lutaml/uml_repository/repository.rb', line 744

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.

Examples:

tree = repo.package_tree("ModelRoot::i-UR", max_depth: 2)

Parameters:

  • path (String) (defaults to: "ModelRoot")

    The root package path to start from (default: “ModelRoot”)

  • max_depth (Integer, nil) (defaults to: nil)

    Maximum depth to traverse (nil for unlimited)

Returns:

  • (Hash, nil)

    Tree structure with package information, or nil if root not found



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_indexArray<Lutaml::Uml::Package>

Get all packages as an array (excluding root Document)

Returns:



636
637
638
# File 'lib/lutaml/uml_repository/repository.rb', line 636

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



675
676
677
# File 'lib/lutaml/uml_repository/repository.rb', line 675

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.

Examples:

Basic query

results = repo.query do |q|
  q.classes.where(stereotype: 'featureType')
end.all

Complex query

results = repo.query do |q|
  q.classes
    .in_package('ModelRoot::i-UR', recursive: true)
    .where { |c| c.attributes&.size.to_i > 10 }
    .order_by(:name, direction: :desc)
    .limit(5)
end.execute

Yields:

Returns:



612
613
614
615
616
# File 'lib/lutaml/uml_repository/repository.rb', line 612

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.

Examples:

results = repo.query! do |q|
  q.classes.where(stereotype: 'featureType')
end

Yields:

Returns:

  • (Array)

    The query results



628
629
630
# File 'lib/lutaml/uml_repository/repository.rb', line 628

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])

Examples:

results = repo.search("^Building.*", types:[:class])
results = repo.search("address$", types: [:attribute])
results = repo.search("urban", fields: [:documentation])

Parameters:

  • pattern (String, Regexp)

    The regex pattern to match

  • types (Array<Symbol>) (defaults to: %i[class attribute association],)

    Types to search (:class, :attribute, :association) (default: [:class, :attribute, :association])

  • fields (Array<Symbol>) (defaults to: [:name])

    Fields to search in

Returns:

  • (Hash)

    Search results grouped by type (same format as search)



522
523
524
525
526
# File 'lib/lutaml/uml_repository/repository.rb', line 522

def search(
  query, types: %i[class attribute association], fields: [:name]
)
  search_query.search(query, types: types, fields: fields)
end

#search_classes(query_string) ⇒ Object

Deprecated.

Use #search with types filter

DEPRECATED: Use search with types: [:class] instead



695
696
697
# File 'lib/lutaml/uml_repository/repository.rb', line 695

def search_classes(query_string)
  search(query_string, types: [:class])[:classes]
end

#statisticsHash

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.

Examples:

stats = repo.statistics
puts "Total packages: #{stats[:total_packages]}"
puts "Max package depth: #{stats[:max_package_depth]}"
puts "Most complex class:
  #{stats[:most_complex_classes].first[:name]}"

Returns:

  • (Hash)

    Comprehensive statistics hash



565
566
567
# File 'lib/lutaml/uml_repository/repository.rb', line 565

def statistics
  @statistics
end

#subtypes_of(class_or_qname, recursive: false) ⇒ Array

Get direct child classes (subtypes).

Examples:

children = repo.subtypes_of("ModelRoot::Parent")
all_descendants = repo.subtypes_of(
"ModelRoot::Parent", recursive: true)

Parameters:

  • class_or_qname (Lutaml::Uml::Class, String)

    The class object or qualified name string

  • recursive (Boolean) (defaults to: false)

    Whether to include all descendants (default: false)

Returns:

  • (Array)

    Array of child class objects



429
430
431
# File 'lib/lutaml/uml_repository/repository.rb', line 429

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).

Examples:

parent = repo.supertype_of("ModelRoot::Child")
parent = repo.supertype_of(child_class)

Parameters:

  • class_or_qname (Lutaml::Uml::Class, String)

    The class object or qualified name string

Returns:



414
415
416
# File 'lib/lutaml/uml_repository/repository.rb', line 414

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

Examples:

result = repo.validate
if result.valid?
  puts "Repository is valid"
else
  result.errors.each { |error| puts "ERROR: #{error}" }
end

Parameters:

  • verbose (Boolean) (defaults to: false)

    Collect detailed validation information

Returns:



587
588
589
590
# File 'lib/lutaml/uml_repository/repository.rb', line 587

def validate(verbose: false)
  Validators::RepositoryValidator.new(@document,
                                      @indexes).validate(verbose: verbose)
end