Module: Ace::Git::Atoms::PatternFilter

Defined in:
lib/ace/git/atoms/pattern_filter.rb

Overview

Pure functions for pattern matching and filtering Migrated from ace-git-diff

Constant Summary collapse

MAX_CACHE_SIZE =

Maximum cache size to prevent unbounded memory growth in long-running processes

100

Class Method Summary collapse

Class Method Details

.clear_cache!Object

Clear the pattern cache (mainly for testing)



46
47
48
49
50
# File 'lib/ace/git/atoms/pattern_filter.rb', line 46

def clear_cache!
  PatternFilter.instance_variable_get(:@cache_mutex).synchronize do
    PatternFilter.instance_variable_get(:@pattern_cache).clear
  end
end

.extract_file_path(line) ⇒ String

Extract file path from diff header line

Parameters:

  • line (String)

    Diff header line

Returns:

  • (String)

    Extracted file path or empty string



76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/ace/git/atoms/pattern_filter.rb', line 76

def extract_file_path(line)
  return "" if line.nil? || line.empty?

  case line
  when /^diff --git a\/(.+) b\/(.+)$/
    Regexp.last_match(2) # Use the 'b/' path (new file path)
  when /^\+\+\+ b\/(.+)$/
    Regexp.last_match(1)
  when /^--- a\/(.+)$/
    Regexp.last_match(1)
  else
    ""
  end
end

.file_header?(line) ⇒ Boolean

Check if a line is a file header in git diff format

Parameters:

  • line (String)

    Line to check

Returns:

  • (Boolean)

    True if line is a file header



66
67
68
69
70
71
# File 'lib/ace/git/atoms/pattern_filter.rb', line 66

def file_header?(line)
  return false if line.nil? || line.empty?

  line.start_with?("diff --git", "+++", "---") ||
    line.match?(/^index [a-f0-9]+\.\.[a-f0-9]+/)
end

.filter_diff_by_patterns(diff, exclude_patterns) ⇒ String

Filter paths from diff output based on exclude patterns

Parameters:

  • diff (String)

    The diff content

  • exclude_patterns (Array<Regexp>)

    Patterns to exclude

Returns:

  • (String)

    Filtered diff content



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/ace/git/atoms/pattern_filter.rb', line 95

def filter_diff_by_patterns(diff, exclude_patterns)
  return "" if diff.nil? || diff.empty?
  return diff if exclude_patterns.nil? || exclude_patterns.empty?

  lines = diff.split("\n")
  filtered_lines = []
  skip_until_next_file = false

  lines.each do |line|
    # Check if this is a file header
    if file_header?(line)
      file_path = extract_file_path(line)
      if should_exclude?(file_path, exclude_patterns)
        skip_until_next_file = true
      else
        skip_until_next_file = false
        filtered_lines << line
      end
    elsif !skip_until_next_file
      filtered_lines << line
    end
  end

  filtered_lines.join("\n")
end

.glob_to_regex(glob_patterns) ⇒ Array<Regexp>

Convert glob patterns to regex patterns Uses caching for performance when same patterns are used repeatedly

Parameters:

  • glob_patterns (Array<String>)

    Glob patterns like “test/*/

Returns:

  • (Array<Regexp>)

    Regex patterns for matching



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/ace/git/atoms/pattern_filter.rb', line 24

def glob_to_regex(glob_patterns)
  return [] if glob_patterns.nil? || glob_patterns.empty?

  # Generate cache key from sorted patterns
  cache_key = glob_patterns.sort.join("|")

  # Check cache first (thread-safe read)
  PatternFilter.instance_variable_get(:@cache_mutex).synchronize do
    cache = PatternFilter.instance_variable_get(:@pattern_cache)
    return cache[cache_key] if cache.key?(cache_key)

    # Evict oldest entry if cache is full (FIFO eviction)
    cache.shift if cache.size >= MAX_CACHE_SIZE

    # Compile patterns and cache
    patterns = glob_patterns.map { |pattern| compile_glob_pattern(pattern) }
    cache[cache_key] = patterns
    patterns
  end
end

.matches_include?(file_path, include_patterns) ⇒ Boolean

Match a path against include patterns (glob)

Parameters:

  • file_path (String)

    Path to check

  • include_patterns (Array<String>)

    Glob patterns to include

Returns:

  • (Boolean)

    True if path matches any include pattern



125
126
127
128
129
130
131
# File 'lib/ace/git/atoms/pattern_filter.rb', line 125

def matches_include?(file_path, include_patterns)
  return true if include_patterns.nil? || include_patterns.empty?
  return false if file_path.nil? || file_path.empty?

  regex_patterns = glob_to_regex(include_patterns)
  regex_patterns.any? { |pattern| file_path.match?(pattern) }
end

.should_exclude?(file_path, patterns) ⇒ Boolean

Check if a file path should be excluded based on patterns

Parameters:

  • file_path (String)

    Path to check

  • patterns (Array<Regexp>)

    Regex patterns to match against

Returns:

  • (Boolean)

    True if path matches any exclude pattern



56
57
58
59
60
61
# File 'lib/ace/git/atoms/pattern_filter.rb', line 56

def should_exclude?(file_path, patterns)
  return false if file_path.nil? || file_path.empty?
  return false if patterns.nil? || patterns.empty?

  patterns.any? { |pattern| file_path.match?(pattern) }
end