cosine-advanced-fileutils 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,347 @@
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
+ #
36
+ # Many options are not implemented yet.
37
+ #
38
+
39
+ require 'sha1'
40
+ require 'escape'
41
+
42
+ module AdvFileUtils
43
+
44
+ #
45
+ # A superclass of all errors that can be raised from this module's
46
+ # functions.
47
+ #
48
+ class Error < Exception; end
49
+
50
+ #
51
+ # CommandError is raised when an external command has a problem.
52
+ #
53
+ class CommandError < AdvFileUtils::Error; end
54
+
55
+ #
56
+ # FileLockError is raised when trying to open an agreed upon "lockfile"
57
+ # and the file already exists.
58
+ #
59
+ class FileLockError < AdvFileUtils::Error; end
60
+
61
+
62
+ # Hash table to hold command options.
63
+ OPT_TABLE = {} #:nodoc: internal use only
64
+
65
+
66
+ # Method for internal use to prep output for file output verbose messages.
67
+ def write_echo_message (data, op, filename) #:nodoc:
68
+ [ 'echo',
69
+ Escape.shell_single_word(
70
+ if data[-1] == "\n"[0]
71
+ data[0...-1]
72
+ else
73
+ data + '\\c'
74
+ end),
75
+ op,
76
+ Escape.shell_single_word(filename)
77
+ ].join(' ')
78
+ end
79
+ module_function :write_echo_message
80
+ private_class_method :write_echo_message
81
+
82
+
83
+ # Method for internal use to intercept write calls when we are being verbose.
84
+ def hook_write (object, filename, tail_msg = nil) #:nodoc:
85
+ object.instance_eval <<-__EOM__, __FILE__, __LINE__ + 1
86
+ class << self
87
+ def write (data, *args)
88
+ $stderr.puts AdvFileUtils.__send__(:write_echo_message, data, '>>', #{filename.inspect})
89
+ super
90
+ ensure
91
+ send #{tail_msg.inspect} if #{tail_msg.inspect}
92
+ end
93
+ end
94
+ __EOM__
95
+ end
96
+ module_function :hook_write
97
+ private_class_method :hook_write
98
+
99
+
100
+ def parse_data_and_options (data_and_options) #:nodoc#
101
+ if data_and_options.size == 1
102
+ if data_and_options[0].respond_to? :has_key?
103
+ options = data_and_options[0]
104
+ else
105
+ data = data_and_options[0]
106
+ end
107
+ else
108
+ data, options = *data_and_options
109
+ end
110
+
111
+ data ||= ''
112
+ options ||= {}
113
+ [data, options]
114
+ end
115
+ module_function :parse_data_and_options
116
+ private_class_method :parse_data_and_options
117
+
118
+
119
+ #
120
+ # Internally used function to implement .append and .truncate.
121
+ #
122
+ def generic_write (open_arg, filename, *data_and_options) #:nodoc:
123
+ if block_given?
124
+ options = data_and_options[0] || {}
125
+
126
+ if options[:verbose] and open_arg == 'w'
127
+ $stderr.puts "cat /dev/null > #{Escape.shell_single_word(filename)}"
128
+ end
129
+
130
+ if not options[:noop]
131
+ File.open(filename, open_arg) do |f|
132
+ hook_write(f, filename) if options[:verbose]
133
+ yield f
134
+ end
135
+
136
+ elsif options[:verbose]
137
+ f = StringIO.new
138
+ hook_write(f, filename, :rewind)
139
+ yield f
140
+ end
141
+
142
+ else
143
+ data, options = *parse_data_and_options(data_and_options)
144
+
145
+ if options[:verbose]
146
+ if open_arg == 'w'
147
+ $stderr.puts AdvFileUtils.__send__(:write_echo_message, data, '>', filename)
148
+ else
149
+ $stderr.puts AdvFileUtils.__send__(:write_echo_message, data, '>>', filename)
150
+ end
151
+ end
152
+
153
+ if not options[:noop]
154
+ File.open(filename, open_arg) do |f|
155
+ f.write(data)
156
+ end
157
+ end
158
+ end
159
+ end
160
+ module_function :generic_write
161
+ private_class_method :generic_write
162
+
163
+
164
+ #
165
+ # Options: verbose, noop, force, backup
166
+ #
167
+ # Append the given +data+ to the file named by +filename+.
168
+ #
169
+ # If called with a block then the File object is yielded to the block
170
+ # for appending data intead of the data being passed as an argument.
171
+ #
172
+ # AdvFileUtils.append('data.log', "some data for log entry\n")
173
+ # AdvFileUtils.append('data.log') { |f| f.puts "some data for log entry" }
174
+ #
175
+ def append (filename, *data_and_options, &block)
176
+ generic_write 'a', filename, *data_and_options, &block
177
+ end
178
+ module_function :append
179
+ public :append
180
+
181
+ OPT_TABLE['append'] = [:verbose, :noop, :force, :backup]
182
+
183
+
184
+ #
185
+ # Options: verbose, noop, force, backup
186
+ #
187
+ # Replace the given +data+ in the file named by +filename+.
188
+ #
189
+ # If called with a block then the File object is yielded to the block
190
+ # for writing data intead of the data being passed as an argument.
191
+ #
192
+ # AdvFileUtils.truncate('data.log', "some data\n")
193
+ # AdvFileUtils.truncate('data.log') { |f| f.puts "some data" }
194
+ #
195
+ def truncate (filename, *data_and_options, &block)
196
+ generic_write 'w', filename, *data_and_options, &block
197
+ end
198
+ module_function :truncate
199
+ public :truncate
200
+
201
+ OPT_TABLE['truncate'] = [:verbose, :noop, :force, :backup]
202
+
203
+
204
+ #
205
+ # Options: verbose, noop
206
+ #
207
+ # An alternative to Kernel.system that accepts options for verbosity
208
+ # and dry runs.
209
+ #
210
+ def system (*command_and_options)
211
+ if command_and_options[-1].respond_to?(:has_key?)
212
+ command = command_and_options[0...-1]
213
+ options = command_and_options[-1]
214
+ else
215
+ command = command_and_options
216
+ options = {}
217
+ end
218
+
219
+ raise ArgumentError.new('wrong number of arguments') if command.empty?
220
+
221
+ if options[:verbose]
222
+ if command.size == 1
223
+ $stderr.puts command[0]
224
+ else
225
+ $stderr.puts command.collect { |word|
226
+ Escape.shell_single_word word
227
+ }.join(' ')
228
+ end
229
+ end
230
+
231
+ if not options[:noop]
232
+ Kernel.system(*command)
233
+ end
234
+ end
235
+ module_function :system
236
+ public :system
237
+
238
+ alias sh system
239
+ module_function :sh
240
+ public :sh
241
+
242
+ alias shell system
243
+ module_function :shell
244
+ public :shell
245
+
246
+ OPT_TABLE['sh'] = OPT_TABLE['shell'] =
247
+ OPT_TABLE['system'] = [:verbose, :noop]
248
+
249
+
250
+ #
251
+ # Options: verbose, noop, force, backup
252
+ #
253
+ # Invoke an external editor to edit some text or a file.
254
+ #
255
+ # edit(filename, options)
256
+ # edit(filename, data, options)
257
+ # edit(nil, data, options)
258
+ #
259
+ # Return values
260
+ #
261
+ # true, if successful and file was edited
262
+ # false, if successful and file was not edited
263
+ # nil, if successful and file was not saved
264
+ #
265
+ def edit (filename, *data_and_options)
266
+ data, options = *parse_data_and_options(data_and_options)
267
+ editor =
268
+ ENV.has_key?('VISUAL') ? ENV['VISUAL'] :
269
+ ENV.has_key?('EDITOR') ? ENV['EDITOR'] : 'vi'
270
+
271
+ file_stat = File.stat(filename)
272
+ file_checksum = SHA1.file(filename)
273
+ system(editor, filename, options)
274
+ proc_status = $?
275
+
276
+ if options[:noop]
277
+ return true
278
+
279
+ elsif proc_status.success?
280
+ return nil if file_stat == File.stat(filename)
281
+ return false if file_checksum == SHA1.file(filename)
282
+ return true
283
+
284
+ elsif proc_status.signaled?
285
+ raise AdvFileUtils::CommandError.new("editor terminated on signal #{proc_status.termsig}")
286
+
287
+ else
288
+ raise AdvFileUtils::CommandError.new("editor had non-zero exit code #{proc_status.exitstatus}")
289
+ end
290
+ end
291
+ module_function :edit
292
+ public :edit
293
+
294
+ OPT_TABLE['edit'] = [:verbose, :noop, :force, :backup]
295
+
296
+
297
+ #
298
+ # Options: verbose, noop, force, backup, lockfile, retry
299
+ #
300
+ # Edit a file, but open a temporary lockfile instead and move it in place
301
+ # after editting is complete.
302
+ #
303
+ def replace (filename, *data_and_options)
304
+ data, options = *parse_data_and_options(data_and_options)
305
+ lockfile = options[:lockfile] ? options[:lockfile] : "#{filename}.lock"
306
+
307
+ begin
308
+ if not options[:noop]
309
+ fd = IO.sysopen(lockfile, IO::WRONLY | IO::CREAT | IO::EXCL, 0700)
310
+ f = IO.new(fd, 'w')
311
+ hook_write(f, lockfile) if block_given? and options[:verbose]
312
+ else
313
+ f = StringIO.new
314
+ hook_write(f, lockfile, :rewind) if block_given? and options[:verbose]
315
+ end
316
+
317
+ file_stat = File.stat(filename) rescue nil
318
+
319
+ if block_given?
320
+ $stderr.puts "cat /dev/null > #{Escape.shell_single_word(lockfile)}" if options[:verbose]
321
+ yield f
322
+ else
323
+ $stderr.puts AdvFileUtils.__send__(:write_echo_message, data, '>', lockfile) if options[:verbose]
324
+ f.write(data)
325
+ end
326
+
327
+ f.close
328
+
329
+ if file_stat
330
+ FileUtils.chown(file_stat.uid.to_s, file_stat.gid.to_s, lockfile, options)
331
+ FileUtils.chmod(file_stat.mode & 07777, lockfile, options)
332
+ end
333
+ FileUtils.mv(lockfile, filename, options)
334
+
335
+ ensure
336
+ f.close if f and not f.closed?
337
+ begin
338
+ File.delete(lockfile) if fd
339
+ rescue Errno::ENOENT
340
+ end
341
+ end
342
+ end
343
+ module_function :replace
344
+ public :replace
345
+
346
+ OPT_TABLE['replace'] = [:verbose, :noop, :force, :backup, :lockfile, :retry]
347
+ 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cosine-advanced-fileutils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael H Buselli
@@ -32,8 +32,10 @@ extensions: []
32
32
 
33
33
  extra_rdoc_files: []
34
34
 
35
- files: []
36
-
35
+ files:
36
+ - lib/fileutils
37
+ - lib/fileutils/advanced.rb
38
+ - lib/advanced-fileutils.rb
37
39
  has_rdoc: true
38
40
  homepage: http://cosine.org/ruby/advanced-fileutils/
39
41
  post_install_message: