advanced-fileutils 0.0.3

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