Class: Ace::Support::Fs::Molecules::DirectoryTraverser

Inherits:
Object
  • Object
show all
Defined in:
lib/ace/support/fs/molecules/directory_traverser.rb

Overview

Traverses directory tree to find configuration directories

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config_dir: ".ace", start_path: nil) ⇒ DirectoryTraverser

Initialize traverser

Parameters:

  • config_dir (String) (defaults to: ".ace")

    Name of config directory to find (default: “.ace”)

  • start_path (String) (defaults to: nil)

    Path to start traversal from



16
17
18
19
# File 'lib/ace/support/fs/molecules/directory_traverser.rb', line 16

def initialize(config_dir: ".ace", start_path: nil)
  @config_dir = config_dir
  @start_path = start_path ? Atoms::PathExpander.expand(start_path) : Dir.pwd
end

Instance Attribute Details

#config_dirObject (readonly)

Returns the value of attribute config_dir.



11
12
13
# File 'lib/ace/support/fs/molecules/directory_traverser.rb', line 11

def config_dir
  @config_dir
end

Instance Method Details

#build_cascade_prioritiesHash<String, Integer>

Build cascade paths with priorities

Returns:

  • (Hash<String, Integer>)

    Map of directory paths to priorities



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/ace/support/fs/molecules/directory_traverser.rb', line 96

def build_cascade_priorities
  priorities = {}
  directories = find_config_directories

  # Assign priorities - closer to cwd = higher priority (lower number)
  directories.each_with_index do |dir, index|
    priorities[dir] = index * 10
  end

  # Add home directory with lower priority
  home_config = File.expand_path("~/#{@config_dir}")
  if Dir.exist?(home_config) && !priorities.key?(home_config)
    priorities[home_config] = (directories.length + 1) * 10
  end

  priorities
end

#directory_hierarchyArray<String>

Get the directory hierarchy from current to root

Returns:

  • (Array<String>)

    All directories from current to project/filesystem root



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/ace/support/fs/molecules/directory_traverser.rb', line 68

def directory_hierarchy
  hierarchy = []
  current_path = @start_path

  # Find project root
  project_root = ProjectRootFinder.find(start_path: @start_path)
  stop_at = project_root || "/"

  loop do
    hierarchy << current_path

    # Stop if we've reached our stopping point
    break if current_path == stop_at

    # Get parent directory
    parent = File.dirname(current_path)

    # Stop if we've reached filesystem root
    break if parent == current_path

    current_path = parent
  end

  hierarchy
end

#find_config_directoriesArray<String>

Find all config directories in the traversal path

Returns:

  • (Array<String>)

    Full paths to config directories



62
63
64
# File 'lib/ace/support/fs/molecules/directory_traverser.rb', line 62

def find_config_directories
  traverse.map { |dir| File.join(dir, @config_dir) }
end

#traverseArray<String>

Traverse from current directory up to project root or filesystem root

Returns:

  • (Array<String>)

    Ordered list of directories containing config folders



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/ace/support/fs/molecules/directory_traverser.rb', line 23

def traverse
  directories = []
  current_path = @start_path
  visited = Set.new

  # Find project root
  project_root = ProjectRootFinder.find(start_path: @start_path)

  # Traverse up from current directory
  loop do
    # Avoid infinite loops
    break if visited.include?(current_path)
    visited.add(current_path)

    # Check if config directory exists at this level
    config_path = File.join(current_path, @config_dir)
    directories << current_path if Dir.exist?(config_path)

    # Get parent directory
    parent = File.dirname(current_path)

    # Stop if we've reached filesystem root
    break if parent == current_path

    # Stop if we've gone past project root (if it exists)
    # We check after adding current_path to allow project root itself
    if project_root && current_path == project_root
      # Already processed project root, can stop
      break
    end

    current_path = parent
  end

  directories
end