Module: Ace::Core::Atoms::GlobExpander

Defined in:
lib/ace/core/atoms/glob_expander.rb

Overview

Pure glob pattern expansion and file matching functions

Class Method Summary collapse

Class Method Details

.expand(pattern, base_dir: Dir.pwd, flags: 0) ⇒ Array<String>

Expand glob pattern to file paths

Parameters:

  • pattern (String)

    Glob pattern

  • base_dir (String) (defaults to: Dir.pwd)

    Base directory for relative patterns

  • flags (Integer) (defaults to: 0)

    File::FNM_* flags for matching

Returns:

  • (Array<String>)

    Matched file paths



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/ace/core/atoms/glob_expander.rb', line 17

def expand(pattern, base_dir: Dir.pwd, flags: 0)
  return [] if pattern.nil? || pattern.empty?

  # Ensure base_dir is absolute
  base_dir = File.expand_path(base_dir)

  # Handle absolute patterns
  if pattern.start_with?("/")
    Dir.glob(pattern, flags).sort
  else
    # Make pattern relative to base_dir
    full_pattern = File.join(base_dir, pattern)
    Dir.glob(full_pattern, flags).map do |path|
      # Return relative paths from base_dir
      Pathname.new(path).relative_path_from(Pathname.new(base_dir)).to_s
    rescue ArgumentError
      # If we can't make it relative, return absolute
      path
    end.sort
  end
rescue
  []
end

.expand_multiple(patterns, base_dir: Dir.pwd, flags: 0) ⇒ Array<String>

Expand multiple glob patterns

Parameters:

  • patterns (Array<String>)

    Array of glob patterns

  • base_dir (String) (defaults to: Dir.pwd)

    Base directory for relative patterns

  • flags (Integer) (defaults to: 0)

    File::FNM_* flags for matching

Returns:

  • (Array<String>)

    Unique sorted file paths



46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/ace/core/atoms/glob_expander.rb', line 46

def expand_multiple(patterns, base_dir: Dir.pwd, flags: 0)
  return [] if patterns.nil? || patterns.empty?

  patterns = Array(patterns)
  results = []

  patterns.each do |pattern|
    results.concat(expand(pattern, base_dir: base_dir, flags: flags))
  end

  results.uniq.sort
end

.expand_with_exclusions(pattern, exclude: [], base_dir: Dir.pwd) ⇒ Array<String>

Expand pattern with exclusions

Parameters:

  • pattern (String)

    Glob pattern to expand

  • exclude (Array<String>) (defaults to: [])

    Patterns to exclude

  • base_dir (String) (defaults to: Dir.pwd)

    Base directory

Returns:

  • (Array<String>)

    Matched paths excluding excluded ones



91
92
93
94
95
96
# File 'lib/ace/core/atoms/glob_expander.rb', line 91

def expand_with_exclusions(pattern, exclude: [], base_dir: Dir.pwd)
  expanded = expand(pattern, base_dir: base_dir)
  return expanded if exclude.nil? || exclude.empty?

  filter_excluded(expanded, exclude)
end

.filter_excluded(paths, exclude_patterns, flags: File::FNM_PATHNAME) ⇒ Array<String>

Filter paths by exclusion patterns

Parameters:

  • paths (Array<String>)

    Paths to filter

  • exclude_patterns (Array<String>)

    Patterns to exclude

  • flags (Integer) (defaults to: File::FNM_PATHNAME)

    File::FNM_* flags for matching

Returns:

  • (Array<String>)

    Filtered paths



78
79
80
81
82
83
84
# File 'lib/ace/core/atoms/glob_expander.rb', line 78

def filter_excluded(paths, exclude_patterns, flags: File::FNM_PATHNAME)
  return paths if paths.nil? || exclude_patterns.nil? || exclude_patterns.empty?

  paths.reject do |path|
    matches?(path, exclude_patterns, flags: flags)
  end
end

.find_files(pattern, base_dir: Dir.pwd, max_depth: nil) ⇒ Array<String>

Find files recursively with pattern

Parameters:

  • pattern (String)

    File name pattern

  • base_dir (String) (defaults to: Dir.pwd)

    Starting directory

  • max_depth (Integer) (defaults to: nil)

    Maximum directory depth (nil for unlimited)

Returns:

  • (Array<String>)

    Found file paths



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/ace/core/atoms/glob_expander.rb', line 103

def find_files(pattern, base_dir: Dir.pwd, max_depth: nil)
  return [] if pattern.nil?

  base_dir = File.expand_path(base_dir)
  results = []

  # Build the glob pattern based on depth
  if max_depth.nil?
    glob_pattern = File.join(base_dir, "**", pattern)
  else
    # Build pattern with limited depth
    depth_pattern = (0..max_depth).map do |depth|
      parts = ["*"] * depth
      File.join(base_dir, *parts, pattern)
    end

    depth_pattern.each do |p|
      results.concat(Dir.glob(p))
    end

    return results.map do |path|
      Pathname.new(path).relative_path_from(Pathname.new(base_dir)).to_s
    rescue ArgumentError
      path
    end.uniq.sort
  end

  Dir.glob(glob_pattern).map do |path|
    Pathname.new(path).relative_path_from(Pathname.new(base_dir)).to_s
  rescue ArgumentError
    path
  end.sort
end

.glob_pattern?(pattern) ⇒ Boolean

Check if pattern is a glob pattern

Parameters:

  • pattern (String)

    Pattern to check

Returns:

  • (Boolean)

    true if pattern contains glob characters



140
141
142
143
144
# File 'lib/ace/core/atoms/glob_expander.rb', line 140

def glob_pattern?(pattern)
  return false if pattern.nil?

  pattern.match?(/[*?\[{]/)
end

.matches?(path, patterns, flags: File::FNM_PATHNAME) ⇒ Boolean

Check if path matches any of the patterns

Parameters:

  • path (String)

    File path to check

  • patterns (Array<String>)

    Patterns to match against

  • flags (Integer) (defaults to: File::FNM_PATHNAME)

    File::FNM_* flags for matching

Returns:

  • (Boolean)

    true if path matches any pattern



64
65
66
67
68
69
70
71
# File 'lib/ace/core/atoms/glob_expander.rb', line 64

def matches?(path, patterns, flags: File::FNM_PATHNAME)
  return false if path.nil? || patterns.nil?

  patterns = Array(patterns)
  patterns.any? do |pattern|
    File.fnmatch(pattern, path, flags)
  end
end

.normalize_separators(path) ⇒ String

Normalize path separators for current OS

Parameters:

  • path (String)

    Path to normalize

Returns:

  • (String)

    Normalized path



149
150
151
152
153
# File 'lib/ace/core/atoms/glob_expander.rb', line 149

def normalize_separators(path)
  return nil if path.nil?

  path.gsub(/[\\\/]+/, File::SEPARATOR)
end

.to_regex(pattern) ⇒ Regexp

Convert glob pattern to regex

Parameters:

  • pattern (String)

    Glob pattern

Returns:

  • (Regexp)

    Regular expression equivalent



158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/ace/core/atoms/glob_expander.rb', line 158

def to_regex(pattern)
  return nil if pattern.nil?

  # Escape special regex characters except glob ones
  escaped = pattern.gsub(/[.+^$()|\[\]{}\\]/) { |m| "\\#{m}" }

  # Convert glob patterns to regex
  regex_pattern = escaped
    .gsub("**/", ".*/")      # ** matches any depth
    .gsub("*", "[^/]*")       # * matches within directory
    .tr("?", ".")           # ? matches single character

  Regexp.new("^#{regex_pattern}$")
end