tap-tasks 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/History CHANGED
@@ -1,3 +1,11 @@
1
+ == 0.3.0 / 2009-06-17
2
+
3
+ * added load/dump csv tasks
4
+ * updated glob task with include/exclude filters
5
+ * updated load/yaml with stream loading
6
+ * removed argv task
7
+ * removed FileTask
8
+
1
9
  == 0.2.0 / 2009-05-25
2
10
 
3
11
  Updates for use with Tap-0.17.0
@@ -0,0 +1,30 @@
1
+ require 'tap/tasks/dump'
2
+ require 'csv'
3
+
4
+ module Tap
5
+ module Tasks
6
+ class Dump
7
+
8
+ # :startdoc::task dumps data as csv
9
+ #
10
+ # Dumps arrays as CSV data. Each array passed to dump will be formatted
11
+ # into a single line of csv, ie multiple dumps build the csv results.
12
+ # Non-array objects are converted to arrays using to_ary.
13
+ #
14
+ # % tap run -- load/yaml ["a", "b", "c"] --: dump/csv
15
+ # a,b,c
16
+ #
17
+ class Csv < Dump
18
+
19
+ config :col_sep, ",", &c.string # The column separator (",")
20
+ config :row_sep, "\n", &c.string # The row separator ("\n")
21
+
22
+ # Dumps the data to io as CSV. Data is converted to an array using
23
+ # to_ary.
24
+ def dump(data, io)
25
+ io << CSV.generate_line(data.to_ary, col_sep) + row_sep
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -2,7 +2,7 @@ require 'tap/tasks/dump'
2
2
 
3
3
  module Tap
4
4
  module Tasks
5
- class Dump < Tap::Task
5
+ class Dump
6
6
 
7
7
  # :startdoc::task inspect and dump an object
8
8
  #
@@ -2,7 +2,7 @@ require 'tap/tasks/dump'
2
2
 
3
3
  module Tap
4
4
  module Tasks
5
- class Dump < Tap::Task
5
+ class Dump
6
6
 
7
7
  # :startdoc::task dumps data as YAML
8
8
  #
@@ -9,23 +9,39 @@ module Tap
9
9
  #
10
10
  # % tap run -- glob * --: dump/yaml
11
11
  #
12
+ # A variety of filters are available as configurations.
13
+ #
14
+ # == Glob Expansion
15
+ #
16
+ # NOTE that glob patterns are normally expanded on the command line,
17
+ # meaning the task will receive an array of files and not glob patterns.
18
+ # Usually this doesn't make a difference in the task results, but it can
19
+ # slow down launch times.
20
+ #
21
+ # To glob within the task and not the command line, quote the glob.
22
+ #
23
+ # % tap run -- glob '*' --: dump/yaml
24
+ #
12
25
  class Glob < Tap::Task
13
26
 
14
- config :filters, [], &c.list(&c.regexp) # regexp filters for results
15
- config :unique, true, &c.switch # ensure results are unique
16
- config :files, true, &c.switch # glob for files
17
- config :dirs, false, &c.switch # glob for directories
27
+ config :includes, [/./], :long => :include, &c.list(&c.regexp) # Regexp include filters
28
+ config :excludes, [], :long => :exclude, &c.list(&c.regexp) # Regexp exclude filters
29
+ config :unique, true, &c.switch # Ensure results are unique
30
+ config :files, true, &c.switch # Glob for files
31
+ config :dirs, false, &c.switch # Glob for directories
18
32
 
19
33
  def process(*patterns)
20
34
  results = []
21
35
  patterns.each do |pattern|
22
36
  Dir[pattern].each do |path|
23
- next if !files && File.file?(path)
24
- next if !dirs && File.directory?(path)
37
+ next if files == false && File.file?(path)
38
+ next if dirs == false && File.directory?(path)
25
39
 
26
40
  case path
27
- when *filters then next
28
- else results << path
41
+ when *excludes
42
+ next
43
+ when *includes
44
+ results << path
29
45
  end
30
46
  end
31
47
  end
@@ -0,0 +1,48 @@
1
+ require 'tap/tasks/load'
2
+ require 'csv'
3
+
4
+ module Tap
5
+ module Tasks
6
+ class Load
7
+
8
+ # :startdoc::task reads csv data
9
+ #
10
+ # Load CSV data as an array of arrays, selecting the specified rows and
11
+ # columns.
12
+ #
13
+ # % tap run -- load/csv 'a,b,c.d,e,f' --row-sep '.' --: inspect
14
+ # [["a", "b", "c"], ["d", "e", "f"]]
15
+ #
16
+ # Note this task is quite inefficient in that it will load all data
17
+ # before making a selection; large files or edge selections may benefit
18
+ # from an alternate task.
19
+ #
20
+ class Csv < Load
21
+
22
+ config :columns, nil, &c.range_or_nil # Specify a range of columns
23
+ config :rows, nil, &c.range_or_nil # Specify a range of rows
24
+
25
+ config :col_sep, nil, &c.string_or_nil # The column separator (",")
26
+ config :row_sep, nil, &c.string_or_nil # The row separator ("\r\n" or "\n")
27
+
28
+ # Loads the io data as CSV, into an array of arrays.
29
+ def load(io)
30
+ data = CSV.parse(io.read, col_sep, row_sep)
31
+
32
+ if rows
33
+ data = data[rows]
34
+ end
35
+
36
+ if columns
37
+ data.collect! do |cols|
38
+ cols[columns]
39
+ end
40
+ end
41
+
42
+ data
43
+ end
44
+
45
+ end
46
+ end
47
+ end
48
+ end
@@ -2,7 +2,7 @@ require 'tap/tasks/load'
2
2
 
3
3
  module Tap
4
4
  module Tasks
5
- class Load < Tap::Task
5
+ class Load
6
6
 
7
7
  # :startdoc::task loads data as YAML
8
8
  #
@@ -14,10 +14,37 @@ module Tap
14
14
  #
15
15
  class Yaml < Load
16
16
 
17
+ config :stream, false, &c.flag # Load documents from a stream
18
+
17
19
  # Loads data from io as YAML.
18
20
  def load(io)
19
- YAML.load(io)
21
+ if stream
22
+ load_stream(io)
23
+ else
24
+ YAML.load(io)
25
+ end
26
+ end
27
+
28
+ def load_stream(io)
29
+ lines = []
30
+ while !io.eof?
31
+ line = io.readline
32
+
33
+ if line =~ /^---/ && !lines.empty?
34
+ io.pos = io.pos - line.length
35
+ break
36
+ else
37
+ lines << line
38
+ end
39
+ end
40
+
41
+ YAML.load(lines.join)
42
+ end
43
+
44
+ def complete?(io, last)
45
+ !stream || io.eof?
20
46
  end
47
+
21
48
  end
22
49
  end
23
50
  end
@@ -0,0 +1,42 @@
1
+ require 'tap/tasks/load'
2
+
3
+ module Tap
4
+ module Tasks
5
+ # :startdoc::task an input prompt
6
+ #
7
+ # Prompt reads lines from the input until the exit sequence is reached
8
+ # or the source io is closed. This is effectively an echo:
9
+ #
10
+ # % tap run -- prompt --: dump
11
+ # >
12
+ #
13
+ class Prompt < Load
14
+ config :prompt, "> ", &c.string_or_nil # The prompt sequence
15
+ config :exit_seq, "\n", &c.string_or_nil # The prompt exit sequence
16
+ config :terminal, $stdout, &c.io_or_nil # The terminal IO
17
+
18
+ configurations[:use_close].default = true
19
+
20
+ def load(io)
21
+ open_io(terminal) do |terminal|
22
+ terminal.print prompt
23
+ end if prompt
24
+
25
+ if io.eof?
26
+ nil
27
+ else
28
+ io.readline
29
+ end
30
+ end
31
+
32
+ def complete?(io, line)
33
+ line == nil || line == exit_seq
34
+ end
35
+
36
+ def close(io)
37
+ super
38
+ app.terminate
39
+ end
40
+ end
41
+ end
42
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tap-tasks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Chiang
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-25 00:00:00 -06:00
12
+ date: 2009-06-17 00:00:00 -06:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -20,7 +20,7 @@ dependencies:
20
20
  requirements:
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 0.17.0
23
+ version: 0.18.0
24
24
  version:
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: tap-test
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.1.0
33
+ version: 0.2.0
34
34
  version:
35
35
  description:
36
36
  email: simon.a.chiang@gmail.com
@@ -43,13 +43,13 @@ extra_rdoc_files:
43
43
  - README
44
44
  - MIT-LICENSE
45
45
  files:
46
- - lib/tap/tasks/file_task/shell_utils.rb
47
- - lib/tap/tasks/argv.rb
46
+ - lib/tap/tasks/dump/csv.rb
48
47
  - lib/tap/tasks/dump/inspect.rb
49
48
  - lib/tap/tasks/dump/yaml.rb
50
49
  - lib/tap/tasks/load/yaml.rb
50
+ - lib/tap/tasks/load/csv.rb
51
51
  - lib/tap/tasks/glob.rb
52
- - lib/tap/tasks/file_task.rb
52
+ - lib/tap/tasks/prompt.rb
53
53
  - tap.yml
54
54
  - History
55
55
  - README
@@ -1,30 +0,0 @@
1
- require 'tap/task'
2
-
3
- module Tap
4
- module Tasks
5
- # :startdoc::task provides a handle to ARGV
6
- #
7
- # Simply returns ARGV. This task can be a useful hook when executing
8
- # saved workflows via run (given that all arguments after the workflow
9
- # file are preserved in ARGV).
10
- #
11
- # # [workflow.yml]
12
- # # - - argv
13
- # # - - dump/yaml
14
- # # - 0[1]
15
- #
16
- # % tap run -w workflow.yml a b c
17
- # ---
18
- # - a
19
- # - b
20
- # - c
21
- #
22
- class Argv < Tap::Task
23
-
24
- # Simply returns ARGV.
25
- def process
26
- ARGV
27
- end
28
- end
29
- end
30
- end
@@ -1,383 +0,0 @@
1
- require 'tap/tasks/file_task/shell_utils'
2
-
3
- module Tap
4
- module Tasks
5
- # FileTask is a base class for tasks that work with a file system. FileTask
6
- # tracks changes it makes so they may be rolled back to their original state.
7
- # Rollback automatically occurs on an execute error.
8
- #
9
- # File.open("file.txt", "w") {|file| file << "original content"}
10
- #
11
- # t = FileTask.intern do |task, raise_error|
12
- # task.mkdir_p("some/dir") # marked for rollback
13
- # task.prepare("file.txt") do |file| # marked for rollback
14
- # file << "new content"
15
- # end
16
- #
17
- # # raise an error to start rollback
18
- # raise "error!" if raise_error
19
- # end
20
- #
21
- # begin
22
- # t.execute(true)
23
- # rescue
24
- # $!.message # => "error!"
25
- # File.exists?("some/dir") # => false
26
- # File.read("file.txt") # => "original content"
27
- # end
28
- #
29
- # t.execute(false)
30
- # File.exists?("some/dir") # => true
31
- # File.read("file.txt") # => "new content"
32
- #
33
- class FileTask < Task
34
- include ShellUtils
35
-
36
- # The backup directory
37
- config_attr :backup_dir, 'backup' # The backup directory
38
-
39
- # A flag indicating whether or track changes
40
- # for rollback on execution error
41
- config :rollback_on_error, true, &c.switch # Rollback changes on error
42
-
43
- def initialize(config={}, app=Tap::App.instance)
44
- super
45
- @actions = []
46
- end
47
-
48
- # Initializes a copy that will rollback independent of self.
49
- def initialize_copy(orig)
50
- super
51
- @actions = []
52
- end
53
-
54
- # Returns the path, exchanging the extension with extname.
55
- # A false or nil extname removes the extension, while true
56
- # preserves the existing extension (and effectively does
57
- # nothing).
58
- #
59
- # t = FileTask.new
60
- # t.basepath('path/to/file.txt') # => 'path/to/file'
61
- # t.basepath('path/to/file.txt', '.html') # => 'path/to/file.html'
62
- #
63
- # t.basepath('path/to/file.txt', false) # => 'path/to/file'
64
- # t.basepath('path/to/file.txt', true) # => 'path/to/file.txt'
65
- #
66
- # Compare to basename.
67
- def basepath(path, extname=false)
68
- case extname
69
- when false, nil then path.chomp(File.extname(path))
70
- when true then path
71
- else Root::Utils.exchange(path, extname)
72
- end
73
- end
74
-
75
- # Returns the basename of path, exchanging the extension
76
- # with extname. A false or nil extname removes the
77
- # extension, while true preserves the existing extension.
78
- #
79
- # t = FileTask.new
80
- # t.basename('path/to/file.txt') # => 'file.txt'
81
- # t.basename('path/to/file.txt', '.html') # => 'file.html'
82
- #
83
- # t.basename('path/to/file.txt', false) # => 'file'
84
- # t.basename('path/to/file.txt', true) # => 'file.txt'
85
- #
86
- # Compare to basepath.
87
- def basename(path, extname=true)
88
- basepath(File.basename(path), extname)
89
- end
90
-
91
- # Constructs a filepath using the dir and the specified paths.
92
- #
93
- # t = FileTask.new
94
- # t.filepath('data', "result.txt") # => File.expand_path("data/tap/tasks/file_task/result.txt")
95
- #
96
- def filepath(dir, *paths)
97
- File.expand_path(File.join(dir, *paths))
98
- end
99
-
100
- # Makes a backup filepath relative to backup_dir by using name, the
101
- # basename of filepath, and an index.
102
- #
103
- # t = FileTask.new({:backup_dir => "/backup"}, "name")
104
- # t.backup_filepath("path/to/file.txt", time) # => "/backup/name/file.0.txt"
105
- #
106
- def backup_filepath(path)
107
- extname = File.extname(path)
108
- backup_path = filepath(backup_dir, File.basename(path).chomp(extname))
109
- next_indexed_path(backup_path, 0, extname)
110
- end
111
-
112
- # Returns true if all of the targets are up to date relative to all of the
113
- # listed sources. Single values or arrays can be provided for both targets
114
- # and sources.
115
- #
116
- # Returns false (ie 'not up to date') if app.force is true.
117
- #
118
- #--
119
- # TODO: add check vs date reference (ex config_file date)
120
- def uptodate?(targets, sources=[])
121
- if app && app.force
122
- log_basename(:force, targets)
123
- false
124
- else
125
- targets = [targets] unless targets.kind_of?(Array)
126
- sources = [sources] unless sources.kind_of?(Array)
127
-
128
- targets.each do |target|
129
- return false unless FileUtils.uptodate?(target, sources)
130
- end
131
- true
132
- end
133
- end
134
-
135
- # Makes a backup of path to backup_filepath(path) and returns the backup path.
136
- # If backup_using_copy is true, the backup is a copy of path, otherwise the
137
- # file or directory at path is moved to the backup path. Raises an error if
138
- # the backup path already exists.
139
- #
140
- # Backups are restored on rollback.
141
- #
142
- # file = "file.txt"
143
- # File.open(file, "w") {|f| f << "file content"}
144
- #
145
- # t = FileTask.new
146
- # backup_file = t.backup(file)
147
- #
148
- # File.exists?(file) # => false
149
- # File.exists?(backup_file) # => true
150
- # File.read(backup_file) # => "file content"
151
- #
152
- # File.open(file, "w") {|f| f << "new content"}
153
- # t.rollback
154
- #
155
- # File.exists?(file) # => true
156
- # File.exists?(backup_file ) # => false
157
- # File.read(file) # => "file content"
158
- #
159
- def backup(path, backup_using_copy=false)
160
- return nil unless File.exists?(path)
161
-
162
- source = File.expand_path(path)
163
- target = backup_filepath(source)
164
- raise "backup already exists: #{target}" if File.exists?(target)
165
-
166
- mkdir_p File.dirname(target)
167
-
168
- log :backup, "#{source} to #{target}", Logger::DEBUG
169
- if backup_using_copy
170
- FileUtils.cp(source, target)
171
- else
172
- FileUtils.mv(source, target)
173
- end
174
-
175
- actions << [:backup, source, target]
176
- target
177
- end
178
-
179
- # Creates a directory and all its parent directories. Directories created
180
- # by mkdir_p removed on rollback.
181
- def mkdir_p(dir)
182
- dir = File.expand_path(dir)
183
-
184
- dirs = []
185
- while !File.exists?(dir)
186
- dirs.unshift(dir)
187
- dir = File.dirname(dir)
188
- end
189
-
190
- dirs.each {|d| mkdir(d) }
191
- end
192
-
193
- # Creates a directory. Directories created by mkdir removed on rollback.
194
- def mkdir(dir)
195
- dir = File.expand_path(dir)
196
-
197
- unless File.exists?(dir)
198
- log :mkdir, dir, Logger::DEBUG
199
- FileUtils.mkdir(dir)
200
- actions << [:make, dir]
201
- end
202
- end
203
-
204
- # Prepares the path by backing up any existing file and ensuring that
205
- # the parent directory for path exists. If a block is given, a file
206
- # is opened and yielded to it (as in File.open). Prepared paths are
207
- # removed and the backups restored on rollback.
208
- #
209
- # Returns the expanded path.
210
- def prepare(path, backup_using_copy=false)
211
- raise "not a file: #{path}" if File.directory?(path)
212
- path = File.expand_path(path)
213
-
214
- if File.exists?(path)
215
- # backup or remove existing files
216
- backup(path, backup_using_copy)
217
- else
218
- # ensure the parent directory exists
219
- # for non-existant files
220
- mkdir_p File.dirname(path)
221
- end
222
- log :prepare, path, Logger::DEBUG
223
- actions << [:make, path]
224
-
225
- if block_given?
226
- File.open(path, "w") {|file| yield(file) }
227
- end
228
-
229
- path
230
- end
231
-
232
- # Removes a file. If a directory is provided, it's contents are removed
233
- # recursively. Files and directories removed by rm_r are restored
234
- # upon an execution error.
235
- def rm_r(path)
236
- path = File.expand_path(path)
237
-
238
- backup(path, false)
239
- log :rm_r, path, Logger::DEBUG
240
- end
241
-
242
- # Removes an empty directory. Directories removed by rmdir are restored
243
- # upon an execution error.
244
- def rmdir(dir)
245
- dir = File.expand_path(dir)
246
-
247
- unless Root::Utils.empty?(dir)
248
- raise "not an empty directory: #{dir}"
249
- end
250
-
251
- backup(dir, false)
252
- log :rmdir, dir, Logger::DEBUG
253
- end
254
-
255
- # Removes a file. Directories cannot be removed by this method.
256
- # Files removed by rm are restored upon an execution error.
257
- def rm(path)
258
- path = File.expand_path(path)
259
-
260
- unless File.file?(path)
261
- raise "not a file: #{path}"
262
- end
263
-
264
- backup(path, false)
265
- log :rm, path, Logger::DEBUG
266
- end
267
-
268
- # Copies source to target. Files and directories copied by cp are
269
- # restored upon an execution error.
270
- def cp(source, target)
271
- target = File.join(target, File.basename(source)) if File.directory?(target)
272
- prepare(target)
273
-
274
- log :cp, "#{source} to #{target}", Logger::DEBUG
275
- FileUtils.cp(source, target)
276
- end
277
-
278
- # Copies source to target. If source is a directory, the contents
279
- # are copied recursively. If target is a directory, copies source
280
- # to target/source. Files and directories copied by cp are restored
281
- # upon an execution error.
282
- def cp_r(source, target)
283
- target = File.join(target, File.basename(source)) if File.directory?(target)
284
- prepare(target)
285
-
286
- log :cp_r, "#{source} to #{target}", Logger::DEBUG
287
- FileUtils.cp_r(source, target)
288
- end
289
-
290
- # Moves source to target. Files and directories moved by mv are
291
- # restored upon an execution error.
292
- def mv(source, target, backup_source=true)
293
- backup(source, true) if backup_source
294
- prepare(target)
295
-
296
- log :mv, "#{source} to #{target}", Logger::DEBUG
297
- FileUtils.mv(source, target)
298
- end
299
-
300
- # Rolls back any actions capable of being rolled back.
301
- #
302
- # Rollback is forceful. For instance if you make a folder using
303
- # mkdir, rollback will remove the folder and all files within it
304
- # even if they were not added by self.
305
- def rollback
306
- while !actions.empty?
307
- action, source, target = actions.pop
308
-
309
- case action
310
- when :make
311
- log :rollback, "#{source}", Logger::DEBUG
312
- FileUtils.rm_r(source)
313
- when :backup
314
- log :rollback, "#{target} to #{source}", Logger::DEBUG
315
- dir = File.dirname(source)
316
- FileUtils.mkdir_p(dir) unless File.exists?(dir)
317
- FileUtils.mv(target, source, :force => true)
318
- else
319
- raise "unknown action: #{[action, source, target].inspect}"
320
- end
321
- end
322
- end
323
-
324
- # Removes backup files. Cleanup cannot be rolled back and prevents
325
- # rollback of actions up to when cleanup is called. If cleanup_dirs
326
- # is true, empty directories containing the backup files will be
327
- # removed.
328
- def cleanup(cleanup_dirs=true)
329
- actions.each do |action, source, target|
330
- if action == :backup
331
- log :cleanup, target, Logger::DEBUG
332
- FileUtils.rm_r(target) if File.exists?(target)
333
- cleanup_dir(File.dirname(target)) if cleanup_dirs
334
- end
335
- end
336
- actions.clear
337
- end
338
-
339
- # Removes the directory if empty, and all empty parent directories. This
340
- # method cannot be rolled back.
341
- def cleanup_dir(dir)
342
- while Root::Utils.empty?(dir)
343
- log :rmdir, dir, Logger::DEBUG
344
- FileUtils.rmdir(dir)
345
- dir = File.dirname(dir)
346
- end
347
- end
348
-
349
- # Logs the given action, with the basenames of the input paths.
350
- def log_basename(action, paths, level=Logger::INFO)
351
- msg = [paths].flatten.collect {|path| File.basename(path) }.join(',')
352
- log(action, msg, level)
353
- end
354
-
355
- def call(*_inputs)
356
- actions.clear
357
-
358
- begin
359
- super
360
- rescue(Exception)
361
- rollback if rollback_on_error
362
- raise
363
- end
364
- end
365
-
366
- protected
367
-
368
- # An array tracking actions (backup, rm, mv, etc) performed by self,
369
- # allowing rollback on an execution error. Not intended to be
370
- # modified manually.
371
- attr_reader :actions
372
-
373
- private
374
-
375
- # utility method for backup_filepath; increments index until the
376
- # path base.indexext does not exist.
377
- def next_indexed_path(base, index, ext) # :nodoc:
378
- path = sprintf('%s.%d%s', base, index, ext)
379
- File.exists?(path) ? next_indexed_path(base, index + 1, ext) : path
380
- end
381
- end
382
- end
383
- end
@@ -1,71 +0,0 @@
1
- require 'tap/task'
2
- autoload(:Tempfile, 'tempfile')
3
-
4
- module Tap
5
- module Tasks
6
- class FileTask < Tap::Task
7
-
8
- # Provides several shell utility methods for calling programs.
9
- #
10
- # == Windows
11
- # MSDOS has command line length limits specific to the version of Windows being
12
- # run (from http://www.ss64.com/nt/cmd.html):
13
- #
14
- # Windows NT:: 256 characters
15
- # Windows 2000:: 2046 characters
16
- # Windows XP:: 8190 characters
17
- #
18
- # Commands longer than these limits fail, usually with something like: 'the input
19
- # line is too long'
20
- module ShellUtils
21
-
22
- # Run the system command +cmd+, passing the result to the block, if given.
23
- # Raises an error if the command fails. Uses the same semantics as
24
- # Kernel::exec and Kernel::system.
25
- #
26
- # Based on FileUtils#sh from Rake.
27
- def sh(*cmd) # :yields: ok, status
28
- ok = system(*cmd)
29
-
30
- if block_given?
31
- yield(ok, $?)
32
- else
33
- ok or raise "Command failed with status (#{$?.exitstatus}): [#{ cmd.join(' ')}]"
34
- end
35
- end
36
-
37
- # Runs the system command +cmd+ using sh, redirecting the output to the
38
- # specified file path. Uses the redirection command:
39
- #
40
- # "> \"#{path}\" 2>&1 #{cmd}"
41
- #
42
- # This redirection has been tested on Windows, OS X, and Fedora. See
43
- # http://en.wikipedia.org/wiki/Redirection_(Unix) for pointers on
44
- # redirection. This style of redirection SHOULD NOT be used with
45
- # commands that contain other redirections.
46
- def redirect_sh(cmd, path, &block) # :yields: ok, status
47
- sh( "> \"#{path}\" 2>&1 #{cmd}", &block)
48
- end
49
-
50
- # Runs the system command +cmd+ and returns the output as a string.
51
- def capture_sh(cmd, quiet=false, &block) # :yields: ok, status, tempfile_path
52
- tempfile = Tempfile.new('shell_utils')
53
- tempfile.close
54
- redirect_sh(cmd, tempfile.path) do |ok, status|
55
- if block_given?
56
- yield(ok, $?, tempfile.path)
57
- else
58
- ok or raise %Q{Command failed with status (#{$?.exitstatus}): [#{cmd}]
59
- -------------- command output -------------------
60
- #{File.read(tempfile.path)}
61
- -------------------------------------------------
62
- }
63
- end
64
- end
65
-
66
- quiet == true ? "" : File.read(tempfile.path)
67
- end
68
- end
69
- end
70
- end
71
- end