advanced-fileutils 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/advanced-fileutils.rb +394 -0
- data/lib/fileutils/advanced.rb +19 -0
- metadata +67 -0
@@ -0,0 +1,394 @@
|
|
1
|
+
#
|
2
|
+
# = advanced-fileutils.rb
|
3
|
+
#
|
4
|
+
# Copyright (c) 2009 Michael H. Buselli
|
5
|
+
#
|
6
|
+
# This program is free software.
|
7
|
+
# You can distribute/modify this program under the same terms of Ruby.
|
8
|
+
#
|
9
|
+
# == module AdvFileUtils
|
10
|
+
#
|
11
|
+
# Advanced FileUtils contains more methods you might have wished were in
|
12
|
+
# FileUtils.
|
13
|
+
#
|
14
|
+
# === Module Functions
|
15
|
+
#
|
16
|
+
# append(filename, data, options)
|
17
|
+
# append(filename, options) {|file| .... }
|
18
|
+
# truncate(filename, options)
|
19
|
+
# truncate(filename, data, options)
|
20
|
+
# truncate(filename, options) {|file| .... }
|
21
|
+
# replace(filename, data, options) # does "atomic" file change
|
22
|
+
# replace(filename, options) {|file| .... }
|
23
|
+
# x_insert(filename, data, options) # :line => 7
|
24
|
+
# x_insert(filename, options) {|file| .... }
|
25
|
+
# x_update(filename, data, options) # :line => 7
|
26
|
+
# x_update(filename, options) {|file| .... } # :lines => 7..10
|
27
|
+
# # :separator => ':'
|
28
|
+
# # :where => '$1 = mbuselli'
|
29
|
+
# edit(filename, options) # :visual => true
|
30
|
+
# edit(filename, data, options)
|
31
|
+
# system(command_list, options)
|
32
|
+
# shell(command_list, options) # alias of system
|
33
|
+
# sh(command_list, options) # alias of system
|
34
|
+
# x_sudo(command_list, options) # :runas => 0
|
35
|
+
# pipe(command, data_in, data_out)
|
36
|
+
# pipe(command, data_in, data_out, data_err)
|
37
|
+
# supipe(command, data_in, data_out)
|
38
|
+
# supipe(command, data_in, data_out, data_err)
|
39
|
+
#
|
40
|
+
# Many options are not implemented yet.
|
41
|
+
#
|
42
|
+
# insert(filename,
|
43
|
+
# ["require 'cruby_config'\n", :before, /^require/],
|
44
|
+
# [<<-__EOF__.gsub(/^\s{2}/, ''), :after, /ConfigMap\.merge!/,
|
45
|
+
#
|
46
|
+
# # Inserted by jruby-in-a-box to hack up the default paths.
|
47
|
+
# ConfigMap.merge!(
|
48
|
+
# :bindir => CRubyConfig::CONFIG["bindir"],
|
49
|
+
# :libdir => CRubyConfig::CONFIG["libdir"]
|
50
|
+
# )
|
51
|
+
# __EOF__
|
52
|
+
# :and_before, /^\s*$/])
|
53
|
+
#
|
54
|
+
|
55
|
+
require 'sha1'
|
56
|
+
require 'tempfile'
|
57
|
+
|
58
|
+
require 'escape'
|
59
|
+
|
60
|
+
module AdvFileUtils
|
61
|
+
|
62
|
+
#
|
63
|
+
# A superclass of all errors that can be raised from this module's
|
64
|
+
# functions.
|
65
|
+
#
|
66
|
+
class Error < Exception; end
|
67
|
+
|
68
|
+
#
|
69
|
+
# CommandError is raised when an external command has a problem.
|
70
|
+
#
|
71
|
+
class CommandError < AdvFileUtils::Error; end
|
72
|
+
|
73
|
+
#
|
74
|
+
# FileLockError is raised when trying to open an agreed upon "lockfile"
|
75
|
+
# and the file already exists.
|
76
|
+
#
|
77
|
+
class FileLockError < AdvFileUtils::Error; end
|
78
|
+
|
79
|
+
|
80
|
+
# Hash table to hold command options.
|
81
|
+
OPT_TABLE = {} #:nodoc: internal use only
|
82
|
+
|
83
|
+
|
84
|
+
# Method for internal use to prep output for file output verbose messages.
|
85
|
+
def write_echo_message (data, op, filename) #:nodoc:
|
86
|
+
[ 'echo',
|
87
|
+
Escape.shell_single_word(
|
88
|
+
if data[-1] == "\n"[0]
|
89
|
+
data[0...-1]
|
90
|
+
else
|
91
|
+
data + '\\c'
|
92
|
+
end),
|
93
|
+
op,
|
94
|
+
Escape.shell_single_word(filename)
|
95
|
+
].join(' ')
|
96
|
+
end
|
97
|
+
module_function :write_echo_message
|
98
|
+
private_class_method :write_echo_message
|
99
|
+
|
100
|
+
|
101
|
+
# Method for internal use to intercept write calls when we are being verbose.
|
102
|
+
def hook_write (object, filename, tail_msg = nil) #:nodoc:
|
103
|
+
object.instance_eval <<-__EOM__, __FILE__, __LINE__ + 1
|
104
|
+
class << self
|
105
|
+
def write (data, *args)
|
106
|
+
$stderr.puts AdvFileUtils.__send__(:write_echo_message, data, '>>', #{filename.inspect})
|
107
|
+
super
|
108
|
+
ensure
|
109
|
+
send #{tail_msg.inspect} if #{tail_msg.inspect}
|
110
|
+
end
|
111
|
+
end
|
112
|
+
__EOM__
|
113
|
+
end
|
114
|
+
module_function :hook_write
|
115
|
+
private_class_method :hook_write
|
116
|
+
|
117
|
+
|
118
|
+
def parse_data_and_options (data_and_options) #:nodoc#
|
119
|
+
if data_and_options.size == 1
|
120
|
+
if data_and_options[0].respond_to? :has_key?
|
121
|
+
options = data_and_options[0]
|
122
|
+
else
|
123
|
+
data = data_and_options[0]
|
124
|
+
end
|
125
|
+
else
|
126
|
+
data, options = *data_and_options
|
127
|
+
end
|
128
|
+
|
129
|
+
data ||= ''
|
130
|
+
options ||= {}
|
131
|
+
[data, options]
|
132
|
+
end
|
133
|
+
module_function :parse_data_and_options
|
134
|
+
private_class_method :parse_data_and_options
|
135
|
+
|
136
|
+
|
137
|
+
#
|
138
|
+
# Internally used function to implement .append and .truncate.
|
139
|
+
#
|
140
|
+
def generic_write (open_arg, filename, *data_and_options) #:nodoc:
|
141
|
+
if block_given?
|
142
|
+
options = data_and_options[0] || {}
|
143
|
+
|
144
|
+
if options[:verbose] and open_arg == 'w'
|
145
|
+
$stderr.puts "cat /dev/null > #{Escape.shell_single_word(filename)}"
|
146
|
+
end
|
147
|
+
|
148
|
+
if not options[:noop]
|
149
|
+
File.open(filename, open_arg) do |f|
|
150
|
+
hook_write(f, filename) if options[:verbose]
|
151
|
+
yield f
|
152
|
+
end
|
153
|
+
|
154
|
+
elsif options[:verbose]
|
155
|
+
f = StringIO.new
|
156
|
+
hook_write(f, filename, :rewind)
|
157
|
+
yield f
|
158
|
+
end
|
159
|
+
|
160
|
+
else
|
161
|
+
data, options = *parse_data_and_options(data_and_options)
|
162
|
+
|
163
|
+
if options[:verbose]
|
164
|
+
if open_arg == 'w'
|
165
|
+
$stderr.puts AdvFileUtils.__send__(:write_echo_message, data, '>', filename)
|
166
|
+
else
|
167
|
+
$stderr.puts AdvFileUtils.__send__(:write_echo_message, data, '>>', filename)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
if not options[:noop]
|
172
|
+
File.open(filename, open_arg) do |f|
|
173
|
+
f.write(data)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
module_function :generic_write
|
179
|
+
private_class_method :generic_write
|
180
|
+
|
181
|
+
|
182
|
+
#
|
183
|
+
# Options: verbose, noop, force, backup
|
184
|
+
#
|
185
|
+
# Append the given +data+ to the file named by +filename+.
|
186
|
+
#
|
187
|
+
# If called with a block then the File object is yielded to the block
|
188
|
+
# for appending data intead of the data being passed as an argument.
|
189
|
+
#
|
190
|
+
# AdvFileUtils.append('data.log', "some data for log entry\n")
|
191
|
+
# AdvFileUtils.append('data.log') { |f| f.puts "some data for log entry" }
|
192
|
+
#
|
193
|
+
def append (filename, *data_and_options, &block)
|
194
|
+
generic_write 'a', filename, *data_and_options, &block
|
195
|
+
end
|
196
|
+
module_function :append
|
197
|
+
public :append
|
198
|
+
|
199
|
+
OPT_TABLE['append'] = [:verbose, :noop, :force, :backup]
|
200
|
+
|
201
|
+
|
202
|
+
#
|
203
|
+
# Options: verbose, noop, force, backup
|
204
|
+
#
|
205
|
+
# Replace the given +data+ in the file named by +filename+.
|
206
|
+
#
|
207
|
+
# If called with a block then the File object is yielded to the block
|
208
|
+
# for writing data intead of the data being passed as an argument.
|
209
|
+
#
|
210
|
+
# AdvFileUtils.truncate('data.log', "some data\n")
|
211
|
+
# AdvFileUtils.truncate('data.log') { |f| f.puts "some data" }
|
212
|
+
#
|
213
|
+
def truncate (filename, *data_and_options, &block)
|
214
|
+
generic_write 'w', filename, *data_and_options, &block
|
215
|
+
end
|
216
|
+
module_function :truncate
|
217
|
+
public :truncate
|
218
|
+
|
219
|
+
OPT_TABLE['truncate'] = [:verbose, :noop, :force, :backup]
|
220
|
+
|
221
|
+
|
222
|
+
#
|
223
|
+
# Options: verbose, noop
|
224
|
+
#
|
225
|
+
# An alternative to Kernel.system that accepts options for verbosity
|
226
|
+
# and dry runs.
|
227
|
+
#
|
228
|
+
def system (*command_and_options)
|
229
|
+
if command_and_options[-1].respond_to?(:has_key?)
|
230
|
+
command = command_and_options[0...-1]
|
231
|
+
options = command_and_options[-1]
|
232
|
+
else
|
233
|
+
command = command_and_options
|
234
|
+
options = {}
|
235
|
+
end
|
236
|
+
|
237
|
+
raise ArgumentError.new('wrong number of arguments') if command.empty?
|
238
|
+
|
239
|
+
if options[:verbose]
|
240
|
+
if command.size == 1
|
241
|
+
$stderr.puts command[0]
|
242
|
+
else
|
243
|
+
$stderr.puts command.collect { |word|
|
244
|
+
Escape.shell_single_word word
|
245
|
+
}.join(' ')
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
if not options[:noop]
|
250
|
+
Kernel.system(*command)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
module_function :system
|
254
|
+
public :system
|
255
|
+
|
256
|
+
alias sh system
|
257
|
+
module_function :sh
|
258
|
+
public :sh
|
259
|
+
|
260
|
+
alias shell system
|
261
|
+
module_function :shell
|
262
|
+
public :shell
|
263
|
+
|
264
|
+
OPT_TABLE['sh'] = OPT_TABLE['shell'] =
|
265
|
+
OPT_TABLE['system'] = [:verbose, :noop]
|
266
|
+
|
267
|
+
|
268
|
+
#
|
269
|
+
# Options: verbose, noop, force, backup, editor
|
270
|
+
#
|
271
|
+
# Invoke an external editor to edit some text or a file.
|
272
|
+
#
|
273
|
+
# edit(filename, options)
|
274
|
+
# edit(filename, data, options)
|
275
|
+
#
|
276
|
+
# Return values
|
277
|
+
#
|
278
|
+
# true, if successful and file was edited
|
279
|
+
# false, if successful and file was not edited
|
280
|
+
# nil, if successful and file was not saved
|
281
|
+
#
|
282
|
+
def edit (filename, *data_and_options)
|
283
|
+
data, options = *parse_data_and_options(data_and_options)
|
284
|
+
editor =
|
285
|
+
options[:editor] ? options[:editor] :
|
286
|
+
ENV.has_key?('VISUAL') ? ENV['VISUAL'] :
|
287
|
+
ENV.has_key?('EDITOR') ? ENV['EDITOR'] : 'vi'
|
288
|
+
|
289
|
+
file_stat = File.stat(filename)
|
290
|
+
file_checksum = SHA1.file(filename)
|
291
|
+
|
292
|
+
if not data.nil?
|
293
|
+
truncate_options = options.reject do |k, v|
|
294
|
+
OPT_TABLE['truncate'].index(k).nil?
|
295
|
+
end
|
296
|
+
truncate(filename, data, truncate_options)
|
297
|
+
end
|
298
|
+
|
299
|
+
system(editor, filename, options)
|
300
|
+
proc_status = $?
|
301
|
+
|
302
|
+
if options[:noop]
|
303
|
+
return true
|
304
|
+
|
305
|
+
elsif proc_status.success?
|
306
|
+
return true if file_checksum != SHA1.file(filename)
|
307
|
+
return nil if file_stat == File.stat(filename)
|
308
|
+
return false
|
309
|
+
|
310
|
+
elsif proc_status.signaled?
|
311
|
+
raise AdvFileUtils::CommandError.new("editor terminated on signal #{proc_status.termsig}")
|
312
|
+
|
313
|
+
else
|
314
|
+
raise AdvFileUtils::CommandError.new("editor had non-zero exit code #{proc_status.exitstatus}")
|
315
|
+
end
|
316
|
+
end
|
317
|
+
module_function :edit
|
318
|
+
public :edit
|
319
|
+
|
320
|
+
OPT_TABLE['edit'] = [:verbose, :noop, :force, :backup, :editor]
|
321
|
+
|
322
|
+
|
323
|
+
#
|
324
|
+
# Options: editor
|
325
|
+
#
|
326
|
+
# Edit some data in an external editor and return the result.
|
327
|
+
#
|
328
|
+
# edit_data("Hello World\n")
|
329
|
+
#
|
330
|
+
def edit_data (data, options = {})
|
331
|
+
tmp = Tempfile.new(File.basename($0))
|
332
|
+
tmp.close
|
333
|
+
edit(tmp.path, data, options)
|
334
|
+
File.read(tmp.path)
|
335
|
+
ensure
|
336
|
+
tmp.delete if tmp
|
337
|
+
end
|
338
|
+
module_function :edit_data
|
339
|
+
public :edit_data
|
340
|
+
|
341
|
+
OPT_TABLE['edit_data'] = [:editor]
|
342
|
+
|
343
|
+
|
344
|
+
#
|
345
|
+
# Options: verbose, noop, force, backup, lockfile, retry
|
346
|
+
#
|
347
|
+
# Edit a file, but open a temporary lockfile instead and move it in place
|
348
|
+
# after editting is complete.
|
349
|
+
#
|
350
|
+
def replace (filename, *data_and_options)
|
351
|
+
data, options = *parse_data_and_options(data_and_options)
|
352
|
+
lockfile = options[:lockfile] ? options[:lockfile] : "#{filename}.lock"
|
353
|
+
|
354
|
+
begin
|
355
|
+
if not options[:noop]
|
356
|
+
fd = IO.sysopen(lockfile, IO::WRONLY | IO::CREAT | IO::EXCL, 0700)
|
357
|
+
f = IO.new(fd, 'w')
|
358
|
+
hook_write(f, lockfile) if block_given? and options[:verbose]
|
359
|
+
else
|
360
|
+
f = StringIO.new
|
361
|
+
hook_write(f, lockfile, :rewind) if block_given? and options[:verbose]
|
362
|
+
end
|
363
|
+
|
364
|
+
file_stat = File.stat(filename) rescue nil
|
365
|
+
|
366
|
+
if block_given?
|
367
|
+
$stderr.puts "cat /dev/null > #{Escape.shell_single_word(lockfile)}" if options[:verbose]
|
368
|
+
yield f
|
369
|
+
else
|
370
|
+
$stderr.puts AdvFileUtils.__send__(:write_echo_message, data, '>', lockfile) if options[:verbose]
|
371
|
+
f.write(data)
|
372
|
+
end
|
373
|
+
|
374
|
+
f.close
|
375
|
+
|
376
|
+
if file_stat
|
377
|
+
FileUtils.chown(file_stat.uid.to_s, file_stat.gid.to_s, lockfile, options)
|
378
|
+
FileUtils.chmod(file_stat.mode & 07777, lockfile, options)
|
379
|
+
end
|
380
|
+
FileUtils.mv(lockfile, filename, options)
|
381
|
+
|
382
|
+
ensure
|
383
|
+
f.close if f and not f.closed?
|
384
|
+
begin
|
385
|
+
File.delete(lockfile) if fd
|
386
|
+
rescue Errno::ENOENT
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
module_function :replace
|
391
|
+
public :replace
|
392
|
+
|
393
|
+
OPT_TABLE['replace'] = [:verbose, :noop, :force, :backup, :lockfile, :retry]
|
394
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
require 'advanced-fileutils'
|
3
|
+
|
4
|
+
module FileUtils
|
5
|
+
extend ::AdvFileUtils
|
6
|
+
end
|
7
|
+
|
8
|
+
#module FileUtils::Verbose
|
9
|
+
# extend ::AdvFileUtils::Verbose
|
10
|
+
#end
|
11
|
+
|
12
|
+
#module FileUtils::NoWrite
|
13
|
+
# extend ::AdvFileUtils::NoWrite
|
14
|
+
#end
|
15
|
+
|
16
|
+
#module FileUtils::DryRun
|
17
|
+
# extend ::AdvFileUtils::DryRun
|
18
|
+
#end
|
19
|
+
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: advanced-fileutils
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael H Buselli
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-10-01 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: escape
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.4
|
24
|
+
version:
|
25
|
+
description: " Advanced FileUtils contains more methods you might have wished were in FileUtils.\n"
|
26
|
+
email:
|
27
|
+
- cosine@cosine.org
|
28
|
+
- michael@buselli.com
|
29
|
+
executables: []
|
30
|
+
|
31
|
+
extensions: []
|
32
|
+
|
33
|
+
extra_rdoc_files: []
|
34
|
+
|
35
|
+
files:
|
36
|
+
- lib/fileutils/advanced.rb
|
37
|
+
- lib/advanced-fileutils.rb
|
38
|
+
has_rdoc: true
|
39
|
+
homepage: http://github.com/cosine/advanced-fileutils
|
40
|
+
licenses: []
|
41
|
+
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
requirements: []
|
60
|
+
|
61
|
+
rubyforge_project: advanced-fileutils
|
62
|
+
rubygems_version: 1.3.4
|
63
|
+
signing_key:
|
64
|
+
specification_version: 3
|
65
|
+
summary: Advanced FileUtils contains more methods you might have wished were in FileUtils.
|
66
|
+
test_files: []
|
67
|
+
|