ratch 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
+