fun_with_files 0.0.14 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.markdown +15 -3
- data/Gemfile +17 -7
- data/{README.rdoc → README.markdown} +12 -11
- data/Rakefile +3 -3
- data/VERSION +1 -1
- data/lib/fun_with/files/bootstrapper.rb +87 -0
- data/lib/fun_with/files/core_extensions/file.rb +15 -3
- data/lib/fun_with/files/core_extensions/set.rb +12 -0
- data/lib/fun_with/files/core_extensions/true_class.rb +11 -0
- data/lib/fun_with/files/digest_methods.rb +50 -17
- data/lib/fun_with/files/directory_builder.rb +9 -4
- data/lib/fun_with/files/downloader.rb +29 -16
- data/lib/fun_with/files/errors.rb +9 -1
- data/lib/fun_with/files/file_manipulation_methods.rb +25 -15
- data/lib/fun_with/files/file_orderer.rb +2 -0
- data/lib/fun_with/files/file_path.rb +242 -156
- data/lib/fun_with/files/file_path_class_methods.rb +23 -2
- data/lib/fun_with/files/file_permission_methods.rb +18 -7
- data/lib/fun_with/files/file_requirements.rb +63 -7
- data/lib/fun_with/files/requirements/manager.rb +104 -0
- data/lib/fun_with/files/root_path.rb +3 -3
- data/lib/fun_with/files/stat_methods.rb +33 -0
- data/lib/fun_with/files/string_behavior.rb +6 -2
- data/lib/fun_with/files/utils/byte_size.rb +143 -0
- data/lib/fun_with/files/utils/opts.rb +26 -0
- data/lib/fun_with/files/utils/succession.rb +47 -0
- data/lib/fun_with/files/utils/timestamp.rb +47 -0
- data/lib/fun_with/files/utils/timestamp_format.rb +31 -0
- data/lib/fun_with/files/watcher.rb +157 -0
- data/lib/fun_with/files/watchers/directory_watcher.rb +67 -0
- data/lib/fun_with/files/watchers/file_watcher.rb +45 -0
- data/lib/fun_with/files/watchers/missing_watcher.rb +23 -0
- data/lib/fun_with/files/watchers/node_watcher.rb +44 -0
- data/lib/fun_with/testing/assertions/fun_with_files.rb +91 -0
- data/lib/fun_with/testing/test_case_extensions.rb +12 -0
- data/lib/fun_with_files.rb +5 -31
- data/test/helper.rb +13 -5
- data/test/test_core_extensions.rb +6 -0
- data/test/test_descent.rb +2 -2
- data/test/test_directory_builder.rb +29 -10
- data/test/test_extension_methods.rb +62 -0
- data/test/test_file_manipulation.rb +4 -4
- data/test/test_file_path.rb +83 -56
- data/test/test_file_requirements.rb +36 -0
- data/test/test_fun_with_files.rb +1 -1
- data/test/test_fwf_assertions.rb +62 -0
- data/test/test_moving_files.rb +111 -0
- data/test/test_permission_methods.rb +22 -0
- data/test/test_root_path.rb +9 -0
- data/test/test_stat_methods.rb +17 -0
- data/test/test_timestamping.rb +74 -0
- data/test/test_utils_bytesize.rb +71 -0
- data/test/test_utils_succession.rb +30 -0
- data/test/test_watchers.rb +196 -0
- metadata +59 -13
- /data/lib/fun_with/files/core_extensions/{false.rb → false_class.rb} +0 -0
- /data/lib/fun_with/files/core_extensions/{nil.rb → nil_class.rb} +0 -0
@@ -1,12 +1,11 @@
|
|
1
1
|
module FunWith
|
2
2
|
module Files
|
3
3
|
class FilePath < Pathname
|
4
|
-
|
5
4
|
SUCC_DIGIT_COUNT = 6
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
|
6
|
+
def initialize( *args, &block )
|
7
|
+
super( File.join( *(args.map(&:to_s) ) ) )
|
8
|
+
_yield_and_return( self, &block )
|
10
9
|
end
|
11
10
|
|
12
11
|
attr_accessor :path
|
@@ -25,33 +24,45 @@ module FunWith
|
|
25
24
|
Dir.mktmpdir.fwf_filepath
|
26
25
|
end
|
27
26
|
end
|
27
|
+
|
28
|
+
# The file is created within a temporary directory
|
29
|
+
def self.tmpfile( ext = :tmp, &block )
|
30
|
+
filename = rand( 2 ** 64 ).to_s(16).fwf_filepath.ext( ext )
|
31
|
+
|
32
|
+
if block_given?
|
33
|
+
self.tmpdir do |tmp|
|
34
|
+
yield tmp.join( filename ).touch
|
35
|
+
end
|
36
|
+
else
|
37
|
+
self.tmpdir.join( filename ).touch
|
38
|
+
end
|
39
|
+
end
|
28
40
|
|
29
41
|
def join( *args, &block )
|
30
|
-
joined_path = self.class.new( super(*args) )
|
31
|
-
|
32
|
-
joined_path
|
42
|
+
joined_path = self.class.new( super( *(args.map(&:to_s) ) ) )
|
43
|
+
_yield_and_return( joined_path, &block )
|
33
44
|
end
|
34
45
|
|
35
46
|
def join!( *args, &block )
|
36
47
|
@path = self.join( *args, &block ).to_str
|
37
|
-
self
|
48
|
+
_yield_and_return( self, &block )
|
38
49
|
end
|
39
50
|
|
40
|
-
def / arg
|
41
|
-
self.join( arg )
|
51
|
+
def /( arg, &block )
|
52
|
+
_yield_and_return( self.join( arg ), &block )
|
42
53
|
end
|
43
54
|
|
44
|
-
def [] *args
|
45
|
-
self.join(*args)
|
55
|
+
def []( *args, &block )
|
56
|
+
_yield_and_return( self.join(*args), &block )
|
46
57
|
end
|
47
58
|
|
48
59
|
|
49
|
-
def doesnt_exist?
|
50
|
-
self.exist? == false
|
60
|
+
def doesnt_exist?( &block )
|
61
|
+
_yield_self_on_success( self.exist? == false, &block )
|
51
62
|
end
|
52
63
|
|
53
|
-
def not_a_file?
|
54
|
-
! self.file
|
64
|
+
def not_a_file?( &block )
|
65
|
+
_yield_self_on_success( ! self.file?, &block )
|
55
66
|
end
|
56
67
|
|
57
68
|
alias :absent? :doesnt_exist?
|
@@ -181,8 +192,16 @@ module FunWith
|
|
181
192
|
end
|
182
193
|
end
|
183
194
|
|
184
|
-
def entries
|
185
|
-
self.glob( :recurse => false )
|
195
|
+
def entries( &block )
|
196
|
+
rval = self.glob( :recurse => false )
|
197
|
+
|
198
|
+
if block_given?
|
199
|
+
for entry in rval
|
200
|
+
yield entry
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
rval
|
186
205
|
end
|
187
206
|
|
188
207
|
def expand
|
@@ -195,7 +214,7 @@ module FunWith
|
|
195
214
|
#
|
196
215
|
# Takes an options hash as the last argument, allowing same options as FileUtils.touch
|
197
216
|
def touch( *args, &block )
|
198
|
-
args, opts = extract_opts_from_args( args )
|
217
|
+
args, opts = Utils::Opts.extract_opts_from_args( args )
|
199
218
|
|
200
219
|
raise "Cannot create subdirectory to a file" if self.file? && args.length > 0
|
201
220
|
touched = self.join(*args)
|
@@ -210,7 +229,7 @@ module FunWith
|
|
210
229
|
end
|
211
230
|
|
212
231
|
self.touch_dir( dir_for_touched_file, opts ) unless dir_for_touched_file.directory?
|
213
|
-
FileUtils.touch( touched,
|
232
|
+
FileUtils.touch( touched, ** Utils::Opts.narrow_file_utils_options( opts, :touch ) )
|
214
233
|
|
215
234
|
yield touched if block_given?
|
216
235
|
return touched
|
@@ -219,13 +238,13 @@ module FunWith
|
|
219
238
|
# Takes the options of both FileUtils.touch and FileUtils.mkdir_p
|
220
239
|
# mkdir_p options will only matter if the directory is being created.
|
221
240
|
def touch_dir( *args, &block )
|
222
|
-
args, opts = extract_opts_from_args( args )
|
241
|
+
args, opts = Utils::Opts.extract_opts_from_args( args )
|
223
242
|
|
224
243
|
touched = self.join(*args)
|
225
244
|
if touched.directory?
|
226
|
-
FileUtils.touch( touched,
|
245
|
+
FileUtils.touch( touched, ** Utils::Opts.narrow_file_utils_options( opts, :touch ) ) # update access time
|
227
246
|
else
|
228
|
-
FileUtils.mkdir_p( touched,
|
247
|
+
FileUtils.mkdir_p( touched, ** Utils::Opts.narrow_file_utils_options( opts, :mkdir_p ) ) # create directory (and any needed parents)
|
229
248
|
end
|
230
249
|
|
231
250
|
yield touched if block_given?
|
@@ -233,7 +252,7 @@ module FunWith
|
|
233
252
|
end
|
234
253
|
|
235
254
|
def write( *args, &block )
|
236
|
-
args, opts = extract_opts_from_args( args )
|
255
|
+
args, opts = Utils::Opts.extract_opts_from_args( args )
|
237
256
|
|
238
257
|
content = args.first
|
239
258
|
|
@@ -273,6 +292,8 @@ module FunWith
|
|
273
292
|
end
|
274
293
|
end
|
275
294
|
end
|
295
|
+
|
296
|
+
alias :<< :append
|
276
297
|
|
277
298
|
# Returns a [list] of the lines in the file matching the given file. Contrast with
|
278
299
|
|
@@ -291,29 +312,122 @@ module FunWith
|
|
291
312
|
# empty? has different meanings depending on whether you're talking about a file
|
292
313
|
# or a directory. A directory must not have any files or subdirectories. A file
|
293
314
|
# must not have any data in it.
|
294
|
-
def empty?
|
315
|
+
def empty?( &block )
|
295
316
|
raise Exceptions::FileDoesNotExist unless self.exist?
|
296
|
-
|
317
|
+
|
297
318
|
if self.file?
|
298
|
-
File.size( self ) == 0
|
319
|
+
is_empty = File.size( self ) == 0
|
299
320
|
elsif self.directory?
|
300
|
-
|
321
|
+
is_empty = Dir.entries( self ).length <= 2 # Dir.entries returns ".." and "." even when nothing else is there
|
301
322
|
end
|
323
|
+
|
324
|
+
_yield_self_on_success( is_empty, &block )
|
302
325
|
end
|
303
326
|
|
304
327
|
# Does not return a filepath
|
328
|
+
#
|
329
|
+
# TODO: Why not?
|
305
330
|
def basename_no_ext
|
306
331
|
self.basename.to_s.split(".")[0..-2].join(".")
|
307
332
|
end
|
308
|
-
|
309
|
-
|
310
|
-
|
333
|
+
|
334
|
+
#
|
335
|
+
def size( units = :B )
|
336
|
+
sz = self.stat.size
|
337
|
+
|
338
|
+
case units
|
339
|
+
when :B
|
340
|
+
sz
|
341
|
+
when :K
|
342
|
+
(sz / 1024.0).round(1)
|
343
|
+
when :M
|
344
|
+
(sz / 1048576.0).round(1)
|
345
|
+
when :G
|
346
|
+
(sz / 1073741824.0).round(1)
|
347
|
+
end
|
311
348
|
end
|
312
349
|
|
313
|
-
#
|
314
|
-
#
|
315
|
-
#
|
316
|
-
#
|
350
|
+
# Returns the path, stripped of the final extension (.ARG).
|
351
|
+
# The result cannot destroy a dotfile or leave an empty
|
352
|
+
# path
|
353
|
+
# "~/.bashrc".without_ext( .bashrc ) => "~/.bashrc",
|
354
|
+
# if an argument is given, the final ext must match
|
355
|
+
# the given argument, or a copy of the unaltered path
|
356
|
+
# is returned.
|
357
|
+
#
|
358
|
+
# Also:
|
359
|
+
# Don't add a leading ./ when the original didn't have one
|
360
|
+
# Case InSeNSItive, because I can't think of a use case for "only"
|
361
|
+
# strip the extension if the capitalization matches
|
362
|
+
# Chews up any number of leading '.'s
|
363
|
+
# For the moment,
|
364
|
+
def without_ext( ext_val = nil )
|
365
|
+
ext_chopper_regex = if ext_val.fwf_blank?
|
366
|
+
/\.+\w+$/i # any ending "word characters"
|
367
|
+
else
|
368
|
+
# It's okay for the caller to make the leading period explicit
|
369
|
+
ext_val = ext_val.to_s.gsub( /^\./, '' )
|
370
|
+
/\.+#{ext_val}+$/i
|
371
|
+
end
|
372
|
+
|
373
|
+
chopped_str = @path.gsub( ext_chopper_regex, "" )
|
374
|
+
|
375
|
+
do_we_chop = if chopped_str == @path # no change, then sure, why not?
|
376
|
+
true
|
377
|
+
elsif chopped_str.fwf_blank? || chopped_str[-1] == self.separator() || chopped_str[-1] == "."
|
378
|
+
false
|
379
|
+
else
|
380
|
+
true
|
381
|
+
end
|
382
|
+
|
383
|
+
self.class.new( do_we_chop ? chopped_str : @path )
|
384
|
+
|
385
|
+
# # If the results fail to live up to some pre-defined
|
386
|
+
#
|
387
|
+
#
|
388
|
+
#
|
389
|
+
# # do we or don't we?
|
390
|
+
# chop_extension = true
|
391
|
+
#
|
392
|
+
# # Don't if there's an extension mismatch
|
393
|
+
# # Don't if the remainder is a /. (original was a dot_file)
|
394
|
+
#
|
395
|
+
#
|
396
|
+
#
|
397
|
+
#
|
398
|
+
#
|
399
|
+
#
|
400
|
+
# _dirname, _basename, _ext = self.dirname_and_basename_and_ext
|
401
|
+
# debugger if _basename =~ "hello"
|
402
|
+
# ext_val = ext_val.to_s
|
403
|
+
#
|
404
|
+
# # 1) Only perform if the extension match the one given (or was a blank ext given?)
|
405
|
+
#
|
406
|
+
#
|
407
|
+
#
|
408
|
+
# new_path = @path.clone
|
409
|
+
#
|
410
|
+
# current_ext = self.ext
|
411
|
+
#
|
412
|
+
# e = e.to_s
|
413
|
+
#
|
414
|
+
# if e.fwf_present?
|
415
|
+
# new_path.gsub!( /\.#{e}$/, "" )
|
416
|
+
# result = self.gsub(/#{ not_beginning_of_line_or_separator}\.#{self.ext}$/, '')
|
417
|
+
# else
|
418
|
+
# self.clone
|
419
|
+
# end
|
420
|
+
#
|
421
|
+
# self.class.new( new_path )
|
422
|
+
end
|
423
|
+
|
424
|
+
# Two separate modes.
|
425
|
+
#
|
426
|
+
# With no arguments given, returns the current extension as a string (not a filepath). No leading period.
|
427
|
+
#
|
428
|
+
# With an argument, returns the path with a .(arg) tacked onto the end. Result is the same whether a leading
|
429
|
+
# period is given or not. Multiple extensions can be given, and any object can be used so long as it responds
|
430
|
+
# sensibly to .to_s() (use at your own risk). Integers work, for example.
|
317
431
|
def ext( *args )
|
318
432
|
if args.length == 0
|
319
433
|
split_basename = self.basename.to_s.split(".")
|
@@ -329,6 +443,21 @@ module FunWith
|
|
329
443
|
self.class.new( "#{@path}#{appended_to_path}" )
|
330
444
|
end
|
331
445
|
end
|
446
|
+
|
447
|
+
# asks if the .XXX at the end of the path matches one of the extensions given
|
448
|
+
# as an argument. If a block is given, the block will be run if the extension
|
449
|
+
# matches, ignored if no match is detected.
|
450
|
+
def ext?( *extensions, &block )
|
451
|
+
# Why is .to_str used here instead of to_s?
|
452
|
+
acceptable_extensions = extensions.map do |e|
|
453
|
+
ext = e.is_a?( Pathname ) ? e.to_str : e.to_s
|
454
|
+
ext.gsub(/^\./,'')
|
455
|
+
end
|
456
|
+
|
457
|
+
ext_matches = acceptable_extensions.include?( self.ext )
|
458
|
+
|
459
|
+
_yield_self_on_success( ext_matches, &block )
|
460
|
+
end
|
332
461
|
|
333
462
|
# base, ext = @path.basename_and_ext
|
334
463
|
def basename_and_ext
|
@@ -352,8 +481,8 @@ module FunWith
|
|
352
481
|
self.directory? ? self : self.dirname
|
353
482
|
end
|
354
483
|
|
355
|
-
def original?
|
356
|
-
!self.symlink
|
484
|
+
def original?(&block)
|
485
|
+
_yield_self_on_success( !self.symlink?, &block )
|
357
486
|
end
|
358
487
|
|
359
488
|
def original
|
@@ -366,78 +495,58 @@ module FunWith
|
|
366
495
|
self.class.new( dir )
|
367
496
|
end
|
368
497
|
|
369
|
-
def fwf_filepath
|
498
|
+
def fwf_filepath( &block )
|
499
|
+
yield self if block_given?
|
370
500
|
self
|
371
501
|
end
|
372
|
-
|
373
|
-
#
|
374
|
-
#
|
375
|
-
#
|
376
|
-
#
|
377
|
-
#
|
378
|
-
#
|
379
|
-
# You can change the length of the sequence string by passing
|
380
|
-
# in an argument, but it should always be the same value for
|
381
|
-
# a given set of files.
|
502
|
+
|
503
|
+
# Making this stricter, and failing when the formatting doesn't meet expectations, rather than
|
504
|
+
# guessing wildly about what to do on corner cases.
|
505
|
+
#
|
506
|
+
# This file is part of a sequence of files (file.000001.txt, file.000002.txt, file.000003.txt, etc.)
|
507
|
+
# `succ()` gives you the next file in the sequence. If no counter is present, a counter is
|
508
|
+
# added (file.dat --> file.000000.dat)
|
382
509
|
#
|
383
|
-
#
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
510
|
+
# You can change the length of the sequence string by passing in the `digit_count` argument.
|
511
|
+
# Changing the length for a given sequence will lead to the function not recognizing the existing
|
512
|
+
# counter, and installing a new one after it.
|
513
|
+
def succ( digit_count: SUCC_DIGIT_COUNT )
|
514
|
+
directory_pieces = self.split
|
515
|
+
succession_basename = Utils::Succession.get_successor_name( directory_pieces.pop, digit_count )
|
516
|
+
|
517
|
+
return FilePath.new( * directory_pieces ) / succession_basename
|
518
|
+
end
|
519
|
+
|
520
|
+
|
521
|
+
def timestamp( format: :default, time: Time.now, splitter: ".", &block )
|
522
|
+
timestamped_basename = Utils::Timestamp.timestamp( self.basename, format: format, time: time, splitter: splitter )
|
523
|
+
|
524
|
+
directory_path = self.split[0..-2]
|
525
|
+
|
526
|
+
# sigh....
|
527
|
+
if directory_path.first.path == "." && self.path[0..1] != ".#{File::SEPARATOR}"
|
528
|
+
directory_path.shift
|
392
529
|
end
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
if chunks.length == 1
|
397
|
-
if timestamp
|
398
|
-
chunks.push( timestamp )
|
399
|
-
else
|
400
|
-
chunks.push( "0" * digit_count )
|
401
|
-
end
|
402
|
-
# sequence stamp before file extension
|
403
|
-
elsif match_data = chunks[-2].match( /^(\d{#{digit_count}})$/ )
|
404
|
-
if timestamp
|
405
|
-
chunks[-2] = timestamp
|
406
|
-
else
|
407
|
-
i = match_data[1].to_i + 1
|
408
|
-
chunks[-2] = sprintf("%0#{digit_count}i", i)
|
409
|
-
end
|
410
|
-
# try to match sequence stamp to end of filename
|
411
|
-
elsif match_data = chunks[-1].match( /^(\d{#{digit_count}})$/ )
|
412
|
-
if timestamp
|
413
|
-
chunks[-1] = timestamp
|
414
|
-
else
|
415
|
-
i = match_data[1].to_i + 1
|
416
|
-
chunks[-1] = sprintf("%0#{digit_count}i", i)
|
417
|
-
end
|
418
|
-
# not yet sequence_stamped, has file extension
|
530
|
+
|
531
|
+
if directory_path.length == 0
|
532
|
+
timestamped_file = timestamped_basename.fwf_filepath
|
419
533
|
else
|
420
|
-
|
534
|
+
timestamped_file = FilePath.new( * self.split[0..-2], timestamped_basename )
|
421
535
|
end
|
422
|
-
|
423
|
-
|
536
|
+
|
537
|
+
_yield_and_return( timestamped_file, &block )
|
424
538
|
end
|
425
|
-
|
426
|
-
|
427
|
-
def timestamp( format = true, &block )
|
428
|
-
nxt = self.succ( :timestamp => format )
|
429
|
-
yield nxt if block_given?
|
430
|
-
nxt
|
431
|
-
end
|
432
|
-
|
539
|
+
|
433
540
|
# puts a string between the main part of the basename and the extension
|
434
541
|
# or after the basename if there is no extension. Used to describe some
|
435
542
|
# file variant.
|
543
|
+
#
|
436
544
|
# Example "/home/docs/my_awesome_screenplay.txt".fwf_filepath.specifier("final_draft")
|
437
545
|
# => FunWith::Files::FilePath:/home/docs/my_awesome_screenplay.final_draft.txt
|
438
546
|
#
|
439
|
-
#
|
440
|
-
|
547
|
+
# For the purposes of this method, anything after the last period in the filename
|
548
|
+
# is considered the extension.
|
549
|
+
def specifier( str, &block )
|
441
550
|
str = str.to_s
|
442
551
|
chunks = self.to_s.split(".")
|
443
552
|
|
@@ -447,9 +556,13 @@ module FunWith
|
|
447
556
|
chunks = chunks[0..-2] + [str] + [chunks[-1]]
|
448
557
|
end
|
449
558
|
|
450
|
-
chunks.join(".").fwf_filepath
|
559
|
+
f = chunks.join(".").fwf_filepath
|
560
|
+
_yield_and_return( f, &block )
|
451
561
|
end
|
452
|
-
|
562
|
+
|
563
|
+
# Returns the "wildcarded" version of the filename, suitable for finding related files
|
564
|
+
# with similar timestamps.
|
565
|
+
#
|
453
566
|
# TODO: succession : enumerates a sequence of files that get passed
|
454
567
|
# to a block in order.
|
455
568
|
def succession( opts = { digit_count: SUCC_DIGIT_COUNT, timestamp: false } )
|
@@ -507,45 +620,11 @@ module FunWith
|
|
507
620
|
# the failed requirement in order to try it later. Doesn't fail until it goes through
|
508
621
|
# a full loop where none of the required files were successful.
|
509
622
|
def requir
|
510
|
-
|
511
|
-
requirements = self.glob( :recursive => true, :ext => "rb" )
|
512
|
-
successfully_required = 1337 # need to break into initial loop
|
513
|
-
failed_requirements = []
|
514
|
-
error_messages = []
|
515
|
-
|
516
|
-
while requirements.length > 0 && successfully_required > 0
|
517
|
-
successfully_required = 0
|
518
|
-
failed_requirements = []
|
519
|
-
error_messages = []
|
520
|
-
|
521
|
-
for requirement in requirements
|
522
|
-
begin
|
523
|
-
requirement.requir
|
524
|
-
successfully_required += 1
|
525
|
-
rescue Exception => e
|
526
|
-
failed_requirements << requirement
|
527
|
-
error_messages << "Error while requiring #{requirement} : #{e.message} (#{e.class})"
|
528
|
-
end
|
529
|
-
end
|
530
|
-
|
531
|
-
requirements = failed_requirements
|
532
|
-
end
|
533
|
-
|
534
|
-
if failed_requirements.length > 0
|
535
|
-
msg = "requiring directory #{self} failed:\n"
|
536
|
-
for message in error_messages
|
537
|
-
msg << "\n\terror message: #{message}"
|
538
|
-
end
|
539
|
-
|
540
|
-
raise NameError.new(msg)
|
541
|
-
end
|
542
|
-
else
|
543
|
-
require self.expand.gsub( /\.rb$/, '' )
|
544
|
-
end
|
623
|
+
FunWith::Files::Requirements::Manager.require_files( self )
|
545
624
|
end
|
546
|
-
|
547
|
-
def root?
|
548
|
-
self == self.up
|
625
|
+
|
626
|
+
def root?(&block)
|
627
|
+
_yield_self_on_success( self == self.up, &block)
|
549
628
|
end
|
550
629
|
|
551
630
|
def descend( &block )
|
@@ -584,46 +663,53 @@ module FunWith
|
|
584
663
|
# end
|
585
664
|
# # otherwise we're installing a separator
|
586
665
|
# end
|
666
|
+
def separator
|
667
|
+
File::SEPARATOR
|
668
|
+
end
|
587
669
|
|
588
670
|
protected
|
589
671
|
# TODO: Need a separate API for user to call
|
590
|
-
def
|
672
|
+
def must_be_file
|
591
673
|
unless self.file?
|
592
674
|
calling_method = caller[0][/`.*'/][1..-2]
|
593
|
-
raise Errno::
|
675
|
+
raise Errno::EACCES.new( "Can only call FunWith::Files::FilePath##{calling_method}() on an existing file.")
|
594
676
|
end
|
595
677
|
end
|
596
678
|
|
597
|
-
def
|
679
|
+
def must_be_directory
|
598
680
|
unless self.directory?
|
599
681
|
calling_method = caller[0][/`.*'/][1..-2]
|
600
|
-
raise Errno::
|
682
|
+
raise Errno::EACCES.new( "Can only call FunWith::Files::FilePath##{calling_method}() on an existing directory." )
|
601
683
|
end
|
602
684
|
end
|
603
685
|
|
604
|
-
def
|
686
|
+
def must_be_writable
|
605
687
|
unless self.writable?
|
606
688
|
calling_method = caller[0][/`.*'/][1..-2]
|
607
|
-
raise Errno::
|
689
|
+
raise Errno::EACCES.new( "Error in FunWith::Files::FilePath##{calling_method}(): #{@path} not writable." )
|
608
690
|
end
|
609
691
|
end
|
610
|
-
|
611
|
-
|
612
|
-
|
692
|
+
|
693
|
+
# Simplifies some of the block-formed methods
|
694
|
+
def _yield_and_return( obj = self, &block )
|
695
|
+
yield obj if block_given?
|
696
|
+
obj
|
613
697
|
end
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
698
|
+
|
699
|
+
# There are a bunch of methods that interrogate the path, and I want them all to respond to the
|
700
|
+
# same block form. @file.exists?, run the block. @file.writable?, run the block. In all cases,
|
701
|
+
# the path is yielded as an argument
|
702
|
+
def _yield_self_on_success( success, &block )
|
703
|
+
if block_given?
|
704
|
+
if success
|
705
|
+
yield self
|
706
|
+
else
|
707
|
+
false
|
708
|
+
end
|
618
709
|
else
|
619
|
-
|
710
|
+
success
|
620
711
|
end
|
621
712
|
end
|
622
|
-
|
623
|
-
def yield_and_return( obj, &block )
|
624
|
-
yield obj if block_given?
|
625
|
-
obj
|
626
|
-
end
|
627
713
|
end
|
628
714
|
end
|
629
715
|
end
|
@@ -15,11 +15,32 @@ module FunWith
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def config_dir( *args )
|
18
|
-
|
18
|
+
xdg_get('CONFIG').fwf_filepath.join( *args )
|
19
19
|
end
|
20
20
|
|
21
21
|
def data_dir( *args )
|
22
|
-
|
22
|
+
xdg_get('DATA').fwf_filepath.join( *args )
|
23
|
+
end
|
24
|
+
|
25
|
+
def cache_dir( *args )
|
26
|
+
xdg_get('CACHE_HOME').fwf_filepath.join( *args )
|
27
|
+
end
|
28
|
+
|
29
|
+
def xdg_get( str )
|
30
|
+
if XDG.respond_to?( :"[]" )
|
31
|
+
XDG[str]
|
32
|
+
else
|
33
|
+
case str
|
34
|
+
when "CONFIG"
|
35
|
+
XDG::Environment.new.config_home
|
36
|
+
when "DATA"
|
37
|
+
XDG::Environment.new.data_home
|
38
|
+
when "CACHE_HOME"
|
39
|
+
XDG::Environment.new.cache_home
|
40
|
+
else
|
41
|
+
raise "Not sure what to do with XDG:#{str}"
|
42
|
+
end
|
43
|
+
end
|
23
44
|
end
|
24
45
|
|
25
46
|
# Honestly this is a token attempt at Windows compatibility.
|
@@ -2,20 +2,31 @@ module FunWith
|
|
2
2
|
module Files
|
3
3
|
# view and change file permissions
|
4
4
|
module FilePermissionMethods
|
5
|
-
def readable?
|
6
|
-
File.readable?( self )
|
5
|
+
def readable?( &block )
|
6
|
+
_yield_self_on_success( File.readable?( self ), &block )
|
7
7
|
end
|
8
8
|
|
9
|
-
def writable?
|
10
|
-
File.writable?( self )
|
9
|
+
def writable?( &block )
|
10
|
+
_yield_self_on_success( File.writable?( self ), &block )
|
11
11
|
end
|
12
12
|
|
13
|
-
def executable?
|
14
|
-
File.executable?( self )
|
13
|
+
def executable?( &block )
|
14
|
+
_yield_self_on_success( File.executable?( self ), &block )
|
15
15
|
end
|
16
16
|
|
17
|
+
# options: :noop, :verbose
|
17
18
|
def chmod( mode, opts = {} )
|
18
|
-
FileUtils.chmod( mode, self,
|
19
|
+
FileUtils.chmod( mode, self, ** Utils::Opts.narrow_file_utils_options( opts, :chmod ) )
|
20
|
+
end
|
21
|
+
|
22
|
+
# options: :noop, :verbose
|
23
|
+
def chown( user, opts = {} )
|
24
|
+
FileUtils.chown( user, self, ** Utils::Opts.narrow_file_utils_options( opts, :chown ) )
|
25
|
+
end
|
26
|
+
|
27
|
+
def owner
|
28
|
+
uid = File.stat( self ).uid
|
29
|
+
Etc.getpwuid( uid ).name
|
19
30
|
end
|
20
31
|
end
|
21
32
|
end
|