Class: String

Inherits:
Object show all
Includes:
Random::StringExtensions
Defined in:
lib/core/facets/string/natcmp.rb,
lib/core/facets/boolean.rb,
lib/standard/facets/date.rb,
lib/core/facets/string/tab.rb,
lib/core/facets/string/xor.rb,
lib/standard/facets/random.rb,
lib/core/facets/string/file.rb,
lib/core/facets/string/fold.rb,
lib/core/facets/kernel/blank.rb,
lib/core/facets/string/align.rb,
lib/core/facets/string/bytes.rb,
lib/core/facets/string/lines.rb,
lib/core/facets/string/mscan.rb,
lib/core/facets/string/nchar.rb,
lib/core/facets/string/quote.rb,
lib/core/facets/string/range.rb,
lib/core/facets/string/store.rb,
lib/core/facets/string/tabto.rb,
lib/core/facets/string/to_re.rb,
lib/core/facets/string/words.rb,
lib/core/facets/string/cleave.rb,
lib/core/facets/string/divide.rb,
lib/core/facets/string/indent.rb,
lib/core/facets/string/lchomp.rb,
lib/core/facets/string/margin.rb,
lib/core/facets/string/op_div.rb,
lib/core/facets/string/op_sub.rb,
lib/core/facets/string/splice.rb,
lib/core/facets/string/squish.rb,
lib/core/facets/string/unfold.rb,
lib/supplemental/facets/tuple.rb,
lib/core/facets/comparable/cmp.rb,
lib/core/facets/string/bracket.rb,
lib/core/facets/string/exclude.rb,
lib/core/facets/string/pathize.rb,
lib/core/facets/string/rewrite.rb,
lib/core/facets/string/shatter.rb,
lib/core/facets/string/modulize.rb,
lib/core/facets/string/newlines.rb,
lib/core/facets/string/camelcase.rb,
lib/core/facets/string/each_char.rb,
lib/core/facets/string/each_word.rb,
lib/core/facets/string/index_all.rb,
lib/core/facets/string/line_wrap.rb,
lib/core/facets/string/methodize.rb,
lib/core/facets/string/snakecase.rb,
lib/core/facets/string/titlecase.rb,
lib/core/facets/string/uppercase.rb,
lib/core/facets/string/word_wrap.rb,
lib/core/facets/string/characters.rb,
lib/core/facets/string/cleanlines.rb,
lib/core/facets/string/expand_tab.rb,
lib/core/facets/string/similarity.rb,
lib/core/facets/string/start_with.rb,
lib/core/facets/string/underscore.rb,
lib/core/facets/string/variablize.rb,
lib/core/facets/string/capitalized.rb,
lib/core/facets/string/interpolate.rb,
lib/core/facets/object/object_state.rb,
lib/core/facets/string/edit_distance.rb,
lib/core/facets/string/random_binary.rb,
lib/core-uncommon/facets/string/crypt.rb,
lib/core/facets/string/compress_lines.rb,
lib/core-uncommon/facets/integer/roman.rb,
lib/core-uncommon/facets/string/acronym.rb

Overview

Developer’s Notes

Old definition of start_with? written by Lucas Carlson and Blaine Cook was …

index(prefix) == 0

I like the simplicity of this definition, but I could not find a way define #end_with? in similar terms and still accept regular expressions for the suffix. So I had to use Regexp match. On th upside, we can get MatchData which might be more useful. ++

Constant Summary collapse

BRA2KET =
{ '['=>']', '('=>')', '{'=>'}', '<'=>'>' }
ROMAN =

Taken from O’Reilly’s Perl Cookbook 6.23. Regular Expression Grabbag.

/^M*(D?C{0,3}|C[DM])(L?X{0,3}|X[LC])(V?I{0,3}|I[VX])$/i
ROMAN_VALUES =
Integer::ROMAN_VALUES.inject({}) do |h,(r,a)|
  h[r] = a; h
end

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Random::StringExtensions

#at_rand, #at_rand!, included, #rand_byte, #rand_byte!, #rand_index, #shuffle, #shuffle!

Class Method Details

.interpolate(&str) ⇒ Object

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



14
15
16
# File 'lib/core/facets/string/interpolate.rb', line 14

def self.interpolate(&str)
  eval "%{#{str.call}}", str.binding
end

.random_binary(n_bytes) ⇒ Object

Generate a random binary string of n_bytes size.

CREDIT: Guido De Rosa



6
7
8
# File 'lib/core/facets/string/random_binary.rb', line 6

def self.random_binary(n_bytes)
  ( Array.new(n_bytes){ rand(0x100) } ).pack('c*') 
end

Instance Method Details

#-(pattern) ⇒ Object

Removes occurances of a string or regexp.

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

CREDIT: Benjamin David Oakes



9
10
11
# File 'lib/core/facets/string/op_sub.rb', line 9

def -(pattern)
  self.gsub(pattern, '')
end

#/(path) ⇒ Object

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

('home' / 'trans')  #=> 'home/trans'


9
10
11
# File 'lib/core/facets/string/op_div.rb', line 9

def /(path)
  File.join(self, path.to_s)
end

#^(aString) ⇒ Object

Binary XOR of two strings.

a = "\000\000\001\001" ^ "\000\001\000\001"
b = "\003\003\003" ^ "\000\001\002"

a  #=> "\000\001\001\000"
b  #=> "\003\002\001"


11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/core/facets/string/xor.rb', line 11

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

#_cryptObject



3
# File 'lib/core-uncommon/facets/string/crypt.rb', line 3

alias_method :_crypt, :crypt

#acronymizeObject

CREDIT: Robert Fey



4
5
6
# File 'lib/core-uncommon/facets/string/acronym.rb', line 4

def acronymize
 gsub(/(([a-zA-Z0-9])([a-zA-Z0-9])*)./,"\\2")
end

#align(direction, n, sep = "\n", c = ' ') ⇒ Object

Alignment method dispatches to #align_right, #align_left or #align_center, accorging to the first direction parameter.

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

s.align(:right, 14)

produces

This is a test
           and
         so on

Returns a String aligned right, left or center.



21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/core/facets/string/align.rb', line 21

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 = ' ') ⇒ Object

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

s.align_center(14)

produces

This is a test
     and
    so on

CREDIT: Trans



116
117
118
119
120
121
122
# File 'lib/core/facets/string/align.rb', line 116

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 = ' ') ⇒ Object

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

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

produces

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

CREDIT: Trans



86
87
88
89
90
91
92
# File 'lib/core/facets/string/align.rb', line 86

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 = ' ') ⇒ Object

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

s.align_right(14)

produces

This is a test
           and
         so on

CREDIT: Trans



56
57
58
59
60
61
62
# File 'lib/core/facets/string/align.rb', line 56

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?Boolean

Is this string just whitespace?

"abc".blank?  #=> false
"   ".blank?  #=> true


74
75
76
# File 'lib/core/facets/kernel/blank.rb', line 74

def blank?
  /\S/ !~ self
end

#bracket(bra, ket = nil) ⇒ Object

Return a new string embraced by given brackets. 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



14
15
16
17
18
# 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) ⇒ Object

Inplace version of #bracket.

CREDIT: Trans



24
25
26
# File 'lib/core/facets/string/bracket.rb', line 24

def bracket!(bra, ket=nil)
  self.replace(bracket(bra, ket))
end

#bytes(&blk) ⇒ Object

Upacks string into bytes.

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



10
11
12
13
14
15
16
# 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) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/core/facets/string/camelcase.rb', line 21

def camelcase(first_letter=nil)
  case first_letter
  when :lower, true
    lower_camelcase
  when :inter, false
    inter_camelcase
  else
    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
end

#capitalized?Boolean

Return true if the string is capitalized, otherwise false.

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

Note Ruby’s strange concept of capitalized. See capitalcase for the more command conception.

CREDIT: Phil Tomson



14
15
16
# File 'lib/core/facets/string/capitalized.rb', line 14

def capitalized?
  capitalize == self
end

#charactersObject

Returns an array of characters.

"abc".characters.to_a  #=> ["a","b","c"]

TODO: Probably should make this an enumerator. With #scan?



8
9
10
# File 'lib/core/facets/string/characters.rb', line 8

def characters
  split(//)
end

#cleanlines(&block) ⇒ Object

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

"this\nthat\nother\n".cleanlines.to_a  #=> ['this', 'that', 'other']


11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/core/facets/string/cleanlines.rb', line 11

def cleanlines(&block)
  if block
    scan(/^.*?$/) do |line|
      block.call(line.strip)
    end
  else
    str = self
    Enumerator.new do |output|
      str.scan(/^.*?$/) do |line|
        output.yield(line.strip)
      end
    end
  end
end

#cleave(threshold = nil, len = nil) ⇒ Object

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

CREDIT: Trans



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 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) ⇒ Object

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

TODO: Move String#cmp to string/ directory.



34
35
36
37
38
# File 'lib/core/facets/comparable/cmp.rb', line 34

def cmp(other)
  return -1 if length < other.length
  return 1 if length > other.length
  self <=> other  # alphabetic compare
end

#compress_lines(spaced = true) ⇒ Object

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

string = <<-QUERY.compress_lines
  SELECT name
  FROM users
QUERY

string  #=> "SELECT name FROM users"


12
13
14
# File 'lib/core/facets/string/compress_lines.rb', line 12

def compress_lines(spaced = true)
  split($/).map{ |line| line.strip }.join(spaced ? ' ' : '')
end

#crypt(salt = nil) ⇒ Object

Common Unix cryptography method. This adds a default salt to the built-in crypt method.

NOTE: This is not (presently) a common core extension and is not loaded automatically when using require 'facets'.



11
12
13
14
15
16
17
# File 'lib/core-uncommon/facets/string/crypt.rb', line 11

def crypt(salt=nil)
  salt ||= (
    (rand(26) + (rand(2) == 0 ? 65 : 97) ).chr +
    (rand(26) + (rand(2) == 0 ? 65 : 97) ).chr
  )
  _crypt(salt)
end

#divide(re) ⇒ Object

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( /\<.*?\>/ )
#=> ["<p>This", "<b>is", "</b>a test.", "</p>"]

CREDIT: Trans



12
13
14
15
# File 'lib/core/facets/string/divide.rb', line 12

def divide( re )
  re2 = /#{re}.*?(?=#{re}|\Z)/
  scan(re2) #{re}(?=#{re})/)
end

#downcase?Boolean

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

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

CREDIT: Phil Tomson



26
27
28
# File 'lib/core/facets/string/capitalized.rb', line 26

def downcase?
  downcase == self
end

#each_charObject

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

a = ''
"HELLO".each_char{ |c| a << "#{c.downcase}" }
a  #=> 'hello'


14
15
16
17
# File 'lib/core/facets/string/each_char.rb', line 14

def each_char # :yield:
  scanner, char = StringScanner.new(self), /./mu
  loop{ yield(scanner.scan(char) || break) }
end

#each_word(&block) ⇒ Object

Iterate through each word of a string.

a = []

"list of words".each_word { |word| a << word }

a  #=> ['list', 'of', 'words']


13
14
15
# File 'lib/core/facets/string/each_word.rb', line 13

def each_word(&block)
  words.each(&block)
end

#edit_distance(str2) ⇒ Object

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



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/core/facets/string/edit_distance.rb', line 25

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) ⇒ Boolean Also known as: ends_with?

Does a string end with the given suffix?

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

Note: This definition is better than standard Ruby’s becuase it handles regular expressions.

CREDIT: Juris Galang



49
50
51
52
# File 'lib/core/facets/string/start_with.rb', line 49

def end_with?(suffix)
  suffix = Regexp.escape(suffix.to_s) unless Regexp===suffix
  /#{suffix}$/.match(self) ? true : false
end

#exclude?(str) ⇒ Boolean

The inverse of include?.



5
6
7
# File 'lib/core/facets/string/exclude.rb', line 5

def exclude?(str)
  !include?(str)
end

#expand_tabs(n = 8) ⇒ Object Also known as: expand_tab

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_tabs(2)  #=> "    Hey"

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

CREDIT: Gavin Sinclair, Noah Gibbs, GGaramuno

TODO: Don’t much care for the name String#expand_tabs. What about a more concise name like #detab?

Raises:

  • (ArgumentError)


16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/core/facets/string/expand_tab.rb', line 16

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

#fileObject

Use fluent notation for making file directives.

For instance, if we had a file ‘foo.txt’,

'foo.txt'.file.mtime


11
12
13
# File 'lib/core/facets/string/file.rb', line 11

def file
  Functor.new(&method(:file_send).to_proc)
end

#fold(ignore_indented = false) ⇒ Object

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



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 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 = ' ') ⇒ Object

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

CREDIT: Gavin Sinclair, Trans, Tyler Rick



8
9
10
11
12
13
14
# 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

#indent!(n, c = ' ') ⇒ Object

Equivalent to String#indent, but modifies the receiver in place.



18
19
20
# File 'lib/core/facets/string/indent.rb', line 18

def indent!(n, c=' ')
  replace(indent(n,c))
end

#index_all(s, reuse = false) ⇒ Object

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.



14
15
16
17
18
19
20
21
22
# 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

#inter_camelcaseObject



38
39
40
41
42
43
44
# File 'lib/core/facets/string/camelcase.rb', line 38

def inter_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

#lchomp(match) ⇒ Object

Left chomp.

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

CREDIT: Trans



10
11
12
13
14
15
16
# File 'lib/core/facets/string/lchomp.rb', line 10

def lchomp(match)
  if index(match) == 0
    self[match.size..-1]
  else
    self.dup
  end
end

#lchomp!(match) ⇒ Object

In-place left chomp.

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

CREDIT: Trans



25
26
27
28
29
30
# File 'lib/core/facets/string/lchomp.rb', line 25

def lchomp!(match)
  if index(match) == 0
    self[0...match.size] = ''
    self
  end
end

#line_wrap(width, tabs = 4) ⇒ Object

Line wrap at width.

s = "1234567890".line_wrap(5)

s  #=> "12345\n67890\n"

CREDIT: Trans



11
12
13
14
15
16
# File 'lib/core/facets/string/line_wrap.rb', line 11

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) ⇒ Object

Returns an array of characters.

"abc\n123".lines.to_a  #=> ["abc\n","123"]


9
10
11
12
13
14
15
# 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_camelcaseObject



53
54
55
56
57
58
59
# File 'lib/core/facets/string/camelcase.rb', line 53

def lower_camelcase
  #str = dup
  #str = dup
  #str.gsub!(/\/(.?)/){ "::#{$1.upcase}" }  # NOT SO SURE ABOUT THIS
  #str.gsub!(/(?:_+|-+)([a-z])/){ $1.upcase }
  inter_camelcase.gsub(/(\A|\s)([A-Z])/){ $1 + $2.downcase }
end

#lowercaseObject

Downcase first letter.



17
18
19
20
# 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) ⇒ Object

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



17
18
19
20
21
22
23
24
25
26
27
28
# 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

#methodizeObject

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

"My::CoolClass".methodize  #=> "my__cool_class"

– Rails definition …

gsub(/\//, '__').
gsub(/::/, '__').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr("-", "_").
downcase

++



18
19
20
21
22
23
24
# File 'lib/core/facets/string/methodize.rb', line 18

def methodize
  gsub(/([A-Z]+)([A-Z])/,'\1_\2').
  gsub(/([a-z])([A-Z])/,'\1_\2').
  gsub('/' ,'__').
  gsub('::','__').
  downcase
end

#modulizeObject

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"

– Rails definition …

gsub(/__(.?)/){ "::#{$1.upcase}" }.
gsub(/\/(.?)/){ "::#{$1.upcase}" }.
gsub(/(?:_+)([a-z])/){ $1.upcase }.
gsub(/(^|\s+)([a-z])/){ $1 + $2.upcase }

++



23
24
25
26
27
28
# File 'lib/core/facets/string/modulize.rb', line 23

def modulize
  gsub('__','/').
  gsub(/\/(.?)/){ "::#{$1.upcase}" }.
  gsub(/(?:_+|-+)([a-z])/){ $1.upcase }.
  gsub(/(\A|\s)([a-z])/){ $1 + $2.upcase }
end

#mscan(re) ⇒ Object

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

CREDIT: Trans



8
9
10
11
12
13
14
15
16
# 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) ⇒ Object

‘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.1".natcmp("Hello.10")  #=> -1

TODO: Invert case flag?

CREDIT: Alan Davies, Martin Pool



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/core/facets/string/natcmp.rb', line 45

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

#nchar(n, replacement = nil) ⇒ Object

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.

str = "this is text"

str.nchar(4)            #=> "this"
str.nchar(-4)           #=> "text"

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

str.nchar(4, 'that')    #=> "that is text"

The original string remains unaffected.

str  #=> "this is text"


21
22
23
24
25
26
27
28
29
# File 'lib/core/facets/string/nchar.rb', line 21

def nchar(n, replacement=nil)
  if replacement
    s = self.dup
    n > 0 ? (s[0...n] = replacement) : (s[n..-1] = replacement)
    s
  else
    n > 0 ? self[0...n] : self[n..-1]
  end
end

#newlines(&block) ⇒ Object

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

"a\nb\nc".newlines.class.assert == Enumerator
"a\nb\nc".newlines.to_a.assert == %w{a b c}

a = []
"a\nb\nc".newlines{|nl| a << nl}
a.assert == %w{a b c}


16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/core/facets/string/newlines.rb', line 16

def newlines(&block)
  if block
    scan(/^.*?$/) do |line|
      block.call(line.chomp)
    end
  else
    str = self
    Enumerator.new do |output|
      str.scan(/^.*?$/) do |line|
        output.yield(line.chomp)
      end
    end
  end
end

#object_state(data = nil) ⇒ Object



54
55
56
# File 'lib/core/facets/object/object_state.rb', line 54

def object_state(data=nil)
  data ? replace(data) : dup
end

#pathizeObject

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

"ExamplePathize".pathize           #=> "example_pathize"
"ExamplePathize::Example".pathize  #=> "example_pathize/example"


8
9
10
11
12
13
14
# File 'lib/core/facets/string/pathize.rb', line 8

def pathize
  gsub(/([A-Z]+)([A-Z])/,'\1_\2').
  gsub(/([a-z])([A-Z])/,'\1_\2').
  gsub('__','/').
  gsub('::','/').
  downcase
end

#quote(type = :double, count = nil) ⇒ Object

Return a new string embraced by given type and count of quotes. The arguments can be given in any order.

If no type is given, double quotes are assumed.

"quote me".quote     #=> '"quote me"'

If no type but a count is given then :mixed is assumed.

"quote me".quote(1)  #=> %q{'quote me'}
"quote me".quote(2)  #=> %q{"quote me"}
"quote me".quote(3)  #=> %q{'"quote me"'}

Symbols can be used to describe the type.

"quote me".quote(:single)    #=> %q{'quote me'}
"quote me".quote(:double)    #=> %q{"quote me"}
"quote me".quote(:back)      #=> %q{`quote me`}
"quote me".quote(:bracket)   #=> %q{`quote me'}

Or the character itself.

"quote me".quote("'")     #=> %q{'quote me'}
"quote me".quote('"')     #=> %q{"quote me"}
"quote me".quote("`")     #=> %q{`quote me`}
"quote me".quote("`'")    #=> %q{`quote me'}

CREDIT: Trans



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/core/facets/string/quote.rb', line 32

def quote(type=:double, count=nil)
  if Integer === type
    tmp   = count
    count = type
    type  = tmp || :mixed
  else
    count ||= 1
  end

  type = type.to_s unless Integer===type

  case type
  when "'", 'single', 's', 1
    f = "'" * count
    b = f
  when '"', 'double', 'd', 2
    f = '"' * count
    b = f
  when '`', 'back', 'b', -1
    f = '`' * count
    b = f
  when "`'", 'bracket', 'sb'
    f = "`" * count
    b = "'" * count
  when "'\"", 'mixed', "m", Integer
    c = (count.to_f / 2).to_i
    f = '"' * c
    b = f
    if count % 2 != 0
      f = "'" + f
      b = b + "'"
    end
  else
    raise ArgumentError, "unrecognized quote type -- #{type}"
  end
  "#{f}#{self}#{b}"
end

#range(pattern, offset = 0) ⇒ Object

Like #index but returns a Range.

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

CREDIT: Trans



9
10
11
12
13
14
15
16
17
18
# File 'lib/core/facets/string/range.rb', line 9

def range(pattern, offset=0)
  unless Regexp === pattern
    pattern = Regexp.new(Regexp.escape(pattern.to_s))
  end
  string = self[offset..-1]
  if md = pattern.match(string)
    return (md.begin(0)+offset)..(md.end(0)+offset-1)
  end
  nil
end

#range_all(pattern, reuse = false) ⇒ Object

Like #index_all but returns an array of Ranges.

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

TODO: Add offset ?

CREDIT: Trans



28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/core/facets/string/range.rb', line 28

def range_all(pattern, reuse=false)
  r = []; i = 0
  while i < self.length
    rng = range(pattern, i)
    if rng
      r << rng
      i += reuse ? 1 : rng.end + 1
    else
      break
    end
  end
  r.uniq
end

#range_of_lineObject

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



50
51
52
53
54
55
56
57
# File 'lib/core/facets/string/range.rb', line 50

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) ⇒ Object

Apply a set of rules in the form of regular expression matches to the string.

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

Keep in mind that the order of rules is significant.

Returns the rewritten String.

CREDIT: George Moschovitis

Raises:

  • (ArgumentError)


14
15
16
17
18
19
20
21
# File 'lib/core/facets/string/rewrite.rb', line 14

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

#romanObject

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

NOTE: This is not (presently) a common core extension and is not loaded automatically when using require 'facets'.



54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/core-uncommon/facets/integer/roman.rb', line 54

def roman
  roman = upcase
  raise unless roman?
  last = roman[-1,1]
  roman.reverse.split('').inject(0) do |result, c|
    if ROMAN_VALUES[c] < ROMAN_VALUES[last]
      result -= ROMAN_VALUES[c]
    else
      last = c
      result += ROMAN_VALUES[c]
    end
  end
end

#roman?Boolean

Returns true iif the subject is a valid Roman numeral.

NOTE: This is not (presently) a common core extension and is not loaded automatically when using require 'facets'.



72
73
74
# File 'lib/core-uncommon/facets/integer/roman.rb', line 72

def roman?
  ROMAN =~ upcase
end

#shatter(re) ⇒ Object

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



15
16
17
18
19
20
# 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) ⇒ Object

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".similarity("Aleksander")  #=> 0.9

The way it works:

  1. Converts each string into a “graph like” object, with edges …

    "alexsander" -> [ alexsander, alexsand, alexsan ... lexsand ... san ... an, etc ]
    "aleksander" -> [ aleksander, aleksand ... etc. ]
    
  2. 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 ]
  1. 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.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/core/facets/string/similarity.rb', line 28

def similarity(str_in)
  return 0 if str_in == nil
  return 1 if self == str_in

  # -- make a graph of each word (okay, 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{ |aa| matches.push(aa) if( graph_B.include?(aa) ) }
  #++

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

#snakecaseObject

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"


12
13
14
15
16
17
18
# 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) ⇒ Object

String#slice is essentially the same as #store.

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

But it acts like #slice! when given a single argument.

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

CREDIT: Trans



19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/core/facets/string/splice.rb', line 19

def splice(idx, sub=nil)
  if sub
    store(idx, sub)
  else
    case idx
    when Range
      slice!(idx)
    else
      slice!(idx,1)
    end
  end
end

#squishObject

Returns the string, first removing all whitespace on both ends of the string, and then changing remaining consecutive whitespace groups into one space each.

%{ Multi-line
   string }.squish                   # => "Multi-line string"

" foo   bar    \n   \t   boo".squish # => "foo bar boo"


11
12
13
# File 'lib/core/facets/string/squish.rb', line 11

def squish
  dup.squish!
end

#squish!Object

Performs a destructive squish. See String#squish.



16
17
18
19
20
# File 'lib/core/facets/string/squish.rb', line 16

def squish!
  strip!
  gsub!(/\s+/, ' ')
  self
end

#start_with?(prefix) ⇒ Boolean Also known as: starts_with?

Does a string start with the given prefix?

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

Note: This definition is better than standard Ruby’s becuase it handles regular expressions.

CREDIT: Juris Galang



30
31
32
33
# File 'lib/core/facets/string/start_with.rb', line 30

def start_with?(prefix)
  prefix = Regexp.escape(prefix.to_s) unless Regexp===prefix
  /^#{prefix}/.match(self) ? true : false
end

#tab(n) ⇒ Object

Aligns each line n spaces.

CREDIT: Gavin Sinclair



9
10
11
# File 'lib/core/facets/string/tab.rb', line 9

def tab(n)
  gsub(/^ */, ' ' * n)
end

#tabto(n) ⇒ Object

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

CREDIT: Gavin Sinclair



10
11
12
13
14
15
16
# File 'lib/core/facets/string/tabto.rb', line 10

def tabto(n)
  if self =~ /^( *)\S/
    indent(n - $1.length)
  else
    self
  end
end

#titlecaseObject

Title case.

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

CREDIT: Eliazar Parra



10
11
12
# File 'lib/core/facets/string/titlecase.rb', line 10

def titlecase
  gsub(/\b\w/){ $`[-1,1] == "'" ? $& : $&.upcase }
end

#to_bObject

Interpret common affirmative string meanings as true, otherwise nil or false. Blank space and case are ignored. The following strings that will return true …

true
yes
on
t
1
y
==

The following strings will return nil …

nil
null

All other strings return false.

Here are some exmamples.

"true".to_b   #=> true
"yes".to_b    #=> true
"no".to_b     #=> false
"123".to_b    #=> false


96
97
98
99
100
101
102
103
104
105
# File 'lib/core/facets/boolean.rb', line 96

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_dateObject

Parse data from string.



427
428
429
430
# File 'lib/standard/facets/date.rb', line 427

def to_date
  #::Date::civil(*ParseDate.parsedate(self)[0..2])
  ::Date.new(*::Date._parse(self, false).values_at(:year, :mon, :mday))
end

#to_datetimeObject

Convert string to DateTime.



421
422
423
424
# File 'lib/standard/facets/date.rb', line 421

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_re(esc = false) ⇒ Object

Turns a string into a regular expression.

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

CREDIT: Trans



9
10
11
# 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) ⇒ Object

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



21
22
23
# 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) ⇒ Object

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.



266
267
268
# File 'lib/supplemental/facets/tuple.rb', line 266

def to_t( &yld )
  Tuple.cast_from_string( self, &yld )
end

#to_time(form = :utc) ⇒ Object



416
417
418
# File 'lib/standard/facets/date.rb', line 416

def to_time(form = :utc)
  ::Time.__send__("#{form}_time", *::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec).map{|arg| arg || 0 })
end

#unbracket(bra = nil, ket = nil) ⇒ Object

Return a new string with the given brackets removed. If only one bracket char is given it will be removed from either side.

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

CREDIT: Trans



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 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) ⇒ Object

Inplace version of #unbracket.

CREDIT: Trans



57
58
59
# File 'lib/core/facets/string/bracket.rb', line 57

def unbracket!(bra=nil, ket=nil)
  self.replace( unbracket(bra, ket) )
end

#underscoreObject

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"


13
14
15
16
17
18
19
# File 'lib/core/facets/string/underscore.rb', line 13

def underscore
  gsub(/::/, '/').
  gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
  gsub(/([a-z\d])([A-Z])/,'\1_\2').
  tr("-", "_").
  downcase
end

#unfoldObject

Unfold paragrpahs.

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



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 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

#unindent(size = nil) ⇒ Object

Remove excessive indentation. Useful for multi-line strings embeded in already indented code.

<<-END.unindent
    ohaie
      wurld
END

Outputs …

ohaie
  wurld

Instead of …

ohaie
  wurld

CREDIT: Noah Gibbs, mynyml



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/core/facets/string/indent.rb', line 42

def unindent(size=nil)
  if size
    indent(-size)
  else
    char = ' '
    self.scan(/^[\ \t]*\S/) do |m|
      if size.nil? || m.size < size
        size = m.size
        char = m[0...-1]
      end
    end
    size -= 1
    indent(-size, char)
  end
end

#unindent!Object

Equivalent to String#unindent, but modifies the receiver in place.

CREDIT: mynyml



62
63
64
# File 'lib/core/facets/string/indent.rb', line 62

def unindent!
  self.replace(self.unindent)
end

#unquoteObject

Remove quotes from string.

"'hi'".unquote    #=> "hi"

CREDIT: Trans



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/core/facets/string/quote.rb', line 76

def unquote
  s = self.dup

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

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

  return s
end

#upcase?Boolean

Is the string upcase/uppercase?

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

CREDIT: Phil Tomson



41
42
43
# File 'lib/core/facets/string/capitalized.rb', line 41

def upcase?
  upcase == self
end

#upper_camelcaseObject



46
47
48
49
50
51
# File 'lib/core/facets/string/camelcase.rb', line 46

def upper_camelcase
  #str = dup
  #str.gsub!(/\/(.?)/){ "::#{$1.upcase}" }  # NOT SO SURE ABOUT THIS
  #str.gsub!(/(?:_+|-+)([a-z])/){ $1.upcase }
  inter_camelcase.gsub(/(\A|\s)([a-z])/){ $1 + $2.upcase }
end

#uppercaseObject

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.



10
11
12
13
# File 'lib/core/facets/string/uppercase.rb', line 10

def uppercase
  str = to_s
  str[0,1].upcase + str[1..-1]
end

#variablizeObject

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



7
8
9
10
# File 'lib/core/facets/string/variablize.rb', line 7

def variablize
  v = gsub(/\W/, '_')
  "@#{v}"
end

#word_wrap(col_width = 80) ⇒ Object

Word wrap a string not exceeding max width.

"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



18
19
20
# 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) ⇒ Object

As with #word_wrap, but modifies the string in place.

CREDIT: Gavin Kistner, Dayne Broderson



26
27
28
29
30
# 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

#wordsObject

Returns an array of characters.

"abc 123".words  #=> ["abc","123"]


7
8
9
# File 'lib/core/facets/string/words.rb', line 7

def words
  self.split(/\s+/)
end