io-reactor 0.05

Sign up to get free protection for your applications and to get access to all the features.
data/test.rb ADDED
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/ruby
2
+ # = test.rb
3
+ #
4
+ # Test suite for Ruby-Poll
5
+ #
6
+ # == Author
7
+ #
8
+ # Michael Granger <ged@FaerieMUD.org>
9
+ #
10
+ # Copyright (c) 2002, 2003 The FaerieMUD Consortium. All rights reserved.
11
+ #
12
+ # This program is free software. You may use, modify, and/or redistribute this
13
+ # software under the same terms as Ruby itself.
14
+ #
15
+ # This program is distributed in the hope that it will be useful, but WITHOUT
16
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
+ # FOR A PARTICULAR PURPOSE.
18
+ #
19
+ # == Version
20
+ #
21
+ # $Id: test.rb,v 1.8 2003/08/04 23:57:07 deveiant Exp $
22
+ #
23
+ #
24
+
25
+ $:.unshift "lib", "tests"
26
+
27
+ require 'test/unit'
28
+ require 'io/reactor'
29
+ require 'socket'
30
+
31
+ TMPFILE = "testfile.#{$$}"
32
+ HOST = 'localhost'
33
+ PORT = 5656
34
+
35
+ $stderr.sync = $stdout.sync = true
36
+
37
+ ### Reactor test case
38
+ class IOReactorTestCase < Test::Unit::TestCase
39
+
40
+ # Setup method
41
+ def setup
42
+ @reactor = IO::Reactor::new
43
+ @tmpfile = File::open( TMPFILE, "w" )
44
+ File::unlink TMPFILE
45
+ @sock = TCPServer::new( HOST, PORT )
46
+ end
47
+ alias_method :set_up, :setup
48
+
49
+ # Teardown method
50
+ def teardown
51
+ @reactor = nil
52
+ @tmpfile.close
53
+ @sock.close
54
+ end
55
+ alias_method :tear_down, :teardown
56
+
57
+
58
+ # Test to make sure require worked
59
+ def test_00_Requires
60
+ assert_instance_of Class, IO::Reactor
61
+ assert_instance_of IO::Reactor, @reactor
62
+ end
63
+
64
+
65
+ # Test set and reset with an IO
66
+ def test_10_RegisterIO
67
+ assert_nothing_raised { @reactor.register $stdout, :write }
68
+ assert_nothing_raised { @reactor.add $stdout, :write }
69
+ assert @reactor.registered?( $stdout )
70
+ assert_equal [:write], @reactor.handles[ $stdout ][:events]
71
+ end
72
+
73
+
74
+ # Test set and reset with a File
75
+ def test_11_RegisterFilehandle
76
+ assert_nothing_raised { @reactor.register @tmpfile, :write }
77
+ assert_nothing_raised { @reactor.add @tmpfile, :write }
78
+ assert @reactor.registered?( @tmpfile )
79
+ assert_equal [:write], @reactor.handles[ @tmpfile ][:events]
80
+ end
81
+
82
+
83
+ # Test set and reset with a File
84
+ def test_12_RegisterSocket
85
+ assert_nothing_raised { @reactor.register @sock, :read, :write }
86
+ assert_nothing_raised { @reactor.add @sock, :read }
87
+ assert @reactor.registered?( @sock )
88
+ assert_equal [:read], @reactor.handles[ @sock ][:events]
89
+ end
90
+
91
+
92
+ # Test registration with a callback as an inline block
93
+ def test_20_RegisterWithBlock
94
+ assert_nothing_raised {
95
+ @reactor.register($stdout, :write) {|io,eventMask|
96
+ $stderr.puts "Got an output event for STDOUT"
97
+ }
98
+ }
99
+ assert @reactor.handles.key?( $stdout ),
100
+ "handles hash doesn't contain $stdout"
101
+ assert_equal [:write], @reactor.handles[ $stdout ][:events]
102
+ end
103
+
104
+
105
+ # Test registration with a Proc argument
106
+ def test_21_RegisterWithProc
107
+ handlerProc = Proc::new {|io,eventMask|
108
+ $stderr.puts "Got an output event for STDOUT"
109
+ }
110
+ assert_nothing_raised {
111
+ @reactor.register( $stdout, :write, &handlerProc )
112
+ }
113
+ assert @reactor.handles.key?( $stdout ),
114
+ "handles hash doesn't contain $stdout"
115
+ end
116
+
117
+
118
+ # Test registration with a Method argument
119
+ def test_22_RegisterWithMethod
120
+ assert_nothing_raised {
121
+ @reactor.register $stdout, :write, &$stderr.method( :puts )
122
+ }
123
+ assert @reactor.handles.key?( $stdout ),
124
+ "handles hash doesn't contain $stdout"
125
+ end
126
+
127
+
128
+ # Test registering with an argument
129
+ def test_23_RegisterWithArgs
130
+ assert_nothing_raised {
131
+ @reactor.register $stdout, :write, "foo", &$stderr.method( :puts )
132
+ }
133
+ assert @reactor.handles.key?( $stdout ),
134
+ "handles hash doesn't contain $stdout"
135
+ assert_equal ["foo"], @reactor.handles[$stdout][:args]
136
+ end
137
+
138
+
139
+ # Test the clear method
140
+ def test_30_Clear
141
+ # Make sure it's empty
142
+ assert_nothing_raised {
143
+ @reactor.clear
144
+ }
145
+
146
+ # Test it with one registered
147
+ assert_nothing_raised {
148
+ @reactor.register $stdout, :write, &$stdout.method( :puts )
149
+ @reactor.clear
150
+ }
151
+ assert ! @reactor.registered?( $stdout ),
152
+ "$stdout still registed with the poll handle after clear"
153
+ assert_equal 0, @reactor.handles.length
154
+ end
155
+
156
+
157
+ # Test the #poll method
158
+ def test_40_Poll
159
+ rv = nil
160
+
161
+ @reactor.register $stdout, :write
162
+ @reactor.register @tmpfile, :write
163
+
164
+ assert_nothing_raised { rv = @reactor.poll(0.1) }
165
+ assert_equal 2, rv
166
+
167
+ assert_nothing_raised { @reactor.pendingEvents.keys.include?($stdout) }
168
+ assert_nothing_raised { @reactor.pendingEvents.keys.include?(@tmpfile) }
169
+ end
170
+
171
+
172
+ # Test #poll with a block default handler
173
+ def test_41_PollWithBlock
174
+ rv = nil
175
+
176
+ @reactor.register $stdout, :write
177
+ @reactor.register @tmpfile, :write
178
+
179
+ assert_nothing_raised {
180
+ rv = @reactor.poll( 15 ) {|io,event|
181
+ $stderr.puts "Default handler got #{io.inspect} with mask #{event}" if $VERBOSE
182
+ }
183
+ }
184
+ end
185
+
186
+ # Test polling with an argument
187
+ def test_42_PollWithArgs
188
+ setval = nil
189
+ testAry = %w{foo bar baz}
190
+
191
+ @reactor.register( $stdout, :write, *testAry )
192
+ assert_equal testAry, @reactor.handles[$stdout][:args]
193
+
194
+ assert_nothing_raised {
195
+ @reactor.poll( 15 ) {|io,ev,*args|
196
+ setval = args
197
+ }
198
+ }
199
+
200
+ assert_equal testAry, setval
201
+ end
202
+
203
+
204
+ end # class PollTestCase
205
+
206
+
207
+
208
+
209
+
210
+
211
+
212
+
@@ -0,0 +1,484 @@
1
+ #
2
+ # Install/distribution utility functions
3
+ # $Id: utils.rb,v 1.5 2003/08/04 23:52:07 deveiant Exp $
4
+ #
5
+ # Copyright (c) 2001-2003, The FaerieMUD Consortium.
6
+ #
7
+ # This is free software. You may use, modify, and/or redistribute this
8
+ # software under the terms of the Perl Artistic License. (See
9
+ # http://language.perl.com/misc/Artistic.html)
10
+ #
11
+
12
+
13
+ BEGIN {
14
+ begin
15
+ require 'readline'
16
+ include Readline
17
+ rescue LoadError => e
18
+ $stderr.puts "Faking readline..."
19
+ def readline( prompt )
20
+ $stderr.print prompt.chomp
21
+ return $stdin.gets.chomp
22
+ end
23
+ end
24
+
25
+ begin
26
+ require 'yaml'
27
+ $yaml = true
28
+ rescue LoadError => e
29
+ $stderr.puts "No YAML; try() will use .inspect instead."
30
+ $yaml = false
31
+ end
32
+ }
33
+
34
+ module UtilityFunctions
35
+
36
+ # The list of regexen that eliminate files from the MANIFEST
37
+ ANTIMANIFEST = [
38
+ /makedist\.rb/,
39
+ /\bCVS\b/,
40
+ /~$/,
41
+ /^#/,
42
+ %r{docs/html},
43
+ %r{docs/man},
44
+ /^TEMPLATE/,
45
+ /\.cvsignore/,
46
+ /\.s?o$/
47
+ ]
48
+
49
+ # Set some ANSI escape code constants (Shamelessly stolen from Perl's
50
+ # Term::ANSIColor by Russ Allbery <rra@stanford.edu> and Zenin <zenin@best.com>
51
+ AnsiAttributes = {
52
+ 'clear' => 0,
53
+ 'reset' => 0,
54
+ 'bold' => 1,
55
+ 'dark' => 2,
56
+ 'underline' => 4,
57
+ 'underscore' => 4,
58
+ 'blink' => 5,
59
+ 'reverse' => 7,
60
+ 'concealed' => 8,
61
+
62
+ 'black' => 30, 'on_black' => 40,
63
+ 'red' => 31, 'on_red' => 41,
64
+ 'green' => 32, 'on_green' => 42,
65
+ 'yellow' => 33, 'on_yellow' => 43,
66
+ 'blue' => 34, 'on_blue' => 44,
67
+ 'magenta' => 35, 'on_magenta' => 45,
68
+ 'cyan' => 36, 'on_cyan' => 46,
69
+ 'white' => 37, 'on_white' => 47
70
+ }
71
+
72
+ ErasePreviousLine = "\033[A\033[K"
73
+
74
+
75
+ ###############
76
+ module_function
77
+ ###############
78
+
79
+ # Create a string that contains the ANSI codes specified and return it
80
+ def ansiCode( *attributes )
81
+ return '' unless /(?:vt10[03]|xterm(?:-color)?|linux)/i =~ ENV['TERM']
82
+ attr = attributes.collect {|a| AnsiAttributes[a] ? AnsiAttributes[a] : nil}.compact.join(';')
83
+ if attr.empty?
84
+ return ''
85
+ else
86
+ return "\e[%sm" % attr
87
+ end
88
+ end
89
+
90
+ # Test for the presence of the specified <tt>library</tt>, and output a
91
+ # message describing the test using <tt>nicename</tt>. If <tt>nicename</tt>
92
+ # is <tt>nil</tt>, the value in <tt>library</tt> is used to build a default.
93
+ def testForLibrary( library, nicename=nil )
94
+ nicename ||= library
95
+ message( "Testing for the #{nicename} library..." )
96
+ if $:.detect {|dir| File.exists?(File.join(dir,"#{library}.rb")) || File.exists?(File.join(dir,"#{library}.so"))}
97
+ message( "found.\n" )
98
+ return true
99
+ else
100
+ message( "not found.\n" )
101
+ return false
102
+ end
103
+ end
104
+
105
+ # Test for the presence of the specified <tt>library</tt>, and output a
106
+ # message describing the problem using <tt>nicename</tt>. If
107
+ # <tt>nicename</tt> is <tt>nil</tt>, the value in <tt>library</tt> is used
108
+ # to build a default. If <tt>raaUrl</tt> and/or <tt>downloadUrl</tt> are
109
+ # specified, they are also use to build a message describing how to find the
110
+ # required library. If <tt>fatal</tt> is <tt>true</tt>, a missing library
111
+ # will cause the program to abort.
112
+ def testForRequiredLibrary( library, nicename=nil, raaUrl=nil, downloadUrl=nil, fatal=true )
113
+ nicename ||= library
114
+ unless testForLibrary( library, nicename )
115
+ msgs = [ "You are missing the required #{nicename} library.\n" ]
116
+ msgs << "RAA: #{raaUrl}\n" if raaUrl
117
+ msgs << "Download: #{downloadUrl}\n" if downloadUrl
118
+ if fatal
119
+ abort msgs.join('')
120
+ else
121
+ errorMessage msgs.join('')
122
+ end
123
+ end
124
+ return true
125
+ end
126
+
127
+ ### Output <tt>msg</tt> as a ANSI-colored program/section header (white on
128
+ ### blue).
129
+ def header( msg )
130
+ msg.chomp!
131
+ $stderr.puts ansiCode( 'bold', 'white', 'on_blue' ) + msg + ansiCode( 'reset' )
132
+ $stderr.flush
133
+ end
134
+
135
+ ### Output <tt>msg</tt> to STDERR and flush it.
136
+ def message( msg )
137
+ $stderr.print ansiCode( 'cyan' ) + msg + ansiCode( 'reset' )
138
+ $stderr.flush
139
+ end
140
+
141
+ ### Output the specified <tt>msg</tt> as an ANSI-colored error message
142
+ ### (white on red).
143
+ def errorMessage( msg )
144
+ message ansiCode( 'bold', 'white', 'on_red' ) + msg + ansiCode( 'reset' )
145
+ end
146
+
147
+ ### Output the specified <tt>msg</tt> as an ANSI-colored debugging message
148
+ ### (yellow on blue).
149
+ def debugMsg( msg )
150
+ return unless $DEBUG
151
+ msg.chomp!
152
+ $stderr.puts ansiCode( 'bold', 'yellow', 'on_blue' ) + ">>> #{msg}" + ansiCode( 'reset' )
153
+ $stderr.flush
154
+ end
155
+
156
+ ### Erase the previous line (if supported by your terminal) and output the
157
+ ### specified <tt>msg</tt> instead.
158
+ def replaceMessage( msg )
159
+ print ErasePreviousLine
160
+ message( msg )
161
+ end
162
+
163
+ ### Output a divider made up of <tt>length</tt> hyphen characters.
164
+ def divider( length=75 )
165
+ puts "\r" + ("-" * length )
166
+ end
167
+ alias :writeLine :divider
168
+
169
+ ### Output the specified <tt>msg</tt> colored in ANSI red and exit with a
170
+ ### status of 1.
171
+ def abort( msg )
172
+ print ansiCode( 'bold', 'red' ) + "Aborted: " + msg.chomp + ansiCode( 'reset' ) + "\n\n"
173
+ Kernel.exit!( 1 )
174
+ end
175
+
176
+ ### Output the specified <tt>promptString</tt> as a prompt (in green) and
177
+ ### return the user's input with leading and trailing spaces removed.
178
+ def prompt( promptString )
179
+ promptString.chomp!
180
+ return readline( ansiCode('bold', 'green') + "#{promptString}: " + ansiCode('reset') ).strip
181
+ end
182
+
183
+ ### Prompt the user with the given <tt>promptString</tt> via #prompt,
184
+ ### substituting the given <tt>default</tt> if the user doesn't input
185
+ ### anything.
186
+ def promptWithDefault( promptString, default )
187
+ response = prompt( "%s [%s]" % [ promptString, default ] )
188
+ if response.empty?
189
+ return default
190
+ else
191
+ return response
192
+ end
193
+ end
194
+
195
+ ### Search for the program specified by the given <tt>progname</tt> in the
196
+ ### user's <tt>PATH</tt>, and return the full path to it, or <tt>nil</tt> if
197
+ ### no such program is in the path.
198
+ def findProgram( progname )
199
+ ENV['PATH'].split(File::PATH_SEPARATOR).each {|d|
200
+ file = File.join( d, progname )
201
+ return file if File.executable?( file )
202
+ }
203
+ return nil
204
+ end
205
+
206
+ ### Using the CVS log for the given <tt>file</tt> attempt to guess what the
207
+ ### next release version might be. This only works if releases are tagged
208
+ ### with tags like 'RELEASE_x_y'.
209
+ def extractNextVersionFromTags( file )
210
+ message "Attempting to extract next release version from CVS tags for #{file}...\n"
211
+ raise RuntimeError, "No such file '#{file}'" unless File.exists?( file )
212
+ cvsPath = findProgram( 'cvs' ) or
213
+ raise RuntimeError, "Cannot find the 'cvs' program. Aborting."
214
+
215
+ output = %x{#{cvsPath} log #{file}}
216
+ release = [ 0, 0 ]
217
+ output.scan( /RELEASE_(\d+)_(\d+)/ ) {|match|
218
+ if $1.to_i > release[0] || $2.to_i > release[1]
219
+ release = [ $1.to_i, $2.to_i ]
220
+ replaceMessage( "Found %d.%02d...\n" % release )
221
+ end
222
+ }
223
+
224
+ if release[1] >= 99
225
+ release[0] += 1
226
+ release[1] = 1
227
+ else
228
+ release[1] += 1
229
+ end
230
+
231
+ return "%d.%02d" % release
232
+ end
233
+
234
+ ### Extract the project name (CVS Repository name) for the given directory.
235
+ def extractProjectName
236
+ File.open( "CVS/Repository", "r").readline.chomp
237
+ end
238
+
239
+ ### Read the specified <tt>manifestFile</tt>, which is a text file
240
+ ### describing which files to package up for a distribution. The manifest
241
+ ### should consist of one or more lines, each containing one filename or
242
+ ### shell glob pattern.
243
+ def readManifest( manifestFile="MANIFEST" )
244
+ message "Building manifest..."
245
+ raise "Missing #{manifestFile}, please remake it" unless File.exists? manifestFile
246
+
247
+ manifest = IO::readlines( manifestFile ).collect {|line|
248
+ line.chomp
249
+ }.select {|line|
250
+ line !~ /^(\s*(#.*)?)?$/
251
+ }
252
+
253
+ filelist = []
254
+ for pat in manifest
255
+ $stderr.puts "Adding files that match '#{pat}' to the file list" if $VERBOSE
256
+ filelist |= Dir.glob( pat ).find_all {|f| FileTest.file?(f)}
257
+ end
258
+
259
+ message "found #{filelist.length} files.\n"
260
+ return filelist
261
+ end
262
+
263
+ ### Given a <tt>filelist</tt> like that returned by #readManifest, remove
264
+ ### the entries therein which match the Regexp objects in the given
265
+ ### <tt>antimanifest</tt> and return the resultant Array.
266
+ def vetManifest( filelist, antimanifest=ANITMANIFEST )
267
+ origLength = filelist.length
268
+ message "Vetting manifest..."
269
+
270
+ for regex in antimanifest
271
+ if $VERBOSE
272
+ message "\n\tPattern /#{regex.source}/ removed: " +
273
+ filelist.find_all {|file| regex.match(file)}.join(', ')
274
+ end
275
+ filelist.delete_if {|file| regex.match(file)}
276
+ end
277
+
278
+ message "removed #{origLength - filelist.length} files from the list.\n"
279
+ return filelist
280
+ end
281
+
282
+ ### Combine a call to #readManifest with one to #vetManifest.
283
+ def getVettedManifest( manifestFile="MANIFEST", antimanifest=ANTIMANIFEST )
284
+ vetManifest( readManifest(manifestFile), antimanifest )
285
+ end
286
+
287
+ ### Given a documentation <tt>catalogFile</tt>, extract the title, if
288
+ ### available, and return it. Otherwise generate a title from the name of
289
+ ### the CVS module.
290
+ def findRdocTitle( catalogFile="docs/CATALOG" )
291
+
292
+ # Try extracting it from the CATALOG file from a line that looks like:
293
+ # Title: Foo Bar Module
294
+ title = findCatalogKeyword( 'title', catalogFile )
295
+
296
+ # If that doesn't work for some reason, try grabbing the name of the CVS
297
+ # repository the directory belongs to.
298
+ if title.nil? && File::directory?( "CVS" ) &&
299
+ File::exists?( "CVS/Repository" )
300
+ title = File::read( "CVS/Repository" ).chomp
301
+ end
302
+
303
+ # As a last resort, use the name of the project directory
304
+ if title.nil?
305
+ distdir = File::dirname( __FILE__ )
306
+ distdir = File::dirname( distdir ) if /docs$/ =~ distdir
307
+ title = File::basename( distdir )
308
+ end
309
+
310
+ return title
311
+ end
312
+
313
+ ### Given a documentation <tt>catalogFile</tt>, extract the name of the file
314
+ ### to use as the initally displayed page. If extraction fails, the
315
+ ### +default+ will be used if it exists. Returns +nil+ if there is no main
316
+ ### file to be found.
317
+ def findRdocMain( catalogFile="docs/CATALOG", default="README" )
318
+
319
+ # Try extracting it from the CATALOG file from a line that looks like:
320
+ # Main: Foo Bar Module
321
+ main = findCatalogKeyword( 'main', catalogFile )
322
+
323
+ # Try to make some educated guesses if that doesn't work
324
+ if main.nil?
325
+ basedir = File::dirname( __FILE__ )
326
+ basedir = File::dirname( basedir ) if /docs$/ =~ basedir
327
+
328
+ if File::exists?( File::join(basedir, default) )
329
+ main = default
330
+ end
331
+ end
332
+
333
+ return main
334
+ end
335
+
336
+
337
+ ### Given a documentation <tt>catalogFile</tt>, extract an upload URL for
338
+ ### RDoc.
339
+ def findRdocUpload( catalogFile="docs/CATALOG" )
340
+ findCatalogKeyword( 'upload', catalogFile )
341
+ end
342
+
343
+
344
+ ### Given a documentation <tt>catalogFile</tt>, extract a CVS web frontend
345
+ ### URL for RDoc.
346
+ def findRdocCvsURL( catalogFile="docs/CATALOG" )
347
+ findCatalogKeyword( 'webcvs', catalogFile )
348
+ end
349
+
350
+
351
+ ### Given a documentation <tt>catalogFile</tt>, try extracting the given
352
+ ### +keyword+'s value from it. Keywords are lines that look like:
353
+ ### # <keyword>: <value>
354
+ ### Returns +nil+ if the catalog file was unreadable or didn't contain the
355
+ ### specified +keyword+.
356
+ def findCatalogKeyword( keyword, catalogFile="docs/CATALOG" )
357
+ val = nil
358
+
359
+ if File::exists? catalogFile
360
+ message "Extracting '#{keyword}' from CATALOG file (%s).\n" % catalogFile
361
+ File::foreach( catalogFile ) {|line|
362
+ debugMsg( "Examining line #{line.inspect}..." )
363
+ val = $1.strip and break if /^#\s*#{keyword}:\s*(.*)$/i =~ line
364
+ }
365
+ end
366
+
367
+ return val
368
+ end
369
+
370
+
371
+ ### Given a documentation <tt>catalogFile</tt>, which is in the same format
372
+ ### as that described by #readManifest, read and expand it, and then return
373
+ ### a list of those files which appear to have RDoc documentation in
374
+ ### them. If <tt>catalogFile</tt> is nil or does not exist, the MANIFEST
375
+ ### file is used instead.
376
+ def findRdocableFiles( catalogFile="docs/CATALOG" )
377
+ startlist = []
378
+ if File.exists? catalogFile
379
+ message "Using CATALOG file (%s).\n" % catalogFile
380
+ startlist = getVettedManifest( catalogFile )
381
+ else
382
+ message "Using default MANIFEST\n"
383
+ startlist = getVettedManifest()
384
+ end
385
+
386
+ message "Looking for RDoc comments in:\n" if $VERBOSE
387
+ startlist.select {|fn|
388
+ message " #{fn}: " if $VERBOSE
389
+ found = false
390
+ File::open( fn, "r" ) {|fh|
391
+ fh.each {|line|
392
+ if line =~ /^(\s*#)?\s*=/ || line =~ /:\w+:/ || line =~ %r{/\*}
393
+ found = true
394
+ break
395
+ end
396
+ }
397
+ }
398
+
399
+ message( (found ? "yes" : "no") + "\n" ) if $VERBOSE
400
+ found
401
+ }
402
+ end
403
+
404
+ ### Open a file and filter each of its lines through the given block a
405
+ ### <tt>line</tt> at a time. The return value of the block is used as the
406
+ ### new line, or omitted if the block returns <tt>nil</tt> or
407
+ ### <tt>false</tt>.
408
+ def editInPlace( file ) # :yields: line
409
+ raise "No block specified for editing operation" unless block_given?
410
+
411
+ tempName = "#{file}.#{$$}"
412
+ File::open( tempName, File::RDWR|File::CREAT, 0600 ) {|tempfile|
413
+ File::unlink( tempName )
414
+ File::open( file, File::RDONLY ) {|fh|
415
+ fh.each {|line|
416
+ newline = yield( line ) or next
417
+ tempfile.print( newline )
418
+ }
419
+ }
420
+
421
+ tempfile.seek(0)
422
+
423
+ File::open( file, File::TRUNC|File::WRONLY, 0644 ) {|newfile|
424
+ newfile.print( tempfile.read )
425
+ }
426
+ }
427
+ end
428
+
429
+ ### Execute the specified shell <tt>command</tt>, read the results, and
430
+ ### return them. Like a %x{} that returns an Array instead of a String.
431
+ def shellCommand( *command )
432
+ raise "Empty command" if command.empty?
433
+
434
+ cmdpipe = IO::popen( command.join(' '), 'r' )
435
+ return cmdpipe.readlines
436
+ end
437
+
438
+ ### Execute a block with $VERBOSE set to +false+, restoring it to its
439
+ ### previous value before returning.
440
+ def verboseOff
441
+ raise LocalJumpError, "No block given" unless block_given?
442
+
443
+ thrcrit = Thread.critical
444
+ oldverbose = $VERBOSE
445
+ begin
446
+ Thread.critical = true
447
+ $VERBOSE = false
448
+ yield
449
+ ensure
450
+ $VERBOSE = oldverbose
451
+ Thread.critical = false
452
+ end
453
+ end
454
+
455
+
456
+ ### Try the specified code block, printing the given
457
+ def try( msg, bind=nil )
458
+ result = nil
459
+ message "Trying #{msg}..."
460
+
461
+ begin
462
+ rval = nil
463
+ if block_given?
464
+ rval = yield
465
+ else
466
+ file, line = caller(1)[0].split(/:/,2)
467
+ rval = eval( msg, bind, file, line.to_i )
468
+ end
469
+
470
+ if $yaml
471
+ result = rval.to_yaml
472
+ else
473
+ result = rval.inspect
474
+ end
475
+ rescue Exception => err
476
+ nicetrace = err.backtrace.delete_if {|frame|
477
+ /in `(try|eval)'/ =~ frame
478
+ }.join("\n\t")
479
+ result = err.message + "\n\t" + nicetrace
480
+ ensure
481
+ puts result
482
+ end
483
+ end
484
+ end