Home | History | Annotate | Download | only in test
      1 #!/usr/bin/ruby
      2 # encoding: utf-8
      3 
      4 class String
      5   def /( subpath )
      6     File.join( self, subpath.to_s )
      7   end
      8 
      9   def here_indent( chr = '| ' )
     10     dup.here_indent!( chr )
     11   end
     12 
     13   def here_indent!( chr = '| ' )
     14     chr = Regexp.escape( chr )
     15     exp = Regexp.new( "^ *#{ chr }" )
     16     self.gsub!( exp,'' )
     17     return self
     18   end
     19 
     20   def here_flow( chr = '| ' )
     21     dup.here_flow!( chr )
     22   end
     23 
     24   def here_flow!( chr = '| ' )
     25     here_indent!( chr ).gsub!( /\n\s+/,' ' )
     26     return( self )
     27   end
     28 
     29   # Indent left or right by n spaces.
     30   # (This used to be called #tab and aliased as #indent.)
     31   #
     32   #  CREDIT: Gavin Sinclair
     33   #  CREDIT: Trans
     34 
     35   def indent( n )
     36     if n >= 0
     37       gsub( /^/, ' ' * n )
     38     else
     39       gsub( /^ {0,#{ -n }}/, "" )
     40     end
     41   end
     42 
     43   # Outdent just indents a negative number of spaces.
     44   #
     45   #  CREDIT: Noah Gibbs
     46 
     47   def outdent( n )
     48     indent( -n )
     49   end
     50   
     51   # Returns the shortest length of leading whitespace for all non-blank lines
     52   #
     53   #   n = %Q(
     54   #     a = 3
     55   #       b = 4
     56   #   ).level_of_indent  #=> 2
     57   #
     58   #   CREDIT: Kyle Yetter
     59   def level_of_indent
     60     self.scan( /^ *(?=\S)/ ).map { |space| space.length }.min || 0
     61   end
     62   
     63   def fixed_indent( n )
     64     self.outdent( self.level_of_indent ).indent( n )
     65   end
     66   
     67   # Provides a margin controlled string.
     68   #
     69   #   x = %Q{
     70   #         | This
     71   #         |   is
     72   #         |     margin controlled!
     73   #         }.margin
     74   #
     75   #
     76   #   NOTE: This may still need a bit of tweaking.
     77   #
     78   #  CREDIT: Trans
     79 
     80   def margin( n=0 )
     81     #d = /\A.*\n\s*(.)/.match( self )[1]
     82     #d = /\A\s*(.)/.match( self)[1] unless d
     83     d = ( ( /\A.*\n\s*(.)/.match( self ) ) ||
     84         ( /\A\s*(.)/.match( self ) ) )[ 1 ]
     85     return '' unless d
     86     if n == 0
     87       gsub( /\n\s*\Z/,'' ).gsub( /^\s*[#{ d }]/, '' )
     88     else
     89       gsub( /\n\s*\Z/,'' ).gsub( /^\s*[#{ d }]/, ' ' * n )
     90     end
     91   end
     92 
     93   # Expands tabs to +n+ spaces.  Non-destructive.  If +n+ is 0, then tabs are
     94   # simply removed.  Raises an exception if +n+ is negative.
     95   #
     96   # Thanks to GGaramuno for a more efficient algorithm.  Very nice.
     97   #
     98   #  CREDIT: Gavin Sinclair
     99   #  CREDIT: Noah Gibbs
    100   #  CREDIT: GGaramuno
    101 
    102   def expand_tabs( n=8 )
    103     n = n.to_int
    104     raise ArgumentError, "n must be >= 0" if n < 0
    105     return gsub( /\t/, "" ) if n == 0
    106     return gsub( /\t/, " " ) if n == 1
    107     str = self.dup
    108     while
    109       str.gsub!( /^([^\t\n]*)(\t+)/ ) { |f|
    110         val = ( n * $2.size - ( $1.size % n ) )
    111         $1 << ( ' ' * val )
    112       }
    113     end
    114     str
    115   end
    116 
    117 
    118   # The reverse of +camelcase+. Makes an underscored of a camelcase string.
    119   #
    120   # Changes '::' to '/' to convert namespaces to paths.
    121   #
    122   # Examples
    123   #   "SnakeCase".snakecase           #=> "snake_case"
    124   #   "Snake-Case".snakecase          #=> "snake_case"
    125   #   "SnakeCase::Errors".underscore  #=> "snake_case/errors"
    126 
    127   def snakecase
    128     gsub( /::/, '/' ).  # NOT SO SURE ABOUT THIS -T
    129     gsub( /([A-Z]+)([A-Z][a-z])/,'\1_\2' ).
    130     gsub( /([a-z\d])([A-Z])/,'\1_\2' ).
    131     tr( "-", "_" ).
    132     downcase
    133   end
    134   
    135 end
    136 
    137 
    138 class Module
    139   # Returns the module's container module.
    140   #
    141   #   module Example
    142   #     class Demo
    143   #     end
    144   #   end
    145   #
    146   #   Example::Demo.modspace   #=> Example
    147   #
    148   # See also Module#basename.
    149   #
    150   #   CREDIT: Trans
    151 
    152   def modspace
    153     space = name[ 0...( name.rindex( '::' ) || 0 ) ]
    154     space.empty? ? Object : eval( space )
    155   end
    156 end
    157 
    158 module Kernel
    159   autoload :Tempfile, 'tempfile'
    160   
    161   def screen_width( out=STDERR )
    162     default_width = ENV[ 'COLUMNS' ] || 80
    163     tiocgwinsz = 0x5413
    164     data = [ 0, 0, 0, 0 ].pack( "SSSS" )
    165     if out.ioctl( tiocgwinsz, data ) >= 0 then
    166       rows, cols, xpixels, ypixels = data.unpack( "SSSS" )
    167       if cols >= 0 then cols else default_width end
    168     else
    169       default_width
    170     end
    171   rescue Exception => e
    172     default_width rescue ( raise e )
    173   end
    174 end
    175 
    176 
    177 class File
    178   
    179   # given some target path string, and an optional reference path
    180   # (Dir.pwd by default), this method returns a string containing
    181   # the relative path of the target path from the reference path
    182   # 
    183   # Examples:
    184   #    File.relative_path('rel/path')   # => './rel/path'
    185   #    File.relative_path('/some/abs/path', '/some')  # => './abs/path'
    186   #    File.relative_path('/some/file.txt', '/some/abs/path')  # => '../../file.txt'
    187   def self.relative_path( target, reference = Dir.pwd )
    188     pair = [ target, reference ].map! do |path|
    189       File.expand_path( path.to_s ).split( File::Separator ).tap do |list|
    190         if list.empty? then list << String.new( File::Separator )
    191         elsif list.first.empty? then list.first.replace( File::Separator )
    192         end
    193       end
    194     end
    195 
    196     target_list, reference_list = pair
    197     while target_list.first == reference_list.first
    198       target_list.shift
    199       reference_list.shift or break
    200     end
    201     
    202     relative_list = Array.new( reference_list.length, '..' )
    203     relative_list.empty? and relative_list << '.'
    204     relative_list.concat( target_list ).compact!
    205     return relative_list.join( File::Separator )
    206   end
    207   
    208 end
    209 
    210 class Dir
    211   defined?( DOTS ) or DOTS = %w(. ..).freeze
    212   def self.children( directory )
    213     entries = Dir.entries( directory ) - DOTS
    214     entries.map! do |entry|
    215       File.join( directory, entry )
    216     end
    217   end
    218   
    219   def self.mkpath( path )
    220     $VERBOSE and $stderr.puts( "INFO: Dir.mkpath(%p)" % path )
    221     test( ?d, path ) and return( path )
    222     parent = File.dirname( path )
    223     test( ?d, parent ) or mkpath( parent )
    224     Dir.mkdir( path )
    225     return( path )
    226   end
    227   
    228 end
    229 
    230 class Array
    231 
    232   # Pad an array with a given <tt>value</tt> upto a given <tt>length</tt>.
    233   #
    234   #   [0,1,2].pad(6,"a")  #=> [0,1,2,"a","a","a"]
    235   #
    236   # If <tt>length</tt> is a negative number padding will be added
    237   # to the beginning of the array.
    238   #
    239   #   [0,1,2].pad(-6,"a")  #=> ["a","a","a",0,1,2]
    240   #
    241   #  CREDIT: Richard Laugesen
    242 
    243   def pad( len, val=nil )
    244     return dup if self.size >= len.abs
    245     if len < 0
    246       Array.new( ( len+size ).abs,val ) + self
    247     else
    248       self + Array.new( len-size,val )
    249     end
    250   end
    251 
    252   # Like #pad but changes the array in place.
    253   #
    254   #    a = [0,1,2]
    255   #    a.pad!(6,"x")
    256   #    a  #=> [0,1,2,"x","x","x"]
    257   #
    258   #  CREDIT: Richard Laugesen
    259 
    260   def pad!( len, val=nil )
    261     return self if self.size >= len.abs
    262     if len < 0
    263       replace Array.new( ( len+size ).abs,val ) + self
    264     else
    265       concat Array.new( len-size,val )
    266     end
    267   end
    268 
    269 end
    270