Class: Lutaml::UmlRepository::ErrorHandler

Inherits:
Object
  • Object
show all
Defined in:
lib/lutaml/uml_repository/error_handler.rb

Overview

Error handler for user-friendly error messages with suggestions.

Provides helpful error messages when classes, packages, or other model elements are not found. Uses fuzzy matching (Levenshtein distance) to suggest similar names that might be what the user intended.

Examples:

Class not found

handler = ErrorHandler.new(repository)
handler.class_not_found_error("ModelRoot::Buildng")
# => Raises error with suggestion: Did you mean "ModelRoot::Building"?

Package not found

handler.package_not_found_error("ModelRoot::i-UR::urf")
# => Raises error with suggestions for similar packages

Constant Summary collapse

MAX_SUGGESTION_DISTANCE =

Maximum Levenshtein distance for suggestions

3
MAX_SUGGESTIONS =

Maximum number of suggestions to show

5

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(repository) ⇒ ErrorHandler

Initialize a new ErrorHandler.

Parameters:

  • repository (UmlRepository)

    Repository to use for suggestions



32
33
34
# File 'lib/lutaml/uml_repository/error_handler.rb', line 32

def initialize(repository)
  @repository = repository
end

Instance Attribute Details

#repositoryUmlRepository (readonly)

Returns The repository to search for suggestions.

Returns:



27
28
29
# File 'lib/lutaml/uml_repository/error_handler.rb', line 27

def repository
  @repository
end

Instance Method Details

#class_not_found_error(attempted_qname) ⇒ Object

Raise a class not found error with suggestions.

Searches for similar class names using fuzzy matching and provides helpful suggestions in the error message.

Examples:

handler.class_not_found_error("ModelRoot::Buildng")

Parameters:

  • attempted_qname (String)

    The qualified name that was attempted

Raises:

  • (NameError)

    With helpful message and suggestions



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/lutaml/uml_repository/error_handler.rb', line 45

def class_not_found_error(attempted_qname)
  suggestions = suggest_similar_classes(attempted_qname)

  message = "Class not found: #{attempted_qname}"

  if suggestions.any?
    message += "\n\nDid you mean one of these?"
    suggestions.each { |s| message += "\n  - #{s}" }
  else
    message += "\n\nTip: Use the 'search' or 'find' commands to " \
               "explore available classes."
  end

  raise NameError, message
end

#levenshtein_distance(str1, str2) ⇒ Integer

Calculate Levenshtein distance between two strings.

The Levenshtein distance is the minimum number of single-character edits (insertions, deletions, or substitutions) required to change one string into another.

Examples:

distance = handler.levenshtein_distance("kitten", "sitting")
# => 3

Parameters:

  • str1 (String)

    First string

  • str2 (String)

    Second string

Returns:

  • (Integer)

    The Levenshtein distance



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/lutaml/uml_repository/error_handler.rb', line 128

def levenshtein_distance(str1, str2) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
  return str2.length if str1.empty?
  return str1.length if str2.empty?

  # Create a matrix to store distances
  matrix = Array.new(str1.length + 1) do |i|
    Array.new(str2.length + 1) do |j|
      if i.zero?
        j
      else
        (j.zero? ? i : 0)
      end
    end
  end

  # Calculate distances
  (1..str1.length).each do |i|
    (1..str2.length).each do |j|
      cost = str1[i - 1] == str2[j - 1] ? 0 : 1
      matrix[i][j] = [
        matrix[i - 1][j] + 1,      # deletion
        matrix[i][j - 1] + 1,      # insertion
        matrix[i - 1][j - 1] + cost, # substitution
      ].min
    end
  end

  matrix[str1.length][str2.length]
end

#package_not_found_error(attempted_path) ⇒ Object

Raise a package not found error with suggestions.

Searches for similar package paths using fuzzy matching and provides helpful suggestions in the error message.

Examples:

handler.package_not_found_error("ModelRoot::i-UR::urf")

Parameters:

  • attempted_path (String)

    The package path that was attempted

Raises:

  • (NameError)

    With helpful message and suggestions



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/lutaml/uml_repository/error_handler.rb', line 70

def package_not_found_error(attempted_path)
  suggestions = suggest_similar_packages(attempted_path)

  message = "Package not found: #{attempted_path}"

  if suggestions.any?
    message += "\n\nDid you mean one of these?"
    suggestions.each { |s| message += "\n  - #{s}" }
  else
    message += "\n\nTip: Use the 'list' or 'tree' commands to explore " \
               "available packages."
  end

  raise NameError, message
end

#suggest_similar_classes(attempted) ⇒ Array<String>

Suggest similar class names based on Levenshtein distance.

Examples:

suggestions = handler.suggest_similar_classes("ModelRoot::Buildng")
# => ["ModelRoot::Building", "ModelRoot::BuildingPart"]

Parameters:

  • attempted (String)

    The attempted qualified name

Returns:

  • (Array<String>)

    Array of suggested qualified names, sorted by similarity



94
95
96
97
98
99
# File 'lib/lutaml/uml_repository/error_handler.rb', line 94

def suggest_similar_classes(attempted)
  return [] unless repository.indexes[:class_to_qname]

  all_qnames = repository.indexes[:class_to_qname].values
  find_similar_names(attempted, all_qnames)
end

#suggest_similar_packages(attempted) ⇒ Array<String>

Suggest similar package paths based on Levenshtein distance.

Examples:

suggestions = handler.suggest_similar_packages("ModelRoot::i-UR")
# => ["ModelRoot::i-UR::urf", "ModelRoot::i-UR::core"]

Parameters:

  • attempted (String)

    The attempted package path

Returns:

  • (Array<String>)

    Array of suggested package paths, sorted by similarity



109
110
111
112
113
114
# File 'lib/lutaml/uml_repository/error_handler.rb', line 109

def suggest_similar_packages(attempted)
  return [] unless repository.indexes[:package_to_path]

  all_paths = repository.indexes[:package_to_path].values
  find_similar_names(attempted, all_paths)
end