class Lutaml::UmlRepository::ErrorHandler
# => Raises error with suggestions for similar packages
handler.package_not_found_error(“ModelRoot::i-UR::urf”)
@example Package not found
# => Raises error with suggestion: Did you mean “ModelRoot::Building”?
handler.class_not_found_error(“ModelRoot::Buildng”)
handler = ErrorHandler.new(repository)
@example Class not found
suggest similar names that might be what the user intended.
elements are not found. Uses fuzzy matching (Levenshtein distance) to
Provides helpful error messages when classes, packages, or other model
Error handler for user-friendly error messages with suggestions.
def class_not_found_error(attempted_qname)
-
(NameError)- With helpful message and suggestions
Parameters:
-
attempted_qname(String) -- The qualified name that was attempted
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
def find_similar_names(attempted, candidates) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
-
(Array- Sorted array of similar names)
Parameters:
-
candidates(Array) -- List of candidate names -
attempted(String) -- The attempted name
def find_similar_names(attempted, candidates) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength # Calculate distances for all candidates distances = candidates.map do |candidate| distance = levenshtein_distance( attempted.downcase, candidate.downcase, ) { name: candidate, distance: distance } end # Filter by maximum distance and sort similar = distances .select { |d| d[:distance] <= MAX_SUGGESTION_DISTANCE } .sort_by { |d| d[:distance] } .take(MAX_SUGGESTIONS) .map { |d| d[:name] } # If no close matches, try substring matching if similar.empty? similar = find_substring_matches(attempted, candidates) end similar end
def find_substring_matches(attempted, candidates)
-
(Array- Array of matching names)
Parameters:
-
candidates(Array) -- List of candidate names -
attempted(String) -- The attempted name
def find_substring_matches(attempted, candidates) attempted_lower = attempted.downcase candidates .select { |c| c.downcase.include?(attempted_lower) } .take(MAX_SUGGESTIONS) end
def initialize(repository)
-
repository(UmlRepository) -- Repository to use for suggestions
def initialize(repository) @repository = repository end
def levenshtein_distance(str1, str2) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
-
(Integer)- The Levenshtein distance
Parameters:
-
str2(String) -- Second string -
str1(String) -- First string
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
def package_not_found_error(attempted_path)
-
(NameError)- With helpful message and suggestions
Parameters:
-
attempted_path(String) -- The package path that was attempted
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
def suggest_similar_classes(attempted)
-
(Array- Array of suggested qualified names, sorted by)
Parameters:
-
attempted(String) -- The attempted qualified name
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
def suggest_similar_packages(attempted)
-
(Array- Array of suggested package paths, sorted by)
Parameters:
-
attempted(String) -- The attempted package path
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