Module: CovLoupe::VolumeCaseSensitivity
- Defined in:
- lib/cov_loupe/paths/volume_case_sensitivity.rb
Overview
Handles detection and caching of filesystem volume case sensitivity. Provides thread-safe case sensitivity detection with caching for performance.
Constant Summary collapse
- CACHE_MUTEX =
Mutex for thread-safe cache access
Mutex.new
Class Method Summary collapse
-
.cache ⇒ Hash
Returns the current cache contents (useful for testing).
-
.clear_cache ⇒ void
Clears the case sensitivity cache (useful for testing).
-
.detect_case_sensitive_using_existing_file?(abs_path, existing_file) ⇒ Boolean
Detects case sensitivity using an existing file in the directory.
-
.detect_case_sensitive_using_temp_file?(abs_path) ⇒ Boolean
Detects case sensitivity using a temporary test file.
-
.detect_case_sensitivity?(abs_path) ⇒ Boolean
Detects case sensitivity for a given directory.
-
.find_existing_file(abs_path) ⇒ String?
Finds an existing file in the directory suitable for case sensitivity testing.
-
.generate_unique_test_filename(abs_path) ⇒ String
Generates a unique test filename that doesn't conflict with existing files.
-
.get_from_cache(abs_path) ⇒ Boolean?
Retrieves a value from the cache (thread-safe).
-
.set_in_cache(abs_path, value) ⇒ void
Stores a value in the cache (thread-safe).
-
.volume_case_sensitive?(path = nil) ⇒ Boolean
Detects whether the volume at the given path is case-sensitive.
Class Method Details
.cache ⇒ Hash
Returns the current cache contents (useful for testing)
58 59 60 61 62 63 |
# File 'lib/cov_loupe/paths/volume_case_sensitivity.rb', line 58 def cache CACHE_MUTEX.synchronize do @cache ||= {} @cache.dup end end |
.clear_cache ⇒ void
This method returns an undefined value.
Clears the case sensitivity cache (useful for testing)
49 50 51 52 53 |
# File 'lib/cov_loupe/paths/volume_case_sensitivity.rb', line 49 def clear_cache CACHE_MUTEX.synchronize do @cache = {} end end |
.detect_case_sensitive_using_existing_file?(abs_path, existing_file) ⇒ Boolean
Detects case sensitivity using an existing file in the directory
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/cov_loupe/paths/volume_case_sensitivity.rb', line 118 def detect_case_sensitive_using_existing_file?(abs_path, existing_file) original = File.join(abs_path, existing_file) # tr('A-Za-z', 'a-zA-Z') swaps the case of every letter in the filename alternate_name = existing_file.tr('A-Za-z', 'a-zA-Z') alternate = File.join(abs_path, alternate_name) if File.exist?(alternate) # Same file -> case-insensitive, different files -> case-sensitive !File.identical?(original, alternate) else # On a case-insensitive filesystem, File.exist? for the alternate-cased name would # resolve to the same inode and return true. Reaching this branch means the filesystem # treats the two paths as distinct entries, confirming case-sensitivity. true end end |
.detect_case_sensitive_using_temp_file?(abs_path) ⇒ Boolean
Detects case sensitivity using a temporary test file
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/cov_loupe/paths/volume_case_sensitivity.rb', line 139 def detect_case_sensitive_using_temp_file?(abs_path) require 'securerandom' # Create a temporary test file with a unique name test_file = generate_unique_test_filename(abs_path) begin FileUtils.touch(test_file) variants = [test_file, test_file.upcase, test_file.downcase] # Test if exactly one variant exists (case-sensitive) vs all exist (case-insensitive) variants.one? { |variant| File.exist?(variant) } ensure # Clean up all potential variants [test_file, test_file.upcase, test_file.downcase].each do |variant| FileUtils.rm_f(variant) end end end |
.detect_case_sensitivity?(abs_path) ⇒ Boolean
Detects case sensitivity for a given directory
92 93 94 95 96 97 98 99 100 101 |
# File 'lib/cov_loupe/paths/volume_case_sensitivity.rb', line 92 def detect_case_sensitivity?(abs_path) # Try to use an existing file to avoid filesystem writes existing_file = find_existing_file(abs_path) if existing_file detect_case_sensitive_using_existing_file?(abs_path, existing_file) else detect_case_sensitive_using_temp_file?(abs_path) end end |
.find_existing_file(abs_path) ⇒ String?
Finds an existing file in the directory suitable for case sensitivity testing
107 108 109 110 111 |
# File 'lib/cov_loupe/paths/volume_case_sensitivity.rb', line 107 def find_existing_file(abs_path) Dir.children(abs_path).find do |name| name.match?(/[A-Za-z]/) && File.file?(File.join(abs_path, name)) end end |
.generate_unique_test_filename(abs_path) ⇒ String
Generates a unique test filename that doesn't conflict with existing files
162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/cov_loupe/paths/volume_case_sensitivity.rb', line 162 def generate_unique_test_filename(abs_path) require 'securerandom' test_file = nil while test_file.nil? candidate = File.join(abs_path, "CovLoupe_CaseSensitivity_Test_#{SecureRandom.hex(16)}.tmp") variants = [candidate, candidate.upcase, candidate.downcase] test_file = candidate if variants.none? { |v| File.exist?(v) } end test_file end |
.get_from_cache(abs_path) ⇒ Boolean?
Retrieves a value from the cache (thread-safe)
69 70 71 72 73 74 |
# File 'lib/cov_loupe/paths/volume_case_sensitivity.rb', line 69 def get_from_cache(abs_path) CACHE_MUTEX.synchronize do @cache ||= {} @cache[abs_path] end end |
.set_in_cache(abs_path, value) ⇒ void
This method returns an undefined value.
Stores a value in the cache (thread-safe)
81 82 83 84 85 86 |
# File 'lib/cov_loupe/paths/volume_case_sensitivity.rb', line 81 def set_in_cache(abs_path, value) CACHE_MUTEX.synchronize do @cache ||= {} @cache[abs_path] = value end end |
.volume_case_sensitive?(path = nil) ⇒ Boolean
Detects whether the volume at the given path is case-sensitive. Prefer using an existing file (via File.identical?) to avoid writing; fall back to a temporary file if no suitable file exists.
This method caches results by path to avoid repeated filesystem checks, which can be expensive, especially on network-mounted volumes.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/cov_loupe/paths/volume_case_sensitivity.rb', line 22 def volume_case_sensitive?(path = nil) require 'securerandom' test_path = path ? File.absolute_path(path) : Dir.pwd abs_path = File.absolute_path(test_path) # Check cache first (thread-safe read) cached_value = get_from_cache(abs_path) return cached_value unless cached_value.nil? # Return false if directory doesn't exist return false unless File.directory?(abs_path) result = detect_case_sensitivity?(abs_path) # Store result in cache (thread-safe write) set_in_cache(abs_path, result) result rescue SystemCallError, IOError # Can't detect from filesystem, assume case-insensitive to be conservative false end |