1#!/usr/bin/ruby 2# encoding: utf-8 3 4class 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 135end 136 137 138class 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 156end 157 158module 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 174end 175 176 177class 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 208end 209 210class 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 228end 229 230class 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 269end 270