io-reactor 0.05

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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