Class: Ace::Lint::Atoms::PatternMatcher
- Inherits:
-
Object
- Object
- Ace::Lint::Atoms::PatternMatcher
- Defined in:
- lib/ace/lint/atoms/pattern_matcher.rb
Overview
Matches file paths against glob patterns with specificity scoring Used for determining which validator group applies to a file
Class Method Summary collapse
-
.best_group_match(path, groups) ⇒ Array<Symbol, Hash>?
Find the best matching group for a path.
-
.best_match(path, patterns) ⇒ String?
Find the best matching pattern for a path.
-
.matches?(path, pattern) ⇒ Boolean
Check if a path matches a pattern.
-
.specificity(pattern) ⇒ Integer
Score a pattern based on specificity (higher = more specific).
Class Method Details
.best_group_match(path, groups) ⇒ Array<Symbol, Hash>?
Find the best matching group for a path
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/ace/lint/atoms/pattern_matcher.rb', line 73 def self.best_group_match(path, groups) return nil if groups.nil? || groups.empty? best_group = nil best_score = -Float::INFINITY groups.each do |name, config| patterns = config[:patterns] || config["patterns"] || [] pattern = best_match(path, patterns) next unless pattern score = specificity(pattern) if score > best_score best_score = score best_group = [name.to_sym, config] end end best_group end |
.best_match(path, patterns) ⇒ String?
Find the best matching pattern for a path
59 60 61 62 63 64 65 66 67 |
# File 'lib/ace/lint/atoms/pattern_matcher.rb', line 59 def self.best_match(path, patterns) return nil if patterns.nil? || patterns.empty? matching = patterns.select { |p| matches?(path, p) } return nil if matching.empty? # Return pattern with highest specificity matching.max_by { |p| specificity(p) } end |
.matches?(path, pattern) ⇒ Boolean
Check if a path matches a pattern
43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/ace/lint/atoms/pattern_matcher.rb', line 43 def self.matches?(path, pattern) return false if path.nil? || pattern.nil? # Normalize path (remove leading ./) normalized_path = path.sub(%r{^\./}, "") # File.fnmatch with FNM_PATHNAME for proper ** handling # FNM_EXTGLOB for brace expansion {rb,rake} File.fnmatch(pattern, normalized_path, File::FNM_PATHNAME | File::FNM_EXTGLOB) || File.fnmatch(pattern, File.basename(normalized_path), File::FNM_PATHNAME | File::FNM_EXTGLOB) end |
.specificity(pattern) ⇒ Integer
Score a pattern based on specificity (higher = more specific)
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/ace/lint/atoms/pattern_matcher.rb', line 12 def self.specificity(pattern) return 0 if pattern.nil? || pattern.empty? score = 0 # Exact filename match (no glob chars) gets highest score unless pattern.include?("*") || pattern.include?("?") || pattern.include?("[") return 1000 + pattern.length end # Directory depth: +100 per path segment score += pattern.count("/") * 100 # Double-star penalty: -50 per ** score -= pattern.scan("**").count * 50 # Single-star bonus: +10 per * (but not **) single_stars = pattern.gsub("**", "").count("*") score += single_stars * 10 # Literal prefix length: +1 per char before first glob literal_prefix = pattern.split(/[*?\[]/).first || "" score += literal_prefix.length score end |