Module: Rubyzen::Matchers::MatcherHelpers

Defined in:
lib/rubyzen/matchers/matcher_helpers.rb

Overview

Shared helper methods used by Rubyzen’s custom RSpec matchers.

Provides utilities for normalizing exception lists, extracting item details, matching items against allowlist/baseline entries, and formatting failure messages.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/rubyzen/matchers/matcher_helpers.rb', line 155

def self.included(base)
  base.define_method(:message_for_failure) do |base_message|
    return @failure_message if @failure_message

    details = formatted_matcher_groups

    if @custom_message
      if details && !details.empty?
        "#{@custom_message}\n#{details}"
      else
        @custom_message
      end
    elsif details && !details.empty?
      "#{base_message}\n#{details}"
    else
      base_message
    end
  end
end

Instance Method Details

#classify_items(subject_collection, allowlist: nil, baseline: nil) ⇒ Hash{Symbol => Array<String>}

Classifies items into violations, baseline matches, allowlist matches, and detects stale entries in either list.

Parameters:

  • subject_collection (Array, Object)

    items to classify

  • allowlist (Array<String>, nil) (defaults to: nil)

    allowed exception entries

  • baseline (Array<String>, nil) (defaults to: nil)

    baseline exception entries

Returns:

  • (Hash{Symbol => Array<String>})

    keys: :violations, :baseline, :allowlist, :stale_baseline, :stale_allowlist



69
70
71
72
73
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
103
104
105
106
107
108
# File 'lib/rubyzen/matchers/matcher_helpers.rb', line 69

def classify_items(subject_collection, allowlist: nil, baseline: nil)
  items = Array(subject_collection).compact
  normalized_allowlist = normalize_exception_entries(allowlist)
  normalized_baseline = normalize_exception_entries(baseline)
  matched_baseline_entries = []
  matched_allowlist_entries = []

  grouped_items = items.group_by do |item|
    matching_baseline_entry = normalized_baseline.find do |entry|
      exception_entry_matches_item?(entry, item)
    end

    if matching_baseline_entry
      matched_baseline_entries << matching_baseline_entry
      :baseline
    else
      matching_allowlist_entry = normalized_allowlist.find do |entry|
        exception_entry_matches_item?(entry, item)
      end

      if matching_allowlist_entry
        matched_allowlist_entries << matching_allowlist_entry
        :allowlist
      else
        :violations
      end
    end
  end

  classifications = {
    baseline: Array(grouped_items[:baseline]).map { |item| element_name(item) },
    allowlist: Array(grouped_items[:allowlist]).map { |item| element_name(item) },
    violations: Array(grouped_items[:violations]).map { |item| element_name(item) }
  }

  classifications.merge(
    stale_baseline: normalized_baseline - matched_baseline_entries.uniq,
    stale_allowlist: normalized_allowlist - matched_allowlist_entries.uniq
  )
end

#element_name(item) ⇒ String

Formats a human-readable description of an item for failure messages.

Parameters:

  • item (Object)

    a declaration object

Returns:

  • (String)

    formatted multi-line description



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/rubyzen/matchers/matcher_helpers.rb', line 114

def element_name(item)
  details = item_details(item)
  location = [details[:file_path], details[:line]].compact.join(':')

  case
  when details[:name] && details[:class_name]
    "  - element: #{details[:name]}\n  - class: #{details[:class_name]}\n  - file: #{location}"
  when details[:name]
    "  - element: #{details[:name]}\n  - file: #{location}"
  when details[:class_name]
    "  - class: #{details[:class_name]}\n  - file: #{location}"
  else
    "  - unknown element in #{location}"
  end
end

#exception_entry_matches_item?(entry, item) ⇒ Boolean

Checks whether a given exception entry string matches an item.

Parameters:

  • entry (String)

    an allowlist or baseline entry

  • item (Object)

    a declaration object

Returns:

  • (Boolean)

    true if the entry matches the item by name, class, or path



50
51
52
53
54
55
56
57
58
59
# File 'lib/rubyzen/matchers/matcher_helpers.rb', line 50

def exception_entry_matches_item?(entry, item)
  normalized_entry = entry.to_s.strip
  return false if normalized_entry.empty?

  details = item_details(item)
  return true if item_identifiers(item).include?(normalized_entry)

  file_path = details[:file_path]
  file_path && (file_path.end_with?(normalized_entry) || file_path.end_with?("/#{normalized_entry}"))
end

#formatted_matcher_groupsString?

Builds a formatted string of violations and stale entries for failure output.

Returns:

  • (String, nil)

    formatted sections or nil if no classified items



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/rubyzen/matchers/matcher_helpers.rb', line 133

def formatted_matcher_groups
  return unless defined?(@classified_items) && @classified_items

  sections = []

  if @classified_items[:violations].any?
    sections << "Violations:\n#{@classified_items[:violations].join("\n")}"
  end

  if @classified_items[:stale_baseline].any?
    stale_entries = @classified_items[:stale_baseline].map { |entry| "  - #{entry}" }
    sections << "Stale baseline entries:\n#{stale_entries.join("\n")}"
  end

  if @classified_items[:stale_allowlist].any?
    stale_entries = @classified_items[:stale_allowlist].map { |entry| "  - #{entry}" }
    sections << "Stale allowlist entries:\n#{stale_entries.join("\n")}"
  end

  sections.join("\n")
end

#item_details(item) ⇒ Hash{Symbol => String, nil}

Extracts identifying details from a declaration item.

Parameters:

  • item (Object)

    a declaration object (e.g., FileDeclaration, ClassDeclaration)

Returns:

  • (Hash{Symbol => String, nil})

    hash with :name, :class_name, :file_path, :line



21
22
23
24
25
26
27
28
# File 'lib/rubyzen/matchers/matcher_helpers.rb', line 21

def item_details(item)
  {
    name: item.respond_to?(:name) ? item.name : nil,
    class_name: item.respond_to?(:class_name) ? item.class_name : nil,
    file_path: item.respond_to?(:file_path) ? item.file_path : 'Unknown file',
    line: item.respond_to?(:line) ? item.line : nil
  }
end

#item_identifiers(item) ⇒ Array<String>

Returns a list of unique identifier strings for an item, used for matching.

Parameters:

  • item (Object)

    a declaration object

Returns:

  • (Array<String>)

    identifiers such as name, class name, file path, and file:line



34
35
36
37
38
39
40
41
42
43
# File 'lib/rubyzen/matchers/matcher_helpers.rb', line 34

def item_identifiers(item)
  details = item_details(item)
  identifiers = [details[:name], details[:class_name], details[:file_path]]

  if details[:line]
    identifiers << "#{details[:file_path]}:#{details[:line]}"
  end

  identifiers.compact.uniq
end

#normalize_exception_entries(entries) ⇒ Array<String>

Normalizes a list of exception entries into unique, non-blank strings.

Parameters:

  • entries (Array<String>, String, nil)

    raw exception entries

Returns:

  • (Array<String>)

    deduplicated, stripped, non-empty strings



13
14
15
# File 'lib/rubyzen/matchers/matcher_helpers.rb', line 13

def normalize_exception_entries(entries)
  Array(entries).flatten.compact.map(&:to_s).map(&:strip).reject(&:empty?).uniq
end