tap-tasks 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|