Class: FunWith::Files::FilePath

Inherits:
Pathname
  • Object
show all
Defined in:
lib/files/file_path.rb

Direct Known Subclasses

RemotePath

Constant Summary collapse

SUCC_DIGIT_COUNT =

Gives a sequence of files. Examples: file.dat –> file.000000.dat file_without_ext –> file_without_ext.000000 If it sees a six-digit number at or near the end of the filename, it increments it.

You can change the length of the sequence string by passing in an argument, but it should always be the same value for a given set of files.

6

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ FilePath

Returns a new instance of FilePath.



4
5
6
# File 'lib/files/file_path.rb', line 4

def initialize( *args )
  super( File.join( *args ) )
end

Class Method Details

.cwd(*args) ⇒ Object

args implicitly joined to cwd



9
10
11
# File 'lib/files/file_path.rb', line 9

def self.cwd( *args )
  Dir.pwd.fwf_filepath.join( *args )
end

.home(*args) ⇒ Object



27
28
29
# File 'lib/files/file_path.rb', line 27

def self.home( *args )
  Dir.home.fwf_filepath.join( *args )
end

.pwd(*args) ⇒ Object



13
14
15
# File 'lib/files/file_path.rb', line 13

def self.pwd( *args )
  self.cwd( *args )
end

.tmpdir(&block) ⇒ Object



17
18
19
20
21
22
23
24
25
# File 'lib/files/file_path.rb', line 17

def self.tmpdir( &block )
  if block_given?
    Dir.mktmpdir do |d|
      yield d.fwf_filepath
    end
  else
    Dir.mktmpdir.fwf_filepath
  end
end

Instance Method Details

#append(content = nil, &block) ⇒ Object



162
163
164
165
166
167
168
169
# File 'lib/files/file_path.rb', line 162

def append( content = nil, &block )
  File.open( self, "a" ) do |f|
    f << content if content
    if block_given?
      yield f
    end
  end
end

#basename_and_extObject

base, ext = @path.basename_and_ext



208
209
210
# File 'lib/files/file_path.rb', line 208

def basename_and_ext
  [basename_no_ext, ext]
end

#basename_no_extObject

Does not return a filepath



192
193
194
# File 'lib/files/file_path.rb', line 192

def basename_no_ext
  self.basename.to_s.split(".")[0..-2].join(".")
end

#empty?Boolean

Not the same as zero?

Returns:

  • (Boolean)

Raises:

  • (Exceptions::FileDoesNotExist)


181
182
183
184
185
186
187
188
189
# File 'lib/files/file_path.rb', line 181

def empty?
  raise Exceptions::FileDoesNotExist unless self.exist?
  
  if self.file?
    File.size( self ) == 0
  elsif self.directory?
    self.glob( "**", "*" ).length == 0
  end
end

#expandObject



117
118
119
# File 'lib/files/file_path.rb', line 117

def expand
  self.class.new( File.expand_path( self ) )
end

#extObject

Does not return a filepath. Does not include leading period



202
203
204
205
# File 'lib/files/file_path.rb', line 202

def ext
  split_basename = self.basename.to_s.split(".")
  split_basename.length > 1 ? split_basename.last : ""
end

#fwf_filepathObject



228
229
230
# File 'lib/files/file_path.rb', line 228

def fwf_filepath
  self
end

#glob(*args) ⇒ Object

opts:

:flags  =>  File::FNM_CASEFOLD  
            File::FNM_DOTMATCH  
            File::FNM_NOESCAPE  
            File::FNM_PATHNAME  
            File::FNM_SYSCASE   
            See Dir documentation for details.  
              Can be given as an integer: (File::FNM_DOTMATCH | File::FNM_NOESCAPE)  
              or as an array: [File::FNM_CASEFOLD, File::FNM_DOTMATCH]

:class  =>  [self.class] The class of objects you want returned (String, FilePath, ClassLoader, etc.)
            Should probably be a subclass of FilePath or String.  Class.initialize() must accept a string
            [representing a file path] as the sole argument.

:recurse => [false] 

:ext => []  A single symbol, or a list containing strings/symbols representing file name extensions.
            No leading periods kthxbai.
:sensitive => true : do a case sensitive search.  I guess the default is an insensitive search, so
                     the default behaves similarly on Windows and Unix.  Not gonna fight it.

:dots => true      : include dotfiles.  Does not include . and ..s unless you also 
                     specify the option :parent_and_current => true.  

If opts[:recurse] / opts[:ext] not given, the user can get the same
results explicitly with arguments like .glob("**", "*.rb")

:all : if :all is the only argument, this is the same as .glob(“**”, “*”)



75
76
77
78
79
80
81
82
83
84
85
86
87
88
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
# File 'lib/files/file_path.rb', line 75

def glob( *args )
  opts = args.last.is_a?(Hash) ? args.pop : {}
  
  flags = case opts[:flags]
  when NilClass
    0
  when Array      # should be an array of integers
    opts[:flags].inject(0) do |memo, obj|
      memo | obj
    end
  when Integer
    opts[:flags]
  end
  
  flags |= File::FNM_DOTMATCH if opts[:dots]
  flags |= File::FNM_CASEFOLD if opts[:sensitive]
  
  if args.first == :all
    args = ["**", "*"]
  else
    recurser = opts[:recurse] ? "**" : nil
    extensions = case opts[:ext]
    when Symbol, String
      "*.#{opts[:ext]}"
    when Array
      extensions = opts[:ext].map(&:to_s).join(',')
      "*.{#{extensions}}"
    when NilClass
      nil
    end
    
    args += [recurser, extensions]
    args.compact!
  end
  
  opts[:class] ||= self.class
  
  files = Dir.glob( self.join(*args), flags ).map{ |f| opts[:class].new(f) }
  files.reject!{ |f| f.basename.to_s.match(/^\.{1,2}$/) } unless opts[:parent_and_current]
  files
end

#grep(regex) ⇒ Object



171
172
173
174
175
176
177
178
# File 'lib/files/file_path.rb', line 171

def grep( regex )
  return [] unless self.file?
  matching = []
  self.each_line do |line|
    matching.push( line ) if line.match( regex )
  end
  matching
end

#gsub(*args) ⇒ Object

gsub acts on the filepath, not the file contents



219
220
221
# File 'lib/files/file_path.rb', line 219

def gsub( *args )
  self.to_s.gsub(*args).fwf_filepath
end

#gsub!(*args) ⇒ Object



223
224
225
226
# File 'lib/files/file_path.rb', line 223

def gsub!( *args )
  new_path = self.to_s.gsub(*args)
  self.instance_variable_set(:@path, new_path)
end

#join(*args, &block) ⇒ Object Also known as: down



31
32
33
34
35
36
37
# File 'lib/files/file_path.rb', line 31

def join( *args, &block )
  if block_given?
    yield self.class.new( super(*args) )
  else
    self.class.new( super(*args) )
  end
end

#relative_path_from(dir) ⇒ Object

Basically Pathname.relative_path_from, but you can pass in strings



213
214
215
216
# File 'lib/files/file_path.rb', line 213

def relative_path_from( dir )
  dir = super( Pathname.new( dir ) )
  self.class.new( dir )
end

#rename(filename) ⇒ Object

File manipulation



326
327
328
# File 'lib/files/file_path.rb', line 326

def rename( filename )

end

#rename_all(pattern, gsubbed) ⇒ Object



330
331
332
# File 'lib/files/file_path.rb', line 330

def rename_all( pattern, gsubbed )

end

#succ(opts = { digit_count: SUCC_DIGIT_COUNT, timestamp: false }) ⇒ Object



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/files/file_path.rb', line 242

def succ( opts = { digit_count: SUCC_DIGIT_COUNT, timestamp: false } )
  if opts[:timestamp]
    timestamp = Time.now.strftime("%Y%m%d%H%M%S%L")
    digit_count = timestamp.length
  else
    timestamp = false
    digit_count = opts[:digit_count]
  end
  
  chunks = self.basename.to_s.split(".")
  # not yet sequence stamped, no file extension.
  if chunks.length == 1
    if timestamp
      chunks.push( timestamp )
    else
      chunks.push( "0" * digit_count )
    end
  # sequence stamp before file extension
  elsif match_data = chunks[-2].match( /^(\d{#{digit_count}})$/ )
    if timestamp
      chunks[-2] = timestamp
    else
      i = match_data[1].to_i + 1
      chunks[-2] = sprintf("%0#{digit_count}i", i)
    end
  # try to match sequence stamp to end of filename
  elsif match_data = chunks[-1].match( /^(\d{#{digit_count}})$/ )
    if timestamp
      chunks[-1] = timestamp
    else
      i = match_data[1].to_i + 1
      chunks[-1] = sprintf("%0#{digit_count}i", i)
    end
  # not yet sequence_stamped, has file extension
  else
    chunks = [chunks[0..-2], (timestamp ? timestamp : "0" * digit_count), chunks[-1]].flatten
  end

  self.up.join( chunks.join(".") )
end

#succession(opts = { digit_count: SUCC_DIGIT_COUNT, timestamp: false }) ⇒ Object

TODO: succession : enumerates a sequence of files that get passed to a block in order.



286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/files/file_path.rb', line 286

def succession( opts = { digit_count: SUCC_DIGIT_COUNT, timestamp: false } )
  if opts[:timestamp]
    timestamp = Time.now.strftime("%Y%m%d%H%M%S%L")
    digit_count = timestamp.length
  else
    timestamp = false
    digit_count = opts[:digit_count]
  end

  chunks = self.basename.to_s.split(".")
  glob_stamp_matcher = '[0-9]' * digit_count
  
  # unstamped filename, no extension
  if chunks.length == 1
    original = chunks.first
    stamped = [original, glob_stamp_matcher].join(".")
  # stamped filename, no extension
  elsif chunks[-1].match( /^\d{#{digit_count}}$/ )
    original = chunks[0..-2].join(".")
    stamped = [original, glob_stamp_matcher].join(".")
  # stamped filename, has extension
  elsif chunks[-2].match( /^\d{#{digit_count}}$/ )
    original = [chunks[0..-3], chunks.last].flatten.join(".")
    stamped = [chunks[0..-3], glob_stamp_matcher, chunks.last].join(".")
  # unstamped filename, has extension
  else
    original = chunks.join(".")
    stamped = [ chunks[0..-2], glob_stamp_matcher, chunks[-1] ].flatten.join(".")
  end

  [self.dirname.glob(original), self.dirname.glob(stamped)].flatten
end

#touch(*args) ⇒ Object

Raises error if self is a file and args present. Raises error if the file is not accessible for writing, or cannot be created. attempts to create a directory



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/files/file_path.rb', line 124

def touch( *args )
  raise "Cannot create subdirectory to a file" if self.file? && args.length > 0
  touched = self.join(*args)
  dir_for_touched_file = case args.length
    when 0
      self.up
    when 1
      self
    when 2..Infinity
      self.join( *(args[0..-2] ) )
    end
  
  self.touch_dir( dir_for_touched_file ) unless dir_for_touched_file.directory?
  FileUtils.touch( touched )
  return touched
end

#touch_dir(*args) {|touched| ... } ⇒ Object

Yields:

  • (touched)


141
142
143
144
145
146
147
148
149
150
151
# File 'lib/files/file_path.rb', line 141

def touch_dir( *args, &block )
  touched = self.join(*args)
  if touched.directory?
    FileUtils.touch( touched )    # update access time
  else
    FileUtils.mkdir_p( touched )  # create directory (and any needed parents)
  end
  
  yield touched if block_given?
  return touched
end

#upObject



41
42
43
# File 'lib/files/file_path.rb', line 41

def up
  self.class.new( self.join("..") ).expand
end

#without_extObject



196
197
198
# File 'lib/files/file_path.rb', line 196

def without_ext
  self.gsub(/\.#{self.ext}$/, '')
end

#write(content = nil, &block) ⇒ Object



153
154
155
156
157
158
159
160
# File 'lib/files/file_path.rb', line 153

def write( content = nil, &block )
  File.open( self, "w" ) do |f|
    f << content if content
    if block_given?
      yield f
    end
  end
end