Class: PathExpander

Inherits:
Object
  • Object
show all
Defined in:
lib/path_expander.rb

Overview

PathExpander helps pre-process command-line arguments expanding directories into their constituent files. It further helps by providing additional mechanisms to make specifying subsets easier with path subtraction and allowing for command-line arguments to be saved in a file.

NOTE: this is NOT an options processor. It is a path processor (basically everything else besides options). It does provide a mechanism for pre-filtering cmdline options, but not with the intent of actually processing them in PathExpander. Use OptionParser to deal with options either before or after passing ARGV through PathExpander.

Constant Summary collapse

VERSION =

:nodoc:

"1.1.3"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args, glob, path = ".") ⇒ PathExpander

Create a new path expander that operates on args and expands via glob as necessary. Takes an optional path arg to fall back on if no paths are found on the initial scan (see #process_args).



38
39
40
41
42
# File 'lib/path_expander.rb', line 38

def initialize args, glob, path = "."
  self.args = args
  self.glob = glob
  self.path = path
end

Instance Attribute Details

#argsObject

The args array to process.



21
22
23
# File 'lib/path_expander.rb', line 21

def args
  @args
end

#globObject

The glob used to expand dirs to files.



26
27
28
# File 'lib/path_expander.rb', line 26

def glob
  @glob
end

#pathObject

The path to scan if no paths are found in the initial scan.



31
32
33
# File 'lib/path_expander.rb', line 31

def path
  @path
end

Instance Method Details

#expand_dirs_to_files(*dirs) ⇒ Object

Takes an array of paths and returns an array of paths where all directories are expanded to all files found via the glob provided to PathExpander.

Paths are normalized to not have a leading “./”.



51
52
53
54
55
56
57
58
59
# File 'lib/path_expander.rb', line 51

def expand_dirs_to_files *dirs
  dirs.flatten.map { |p|
    if File.directory? p then
      Dir[File.join(p, glob)].find_all { |f| File.file? f }
    else
      p
    end
  }.flatten.sort.map { |s| s.to_s.delete_prefix "./" }
end

#filter_files(files, ignore) ⇒ Object

A file filter mechanism similar to, but not as extensive as, .gitignore files:

+ If a pattern does not contain a slash, it is treated as a shell glob. + If a pattern ends in a slash, it matches on directories (and contents). + Otherwise, it matches on relative paths.

File.fnmatch is used throughout, so glob patterns work for all 3 types.

Takes a list of files and either an io or path of ignore data and returns a list of files left after filtering.



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/path_expander.rb', line 167

def filter_files files, ignore
  ignore_paths = if ignore.respond_to? :read then
                   ignore.read
                 elsif File.exist? ignore then
                   File.read ignore
                 end

  if ignore_paths then
    nonglobs, globs = ignore_paths.split("\n").partition { |p| p.include? "/" }
    dirs, ifiles    = nonglobs.partition { |p| p.end_with? "/" }
    dirs            = dirs.map { |s| s.chomp "/" }

    dirs.map!   { |i| File.expand_path i }
    globs.map!  { |i| File.expand_path i }
    ifiles.map! { |i| File.expand_path i }

    only_paths = File::FNM_PATHNAME
    files = files.reject { |f|
      f = File.expand_path(f)
      dirs.any?     { |i| File.fnmatch?(i, File.dirname(f), only_paths) } ||
        globs.any?  { |i| File.fnmatch?(i, f) } ||
        ifiles.any? { |i| File.fnmatch?(i, f, only_paths) }
    }
  end

  files
end

#processObject

Top-level method processes args. It replaces args’ contents with a new array of flags to process and returns a list of files to process. Eg

PathExpander.new(ARGV).process.each do |f|
  puts "./#{f}"
end


146
147
148
149
150
151
152
# File 'lib/path_expander.rb', line 146

def process
  files, flags = process_args

  args.replace process_flags flags

  files.uniq
end

#process_argsObject

Enumerate over args passed to PathExpander and return a list of files and flags to process. Arguments are processed as:

-file_path

Subtract path from file to be processed.

-dir_path

Expand and subtract paths from files to be processed.

-not_a_path

Add to flags to be processed.

dir_path

Expand and add to files to be processed.

file_path

Add to files to be processed.

  • Add “-” (stdin) to files to be processed.

See expand_dirs_to_files for details on how expansion occurs.

Subtraction happens last, regardless of argument ordering.

If no files are found (which is not the same as having an empty file list after subtraction), then fall back to expanding on the default #path given to initialize.



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/path_expander.rb', line 89

def process_args
  pos_files = []
  neg_files = []
  flags     = []
  clean     = true

  args.each do |arg|
    case arg
    when /^@(.*)/ then # push back on, so they can have dirs/-/@ as well
      clean = false
      args.concat process_file $1
    when "-" then
      pos_files << arg
    when /^-(.*)/ then
      if File.exist? $1 then
        clean = false
        neg_files += expand_dirs_to_files($1)
      else
        flags << arg
      end
    else
      root_path = File.expand_path(arg) == "/" # eg: -n /./
      if File.exist? arg and not root_path then
        clean = false
        pos_files += expand_dirs_to_files(arg)
      else
        flags << arg
      end
    end
  end

  files = pos_files - neg_files
  files += expand_dirs_to_files(self.path) if files.empty? && clean

  [files, flags]
end

#process_file(path) ⇒ Object

Process a file into more arguments. Override this to add additional capabilities.



65
66
67
# File 'lib/path_expander.rb', line 65

def process_file path
  File.readlines(path).map(&:chomp)
end

#process_flags(flags) ⇒ Object

Process over flags and treat any special ones here. Returns an array of the flags you haven’t processed.

This version does nothing. Subclass and override for customization.



133
134
135
# File 'lib/path_expander.rb', line 133

def process_flags flags
  flags
end