Module: Kettle::Dev::Paths

Defined in:
lib/kettle/dev/paths.rb

Overview

Filesystem path operations shared by Kettle tools.

Class Method Summary collapse

Class Method Details

.canonical(path, base: nil) ⇒ Object

Expands a path and resolves its longest existing ancestor. This keeps comparisons useful for generated paths that do not exist yet.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/kettle/dev/paths.rb', line 11

def canonical(path, base: nil)
  expanded = File.expand_path(path, base)
  existing = expanded
  suffix = []
  until File.exist?(existing) || File.symlink?(existing)
    parent = File.dirname(existing)
    return expanded if parent == existing

    suffix.unshift(File.basename(existing))
    existing = parent
  end

  suffix.reduce(File.realpath(existing)) { |resolved, component| File.join(resolved, component) }
rescue Errno::EACCES, Errno::ENOENT
  expanded
end

.comparable_parts(path) ⇒ Object



59
60
61
62
# File 'lib/kettle/dev/paths.rb', line 59

def comparable_parts(path)
  parts = path_parts(path)
  Gem.win_platform? ? parts.map(&:downcase) : parts
end

.path_parts(path) ⇒ Object



55
56
57
# File 'lib/kettle/dev/paths.rb', line 55

def path_parts(path)
  path.tr("\\", "/").split("/").reject(&:empty?)
end

.same?(left, right, base: nil) ⇒ Boolean

Uses filesystem identity for existing paths, so symlink aliases and platform-specific aliases are compared by the operating system.

Returns:

  • (Boolean)


30
31
32
33
34
35
36
37
38
39
40
# File 'lib/kettle/dev/paths.rb', line 30

def same?(left, right, base: nil)
  left_path = canonical(left, base: base)
  right_path = canonical(right, base: base)
  return true if left_path == right_path

  return File.identical?(left_path, right_path) if File.exist?(left_path) && File.exist?(right_path)

  comparable_parts(left_path) == comparable_parts(right_path)
rescue Errno::EACCES, Errno::ENOENT, Errno::ENOTDIR, NotImplementedError
  comparable_parts(left_path || left) == comparable_parts(right_path || right)
end

.within?(path, root, base: nil) ⇒ Boolean

Tests containment by path components, not by a string prefix.

Returns:

  • (Boolean)


43
44
45
46
47
48
49
50
51
52
53
# File 'lib/kettle/dev/paths.rb', line 43

def within?(path, root, base: nil)
  candidate = canonical(path, base: base)
  boundary = canonical(root, base: base)
  return true if same?(candidate, boundary)

  candidate_parts = comparable_parts(candidate)
  boundary_parts = comparable_parts(boundary)
  candidate_parts[0, boundary_parts.length] == boundary_parts
rescue Errno::EACCES, Errno::ENOENT, Errno::ENOTDIR
  false
end