io-reactor 0.05 → 1.0.4

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/utils.rb DELETED
@@ -1,484 +0,0 @@
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