tap-tasks 0.1.0 → 0.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.
- data/History +6 -0
- data/README +20 -3
- data/lib/tap/tasks/argv.rb +3 -1
- data/lib/tap/tasks/dump/inspect.rb +13 -7
- data/lib/tap/tasks/dump/yaml.rb +6 -7
- data/lib/tap/tasks/file_task.rb +383 -0
- data/lib/tap/tasks/file_task/shell_utils.rb +71 -0
- data/lib/tap/tasks/glob.rb +38 -0
- data/lib/tap/tasks/load/yaml.rb +6 -7
- metadata +17 -4
data/History
CHANGED
data/README
CHANGED
@@ -1,14 +1,31 @@
|
|
1
|
-
= Tap Tasks
|
1
|
+
= {Tap Tasks}[http://tap.rubyforge.org/tap-tasks]
|
2
2
|
|
3
|
+
task n. a piece of work to be done or undertaken
|
4
|
+
|
3
5
|
A set of standard Tap tasks.
|
4
6
|
|
5
7
|
== Description
|
6
8
|
|
7
|
-
|
9
|
+
A standard library of utility tasks, for example tasks to dump/load results as
|
10
|
+
{YAML}[http://www.yaml.org/]. {Tap-Tasks}[http://tap.rubyforge.org/tap-tasks]
|
11
|
+
is a part of the {Tap-Suite}[http://tap.rubyforge.org/tap-suite]. Check out
|
12
|
+
these links for documentation, development, and bug tracking.
|
13
|
+
|
14
|
+
* Website[http://tap.rubyforge.org]
|
15
|
+
* Lighthouse[http://bahuvrihi.lighthouseapp.com/projects/9908-tap-task-application/tickets]
|
16
|
+
* Github[http://github.com/bahuvrihi/tap/tree/master]
|
17
|
+
* {Google Group}[http://groups.google.com/group/ruby-on-tap]
|
18
|
+
|
19
|
+
== Usage
|
20
|
+
|
21
|
+
Usage is the same as for any tasks.
|
22
|
+
|
23
|
+
% tap run -- load/yaml "{key: value}" --: inspect
|
24
|
+
{"key" => "value"}
|
8
25
|
|
9
26
|
== Installation
|
10
27
|
|
11
|
-
Tap
|
28
|
+
Tap-Tasks is available as a gem on RubyForge[http://rubyforge.org/projects/tap]. Use:
|
12
29
|
|
13
30
|
% gem install tap-tasks
|
14
31
|
|
data/lib/tap/tasks/argv.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
require 'tap/task'
|
2
|
+
|
1
3
|
module Tap
|
2
4
|
module Tasks
|
3
|
-
# :startdoc::
|
5
|
+
# :startdoc::task provides a handle to ARGV
|
4
6
|
#
|
5
7
|
# Simply returns ARGV. This task can be a useful hook when executing
|
6
8
|
# saved workflows via run (given that all arguments after the workflow
|
@@ -1,22 +1,28 @@
|
|
1
|
-
require 'tap/dump'
|
1
|
+
require 'tap/tasks/dump'
|
2
2
|
|
3
3
|
module Tap
|
4
4
|
module Tasks
|
5
|
-
|
5
|
+
class Dump < Tap::Task
|
6
6
|
|
7
|
-
# :startdoc::
|
7
|
+
# :startdoc::task inspect and dump an object
|
8
8
|
#
|
9
|
-
# Dumps objects to a file or IO using object.inspect.
|
10
|
-
#
|
9
|
+
# Dumps objects to a file or IO using object.inspect. An alternate
|
10
|
+
# method can be specified for inspection using the inspect_method
|
11
|
+
# config.
|
11
12
|
#
|
12
13
|
# % tap run -- load/yaml "{key: value}" --: inspect
|
13
14
|
# {"key"=>"value"}
|
14
15
|
#
|
15
|
-
|
16
|
+
# % tap run -- load string --: inspect -m length
|
17
|
+
# 6
|
18
|
+
#
|
19
|
+
class Inspect < Dump
|
20
|
+
|
21
|
+
config :inspect_method, 'inspect', :short => :m # The inspection method
|
16
22
|
|
17
23
|
# Dumps the object to io using obj.inspect
|
18
24
|
def dump(obj, io)
|
19
|
-
io.puts obj.
|
25
|
+
io.puts obj.send(inspect_method)
|
20
26
|
end
|
21
27
|
end
|
22
28
|
end
|
data/lib/tap/tasks/dump/yaml.rb
CHANGED
@@ -1,19 +1,18 @@
|
|
1
|
-
require 'tap/dump'
|
1
|
+
require 'tap/tasks/dump'
|
2
2
|
|
3
3
|
module Tap
|
4
4
|
module Tasks
|
5
|
-
|
5
|
+
class Dump < Tap::Task
|
6
6
|
|
7
|
-
# :startdoc::
|
7
|
+
# :startdoc::task dumps data as YAML
|
8
8
|
#
|
9
|
-
# Dumps workflow results to a file or IO as YAML.
|
10
|
-
# dump task for more details.
|
9
|
+
# Dumps workflow results to a file or IO as YAML.
|
11
10
|
#
|
12
11
|
# % tap run -- load/yaml "{key: value}" --: dump/yaml
|
13
|
-
# ---
|
12
|
+
# ---
|
14
13
|
# key: value
|
15
14
|
#
|
16
|
-
class Yaml <
|
15
|
+
class Yaml < Dump
|
17
16
|
|
18
17
|
# Dumps the object to io as YAML.
|
19
18
|
def dump(obj, io)
|
@@ -0,0 +1,383 @@
|
|
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
|
@@ -0,0 +1,71 @@
|
|
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
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'tap/task'
|
2
|
+
|
3
|
+
module Tap
|
4
|
+
module Tasks
|
5
|
+
# :startdoc::task globs for files
|
6
|
+
#
|
7
|
+
# Globs the input patterns for matching patterns. Matching files are
|
8
|
+
# returned as an array.
|
9
|
+
#
|
10
|
+
# % tap run -- glob * --: dump/yaml
|
11
|
+
#
|
12
|
+
class Glob < Tap::Task
|
13
|
+
|
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
|
18
|
+
|
19
|
+
def process(*patterns)
|
20
|
+
results = []
|
21
|
+
patterns.each do |pattern|
|
22
|
+
Dir[pattern].each do |path|
|
23
|
+
next if !files && File.file?(path)
|
24
|
+
next if !dirs && File.directory?(path)
|
25
|
+
|
26
|
+
case path
|
27
|
+
when *filters then next
|
28
|
+
else results << path
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
results.uniq! if unique
|
34
|
+
results
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/tap/tasks/load/yaml.rb
CHANGED
@@ -1,19 +1,18 @@
|
|
1
|
-
require 'tap/load'
|
1
|
+
require 'tap/tasks/load'
|
2
2
|
|
3
3
|
module Tap
|
4
4
|
module Tasks
|
5
|
-
|
5
|
+
class Load < Tap::Task
|
6
6
|
|
7
|
-
# :startdoc::
|
7
|
+
# :startdoc::task loads data as YAML
|
8
8
|
#
|
9
|
-
# Loads data from the input IO as YAML.
|
10
|
-
# for more details.
|
9
|
+
# Loads data from the input IO as YAML.
|
11
10
|
#
|
12
11
|
# % tap run -- load/yaml "{key: value}" --: dump/yaml
|
13
|
-
# ---
|
12
|
+
# ---
|
14
13
|
# key: value
|
15
14
|
#
|
16
|
-
class Yaml <
|
15
|
+
class Yaml < Load
|
17
16
|
|
18
17
|
# Loads data from io as YAML.
|
19
18
|
def load(io)
|
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.
|
4
|
+
version: 0.2.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-
|
12
|
+
date: 2009-05-25 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -20,7 +20,17 @@ dependencies:
|
|
20
20
|
requirements:
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 0.
|
23
|
+
version: 0.17.0
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: tap-test
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.1.0
|
24
34
|
version:
|
25
35
|
description:
|
26
36
|
email: simon.a.chiang@gmail.com
|
@@ -33,10 +43,13 @@ extra_rdoc_files:
|
|
33
43
|
- README
|
34
44
|
- MIT-LICENSE
|
35
45
|
files:
|
46
|
+
- lib/tap/tasks/file_task/shell_utils.rb
|
36
47
|
- lib/tap/tasks/argv.rb
|
37
48
|
- lib/tap/tasks/dump/inspect.rb
|
38
49
|
- lib/tap/tasks/dump/yaml.rb
|
39
50
|
- lib/tap/tasks/load/yaml.rb
|
51
|
+
- lib/tap/tasks/glob.rb
|
52
|
+
- lib/tap/tasks/file_task.rb
|
40
53
|
- tap.yml
|
41
54
|
- History
|
42
55
|
- README
|
@@ -50,7 +63,7 @@ rdoc_options:
|
|
50
63
|
- -S
|
51
64
|
- -N
|
52
65
|
- --title
|
53
|
-
- Tap
|
66
|
+
- Tap-Tasks
|
54
67
|
require_paths:
|
55
68
|
- lib
|
56
69
|
required_ruby_version: !ruby/object:Gem::Requirement
|