ratch 1.1.0 → 1.2.0

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.
Files changed (86) hide show
  1. data/.ruby +99 -0
  2. data/COPYING +203 -21
  3. data/History.rdoc +35 -0
  4. data/License.txt +204 -0
  5. data/README.rdoc +113 -0
  6. data/Version +1 -0
  7. data/bin/ludo +16 -0
  8. data/bin/ratch +1 -8
  9. data/lib/ratch.rb +28 -0
  10. data/lib/ratch.yml +99 -0
  11. data/lib/ratch/batch.rb +500 -0
  12. data/lib/ratch/console.rb +199 -0
  13. data/lib/ratch/core_ext.rb +1 -4
  14. data/lib/ratch/core_ext/facets.rb +15 -1
  15. data/lib/ratch/core_ext/filetest.rb +29 -0
  16. data/lib/ratch/core_ext/{string.rb → to_actual_filename.rb} +0 -23
  17. data/lib/ratch/core_ext/to_console.rb +30 -12
  18. data/lib/ratch/core_ext/{object.rb → to_yamlfrag.rb} +1 -0
  19. data/lib/ratch/core_ext/unfold_paragraphs.rb +27 -0
  20. data/lib/ratch/file_list.rb +411 -0
  21. data/lib/ratch/script.rb +99 -5
  22. data/lib/ratch/script/help.rb +84 -0
  23. data/lib/ratch/shell.rb +783 -0
  24. data/lib/ratch/system.rb +15 -0
  25. data/lib/ratch/utils/cli.rb +49 -0
  26. data/lib/ratch/utils/config.rb +52 -0
  27. data/lib/ratch/{emailer.rb → utils/email.rb} +43 -6
  28. data/lib/ratch/utils/ftp.rb +134 -0
  29. data/lib/ratch/utils/pom.rb +22 -0
  30. data/lib/ratch/utils/rdoc.rb +48 -0
  31. data/lib/ratch/utils/tar.rb +88 -0
  32. data/lib/ratch/utils/xdg.rb +39 -0
  33. data/lib/ratch/utils/zlib.rb +54 -0
  34. data/spec/01_shell.rdoc +198 -0
  35. data/spec/02_script.rdoc +34 -0
  36. data/spec/03_batch.rdoc +71 -0
  37. data/spec/04_system.rdoc +3 -0
  38. data/spec/applique/array.rb +8 -0
  39. data/spec/applique/setup.rb +20 -0
  40. data/test/case_batch.rb +63 -0
  41. data/test/case_shell.rb +46 -0
  42. data/test/core_ext/case_pathname.rb +361 -0
  43. data/test/helper.rb +4 -0
  44. data/test/utils/case_cli.rb +6 -0
  45. data/test/utils/case_config.rb +12 -0
  46. data/test/utils/case_email.rb +10 -0
  47. data/test/utils/case_ftp.rb +6 -0
  48. data/test/utils/case_pom.rb +17 -0
  49. data/test/utils/case_rdoc.rb +23 -0
  50. data/test/utils/case_tar.rb +6 -0
  51. data/test/utils/case_zlib.rb +11 -0
  52. data/test/utils/fixtures/pom_sample/Profile +4 -0
  53. data/test/utils/fixtures/rdoc_sample/README.rdoc +4 -0
  54. data/test/utils/fixtures/rdoc_sample/lib/rdoc_sample/rdoc_sample.rb +9 -0
  55. metadata +139 -82
  56. data/HISTORY +0 -6
  57. data/MANIFEST +0 -53
  58. data/NEWS +0 -12
  59. data/README +0 -87
  60. data/VERSION +0 -1
  61. data/demo/tryme-task.ratch +0 -12
  62. data/demo/tryme1.ratch +0 -6
  63. data/doc/log/basic_stats/index.html +0 -39
  64. data/doc/log/notes.xml +0 -18
  65. data/doc/log/stats.log +0 -14
  66. data/doc/log/syntax.log +0 -0
  67. data/doc/log/testunit.log +0 -156
  68. data/lib/ratch/commandline.rb +0 -16
  69. data/lib/ratch/core_ext/pathname.rb +0 -38
  70. data/lib/ratch/dsl.rb +0 -420
  71. data/lib/ratch/index.rb +0 -4
  72. data/lib/ratch/io.rb +0 -116
  73. data/lib/ratch/plugin.rb +0 -65
  74. data/lib/ratch/service.rb +0 -33
  75. data/lib/ratch/task.rb +0 -249
  76. data/lib/ratch/task2.rb +0 -298
  77. data/meta/abstract +0 -4
  78. data/meta/author +0 -1
  79. data/meta/contact +0 -1
  80. data/meta/homepage +0 -1
  81. data/meta/name +0 -1
  82. data/meta/requires +0 -4
  83. data/meta/summary +0 -1
  84. data/test/README +0 -1
  85. data/test/test_helper.rb +0 -4
  86. data/test/test_task.rb +0 -46
@@ -1,14 +1,108 @@
1
- require 'ratch/dsl'
1
+ require 'ratch/core_ext'
2
+ require 'ratch/shell'
3
+ #require 'ratch/plugin'
4
+
5
+ # CLI extension is required.
6
+ require 'ratch/utils/cli'
2
7
 
3
8
  module Ratch
4
9
 
5
- # = Ratch Script
6
- #
7
- # The Ratch Script class is used to run stand-alone ratch scripts.
10
+ # The Ratch Script class is used to run stand-alone Ratch scripts.
8
11
  # Yep, this is actaully a class named exactly for what it is.
9
12
  # How rare.
10
13
  #
11
- class Script < DSL
14
+ #--
15
+ # Previous versions of this class subclassed Module and called `self extend`
16
+ # in the +initialize+ method. Then used +method_missing+ to route to an
17
+ # instance of Shell. The current design works the other way round.
18
+ # Both approaches seem to work just as well, though perhaps one is more
19
+ # robust than another? I have chosen this design simply b/c the shell
20
+ # methods should dispatch a bit faster.
21
+ #++
22
+ class Script < Shell #< Module
23
+
24
+ include CLI
25
+
26
+ #
27
+ def self.execute(script, *args)
28
+ script = new(script, *args)
29
+ script.execute!
30
+ end
31
+
32
+ #
33
+ def initialize(file, *args)
34
+ @_file = file.to_s
35
+
36
+ #extend self
37
+
38
+ super(*args)
39
+
40
+ #@_stdout = options[:stdout] || $stdout
41
+ #@_stderr = options[:stderr] || $stderr
42
+ #@_stdin = options[:stdin] || $stdin
43
+ end
44
+
45
+ # Returns the file name of the script.
46
+ def script_file
47
+ @_file
48
+ end
49
+
50
+ # Be cautious about calling this in a script --an infinite loop could
51
+ # easily ensue.
52
+ def execute!
53
+ old = $0
54
+ begin
55
+ $0 = script_file
56
+ instance_eval(File.read(script_file), script_file, 1)
57
+ ensure
58
+ $0 = old
59
+ end
60
+ end
61
+
62
+ alias_method :run!, :execute!
63
+
64
+ #
65
+ #def print(str=nil)
66
+ # super(str.to_s) unless quiet?
67
+ #end
68
+
69
+ #
70
+ #def puts(str=nil)
71
+ # super(str.to_s) unless quiet?
72
+ #end
73
+
74
+ # TODO: Deprecate one of the three #report, #status, #trace.
75
+ def report(message)
76
+ #@_stdout.puts(message) unless quiet?
77
+ puts(message) unless quiet?
78
+ end
79
+
80
+ #
81
+ def status(message)
82
+ #@_stdout.puts message unless quiet?
83
+ puts message unless quiet? # dryrun? or trace?
84
+ end
85
+
86
+ # Internal status report. Only output if in trace mode.
87
+ #
88
+ def trace(message)
89
+ #@_stdout.puts message if trace?
90
+ puts message if trace?
91
+ end
92
+
93
+ # Pass-thru to singleton class.
94
+ def define_method(name, &block)
95
+ (class << self; self; end).__send__(:define_method, &block)
96
+ end
97
+
98
+ ## If method is missing, try the singleton class. This allows the script
99
+ ## to use methods like +define_method+.
100
+ ##
101
+ ## TODO: Perhaps it would be best to limit the selection of methods?
102
+ #def method_missing(sym, *args, &blk)
103
+ # (class << self; self; end).__send__(sym, *args, &blk)
104
+ #end
105
+
12
106
  end
13
107
 
14
108
  end
@@ -0,0 +1,84 @@
1
+ module Ratch
2
+
3
+ # Support class for displaying command help lists.
4
+ class Help
5
+
6
+ #
7
+ def self.list(*dirs)
8
+ tasks = script_descriptions(*dirs)
9
+ tmax = tasks.keys.max{ |a,b| a.size <=> b.size }.size
10
+ #dmax = dirs.flatten.max{ |a,b| a.size <=> b.size }.size
11
+ #if dir == ''
12
+ # max += 4 + 2
13
+ #else
14
+ max = tmax + 4
15
+ #end
16
+ tasks = tasks.sort_by{|k,v| k }
17
+ tasks.each do |name, sum|
18
+ #if dir == ''
19
+ # cmd = "ratch #{name}"
20
+ #else
21
+ cmd = name
22
+ #end
23
+ puts "%-#{max}s # %s" % [cmd, sum]
24
+ end
25
+ end
26
+
27
+ # Scan task scripts for descriptions.
28
+ def self.script_descriptions(*dirs)
29
+ opts = Hash === dirs.last ? dirs.pop : {}
30
+ dirs = dirs.flatten
31
+ help = {}
32
+ dirs.each do |dir|
33
+ files = Dir.glob(File.join(dir,'**/*'))
34
+ files.each do |fname|
35
+ next if FileTest.directory?(fname)
36
+ next if opts[:exe] and !FileTest.executable?(fname)
37
+ desc = ''
38
+ File.open(fname) do |f|
39
+ line = ''
40
+ until f.eof?
41
+ line = f.gets
42
+ case line
43
+ when /^(#!|\s*$)/
44
+ next
45
+ when /^\s*#(.*)/
46
+ desc = $1.strip; break
47
+ else
48
+ desc = nil; break
49
+ end
50
+ end
51
+ end
52
+ key = opts[:exe] ? fname : fname.sub(dir+'/', '')
53
+ help[key] = desc
54
+ end
55
+ end
56
+ help
57
+ end
58
+
59
+ # Scan script for description header.
60
+ def self.header(file, opts={})
61
+ #next if FileTest.directory?(file)
62
+ #next if opts[:exe] and !FileTest.executable?(file)
63
+ desc = "\n"
64
+ File.open(file) do |f|
65
+ line = ''
66
+ until f.eof?
67
+ line = f.gets
68
+ case line
69
+ when /^(#!|\s*$)/
70
+ next
71
+ when /^\s*#\s?(.*)/
72
+ desc << $1.rstrip + "\n"
73
+ else
74
+ break
75
+ end
76
+ end
77
+ end
78
+ desc + "\n"
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+
@@ -0,0 +1,783 @@
1
+ require 'thread'
2
+ #require 'rbconfig'
3
+ require 'ratch/core_ext'
4
+ require 'ratch/batch'
5
+
6
+ module Ratch
7
+
8
+ # TODO: Better base class?
9
+ class FileNotFound < StandardError
10
+ end
11
+
12
+ # = Shell Prompt class
13
+ #
14
+ # Ratch Shell object provides a limited file system shell in code.
15
+ # It is similar to having a shell prompt available to you in Ruby.
16
+ #
17
+ # NOTE: We have used the term *trace* in place of *verbose* for command
18
+ # line options. Even though Ruby itself uses the term *verbose* with respect
19
+ # to FileUtils, the term is commonly used for command specific needs, so we
20
+ # want to leave it open for such cases.
21
+ class Shell
22
+
23
+ # New Shell object.
24
+ #
25
+ # Shell.new(:noop=>true)
26
+ # Shell.new('..', :quiet=>true)
27
+ #
28
+ def initialize(*args)
29
+ path, opts = parse_arguments(*args)
30
+
31
+ opts.rekey!(&:to_sym)
32
+
33
+ set_options(opts)
34
+
35
+ if path.empty?
36
+ path = Dir.pwd
37
+ else
38
+ path = File.join(*path)
39
+ end
40
+
41
+ raise FileNotFound, "#{path}" unless ::File.exist?(path)
42
+ raise FileNotFound, "#{path}" unless ::File.directory?(path)
43
+
44
+ @_work = Pathname.new(path).expand_path
45
+ end
46
+
47
+ private
48
+
49
+ #
50
+ def parse_arguments(*args)
51
+ opts = (Hash===args.last ? args.pop : {})
52
+ return args, opts
53
+ end
54
+
55
+ #
56
+ def set_options(opts)
57
+ @_quiet = opts[:quiet]
58
+ @_noop = opts[:noop] || opts[:dryrun]
59
+ @_trace = opts[:trace] || opts[:dryrun]
60
+ #@_force = opts[:force]
61
+ end
62
+
63
+ #
64
+ def mutex
65
+ @mutex ||= Mutex.new
66
+ end
67
+
68
+ public
69
+
70
+ # Opertaton mode. This can be :noop, :verbose or :dryrun.
71
+ # The later is the same as the first two combined.
72
+ #def mode(opts=nil)
73
+ # return @mode unless opts
74
+ # opts.each do |key, val|
75
+ # next unless val
76
+ # case key
77
+ # when :noop
78
+ # @mode = (@mode == :verbose ? :dryrun : :noop)
79
+ # when :verbose
80
+ # @mode = (@mode == :noop ? :dryrun : :verbose)
81
+ # when :dryrun
82
+ # @mode = :dryrun
83
+ # end
84
+ # end
85
+ #end
86
+
87
+ def quiet? ; @_quiet ; end
88
+ def trace? ; @_trace ; end
89
+ def noop? ; @_noop ; end
90
+ #def force? ; @_force ; end
91
+
92
+ def dryrun? ; noop? && trace? ; end
93
+
94
+ # String representation is work directory path.
95
+ def to_s ; work.to_s ; end
96
+
97
+ # Two Shell's are equal if they have the same working path.
98
+ def ==(other)
99
+ return false unless other.is_a?(self.class)
100
+ return false unless work == other.work
101
+ true
102
+ end
103
+
104
+ # Same as #== except that #noop? must also be the same.
105
+ def eql?(other)
106
+ return false unless other.is_a?(self.class)
107
+ return false unless work == other.work
108
+ return false unless noop? == other.noop?
109
+ true
110
+ end
111
+
112
+ # Provides convenient starting points in the file system.
113
+ #
114
+ # root #=> #<Pathname:/>
115
+ # home #=> #<Pathname:/home/jimmy>
116
+ # work #=> #<Pathname:/home/jimmy/Documents>
117
+ #
118
+ # TODO: Replace these with Folio when Folio's is as capable.
119
+
120
+ # Current root path.
121
+ #def root(*args)
122
+ # Pathname['/', *args]
123
+ #end
124
+
125
+ # Current home path.
126
+ #def home(*args)
127
+ # Pathname['~', *args].expand_path
128
+ #end
129
+
130
+ # Current working path.
131
+ #def work(*args)
132
+ # Pathname['.', *args]
133
+ #end
134
+
135
+ # TODO: Should these take *args?
136
+
137
+ # Root location.
138
+ def root(*args)
139
+ dir('/', *args)
140
+ end
141
+
142
+ # Current home path.
143
+ def home(*args)
144
+ dir(File.expand_path('~'), *args)
145
+ end
146
+
147
+ # Current working path.
148
+ def work(*args)
149
+ return @_work if args.empty?
150
+ return dir(@_work, *args)
151
+ end
152
+
153
+ # Alias for #work.
154
+ #alias_method :pwd, :work
155
+
156
+ # Return a new prompt with the same location.
157
+ # NOTE: Use #dup or #clone ?
158
+ #def new ; Shell.new(work) ; end
159
+
160
+ def parent
161
+ dir('..')
162
+ end
163
+
164
+ #
165
+ #def [](name)
166
+ # #FileObject[localize(name)]
167
+ # #Pathname.new(localize(path))
168
+ # Pathlist.new(localize(path))
169
+ #end
170
+
171
+ # Returns a Batch of file +patterns+.
172
+ def batch(*patterns)
173
+ Batch.new patterns.map{|pattern| localize(pattern)}
174
+ end
175
+
176
+ # Returns a Batch of file +patterns+, without any exclusions.
177
+ def batch_all(*patterns)
178
+ Batch.all patterns.map{|pattern| localize(pattern)}
179
+ end
180
+
181
+ #
182
+ def path(path)
183
+ Pathname.new(localize(path))
184
+ end
185
+ alias_method :pathname, :path
186
+
187
+ #
188
+ def file(path)
189
+ #FileObject[name]
190
+ path = localize(path)
191
+ raise FileNotFound unless File.file?(path)
192
+ Pathname.new(path)
193
+ end
194
+
195
+ #def doc(name)
196
+ # Document.new(name)
197
+ #end
198
+
199
+ #
200
+ def dir(path)
201
+ #Directory.new(name)
202
+ path = localize(path)
203
+ raise FileNotFound unless File.directory?(path)
204
+ Pathname.new(path)
205
+ end
206
+
207
+ # Lists all entries.
208
+ def entries
209
+ work.entries
210
+ end
211
+
212
+ #alias_method :ls, :entries
213
+
214
+ # Lists directory entries.
215
+ def directory_entries
216
+ entries.select{ |d| d.directory? }
217
+ end
218
+
219
+ #
220
+ alias_method :dir_entries, :directory_entries
221
+
222
+ # Lists file entries.
223
+ def file_entries
224
+ entries.select{ |f| f.file? }
225
+ end
226
+
227
+ # Likes entries but omits '.' and '..' paths.
228
+ def pathnames
229
+ work.entries - %w{. ..}.map{|f|Pathname.new(f)}
230
+ end
231
+
232
+ # Returns list of directories.
233
+ def directories
234
+ pathnames.select{ |f| f.directory? }
235
+ end
236
+ alias_method :dirs, :directories
237
+ alias_method :folders, :directories
238
+
239
+ # Returns list of files.
240
+ def files
241
+ pathnames.select{ |f| f.file? }
242
+ end
243
+
244
+ # Glob pattern. Returns matches as strings.
245
+ def glob(*patterns, &block)
246
+ opts = (::Integer===patterns.last ? patterns.pop : 0)
247
+ matches = []
248
+ locally do
249
+ matches = patterns.map{ |pattern| ::Dir.glob(pattern, opts) }.flatten
250
+ end
251
+ if block_given?
252
+ matches.each(&block)
253
+ else
254
+ matches
255
+ end
256
+ end
257
+
258
+ # Glob files.
259
+ #def glob(*args, &blk)
260
+ # Dir.glob(*args, &blk)
261
+ #end
262
+
263
+ # TODO: Ultimately merge #glob and #multiglob.
264
+ def multiglob(*args, &blk)
265
+ Dir.multiglob(*args, &blk)
266
+ end
267
+
268
+ def multiglob_r(*args, &blk)
269
+ Dir.multiglob_r(*args, &blk)
270
+ end
271
+
272
+ =begin
273
+ # Match pattern. Like #glob but returns file objects.
274
+ # TODO: There is no FileObject any more. Should there be?
275
+ def match(*patterns, &block)
276
+ opts = (::Integer===patterns.last ? patterns.pop : 0)
277
+ patterns = localize(patterns)
278
+ matches = patterns.map{ |pattern| ::Dir.glob(pattern, opts) }.flatten
279
+ matches = matches.map{ |f| FileObject[f] }
280
+ if block_given?
281
+ matches.each(&block)
282
+ else
283
+ matches
284
+ end
285
+ end
286
+ =end
287
+
288
+ # Join paths.
289
+ # TODO: Should this return a new directory object? Or should it change directories?
290
+ def /(path)
291
+ #@_work += dir # did not work, why?
292
+ @_work = dir(localize(path))
293
+ self
294
+ end
295
+
296
+ # Alias for #/.
297
+ alias_method '+', '/'
298
+
299
+ # TODO: Tie this into the System class.
300
+ def system(cmd)
301
+ locally do
302
+ super(cmd)
303
+ end
304
+ end
305
+
306
+ # Shell runner.
307
+ def sh(cmd)
308
+ #puts "--> system call: #{cmd}" if trace?
309
+ puts cmd if trace?
310
+ return true if noop?
311
+ #locally do
312
+ if quiet?
313
+ silently{ system(cmd) }
314
+ else
315
+ system(cmd)
316
+ end
317
+ #end
318
+ end
319
+
320
+ # Shell runner.
321
+ #def sh(cmd)
322
+ # if dryrun?
323
+ # puts cmd
324
+ # true
325
+ # else
326
+ # puts "--> system call: #{cmd}" if trace?
327
+ # if quiet?
328
+ # silently{ system(cmd) }
329
+ # else
330
+ # system(cmd)
331
+ # end
332
+ # end
333
+ #end
334
+
335
+ # Change working directory.
336
+ #
337
+ # TODO: Make thread safe.
338
+ #
339
+ def cd(path, &block)
340
+ if block
341
+ work_old = @_work
342
+ begin
343
+ @_work = dir(localize(path))
344
+ locally(&block)
345
+ #mutex.synchronize do
346
+ # Dir.chdir(@_work){ block.call }
347
+ #end
348
+ ensure
349
+ @_work = work_old
350
+ end
351
+ else
352
+ @_work = dir(localize(path))
353
+ end
354
+ end
355
+
356
+ #
357
+ alias_method :chdir, :cd
358
+
359
+ # Bonus FileUtils features.
360
+ #def cd(*a,&b)
361
+ # puts "cd #{a}" if dryrun? or trace?
362
+ # fileutils.chdir(*a,&b)
363
+ #end
364
+
365
+ # -- File IO Shortcuts -----------------------------------------------
366
+
367
+ # Read file.
368
+ def read(path)
369
+ File.read(localize(path))
370
+ end
371
+
372
+ # Write file.
373
+ def write(path, text)
374
+ $stderr.puts "write #{path}" if trace?
375
+ File.open(localize(path), 'w'){ |f| f << text } unless noop?
376
+ end
377
+
378
+ # Append to file.
379
+ def append(path, text)
380
+ $stderr.puts "append #{path}" if trace?
381
+ File.open(localize(path), 'a'){ |f| f << text } unless noop?
382
+ end
383
+
384
+
385
+ #############
386
+ # FileTest #
387
+ #############
388
+
389
+ #
390
+ def size(path) ; FileTest.size(localize(path)) ; end
391
+ def size?(path) ; FileTest.size?(localize(path)) ; end
392
+ def directory?(path) ; FileTest.directory?(localize(path)) ; end
393
+ def symlink?(path) ; FileTest.symlink?(localize(path)) ; end
394
+ def readable?(path) ; FileTest.readable?(localize(path)) ; end
395
+ def chardev?(path) ; FileTest.chardev?(localize(path)) ; end
396
+ def exist?(path) ; FileTest.exist?(localize(path)) ; end
397
+ def exists?(path) ; FileTest.exists?(localize(path)) ; end
398
+ def zero?(path) ; FileTest.zero?(localize(path)) ; end
399
+ def pipe?(path) ; FileTest.pipe?(localize(path)) ; end
400
+ def file?(path) ; FileTest.file?(localize(path)) ; end
401
+ def sticky?(path) ; FileTest.sticky?(localize(path)) ; end
402
+ def blockdev?(path) ; FileTest.blockdev?(localize(path)) ; end
403
+ def grpowned?(path) ; FileTest.grpowned?(localize(path)) ; end
404
+ def setgid?(path) ; FileTest.setgid?(localize(path)) ; end
405
+ def setuid?(path) ; FileTest.setuid?(localize(path)) ; end
406
+ def socket?(path) ; FileTest.socket?(localize(path)) ; end
407
+ def owned?(path) ; FileTest.owned?(localize(path)) ; end
408
+ def writable?(path) ; FileTest.writable?(localize(path)) ; end
409
+ def executable?(path) ; FileTest.executable?(localize(path)) ; end
410
+
411
+ def safe?(path) ; FileTest.safe?(localize(path)) ; end
412
+
413
+ def relative?(path) ; FileTest.relative?(path) ; end
414
+ def absolute?(path) ; FileTest.absolute?(path) ; end
415
+
416
+ def writable_real?(path) ; FileTest.writable_real?(localize(path)) ; end
417
+ def executable_real?(path) ; FileTest.executable_real?(localize(path)) ; end
418
+ def readable_real?(path) ; FileTest.readable_real?(localize(path)) ; end
419
+
420
+ def identical?(path, other)
421
+ FileTest.identical?(localize(path), localize(other))
422
+ end
423
+ alias_method :compare_file, :identical?
424
+
425
+ # Assert that a path exists.
426
+ #def exists?(path)
427
+ # paths = Dir.glob(path)
428
+ # paths.not_empty?
429
+ #end
430
+ #alias_method :exist?, :exists? #; module_function :exist?
431
+ #alias_method :path?, :exists? #; module_function :path?
432
+
433
+ # Is a given path a regular file? If +path+ is a glob
434
+ # then checks to see if all matches are regular files.
435
+ #def file?(path)
436
+ # paths = Dir.glob(path)
437
+ # paths.not_empty? && paths.all?{ |f| FileTest.file?(f) }
438
+ #end
439
+
440
+ # Is a given path a directory? If +path+ is a glob
441
+ # checks to see if all matches are directories.
442
+ #def dir?(path)
443
+ # paths = Dir.glob(path)
444
+ # paths.not_empty? && paths.all?{ |f| FileTest.directory?(f) }
445
+ #end
446
+ #alias_method :directory?, :dir? #; module_function :directory?
447
+
448
+
449
+ #############
450
+ # FileUtils #
451
+ #############
452
+
453
+ # Low-level Methods Omitted
454
+ # -------------------------
455
+ # getwd -> pwd
456
+ # compare_file -> cmp
457
+ # remove_file -> rm
458
+ # copy_file -> cp
459
+ # remove_dir -> rmdir
460
+ # safe_unlink -> rm_f
461
+ # makedirs -> mkdir_p
462
+ # rmtree -> rm_rf
463
+ # copy_stream
464
+ # remove_entry
465
+ # copy_entry
466
+ # remove_entry_secure
467
+ # compare_stream
468
+
469
+ # Present working directory.
470
+ def pwd
471
+ work.to_s
472
+ end
473
+
474
+ # Same as #identical?
475
+ def cmp(a,b)
476
+ fileutils.compare_file(a,b)
477
+ end
478
+
479
+ #
480
+ def mkdir(dir, options={})
481
+ dir = localize(dir)
482
+ fileutils.mkdir(dir, options)
483
+ end
484
+
485
+ def mkdir_p(dir, options={})
486
+ dir = localize(dir)
487
+ unless File.directory?(dir)
488
+ fileutils.mkdir_p(dir, options)
489
+ end
490
+ end
491
+ alias_method :mkpath, :mkdir_p
492
+
493
+ def rmdir(dir, options={})
494
+ dir = localize(dir)
495
+ fileutils.rmdir(dir, options)
496
+ end
497
+
498
+ # ln(list, destdir, options={})
499
+ def ln(old, new, options={})
500
+ old = localize(old)
501
+ new = localize(new)
502
+ fileutils.ln(old, new, options)
503
+ end
504
+ alias_method :link, :ln
505
+
506
+ # ln_s(list, destdir, options={})
507
+ def ln_s(old, new, options={})
508
+ old = localize(old)
509
+ new = localize(new)
510
+ fileutils.ln_s(old, new, options)
511
+ end
512
+ alias_method :symlink, :ln_s
513
+
514
+ def ln_sf(old, new, options={})
515
+ old = localize(old)
516
+ new = localize(new)
517
+ fileutils.ln_sf(old, new, options)
518
+ end
519
+
520
+ # cp(list, dir, options={})
521
+ def cp(src, dest, options={})
522
+ src = localize(src)
523
+ dest = localize(dest)
524
+ fileutils.cp(src, dest, options)
525
+ end
526
+ alias_method :copy, :cp
527
+
528
+ # cp_r(list, dir, options={})
529
+ def cp_r(src, dest, options={})
530
+ src = localize(src)
531
+ dest = localize(dest)
532
+ fileutils.cp_r(src, dest, options)
533
+ end
534
+
535
+ # mv(list, dir, options={})
536
+ def mv(src, dest, options={})
537
+ src = localize(src)
538
+ dest = localize(dest)
539
+ fileutils.mv(src, dest, options)
540
+ end
541
+ alias_method :move, :mv
542
+
543
+ def rm(list, options={})
544
+ list = localize(list)
545
+ fileutils.rm(list, options)
546
+ end
547
+ alias_method :remove, :rm
548
+
549
+ def rm_r(list, options={})
550
+ list = localize(list)
551
+ fileutils.rm_r(list, options)
552
+ end
553
+
554
+ def rm_f(list, options={})
555
+ list = localize(list)
556
+ fileutils.rm_f(list, options)
557
+ end
558
+
559
+ def rm_rf(list, options={})
560
+ list = localize(list)
561
+ fileutils.rm_rf(list, options)
562
+ end
563
+
564
+ def install(src, dest, mode, options={})
565
+ src = localize(src)
566
+ dest = localize(dest)
567
+ fileutils.install(src, dest, mode, options)
568
+ end
569
+
570
+ def chmod(mode, list, options={})
571
+ list = localize(list)
572
+ fileutils.chmod(mode, list, options)
573
+ end
574
+
575
+ def chmod_r(mode, list, options={})
576
+ list = localize(list)
577
+ fileutils.chmod_r(mode, list, options)
578
+ end
579
+ #alias_method :chmod_R, :chmod_r
580
+
581
+ def chown(user, group, list, options={})
582
+ list = localize(list)
583
+ fileutils.chown(user, group, list, options)
584
+ end
585
+
586
+ def chown_r(user, group, list, options={})
587
+ list = localize(list)
588
+ fileutils.chown_r(user, group, list, options)
589
+ end
590
+ #alias_method :chown_R, :chown_r
591
+
592
+ def touch(list, options={})
593
+ list = localize(list)
594
+ fileutils.touch(list, options)
595
+ end
596
+
597
+ #
598
+ # TODO: should this have SOURCE diectory?
599
+ # stage(directory, source_dir, files)
600
+ #
601
+ def stage(stage_dir, files)
602
+ #dir = localize(directory)
603
+ #files = localize(files)
604
+ locally do
605
+ fileutils.stage(stage_dir, work, files)
606
+ end
607
+ end
608
+
609
+ # An intergrated glob like method that takes a set of include globs,
610
+ # exclude globs and ignore globs to produce a collection of paths.
611
+ #
612
+ # Ignore_globs differ from exclude_globs in that they match by
613
+ # the basename of the path rather than the whole pathname.
614
+ #
615
+ def amass(include_globs, exclude_globs=[], ignore_globs=[])
616
+ locally do
617
+ fileutils.amass(include_globs, exclude_globs, ignore_globs)
618
+ end
619
+ end
620
+
621
+ #
622
+ def outofdate?(path, *sources)
623
+ #fileutils.outofdate?(localize(path), localize(sources)) # DIDN'T WORK, why?
624
+ locally do
625
+ fileutils.outofdate?(path, sources.flatten)
626
+ end
627
+ end
628
+
629
+ # # Does a path need updating, based on given +sources+?
630
+ # # This compares mtimes of give paths. Returns false
631
+ # # if the path needs to be updated.
632
+ # #
633
+ # # TODO: Put this in FileTest instead?
634
+ #
635
+ # def out_of_date?(path, *sources)
636
+ # return true unless File.exist?(path)
637
+ #
638
+ # sources = sources.collect{ |source| Dir.glob(source) }.flatten
639
+ # mtimes = sources.collect{ |file| File.mtime(file) }
640
+ #
641
+ # return true if mtimes.empty? # TODO: This the way to go here?
642
+ #
643
+ # File.mtime(path) < mtimes.max
644
+ # end
645
+
646
+ #
647
+ def uptodate?(path, *sources)
648
+ locally do
649
+ fileutils.uptodate?(path, sources.flatten)
650
+ end
651
+ end
652
+
653
+ #
654
+ #def uptodate?(new, old_list, options=nil)
655
+ # new = localize(new)
656
+ # old = localize(old_list)
657
+ # fileutils.uptodate?(new, old, options)
658
+ #end
659
+
660
+ =begin
661
+ # TODO: Deprecate these?
662
+
663
+ # Assert that a path exists.
664
+ def exists!(*paths)
665
+ abort "path not found #{path}" unless paths.any?{|path| exists?(path)}
666
+ end
667
+ alias_method :exist!, :exists! #; module_function :exist!
668
+ alias_method :path!, :exists! #; module_function :path!
669
+
670
+ # Assert that a given path is a file.
671
+ def file!(*paths)
672
+ abort "file not found #{path}" unless paths.any?{|path| file?(path)}
673
+ end
674
+
675
+ # Assert that a given path is a directory.
676
+ def dir!(*paths)
677
+ paths.each do |path|
678
+ abort "Directory not found: '#{path}'." unless dir?(path)
679
+ end
680
+ end
681
+ alias_method :directory!, :dir! #; module_function :directory!
682
+ =end
683
+
684
+ #private ?
685
+
686
+ # Returns a path local to the current working path.
687
+ def localize(local_path)
688
+ # some path arguments are optional
689
+ return local_path unless local_path
690
+ #
691
+ case local_path
692
+ when Array
693
+ local_path.collect do |lp|
694
+ if absolute?(lp)
695
+ lp
696
+ else
697
+ File.expand_path(File.join(work.to_s, lp))
698
+ end
699
+ end
700
+ else
701
+ # do not localize an absolute path
702
+ return local_path if absolute?(local_path)
703
+ File.expand_path(File.join(work.to_s, local_path))
704
+ #(work + local_path).expand_path.to_s
705
+ end
706
+ end
707
+
708
+ # Change directory to the shell's work directory,
709
+ # process the +block+ and then return to user directory.
710
+ def locally(&block)
711
+ if work.to_s == Dir.pwd
712
+ block.call
713
+ else
714
+ mutex.synchronize do
715
+ #work.chdir(&block)
716
+ Dir.chdir(work, &block)
717
+ end
718
+ end
719
+ end
720
+
721
+ # TODO: Should naming policy be in a utility extension module?
722
+
723
+ #
724
+ #
725
+ def naming_policy(*policies)
726
+ if policies.empty?
727
+ @naming_policy ||= ['down', 'ext']
728
+ else
729
+ @naming_policy = policies
730
+ end
731
+ end
732
+
733
+ #
734
+ #
735
+ def apply_naming_policy(name, ext)
736
+ naming_policy.each do |policy|
737
+ case policy.to_s
738
+ when /^low/, /^down/
739
+ name = name.downcase
740
+ when /^up/
741
+ name = name.upcase
742
+ when /^cap/
743
+ name = name.capitalize
744
+ when /^ext/
745
+ name = name + ".#{ext}"
746
+ end
747
+ end
748
+ name
749
+ end
750
+
751
+ private
752
+
753
+ # Returns FileUtils module based on mode.
754
+ def fileutils
755
+ if dryrun?
756
+ ::FileUtils::DryRun
757
+ elsif noop?
758
+ ::FileUtils::Noop
759
+ elsif trace?
760
+ ::FileUtils::Verbose
761
+ else
762
+ ::FileUtils
763
+ end
764
+ end
765
+
766
+ # This may be used by script commands to allow for per command
767
+ # noop and trace options. Global options have precedence.
768
+ def util_options(options)
769
+ noop = noop? || options[:noop] || options[:dryrun]
770
+ trace = trace? || options[:trace] || options[:dryrun]
771
+ return noop, trace
772
+ end
773
+
774
+ public#class
775
+
776
+ def self.[](path)
777
+ new(path)
778
+ end
779
+
780
+ end
781
+
782
+ end
783
+