class String

require 'facets/string/cmp' require 'facets/blank' require 'facets/string/natcmp'

Conveniently turn a string into a tuple.

Constants

BRA2KET

Public Class Methods

interpolate(&str) click to toggle source

Interpolate. Provides a means of extenally using Ruby string interpolation mechinism.

try = "hello"
str = "\#{try}!!!"
String.interpolate{ str }    #=> "hello!!!"

NOTE: The block neccessary in order to get
      then binding of the caller.

CREDIT: Trans

# File lib/core/facets/string/interpolate.rb, line 15
def self.interpolate(&str)
  eval "%{#{str.call}}", str.binding
end

Public Instance Methods

-(pattern) click to toggle source

Removes occurances of a string or regexp.

"HELLO HELLO" - "LL"    #=> "HEO HEO"

CREDIT: Benjamin David Oakes

# File lib/core/facets/string/op_sub.rb, line 9
def -(pattern)
  self.gsub(pattern, '')
end
/(path) click to toggle source

Treats self and path as representations of pathnames, joining thme together as a single path.

'home'/'trans' #=> 'home/trans'
# File lib/core/facets/string/op_div.rb, line 9
def /(path)
  File.join(self, path)
end
^(aString) click to toggle source

Binary XOR of two strings.

puts "\000\000\001\001" ^ "\000\001\000\001"
puts  "\003\003\003" ^ "\000\001\002"

produces

"\000\001\001\000"
"\003\002\001"
# File lib/core/facets/string/xor.rb, line 13
def ^(aString)
  a = self.unpack('C'*(self.length))
  b = aString.unpack('C'*(aString.length))
  if (b.length < a.length)
    (a.length - b.length).times { b << 0 }
  end
  xor = ""
  0.upto(a.length-1) { |pos|
    x = a[pos] ^ b[pos]
    xor << x.chr()
  }
  return(xor)
end
align(direction, n, sep="\n", c=' ') click to toggle source
# File lib/core/facets/string/align.rb, line 3
def align(direction, n, sep="\n", c=' ')
  case direction
  when :right
    align_right(n, sep="\n", c=' ')
  when :left
    align_left(n, sep="\n", c=' ')
  when :center
    align_center(n, sep="\n", c=' ')
  else
    raise ArgumentError
  end
end
align_center(n, sep="\n", c=' ') click to toggle source

Centers each line of a string.

The default alignment separation is a new line (“n”). This can be changed as can be the padding string which defaults to a single space (' ').

s = <<-EOS
  This is a test
  and
  so on
EOS

puts s.align_center(14)

produces

This is a test
     and
    so on

CREDIT: Trans

# File lib/core/facets/string/align.rb, line 98
def align_center(n, sep="\n", c=' ')
  return center(n.to_i,c.to_s) if sep==nil
  q = split(sep.to_s).collect { |line|
    line.center(n.to_i,c.to_s)
  }
  q.join(sep.to_s)
end
align_left(n, sep="\n", c=' ') click to toggle source

Align a string to the left.

The default alignment separation is a new line (“n”). This can be changed as can be the padding string which defaults to a single space (' ').

s = <<-EOS
This is a test
  and
  so on
EOS

puts s.align_left(20, "\n", '.')

produces

This is a test......
and.................
so on...............

CREDIT: Trans

# File lib/core/facets/string/align.rb, line 68
def align_left(n, sep="\n", c=' ')
  return ljust(n.to_i,c.to_s) if sep==nil
  q = split(sep.to_s).map do |line|
    line.strip.ljust(n.to_i,c.to_s)
  end
  q.join(sep.to_s)
end
align_right(n, sep="\n", c=' ') click to toggle source

Align a string to the right.

The default alignment separation is a new line (“n”). This can be changed as can be the padding string which defaults to a single space (' ').

s = <<-EOS
This is a test
  and
  so on
EOS

puts s.align_right(14)

produces

This is a test
           and
         so on

CREDIT: Trans

# File lib/core/facets/string/align.rb, line 38
def align_right(n, sep="\n", c=' ')
  return rjust(n.to_i,c.to_s) if sep==nil
  q = split(sep.to_s).map do |line|
    line.rjust(n.to_i,c.to_s)
  end
  q.join(sep.to_s)
end
blank?() click to toggle source

Is this string just whitespace?

"abc".blank?  #=> false
"   ".blank?  #=> true
# File lib/core/facets/blank.rb, line 50
def blank?
  self !~ /\S/
end
bracket(bra, ket=nil) click to toggle source

Return a new string embraced by given brakets. If only one bracket char is given it will be placed on either side.

"wrap me".bracket('{')        #=> "{wrap me}"
"wrap me".bracket('--','!')   #=> "--wrap me!"

CREDIT: Trans

# File lib/core/facets/string/bracket.rb, line 14
def bracket(bra, ket=nil)
  #ket = String.bra2ket[$&] if ! ket && /^[\[({<]$/ =~ bra
  ket = BRA2KET[bra] unless ket
  "#{bra}#{self}#{ket ? ket : bra}"
end
bracket!(bra, ket=nil) click to toggle source

Inplace version of braket.

CREDIT: Trans

# File lib/core/facets/string/bracket.rb, line 24
def bracket!(bra, ket=nil)
  self.replace(bracket(bra, ket))
end
bytes(&blk) click to toggle source

Upacks string into bytes.

Note, this is not 100% compatible with 1.8.7+ which returns an enumerator instead of an array.

# File lib/core/facets/string/bytes.rb, line 10
def bytes(&blk)
  if block_given?
    self.unpack('C*').each(&blk)
  else
    self.unpack('C*')
  end
end
camelcase(first_letter=nil) click to toggle source
# File lib/core/facets/string/camelcase.rb, line 20
def camelcase(first_letter=nil)
  case first_letter
  when :upper, true
    upper_camelcase
  when :lower, false
    lower_camelcase
  else
    str = dup
    str.gsub!(/\/(.?)/){ "::#{$1.upcase}" }  # NOT SO SURE ABOUT THIS
    str.gsub!(/(?:_+|-+)([a-z])/){ $1.upcase }
    #str.gsub!(/(\A|\s)([a-z])/){ $1 + $2.upcase }
    str
  end
end
capitalized?() click to toggle source

Return true if the string is capitalized, otherwise false.

"THIS".capitalized?  #=> true
"This".capitalized?  #=> true
"this".capitalized?  #=> false

CREDIT: Phil Tomson

# File lib/core/facets/string/capitalized.rb, line 11
def capitalized?
  self =~ /^[A-Z]/
end
chars() click to toggle source

Returns an array of characters.

"abc".chars  #=> ["a","b","c"]
# File lib/core/facets/string/chars.rb, line 13
def chars
  split(//)
end
cleanlines(&block) click to toggle source

Returns an Enumerator for iterating over each line of the string, stripped of whitespace on either side.

# File lib/core/facets/string/cleanlines.rb, line 9
def cleanlines(&block)
  if block
    scan(/^.*?$/) do |line|
      block.call(line.strip)
    end
  else
    Enumerator.new(self) do |output|
      scan(/^.*?$/) do |line|
        output.yield(line.strip)
      end
    end
  end
end
cleave(threshold=nil, len=nil) click to toggle source

Cleave a string. Break a string in two parts at the nearest whitespace.

CREDIT: Trans

# File lib/core/facets/string/cleave.rb, line 8
def cleave(threshold=nil, len=nil)
  l = (len || size / 2)
  t = threshold || size

  h1 = self[0...l]
  h2 = self[l..-1]

  i1 = h1.rindex(/\s/) || 0
  d1 = (i1 - l).abs

  d2 = h2.index(/\s/) || l
  i2 = d2 + l

  d1 = (i1-l).abs
  d2 = (i2-l).abs

  if [d1, d2].min > t
    i = t
  elsif d1 < d2
    i = i1
  else
    i = i2
  end

  #dup.insert(l, "\n").gsub(/^\s+|\s+$/, '')
  return self[0..i].to_s.strip, self[i+1..-1].to_s.strip
end
cmp(other) click to toggle source

Compare method that takes length into account. Unlike #<=>, this is compatible with succ.

"abc".cmp("abc")   #=>  0
"abcd".cmp("abc")  #=>  1
"abc".cmp("abcd")  #=> -1
"xyz".cmp("abc")   #=>  1

CREDIT: Peter Vanbroekhoven

# File lib/core/facets/comparable/cmp.rb, line 31
def cmp(other)
  return -1 if length < other.length
  return 1 if length > other.length
  self <=> other  # alphabetic compare
end
compress_lines(spaced = true) click to toggle source

Matches any whitespace (including newline) and replaces with a single space

@example

"  SELECT name
  FROM users
".compress_lines
=> "SELECT name FROM users"
# File lib/core/facets/string/compress_lines.rb, line 12
def compress_lines(spaced = true)
  split($/).map { |line| line.strip }.join(spaced ? ' ' : '')
end
dequote() click to toggle source

Remove quotes from string.

"'hi'".dequite    #=> "hi"

CREDIT: Trans

# File lib/core/facets/string/bracket.rb, line 88
def dequote
  s = self.dup

  case self[0,1]
  when "'", '"', '`'
    s[0] = ''
  end

  case self[-1,1]
  when "'", '"', '`'
    s[-1] = ''
  end

  return s
end
divide( re ) click to toggle source

Breaks a string up into an array based on a regular expression. Similar to scan, but includes the matches.

s = "<p>This<b>is</b>a test.</p>"
s.divide( /\<.*?\>/ )

produces

["<p>This", "<b>is", "</b>a test.", "</p>"]

CREDIT: Trans

# File lib/core/facets/string/divide.rb, line 15
def divide( re )
  re2 = /#{re}.*?(?=#{re}|\Z)/
  scan(re2) #{re}(?=#{re})/)
end
downcase?() click to toggle source

Return true if the string is lowercase (downcase), otherwise false.

"THIS".downcase?  #=> false
"This".downcase?  #=> false
"this".downcase?  #=> true

CREDIT: Phil Tomson

# File lib/core/facets/string/capitalized.rb, line 23
def downcase?
  downcase == self
end
each_char() { |scan|| break| ... } click to toggle source

Yields a single-character string for each character in the string. When $KCODE = 'UTF8', multi-byte characters are yielded appropriately.

# File lib/core/facets/string/each_char.rb, line 22
def each_char
  scanner, char = StringScanner.new(self), /./mu
  loop { yield(scanner.scan(char) || break) }
end
each_word(&block) click to toggle source

Iterate through each word of a string.

"a string".each_word { |word| ... }
# File lib/core/facets/string/each_word.rb, line 9
def each_word(&block)
  words.each(&block)
end
edit_distance(str2) click to toggle source

Levenshtein distance algorithm implementation for Ruby, with UTF-8 support.

The Levenshtein distance is a measure of how similar two strings s and t are, calculated as the number of deletions/insertions/substitutions needed to transform s into t. The greater the distance, the more the strings differ.

The Levenshtein distance is also sometimes referred to as the easier-to-pronounce-and-spell 'edit distance'.

Calculate the Levenshtein distance between two strings self and str2. self and str2 should be ASCII, UTF-8, or a one-byte-per character encoding such as ISO-8859-*.

The strings will be treated as UTF-8 if $KCODE is set appropriately (i.e. 'u'). Otherwise, the comparison will be performed byte-by-byte. There is no specific support for Shift-JIS or EUC strings.

When using Unicode text, be aware that this algorithm does not perform normalisation. If there is a possibility of different normalised forms being used, normalisation should be performed beforehand.

CREDIT: Paul Battley

# File lib/core/facets/string/edit_distance.rb, line 26
def edit_distance(str2)
  str1 = self
  if $KCODE =~ /^U/i
    unpack_rule = 'U*'
  else
    unpack_rule = 'C*'
  end
  s = str1.unpack(unpack_rule)
  t = str2.unpack(unpack_rule)
  n = s.length
  m = t.length
  return m if (0 == n)
  return n if (0 == m)

  d = (0..m).to_a
  x = nil

  (0...n).each do |i|
    e = i+1
    (0...m).each do |j|
      cost = (s[i] == t[j]) ? 0 : 1
      x = [
        d[j+1] + 1, # insertion
        e + 1,      # deletion
        d[j] + cost # substitution
      ].min
      d[j] = e
      e = x
    end
    d[m] = x
  end

  return x
end
end_with?(suffix) click to toggle source

Does a string end with the given suffix?

"hello".end_with?("lo")    #=> true
"hello".end_with?("to")    #=> false

CREDIT: Lucas Carlson, Blaine Cook

# File lib/core/facets/string/start_with.rb, line 27
def end_with?(suffix)
  self.rindex(suffix) == size - suffix.size
end
Also aliased as: ends_with?
ends_with?(suffix)
Alias for: end_with?
expand_tab(n=8) click to toggle source

Expands tabs to n spaces. Non-destructive. If n is 0, then tabs are simply removed. Raises an exception if n is negative.

"\t\tHey".expand_tab(2)  #=> "    Hey"

Thanks to GGaramuno for a more efficient algorithm. Very nice.

CREDIT: Gavin Sinclair, Noah Gibbs, GGaramuno

# File lib/core/facets/string/expand_tab.rb, line 13
def expand_tab(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
Also aliased as: expand_tabs
expand_tabs(n=8)

Pluralized form of expand_tab.

Alias for: expand_tab
file() click to toggle source

Use fluent notation for making file directives.

'~/trans/Desktop/notes.txt'.file.mtime
# File lib/core/facets/string/file.rb, line 9
def file
  f = self
  Functor.new do |op, *a|
    File.send(op, f, *a)
  end
end
fold(ignore_indented=false) click to toggle source

Returns a new string with all new lines removed from adjacent lines of text.

s = "This is\na test.\n\nIt clumps\nlines of text."
s.fold

produces

"This is a test.\n\nIt clumps lines of text. "

One arguable flaw with this, that might need a fix: if the given string ends in a newline, it is replaced with a single space.

CREDIT: Trans

# File lib/core/facets/string/fold.rb, line 19
def fold(ignore_indented=false)
  ns = ''
  i = 0
  br = self.scan(/(\n\s*\n|\Z)/m) do |m|
    b = $~.begin(1)
    e = $~.end(1)
    nl = $&
    tx = slice(i...b)
    if ignore_indented and slice(i...b) =~ /^[ ]+/
      ns << tx
    else
      ns << tx.gsub(/[ ]*\n+/,' ')
    end
    ns << nl
    i = e
  end
  ns
end
indent(n, c=' ') click to toggle source

Indent left or right by n spaces. (This used to be called tab and aliased as indent.)

CREDIT: Gavin Sinclair, Trans, Tyler Rick

# File lib/core/facets/string/indent.rb, line 8
def indent(n, c=' ')
  if n >= 0
    gsub(/^/, c * n)
  else
    gsub(/^#{Regexp.escape(c)}{0,#{-n}}/, "")
  end
end
index_all(s, reuse=false) click to toggle source

Like index but returns an array of all index locations. The reuse flag allows the trailing portion of a match to be reused for subsquent matches.

"abcabcabc".index_all('a')  #=> [0,3,6]

"bbb".index_all('bb', false)  #=> [0]
"bbb".index_all('bb', true)   #=> [0,1]

TODO: Culd probably be defined for Indexable in general too.

# File lib/core/facets/string/index_all.rb, line 14
def index_all(s, reuse=false)
  s = Regexp.new(Regexp.escape(s)) unless Regexp===s
  ia = []; i = 0
  while (i = index(s,i))
    ia << i
    i += (reuse ? 1 : $~[0].size)
  end
  ia
end
is_roman_numeral?() click to toggle source

Returns true iif the subject is a roman numeral.

# File lib/more/facets/roman.rb, line 157
def is_roman_numeral?
  English::RomanNumerals.is_roman_numeral?(self)
end
lchomp(match) click to toggle source

Left chomp.

"help".lchomp("h")  #=> "elp"
"help".lchomp("k")  #=> "help"

CREDIT: Trans

# File lib/core/facets/string/chomp.rb, line 10
def lchomp(match)
  if index(match) == 0
    self[match.size..-1]
  else
    self.dup
  end
end
lchomp!(match) click to toggle source

In-place left chomp.

"help".lchomp("h")  #=> "elp"
"help".lchomp("k")  #=> "help"

CREDIT: Trans

# File lib/core/facets/string/chomp.rb, line 25
def lchomp!(match)
  if index(match) == 0
    self[0...match.size] = ''
    self
  end
end
line_wrap(width, tabs=4) click to toggle source

Line wrap at width.

puts "1234567890".line_wrap(5)

produces

12345
67890

CREDIT: Trans

# File lib/core/facets/string/line_wrap.rb, line 14
def line_wrap(width, tabs=4)
  s = gsub(/\t/,' ' * tabs) # tabs default to 4 spaces
  s = s.gsub(/\n/,' ')
  r = s.scan( /.{1,#{width}}/ )
  r.join("\n") << "\n"
end
lines(&blk) click to toggle source

Returns an array of characters.

"abc\n123".lines  #=> ["abc\n","123"]
# File lib/core/facets/string/lines.rb, line 9
def lines(&blk)
  if block_given?
    each_line(&blk) #scan(/$.*?\n/).each(&blk)
  else
    Enumerator.new(self, :lines) #.split(/\n/)
  end
end
lower_camelcase() click to toggle source
# File lib/core/facets/string/camelcase.rb, line 44
def lower_camelcase
  str = dup
  str.gsub!(/\/(.?)/){ "::#{$1.upcase}" }  # NOT SO SURE ABOUT THIS
  str.gsub!(/(?:_+|-+)([a-z])/){ $1.upcase }
  str.gsub!(/(\A|\s)([A-Z])/){ $1 + $2.downcase }
  str
end
lowercase() click to toggle source

Downcase first letter.

# File lib/core/facets/string/uppercase.rb, line 17
def lowercase
  str = to_s
  str[0,1].downcase + str[1..-1]
end
margin(n=0) click to toggle source

Provides a margin controlled string.

x = %Q{
      | This
      |   is
      |     margin controlled!
      }.margin

NOTE: This may still need a bit of tweaking.

TODO: describe its limits and caveats and edge cases

CREDIT: Trans

# File lib/core/facets/string/margin.rb, line 17
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
methodize() click to toggle source

Translate a (class or module) name to a suitable method name.

My::CoolClass.name.methodize => "my__cool_class"
# File lib/core/facets/string/methodize.rb, line 17
def methodize
  gsub(/([A-Z]+)([A-Z])/,'\1_\2').
  gsub(/([a-z])([A-Z])/,'\1_\2').
  gsub('/' ,'__').
  gsub('::','__').
  downcase
end
modulize() click to toggle source

Converts a string to module name representation.

This is essentially camelcase. It also converts '/' to '::' which is useful for converting paths to namespaces.

Examples

"method_name".modulize    #=> "MethodName"
"method/name".modulize    #=> "Method::Name"
# File lib/core/facets/string/modulize.rb, line 21
def modulize
  gsub('__','/').
  gsub(/\/(.?)/){ "::#{$1.upcase}" }.
  gsub(/(?:_+|-+)([a-z])/){ $1.upcase }.
  gsub(/(\A|\s)([a-z])/){ $1 + $2.upcase }
end
mscan(re) { || ... } click to toggle source

Like scan but returns MatchData ($~) rather then matched string ($&).

CREDIT: Trans

# File lib/core/facets/string/mscan.rb, line 8
def mscan(re) #:yield:
  if block_given?
    scan(re) { yield($~) }
  else
    m = []
    scan(re) { m << $~ }
    m
  end
end
natcmp(str2, caseInsensitive=false) click to toggle source

'Natural order' comparison of strings, e.g.

"my_prog_v1.1.0" < "my_prog_v1.2.0" < "my_prog_v1.10.0"

which does not follow alphabetically. A secondary parameter, if set to true, makes the comparison case insensitive.

"Hello.10".natcmp("Hello.1")  #=> -1

TODO: Invert case flag?

CREDIT: Alan Davies, Martin Pool

# File lib/core/facets/string/natcmp.rb, line 46
  def natcmp(str2, caseInsensitive=false)
    str1 = self.dup
    str2 = str2.dup
    compareExpression = /^(\D*)(\d*)(.*)$/

    if caseInsensitive
      str1.downcase!
      str2.downcase!
    end

    # remove all whitespace
    str1.gsub!(/\s*/, '')
    str2.gsub!(/\s*/, '')

    while (str1.length > 0) or (str2.length > 0) do
      # Extract non-digits, digits and rest of string
      str1 =~ compareExpression
      chars1, num1, str1 = $1.dup, $2.dup, $3.dup
      str2 =~ compareExpression
      chars2, num2, str2 = $1.dup, $2.dup, $3.dup
      # Compare the non-digits
      case (chars1 <=> chars2)
        when 0 # Non-digits are the same, compare the digits...
          # If either number begins with a zero, then compare alphabetically,
          # otherwise compare numerically
          if (num1[0] != 48) and (num2[0] != 48)
            num1, num2 = num1.to_i, num2.to_i
          end
          case (num1 <=> num2)
            when -1 then return -1
            when 1 then return 1
          end
        when -1 then return -1
        when 1 then return 1
      end # case
    end # while

    # strings are naturally equal.
    return 0
  end

end
nchar(n, replacement=nil) click to toggle source

Returns n characters of the string. If n is positive the characters are from the beginning of the string. If n is negative from the end of the string.

Alternatively a replacement string can be given, which will replace the n characters.

str = "this is text"
str.nchar(4)            #=> "this"
str.nchar(4, 'that')    #=> "that"
str                     #=> "that is text"
# File lib/core/facets/string/nchar.rb, line 15
def nchar(n, replacement=nil)
  if replacement
    s = self.dup
    n > 0 ? (s[0...n] = replacement) : (s[n..-1] = replacement)
    return s
  else
    n > 0 ? self[0...n] : self[n..-1]
  end
end
newlines(&block) click to toggle source

Returns an Enumerator for iterating over each line of the string, void of the termining newline character, in contrast to lines which retains it.

# File lib/core/facets/string/newlines.rb, line 9
def newlines(&block)
  if block
    scan(/^.*?$/) do |line|
      block.call(line.chomp)
    end
  else
    Enumerator.new(self) do |output|
      scan(/^.*?$/) do |line|
        output.yield(line.chomp)
      end
    end
  end
end
object_state(data=nil) click to toggle source
# File lib/core/facets/kernel/object_state.rb, line 37
def object_state(data=nil)
  data ? replace(data) : dup
end
outdent(n) click to toggle source

Outdent just indents a negative number of spaces.

CREDIT: Noah Gibbs

# File lib/core/facets/string/indent.rb, line 20
def outdent(n)
  indent(-n)
end
pathize() click to toggle source

Converts a (class or module) name to a unix path.

My::CoolClass.name.pathize  #=> "my/cool_class"
# File lib/core/facets/string/pathize.rb, line 17
def pathize
  gsub(/([A-Z]+)([A-Z])/,'\1_\2').
  gsub(/([a-z])([A-Z])/,'\1_\2').
  gsub('__','/').
  gsub('::','/').
  downcase
end
quote(type=:s) click to toggle source

Return a new string embraced by given quotes. If no quotes are specified, then assumes single quotes.

"quote me".quote     #=> "'quote me'"
"quote me".quote(2)  #=> "\"quote me\""

CREDIT: Trans

# File lib/core/facets/string/bracket.rb, line 69
def quote(type=:s)
  case type.to_s.downcase
  when 's', 'single'
    bracket("'")
  when 'd', 'double'
    bracket('"')
  when 'b', 'back'
    bracket('`')
  else
    bracket("'")
  end
end
range(s, offset=0) click to toggle source

Like index but returns a Range.

"This is a test!".range('test')  #=> 10..13

CREDIT: Trans

# File lib/core/facets/string/range.rb, line 9
def range(s, offset=0)
  if index(s, offset)
    return ($~.begin(0))..($~.end(0)-1)
  end
  nil
end
range_all(s, reuse=false) click to toggle source

Like index_all but returns an array of Ranges.

"abc123abc123".range_all('abc')  #=> [0..2, 6..8]

TODO: Add offset, perhaps ?

CREDIT: Trans

# File lib/core/facets/string/range.rb, line 24
def range_all(s, reuse=false)
  r = []; i = 0
  while i < self.length
    rng = range(s, i)
    if rng
      r << rng
      i += reuse ? 1 : rng.end + 1
    else
      break
    end
  end
  r.uniq
end
range_of_line() click to toggle source

Returns an array of ranges mapping the characters per line.

"this\nis\na\ntest".range_of_line
#=> [0..4, 5..7, 8..9, 10..13]

CREDIT: Trans

# File lib/core/facets/string/range.rb, line 46
def range_of_line
  offset=0; charmap = []
  each_line do |line|
    charmap << (offset..(offset + line.length - 1))
    offset += line.length
  end
  charmap
end
rewrite(rules) click to toggle source

Apply a set of rules (regular expression matches) to the string.

Requirements:

The rules must be applied in order! So we cannot use a hash because the ordering is not guaranteed! we use an array instead.

Input:

The array containing rule-pairs (match, write).

Output:

The rewritten string.

CREDIT: George Moschovitis

# File lib/core/facets/string/rewrite.rb, line 18
def rewrite(rules)
  raise ArgumentError.new('The rules parameter is nil') unless rules
  rewritten_string = dup
  rules.each do |match,write|
    rewritten_string.gsub!(match,write)
  end
  return rewritten_string
end
shatter( re ) click to toggle source

Breaks a string up into an array based on a regular expression. Similar to scan, but includes the matches.

s = "<p>This<b>is</b>a test.</p>"
s.shatter( /\<.*?\>/ )

produces

["<p>", "This", "<b>", "is", "</b>", "a test.", "</p>"]

CREDIT: Trans

# File lib/core/facets/string/shatter.rb, line 15
def shatter( re )
  r = self.gsub( re ){ |s| "\1" + s + "\1" }
  while r[0,1] == "\1" ; r[0] = '' ; end
  while r[-1,1] == "\1" ; r[-1] = '' ; end
  r.split("\1")
end
similarity(str_in) click to toggle source

A fuzzy matching mechanism. Returns a score from 0-1, based on the number of shared edges. To be effective, the strings must be of length 2 or greater.

"Alexsander".fuzzy_match( "Aleksander" )  #=> 0.9

The way it works:

  • Converts each string into a “graph like” object, with edges

    "alexsander" -> [ alexsander, alexsand, alexsan ... lexsand ... san ... an, etc ]
    "aleksander" -> [ aleksander, aleksand ... etc. ]
  • Perform match, then remove any subsets from this matched set (i.e. a hit on “san” is a subset of a hit on “sander”)

    Above example, once reduced -> [ ale, sander ]
  • See's how many of the matches remain, and calculates a score based on how many matches, their length, and compare to the length of the larger of the two words.

Still a bit rough. Any suggestions for improvement are welcome.

CREDIT: Derek Lewis.

# File lib/core/facets/string/similarity.rb, line 29
def similarity(str_in)
  return 0 if str_in == nil
  return 1 if self == str_in

  # Make a graph of each word (okay, so its not a true graph, but is similar)
  graph_A = Array.new
  graph_B = Array.new

  # "graph" self
  last = self.length
  (0..last).each do |ff|
    loc  = self.length
    break if ff == last - 1
    wordB = (1..(last-1)).to_a.reverse!
    if (wordB != nil)
      wordB.each do |ss|
        break if ss == ff
        graph_A.push( "#{self[ff..ss]}" )
      end
    end
  end

  # "graph" input string
  last = str_in.length
  (0..last).each{ |ff|
    loc  = str_in.length
    break if ff == last - 1
    wordB = (1..(last-1)).to_a.reverse!
    wordB.each do |ss|
      break if ss == ff
      graph_B.push( "#{str_in[ff..ss]}" )
    end
  }

  # count how many of these "graph edges" we have that are the same
  matches = graph_A & graph_B
  #matches = Array.new
  #graph_A.each do |aa|
  #  matches.push( aa ) if( graph_B.include?( aa ) )
  #end

  # For eliminating subsets, we want to start with the smallest hits.
  matches.sort!{|x,y| x.length <=> y.length}

  # eliminate any subsets
  mclone = matches.dup
  mclone.each_index do |ii|
    reg = Regexp.compile( Regexp.escape(mclone[ii]) )
    count = 0.0
    matches.each{|xx| count += 1 if xx =~ reg}
    matches.delete(mclone[ii]) if count > 1
  end

  score = 0.0
  matches.each{ |mm| score += mm.length }
  self.length > str_in.length ? largest = self.length : largest = str_in.length
  return score/largest
end
snakecase() click to toggle source

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".snakecase   #=> "snake_case/errors"
# File lib/core/facets/string/snakecase.rb, line 12
def snakecase
  gsub(/::/, '/').  # NOT SO SURE ABOUT THIS
  gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
  gsub(/([a-z\d])([A-Z])/,'\1_\2').
  tr("-", "_").
  downcase
end
splice(idx, sub=nil) click to toggle source

This is basically the same as store, but it acts like slice! when given only one argument.

Essentlay slice, but writes rather than reads.

a = "HELLO"
a.splice("X", 1)
a                #=> "HXLLO"

a = "HELLO"
a.splice(1)    #=> "E"
a              #=> "HLLO"

CREDIT: Trans

# File lib/core/facets/string/splice.rb, line 20
def splice(idx, sub=nil)
  if sub
    store(idx, sub)
  else
    case idx
    when Range
      slice!(idx)
    else
      slice!(idx,1)
    end
  end
end
start_with?(prefix) click to toggle source

Does a string start with the given prefix?

"hello".start_with?("he")    #=> true
"hello".start_with?("to")    #=> false

CREDIT: Lucas Carlson, Blaine Cook

# File lib/core/facets/string/start_with.rb, line 12
def start_with?(prefix)
  self.index(prefix) == 0
end
Also aliased as: starts_with?
starts_with?(prefix)
Alias for: start_with?
succ(n=1) click to toggle source

Allows succ to take n step increments.

"abc".succ      #=> "abd"
"abc".succ(4)   #=> "abg"
"abc".succ(24)  #=> "aca"

CREDIT: Trans

# File lib/more/facets/succ.rb, line 37
def succ(n=1)
  s = self
  n.times { s = s.succ1 }
  s
end
Also aliased as: succ1
succ1(n=1)
Alias for: succ
tab(n) click to toggle source

Aligns each line n spaces.

CREDIT: Gavin Sinclair

# File lib/core/facets/string/tab.rb, line 12
def tab(n)
  gsub(/^ */, ' ' * n)
end
tabto(n) click to toggle source

Preserves relative tabbing. The first non-empty line ends up with n spaces before nonspace.

CREDIT: Gavin Sinclair

# File lib/core/facets/string/tabto.rb, line 10
def tabto(n)
  if self =~ /^( *)\S/
    indent(n - $1.length)
  else
    self
  end
end
titlecase() click to toggle source

Title case.

"this is a string".titlecase
=> "This Is A String"

CREDIT: Eliazar Parra

# File lib/core/facets/string/titlecase.rb, line 10
def titlecase
  gsub(/\b\w/){ $`[-1,1] == "'" ? $& : $&.upcase }
end
to_b() click to toggle source

Interpret common affirmative string meanings as true, otherwise false. Balnk sapce and case are ignored. The following strings that will return true:

<tt>true</tt>,<tt>yes</tt>,<tt>on</tt>,<tt>t</tt>,<tt>1</tt>,<tt>y</tt>,<tt>==</tt>

Examples:

"true".to_b   #=> true
"yes".to_b    #=> true
"no".to_b     #=> false
"123".to_b    #=> false
# File lib/core/facets/boolean.rb, line 43
def to_b
  case self.downcase.strip
  when 'true', 'yes', 'on', 't', '1', 'y', '=='
    return true
  when 'nil', 'null'
    return nil
  else
    return false
  end
end
to_date() click to toggle source

Parse data from string.

# File lib/more/facets/date.rb, line 390
def to_date
  #::Date::civil(*ParseDate.parsedate(self)[0..2])
  ::Date.new(*::Date._parse(self, false).values_at(:year, :mon, :mday))
end
to_datetime() click to toggle source

Convert string to DateTime.

# File lib/more/facets/date.rb, line 384
def to_datetime
  date = ::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec).map { |arg| arg || 0 }
  ::DateTime.civil(*date)
end
to_i_roman() click to toggle source

Considers string a roman numeral numeral, and converts it to the corresponding integer.

# File lib/more/facets/roman.rb, line 151
def to_i_roman
  English::RomanNumerals.to_integer(self)
end
to_re(esc=false) click to toggle source

Turns a string into a regular expression.

"a?".to_re  #=> /a?/

CREDIT: Trans

# File lib/core/facets/string/to_re.rb, line 9
def to_re(esc=false)
  Regexp.new((esc ? Regexp.escape(self) : self))
end
to_rx(esc=true) click to toggle source

Turns a string into a regular expression. By default it will escape all characters. Use false argument to turn off escaping.

"[".to_rx  #=> /\[/

CREDIT: Trans

# File lib/core/facets/string/to_re.rb, line 21
def to_rx(esc=true)
  Regexp.new((esc ? Regexp.escape(self) : self))
end
to_t( &yld ) click to toggle source

Translates a string in the form on a set of numerical and/or alphanumerical characters separated by non-word characters (eg W+) into a Tuple. The values of the tuple will be converted to integers if they are purely numerical.

'1.2.3a'.to_t  #=> [1,2,"3a"]

It you would like to control the interpretation of each value as it is added to the tuple you can supply a block.

'1.2.3a'.to_t { |v| v.upcase }  #=> ["1","2","3A"]

This method calls Tuple#cast_from_string.

# File lib/more/facets/tuple.rb, line 309
def to_t( &yld )
  Tuple.cast_from_string( self, &yld )
end
unbracket(bra=nil, ket=nil) click to toggle source

Return a new string embraced by given brakets. If only one bracket char is given it will be placed on either side.

"{unwrap me}".debracket('{')        #=> "unwrap me"
"--unwrap me!".debracket('--','!')  #=> "unwrap me!"

CREDIT: Trans

# File lib/core/facets/string/bracket.rb, line 37
def unbracket(bra=nil, ket=nil)
  if bra
    ket = BRA2KET[bra] unless ket
    ket = ket ? ket : bra
    s = self.dup
    s.gsub!(%r[^#{Regexp.escape(bra)}], '')
    s.gsub!(%r[#{Regexp.escape(ket)}$], '')
    return s
  else
    if m = BRA2KET[ self[0,1] ]
      return self.slice(1...-1) if self[-1,1]  == m
    end
  end
  return self.dup  # if nothing else
end
unbracket!(bra=nil, ket=nil) click to toggle source

Inplace version of debraket.

CREDIT: Trans

# File lib/core/facets/string/bracket.rb, line 57
def unbracket!(bra=nil, ket=nil)
  self.replace( unbracket(bra, ket) )
end
underscore() click to toggle source

The reverse of camelcase. Makes an underscored of a camelcase string.

Changes '::' to '/' to convert namespaces to paths.

Examples

"SnakeCase".underscore           #=> "snake_case"
"Snake-Case".underscore          #=> "snake_case"
"SnakeCase::Errors".underscore   #=> "snake_case/errors"
# File lib/core/facets/string/underscore.rb, line 12
def underscore
  gsub(/::/, '/').
  gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
  gsub(/([a-z\d])([A-Z])/,'\1_\2').
  tr("-", "_").
  downcase
end
unfold() click to toggle source

Unfold paragrpahs.

FIXME: Sometimes adds one too many blank lines. TEST!!!

# File lib/core/facets/string/unfold.rb, line 7
def unfold
  blank = false
  text  = ''
  split(/\n/).each do |line|
    if /\S/ !~ line
      text << "\n\n"
      blank = true
    else
      if /^(\s+|[*])/ =~ line
        text << (line.rstrip + "\n")
      else
        text << (line.rstrip + " ")
      end
      blank = false
    end
  end
  text = text.gsub(/(\n){3,}/,"\n\n")
  text.rstrip
end
upcase?() click to toggle source

Is the string upcase/uppercase?

"THIS".upcase?  #=> true
"This".upcase?  #=> false
"this".upcase?  #=> false

CREDIT: Phil Tomson

# File lib/core/facets/string/capitalized.rb, line 38
def upcase?
  upcase == self
end
upper_camelcase() click to toggle source
# File lib/core/facets/string/camelcase.rb, line 36
def upper_camelcase
  str = dup
  str.gsub!(/\/(.?)/){ "::#{$1.upcase}" }  # NOT SO SURE ABOUT THIS
  str.gsub!(/(?:_+|-+)([a-z])/){ $1.upcase }
  str.gsub!(/(\A|\s)([a-z])/){ $1 + $2.upcase }
  str
end
uppercase() click to toggle source

Upcase first letter.

NOTE: One might argue that this method should behave the same as #upcase and rather this behavior should be in place of #captialize. Probably so, but since Matz has already defined #captialize the way it is, this name seems most fitting to the missing behavior.

# File lib/core/facets/string/uppercase.rb, line 10
def uppercase
  str = to_s
  str[0,1].upcase + str[1..-1]
end
variablize() click to toggle source

Prepend an “@” to the beginning of a string to make a instance variable name. This also replaces non-valid characters with underscores.

# File lib/core/facets/string/variablize.rb, line 7
def variablize
  v = gsub(/\W/, '_')
  "@#{v}"
end
word_wrap( col_width=80 ) click to toggle source

Word wrap a string not exceeding max width.

puts "this is a test".word_wrap(4)

produces

this
is a
test

This is basic implementation of word wrap, but smart enough to suffice for most use cases.

CREDIT: Gavin Kistner, Dayne Broderson

# File lib/core/facets/string/word_wrap.rb, line 18
def word_wrap( col_width=80 )
  self.dup.word_wrap!( col_width )
end
word_wrap!( col_width=80 ) click to toggle source

As with word_wrap, but modifies the string in place.

CREDIT: Gavin Kistner, Dayne Broderson

# File lib/core/facets/string/word_wrap.rb, line 26
def word_wrap!( col_width=80 )
  self.gsub!( /(\S{#{col_width}})(?=\S)/, '\1 ' )
  self.gsub!( /(.{1,#{col_width}})(?:\s+|$)/, "\\1\n" )
  self
end
words() click to toggle source

Returns an array of characters.

"abc 123".words  #=> ["abc","123"]
# File lib/core/facets/string/words.rb, line 7
def words
  self.split(/\s+/)
end