#!/usr/bin/ruby
# encoding: utf-8
class String
def /( subpath )
File.join( self, subpath.to_s )
end
def here_indent( chr = '| ' )
dup.here_indent!( chr )
end
def here_indent!( chr = '| ' )
chr = Regexp.escape( chr )
exp = Regexp.new( "^ *#{ chr }" )
self.gsub!( exp,'' )
return self
end
def here_flow( chr = '| ' )
dup.here_flow!( chr )
end
def here_flow!( chr = '| ' )
here_indent!( chr ).gsub!( /\n\s+/,' ' )
return( self )
end
# Indent left or right by n spaces.
# (This used to be called #tab and aliased as #indent.)
#
# CREDIT: Gavin Sinclair
# CREDIT: Trans
def indent( n )
if n >= 0
gsub( /^/, ' ' * n )
else
gsub( /^ {0,#{ -n }}/, "" )
end
end
# Outdent just indents a negative number of spaces.
#
# CREDIT: Noah Gibbs
def outdent( n )
indent( -n )
end
# Returns the shortest length of leading whitespace for all non-blank lines
#
# n = %Q(
# a = 3
# b = 4
# ).level_of_indent #=> 2
#
# CREDIT: Kyle Yetter
def level_of_indent
self.scan( /^ *(?=\S)/ ).map { |space| space.length }.min || 0
end
def fixed_indent( n )
self.outdent( self.level_of_indent ).indent( n )
end
# Provides a margin controlled string.
#
# x = %Q{
# | This
# | is
# | margin controlled!
# }.margin
#
#
# NOTE: This may still need a bit of tweaking.
#
# CREDIT: Trans
def margin( n=0 )
#d = /\A.*\n\s*(.)/.match( self )[1]
#d = /\A\s*(.)/.match( self)[1] unless d
d = ( ( /\A.*\n\s*(.)/.match( self ) ) ||
( /\A\s*(.)/.match( self ) ) )[ 1 ]
return '' unless d
if n == 0
gsub( /\n\s*\Z/,'' ).gsub( /^\s*[#{ d }]/, '' )
else
gsub( /\n\s*\Z/,'' ).gsub( /^\s*[#{ d }]/, ' ' * n )
end
end
# Expands tabs to +n+ spaces. Non-destructive. If +n+ is 0, then tabs are
# simply removed. Raises an exception if +n+ is negative.
#
# Thanks to GGaramuno for a more efficient algorithm. Very nice.
#
# CREDIT: Gavin Sinclair
# CREDIT: Noah Gibbs
# CREDIT: GGaramuno
def expand_tabs( n=8 )
n = n.to_int
raise ArgumentError, "n must be >= 0" if n < 0
return gsub( /\t/, "" ) if n == 0
return gsub( /\t/, " " ) if n == 1
str = self.dup
while
str.gsub!( /^([^\t\n]*)(\t+)/ ) { |f|
val = ( n * $2.size - ( $1.size % n ) )
$1 << ( ' ' * val )
}
end
str
end
# The reverse of +camelcase+. Makes an underscored of a camelcase string.
#
# Changes '::' to '/' to convert namespaces to paths.
#
# Examples
# "SnakeCase".snakecase #=> "snake_case"
# "Snake-Case".snakecase #=> "snake_case"
# "SnakeCase::Errors".underscore #=> "snake_case/errors"
def snakecase
gsub( /::/, '/' ). # NOT SO SURE ABOUT THIS -T
gsub( /([A-Z]+)([A-Z][a-z])/,'\1_\2' ).
gsub( /([a-z\d])([A-Z])/,'\1_\2' ).
tr( "-", "_" ).
downcase
end
end
class Module
# Returns the module's container module.
#
# module Example
# class Demo
# end
# end
#
# Example::Demo.modspace #=> Example
#
# See also Module#basename.
#
# CREDIT: Trans
def modspace
space = name[ 0...( name.rindex( '::' ) || 0 ) ]
space.empty? ? Object : eval( space )
end
end
module Kernel
autoload :Tempfile, 'tempfile'
def screen_width( out=STDERR )
default_width = ENV[ 'COLUMNS' ] || 80
tiocgwinsz = 0x5413
data = [ 0, 0, 0, 0 ].pack( "SSSS" )
if out.ioctl( tiocgwinsz, data ) >= 0 then
rows, cols, xpixels, ypixels = data.unpack( "SSSS" )
if cols >= 0 then cols else default_width end
else
default_width
end
rescue Exception => e
default_width rescue ( raise e )
end
end
class File
# given some target path string, and an optional reference path
# (Dir.pwd by default), this method returns a string containing
# the relative path of the target path from the reference path
#
# Examples:
# File.relative_path('rel/path') # => './rel/path'
# File.relative_path('/some/abs/path', '/some') # => './abs/path'
# File.relative_path('/some/file.txt', '/some/abs/path') # => '../../file.txt'
def self.relative_path( target, reference = Dir.pwd )
pair = [ target, reference ].map! do |path|
File.expand_path( path.to_s ).split( File::Separator ).tap do |list|
if list.empty? then list << String.new( File::Separator )
elsif list.first.empty? then list.first.replace( File::Separator )
end
end
end
target_list, reference_list = pair
while target_list.first == reference_list.first
target_list.shift
reference_list.shift or break
end
relative_list = Array.new( reference_list.length, '..' )
relative_list.empty? and relative_list << '.'
relative_list.concat( target_list ).compact!
return relative_list.join( File::Separator )
end
end
class Dir
defined?( DOTS ) or DOTS = %w(. ..).freeze
def self.children( directory )
entries = Dir.entries( directory ) - DOTS
entries.map! do |entry|
File.join( directory, entry )
end
end
def self.mkpath( path )
$VERBOSE and $stderr.puts( "INFO: Dir.mkpath(%p)" % path )
test( ?d, path ) and return( path )
parent = File.dirname( path )
test( ?d, parent ) or mkpath( parent )
Dir.mkdir( path )
return( path )
end
end
class Array
# Pad an array with a given value upto a given length.
#
# [0,1,2].pad(6,"a") #=> [0,1,2,"a","a","a"]
#
# If length is a negative number padding will be added
# to the beginning of the array.
#
# [0,1,2].pad(-6,"a") #=> ["a","a","a",0,1,2]
#
# CREDIT: Richard Laugesen
def pad( len, val=nil )
return dup if self.size >= len.abs
if len < 0
Array.new( ( len+size ).abs,val ) + self
else
self + Array.new( len-size,val )
end
end
# Like #pad but changes the array in place.
#
# a = [0,1,2]
# a.pad!(6,"x")
# a #=> [0,1,2,"x","x","x"]
#
# CREDIT: Richard Laugesen
def pad!( len, val=nil )
return self if self.size >= len.abs
if len < 0
replace Array.new( ( len+size ).abs,val ) + self
else
concat Array.new( len-size,val )
end
end
end