Module: UtilityFunctions

Defined in:
lib/linkparser/utils.rb

Constant Summary collapse

ANTIMANIFEST =

The list of regexen that eliminate files from the MANIFEST

[
	/makedist\.rb/,
	/\bCVS\b/,
	/~$/,
	/^#/,
	%r{docs/html},
	%r{docs/man},
	/^TEMPLATE/,
	/\.cvsignore/,
	/\.s?o$/
]
AnsiAttributes =

Set some ANSI escape code constants (Shamelessly stolen from Perl’s Term::ANSIColor by Russ Allbery <[email protected]> and Zenin <[email protected]>

{
	'clear'      => 0,
	'reset'      => 0,
	'bold'       => 1,
	'dark'       => 2,
	'underline'  => 4,
	'underscore' => 4,
	'blink'      => 5,
	'reverse'    => 7,
	'concealed'  => 8,

	'black'      => 30,   'on_black'   => 40, 
	'red'        => 31,   'on_red'     => 41, 
	'green'      => 32,   'on_green'   => 42, 
	'yellow'     => 33,   'on_yellow'  => 43, 
	'blue'       => 34,   'on_blue'    => 44, 
	'magenta'    => 35,   'on_magenta' => 45, 
	'cyan'       => 36,   'on_cyan'    => 46, 
	'white'      => 37,   'on_white'   => 47
}
ErasePreviousLine =
"\033[A\033[K"

Class Method Summary collapse

Class Method Details

.abort(msg) ⇒ Object

Output the specified msg colored in ANSI red and exit with a status of 1.



357
358
359
360
# File 'lib/linkparser/utils.rb', line 357

def abort( msg )
	print ansiCode( 'bold', 'red' ) + "Aborted: " + msg.chomp + ansiCode( 'reset' ) + "\n\n"
	Kernel.exit!( 1 )
end

.ansiCode(*attributes) ⇒ Object

Create a string that contains the ANSI codes specified and return it



267
268
269
270
271
272
273
274
# File 'lib/linkparser/utils.rb', line 267

def ansiCode( *attributes )
	attr = attributes.collect {|a| AnsiAttributes[a] ? AnsiAttributes[a] : nil}.compact.join(';')
	if attr.empty? 
		return ''
	else
		return "\e[%sm" % attr
	end
end

.debugMsg(msg) ⇒ Object

Output the specified msg as an ANSI-colored debugging message (yellow on blue).



335
336
337
338
339
340
# File 'lib/linkparser/utils.rb', line 335

def debugMsg( msg )
	return unless $DEBUG
	msg.chomp!
	$stderr.puts ansiCode( 'bold', 'yellow', 'on_blue' ) + ">>> #{msg}" + ansiCode( 'reset' )
	$stderr.flush
end

.divider(length = 75) ⇒ Object Also known as: writeLine

Output a divider made up of length hyphen characters.



350
351
352
# File 'lib/linkparser/utils.rb', line 350

def divider( length=75 )
	puts "\r" + ("-" * length )
end

.editInPlace(file) ⇒ Object

Open a file and filter each of its lines through the given block a line at a time. The return value of the block is used as the new line, or omitted if the block returns nil or false.



510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
# File 'lib/linkparser/utils.rb', line 510

def editInPlace( file ) # :yields: line
	raise "No block specified for editing operation" unless block_given?

	tempName = "#{file}.#{$$}"
	File::open( tempName, File::RDWR|File::CREAT, 0600 ) {|tempfile|
		File::unlink( tempName )
		File::open( file, File::RDONLY ) {|fh|
			fh.each {|line|
				newline = yield( line ) or next
				tempfile.print( newline )
			}
		}

		tempfile.seek(0)

		File::open( file, File::TRUNC|File::WRONLY, 0644 ) {|newfile|
			newfile.print( tempfile.read )
		}
	}
end

.errorMessage(msg) ⇒ Object

Output the specified msg as an ANSI-colored error message (white on red).



329
330
331
# File 'lib/linkparser/utils.rb', line 329

def errorMessage( msg )
	message ansiCode( 'bold', 'white', 'on_red' ) + msg + ansiCode( 'reset' )
end

.extractNextVersionFromTags(file) ⇒ Object

Using the CVS log for the given file attempt to guess what the next release version might be. This only works if releases are tagged with tags like ‘RELEASE_x_y’.

Raises:

  • (RuntimeError)


395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
# File 'lib/linkparser/utils.rb', line 395

def extractNextVersionFromTags( file )
	message "Attempting to extract next release version from CVS tags for #{file}...\n"
	raise RuntimeError, "No such file '#{file}'" unless File.exists?( file )
	cvsPath = findProgram( 'cvs' ) or
		raise RuntimeError, "Cannot find the 'cvs' program. Aborting."

	output = %x{#{cvsPath} log #{file}}
	release = [ 0, 0 ]
	output.scan( /RELEASE_(\d+)_(\d+)/ ) {|match|
		if $1.to_i > release[0] || $2.to_i > release[1]
			release = [ $1.to_i, $2.to_i ]
			replaceMessage( "Found %d.%02d...\n" % release )
		end
	}

	if release[1] >= 99
		release[0] += 1
		release[1] = 1
	else
		release[1] += 1
	end

	return "%d.%02d" % release
end

.extractProjectNameObject

Extract the project name (CVS Repository name) for the given directory.



421
422
423
# File 'lib/linkparser/utils.rb', line 421

def extractProjectName
	File.open( "CVS/Repository", "r").readline.chomp
end

.findProgram(progname) ⇒ Object

Search for the program specified by the given progname in the user’s PATH, and return the full path to it, or nil if no such program is in the path.



384
385
386
387
388
389
390
# File 'lib/linkparser/utils.rb', line 384

def findProgram( progname )
	ENV['PATH'].split(File::PATH_SEPARATOR).each {|d|
		file = File.join( d, progname )
		return file if File.executable?( file )
	}
	return nil
end

.findRdocableFiles(catalogFile = "docs/CATALOG") ⇒ Object

Given a documentation catalogFile, which is in the same format as that described by #readManifest, read and expand it, and then return a list of those files which appear to have RDoc documentation in them. If catalogFile is nil or does not exist, the MANIFEST file is used instead.



478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
# File 'lib/linkparser/utils.rb', line 478

def findRdocableFiles( catalogFile="docs/CATALOG" )
	startlist = []
	if File.exists? catalogFile
		message "Using CATALOG file (%s).\n" % catalogFile
		startlist = getVettedManifest( catalogFile )
	else
		message "Using default MANIFEST\n"
		startlist = getVettedManifest()
	end

	message "Looking for RDoc comments in:\n" if $VERBOSE
	startlist.select {|fn|
		message "  #{fn}: " if $VERBOSE
		found = false
		File::open( fn, "r" ) {|fh|
			fh.each {|line|
				if line =~ /^(\s*#)?\s*=/ || line =~ /:\w+:/ || line =~ %r{/\*}
					found = true
					break
				end
			}
		}

		message( (found ? "yes" : "no") + "\n" ) if $VERBOSE
		found
	}
end

.getVettedManifest(manifestFile = "MANIFEST", antimanifest = ANTIMANIFEST) ⇒ Object

Combine a call to #readManifest with one to #vetManifest.



469
470
471
# File 'lib/linkparser/utils.rb', line 469

def getVettedManifest( manifestFile="MANIFEST", antimanifest=ANTIMANIFEST )
	vetManifest( readManifest(manifestFile), antimanifest )
end

.header(msg) ⇒ Object

Output msg as a ANSI-colored program/section header (white on blue).



315
316
317
318
319
# File 'lib/linkparser/utils.rb', line 315

def header( msg )
	msg.chomp!
	$stderr.puts ansiCode( 'bold', 'white', 'on_blue' ) + msg + ansiCode( 'reset' )
	$stderr.flush
end

.message(msg) ⇒ Object

Output msg to STDERR and flush it.



322
323
324
325
# File 'lib/linkparser/utils.rb', line 322

def message( msg )
	$stderr.print msg
	$stderr.flush
end

.prompt(promptString) ⇒ Object

Output the specified promptString as a prompt (in green) and return the user’s input with leading and trailing spaces removed.



364
365
366
367
# File 'lib/linkparser/utils.rb', line 364

def prompt( promptString )
	promptString.chomp!
	return readline( ansiCode('bold', 'green') + "#{promptString}: " + ansiCode('reset') ).strip
end

.promptWithDefault(promptString, default) ⇒ Object

Prompt the user with the given promptString via #prompt, substituting the given default if the user doesn’t input anything.



372
373
374
375
376
377
378
379
# File 'lib/linkparser/utils.rb', line 372

def promptWithDefault( promptString, default )
	response = prompt( "%s [%s]" % [ promptString, default ] )
	if response.empty?
		return default
	else
		return response
	end
end

.readManifest(manifestFile = "MANIFEST") ⇒ Object

Read the specified manifestFile, which is a text file describing which files to package up for a distribution. The manifest should consist of one or more lines, each containing one filename or shell glob pattern.



429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
# File 'lib/linkparser/utils.rb', line 429

def readManifest( manifestFile="MANIFEST" )
	message "Building manifest..."
	raise "Missing #{manifestFile}, please remake it" unless File.exists? manifestFile

	manifest = IO::readlines( manifestFile ).collect {|line|
		line.chomp
	}.select {|line|
		line !~ /^(\s*(#.*)?)?$/
	}

	filelist = []
	for pat in manifest
		$stderr.puts "Adding files that match '#{pat}' to the file list" if $VERBOSE
		filelist |= Dir.glob( pat ).find_all {|f| FileTest.file?(f)}
	end

	message "found #{filelist.length} files.\n"
	return filelist
end

.replaceMessage(msg) ⇒ Object

Erase the previous line (if supported by your terminal) and output the specified msg instead.



344
345
346
347
# File 'lib/linkparser/utils.rb', line 344

def replaceMessage( msg )
	print ErasePreviousLine
	message( msg )
end

.shellCommand(*command) ⇒ Object

Execute the specified shell command, read the results, and return them. Like a %x{} that returns an Array instead of a String.



533
534
535
536
537
538
# File 'lib/linkparser/utils.rb', line 533

def shellCommand( *command )
	raise "Empty command" if command.empty?

	cmdpipe = IO::popen( command.join(' '), 'r' )
	return cmdpipe.readlines
end

.testForLibrary(library, nicename = nil) ⇒ Object

Test for the presence of the specified library, and output a message describing the test using nicename. If nicename is nil, the value in library is used to build a default.



279
280
281
282
283
284
285
286
287
288
289
# File 'lib/linkparser/utils.rb', line 279

def testForLibrary( library, nicename=nil )
	nicename ||= library
	message( "Testing for the #{nicename} library..." )
	if $:.detect {|dir| File.exists?(File.join(dir,"#{library}.rb")) || File.exists?(File.join(dir,"#{library}.so"))}
		message( "found.\n" )
		return true
	else
		message( "not found.\n" )
		return false
	end
end

.testForRequiredLibrary(library, nicename = nil, raaUrl = nil, downloadUrl = nil, fatal = true) ⇒ Object

Test for the presence of the specified library, and output a message describing the problem using nicename. If nicename is nil, the value in library is used to build a default. If raaUrl and/or downloadUrl are specified, they are also use to build a message describing how to find the required library. If fatal is true, a missing library will cause the program to abort.



298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/linkparser/utils.rb', line 298

def testForRequiredLibrary( library, nicename=nil, raaUrl=nil, downloadUrl=nil, fatal=true )
	nicename ||= library
	unless testForLibrary( library, nicename )
		msgs = [ "You are missing the required #{nicename} library.\n" ]
		msgs << "RAA: #{raaUrl}\n" if raaUrl
		msgs << "Download: #{downloadUrl}\n" if downloadUrl
		if fatal
			abort msgs.join('')
		else
			errorMessage msgs.join('')
		end
	end
	return true
end

.vetManifest(filelist, antimanifest = ANITMANIFEST) ⇒ Object

Given a filelist like that returned by #readManifest, remove the entries therein which match the Regexp objects in the given antimanifest and return the resultant Array.



452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# File 'lib/linkparser/utils.rb', line 452

def vetManifest( filelist, antimanifest=ANITMANIFEST )
	origLength = filelist.length
	message "Vetting manifest..."

	for regex in antimanifest
		if $VERBOSE
			message "\n\tPattern /#{regex.source}/ removed: " +
				filelist.find_all {|file| regex.match(file)}.join(', ')
		end
		filelist.delete_if {|file| regex.match(file)}
	end

	message "removed #{origLength - filelist.length} files from the list.\n"
	return filelist
end