#!/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