Module: CovLoupe::GlobUtils
- Defined in:
- lib/cov_loupe/paths/glob_utils.rb
Overview
File globbing utilities for filtering coverage data by path patterns.
Uses File.fnmatch? for pure string matching (no filesystem access for glob evaluation). Case-insensitive matching is automatically applied on case-insensitive volumes, detected by testing the volume containing the path being matched.
Constant Summary collapse
- GLOB_MATCH_FLAGS =
File::FNM_PATHNAME | File::FNM_EXTGLOB
Class Method Summary collapse
-
.absolutize_pattern(pattern, root) ⇒ String
Converts a pattern to absolute path relative to a root.
-
.filter_by_pattern(items, patterns, key: 'file') ⇒ Array<Hash>
Filters items where a key contains a file path matching the patterns.
-
.filter_paths(paths, globs, root:) ⇒ Array<String>
Filters an array of absolute file paths by glob patterns.
-
.fn_normalize_path_separators ⇒ Proc
Returns a lambda that normalizes path separators for the current platform.
-
.matches_any_pattern?(abs_path, patterns) ⇒ Boolean
Tests if a file path matches any of the given absolute glob patterns.
- .normalize_patterns(globs) ⇒ Object
Class Method Details
.absolutize_pattern(pattern, root) ⇒ String
Converts a pattern to absolute path relative to a root. Handles both relative patterns ("lib/.rb") and absolute ones ("/tmp/.rb").
37 38 39 |
# File 'lib/cov_loupe/paths/glob_utils.rb', line 37 module_function def absolutize_pattern(pattern, root) File.(pattern, root) end |
.filter_by_pattern(items, patterns, key: 'file') ⇒ Array<Hash>
Filters items where a key contains a file path matching the patterns.
84 85 86 87 88 |
# File 'lib/cov_loupe/paths/glob_utils.rb', line 84 module_function def filter_by_pattern(items, patterns, key: 'file') return items if patterns.nil? || patterns.empty? items.select { |item| matches_any_pattern?(item[key], patterns) } end |
.filter_paths(paths, globs, root:) ⇒ Array<String>
Filters an array of absolute file paths by glob patterns. Handles normalization and absolutization of patterns internally.
97 98 99 100 101 102 103 |
# File 'lib/cov_loupe/paths/glob_utils.rb', line 97 module_function def filter_paths(paths, globs, root:) patterns = normalize_patterns(globs) return paths if patterns.empty? absolute_patterns = patterns.map { |p| absolutize_pattern(p, root) } paths.select { |path| matches_any_pattern?(path, absolute_patterns) } end |
.fn_normalize_path_separators ⇒ Proc
Returns a lambda that normalizes path separators for the current platform. On Windows, returns a lambda that converts backslashes to forward slashes. On Unix, returns a pass-through lambda. The lambda is memoized so platform detection only happens once.
19 20 21 22 23 24 25 |
# File 'lib/cov_loupe/paths/glob_utils.rb', line 19 module_function def fn_normalize_path_separators @fn_normalize_path_separators ||= if CovLoupe.windows? ->(str) { str.tr('\\', '/') } else ->(str) { str } end end |
.matches_any_pattern?(abs_path, patterns) ⇒ Boolean
Tests if a file path matches any of the given absolute glob patterns. Uses File.fnmatch? for pure string matching without filesystem access. Normalizes paths to forward slashes on Windows for cross-platform compatibility. Automatically handles case-insensitive filesystems by detecting volume case-sensitivity.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/cov_loupe/paths/glob_utils.rb', line 49 module_function def matches_any_pattern?(abs_path, patterns) normalizer = fn_normalize_path_separators normalized_path = normalizer.call(abs_path) # Determine match flags based on volume case-sensitivity # Find first existing parent directory to test volume properties test_dir = abs_path until File.directory?(test_dir) parent = File.dirname(test_dir) break if parent == test_dir # Reached root (works on Windows and Unix) test_dir = parent end flags = GLOB_MATCH_FLAGS begin # Add case-insensitive matching for case-insensitive volumes flags |= File::FNM_CASEFOLD unless PathUtils.volume_case_sensitive?(test_dir) rescue SystemCallError, IOError # If we can't detect case sensitivity, assume case-insensitive to be conservative flags |= File::FNM_CASEFOLD end patterns.any? do |pattern| normalized_pattern = normalizer.call(pattern) File.fnmatch?(normalized_pattern, normalized_path, flags) end end |
.normalize_patterns(globs) ⇒ Object
27 28 29 |
# File 'lib/cov_loupe/paths/glob_utils.rb', line 27 module_function def normalize_patterns(globs) Array(globs).compact.map(&:to_s).reject(&:empty?) end |