Class: String

Inherits:
Object show all
Includes:
Indexable, Stackable, Style
Defined in:
lib/more/facets/tuple.rb,
lib/more/facets/crypt.rb,
lib/more/facets/random.rb,
lib/core/facets/boolean.rb,
lib/more/facets/stylize.rb,
lib/more/facets/snapshot.rb,
lib/more/facets/typecast.rb,
lib/core/facets/string/op.rb,
lib/core/facets/conversion.rb,
lib/core/facets/string/case.rb,
lib/core/facets/string/scan.rb,
lib/core/facets/string/tabs.rb,
lib/core/facets/string/align.rb,
lib/core/facets/string/blank.rb,
lib/core/facets/string/crypt.rb,
lib/core/facets/string/nchar.rb,
lib/core/facets/string/range.rb,
lib/core/facets/string/filter.rb,
lib/core/facets/string/format.rb,
lib/core/facets/string/natcmp.rb,
lib/core/facets/string/regesc.rb,
lib/core/facets/string/splice.rb,
lib/core/facets/comparable/cmp.rb,
lib/core/facets/string/bracket.rb,
lib/core/facets/string/indexable.rb,
lib/core/facets/string/stackable.rb,
lib/core/facets/string/partitions.rb,
lib/core/facets/string/interpolate.rb

Overview

Compare method that takes length into account.

Defined Under Namespace

Modules: Style

Constant Summary collapse

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

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Indexable

#body, #ends, #foot, #head, #index_of, #mid, #middle, #pos, #tail, #thru

Methods included from Style

#basename, #camelize, #humanize, #methodize, #modulize, #ordinalize, #pathize, #title, #underscore

Class Method Details

.cast_from(object) ⇒ Object



179
180
181
182
183
184
# File 'lib/more/facets/typecast.rb', line 179

def cast_from(object)
  return super
rescue TypeCastException
  return object.to_s if object.respond_to? :to_s
  raise
end

.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


17
18
19
# File 'lib/core/facets/string/interpolate.rb', line 17

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

.rand_letterObject

Module method to generate a random letter.

String::Random.rand_letter  #=> "q"
String::Random.rand_letter  #=> "r"
String::Random.rand_letter  #=> "a"


297
298
299
# File 'lib/more/facets/random.rb', line 297

def self.rand_letter
  (rand(26) + (rand(2) == 0 ? 65 : 97) ).chr
end

.random(max_length = 8, char_re = /[\w\d]/) ⇒ Object

Returns a randomly generated string. One possible use is password initialization. Takes a max legnth of characters (default 8) and an optional valid char Regexp (default /wd/).

– CREDIT George Moschovitis

NOTE This is not very efficient. Better way? ++

Raises:

  • (ArgumentError)


278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/more/facets/random.rb', line 278

def self.random(max_length = 8, char_re = /[\w\d]/)
  # gmosx: this is a nice example of input parameter checking.
  # this is NOT a real time called method so we can add this
  # check. Congrats to the author.
  raise ArgumentError.new('char_re must be a regular expression!') unless char_re.is_a?(Regexp)
  string = ""
  while string.length < max_length
      ch = rand(255).chr
      string << ch if ch =~ char_re
  end
  return string
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.rb', line 9

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

#^(aString) ⇒ Object



414
415
416
417
418
419
420
421
422
423
424
425
426
# File 'lib/more/facets/crypt.rb', line 414

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/facets/string/crypt.rb', line 3

alias_method :_crypt, :crypt

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

Centers each line of a string.

The defualt alignment seperation is a new line (“/n”) This can be changed as can be the padding string which defaults to a single space (‘ ’).

s = "  This is a test\n  and\n  so on\n"

puts s.align_center(14)

produces

This is a test
     and
    so on

CREDIT: Trans


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

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 defualt alignment seperation is a new line (“/n”) This can be changes as can be the padding string which defaults to a single space (‘ ’).

s = "This is a test\n  and\n  so on\n"

puts s.align_left(2)

produces

  This is a test
  and
  so on

CREDIT: Trans


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

def align_left(n, sep="\n", c=' ')
  return ljust(n.to_i,c.to_s) if sep==nil
  q = split(sep.to_s).collect { |line|
    line.ljust(n.to_i,c.to_s)
  }
  q.join(sep.to_s)
end

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

Align a string to the right. The defualt alignment seperation is a new line (“/n”) This can be changes as can be the padding string which defaults to a single space (‘ ’).

s = "This is a test\n  and\n  so on\n"

puts s.align_right(2)

produces

  This is a test
             and
           so on

CREDIT: Trans


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

def align_right(n, sep="\n", c=' ')
  return rjust(n.to_i,c.to_s) if sep==nil
  q = split(sep.to_s).collect { |line|
    line.rjust(n.to_i,c.to_s)
  }
  q.join(sep.to_s)
end

#at(index) ⇒ Object

An extraneous feature, but make String more ploymorphic with Array.

"HELLO".at(2)  #=> "L"


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

def at(index)
  case index
  when Fixnum
    self[index].chr
  else
    self[index]
  end
end

#at_rand(separator = //) ⇒ Object

Return a random separation of the string. Default separation is by charaacter.

"Ruby rules".at_rand(' ')  #=> ["Ruby"]


306
307
308
309
# File 'lib/more/facets/random.rb', line 306

def at_rand( separator=// )
  #separator = self.class.patterns( separator )
  self.split(separator,-1).at_rand
end

#at_rand!(separator = //) ⇒ Object

Return a random separation while removing it from the string. Default separation is by character.

s = "Ruby rules"
s = at_rand!(' ')  #=> "Ruby"
s                  #=> "rules"


318
319
320
321
322
323
324
325
326
# File 'lib/more/facets/random.rb', line 318

def at_rand!( separator=// )
  #separator = self.class.patterns( separator )
  a = self.shatter( separator )
  w = []; a.each_with_index { |s,i| i % 2 == 0 ? w << s : w.last << s }
  i = rand( w.size )
  r = w.delete_at( i )
  self.replace( w.join('') )
  return r
end

#blank?Boolean Also known as: whitespace?

Is this string just whitespace?

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

CREDIT: ?

Returns:

  • (Boolean)


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

def blank?
  self !~ /\S/
end

#bracket(bra, ket = nil) ⇒ Object

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


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

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

#brief(count = 128, force_cutoff = false, ellipsis = "...") ⇒ Object

Returns short abstract of long strings (first ‘count’ characters, chopped at the nearest word, appended by ‘…’) force_cutoff: break forcibly at ‘count’ chars. Does not accept count < 2

CREDIT: George Moschovitis


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

def brief(count = 128, force_cutoff = false, ellipsis="...")
  return nil if count < 2

  if size > count
    cut_at = force_cutoff ? count : (index(' ', count-1) || count)
    xstring = slice(0, cut_at)
    return xstring.chomp(" ") + ellipsis
  else
    return self
  end
end

#bytesObject

Upacks string into bytes.



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

def bytes
  self.unpack('C*')
end

#camelcase(first = false, on = '_\s') ⇒ Object

Converts a string to camelcase. By default capitalization occurs on whitespace and underscores. By setting the first parameter to true the first character can also be captizlized. The second parameter can be assigned a valid Regualr Expression characeter set to determine which characters to match for capitalizing subsequent parts of the string.

"this_is a test".camelcase             #=> "thisIsATest"
"this_is a test".camelcase(true)       #=> "ThisIsATest"
"this_is a test".camelcase(true, ' ')  #=> "This_isATest"

CREDIT: Trans


70
71
72
73
74
75
76
# File 'lib/core/facets/string/case.rb', line 70

def camelcase(first=false, on='_\s')
  if first
    gsub(/(^|[#{on}]+)([A-Za-z])/){ $2.upcase }
  else
    gsub(/([#{on}]+)([A-Za-z])/){ $2.upcase }
  end
end

#capitalized?Boolean

Return true if the string is capitalized, otherwise false.

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

CREDIT: Phil Tomson

Returns:

  • (Boolean)


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

def capitalized?
  self =~ /^[A-Z]/
end

#charsObject

Returns an array of characters.

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


44
45
46
# File 'lib/core/facets/string/partitions.rb', line 44

def chars
  self.split(//)
end

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

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

CREDIT: Trans


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
# File 'lib/core/facets/string/format.rb', line 27

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


37
38
39
40
41
# File 'lib/core/facets/comparable/cmp.rb', line 37

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

#crypt(salt = nil) ⇒ Object

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



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

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

#crypt!(salt = nil) ⇒ Object

Common Unix cryptography in-place method.



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

def crypt!(salt=nil)
  replace(crypt(salt))
end

#dequoteObject

Remove quotes from string.

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

CREDIT: Trans


88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# 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) ⇒ 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( /\<.*?\>/ )

produces

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

CREDIT: Trans


15
16
17
18
# File 'lib/core/facets/string/scan.rb', line 15

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

#downcase?Boolean Also known as: lowercase?

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

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

CREDIT: Phil Tomson

Returns:

  • (Boolean)


23
24
25
# File 'lib/core/facets/string/case.rb', line 23

def downcase?
  downcase == self
end

#each_charObject Also known as: each_character

Iterates through each character. This is a little faster than using #chars b/c it does not create the intermediate array.

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


28
29
30
31
32
# File 'lib/core/facets/string/partitions.rb', line 28

def each_char  # :yield:
  size.times do |i|
    yield(self[i,1])
  end
end

#each_word(&yld) ⇒ Object

Iterate through each word of a string.

"a string".each_word { |word, range| ... }


60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/core/facets/string/partitions.rb', line 60

def each_word( &yld )
  rest_of_string = self
  wordfind = /([-'\w]+)/
  arity = yld.arity
  offset = 0
  while wmatch = wordfind.match(rest_of_string)
    word = wmatch[0]
    range = offset+wmatch.begin(0) ... offset+wmatch.end(0)
    rest_of_string = wmatch.post_match
    if arity == 1
      yld.call(word)
    else
      yld.call(word, range)
    end
    offset = self.length - rest_of_string.length
  end
end

#ends_with?(suffix) ⇒ Boolean

Does a string end with the given suffix?

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

CREDIT: Lucas Carlson
CREDIT: Blaine Cook

Returns:

  • (Boolean)


23
24
25
# File 'lib/core/facets/string/nchar.rb', line 23

def ends_with?(suffix)
  self.rindex(suffix) == size - suffix.size
end

#expand_tabs(n = 8) ⇒ Object

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

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

CREDIT: Gavin Sinclair
CREDIT: Noah Gibbs
CREDIT: GGaramuno

Raises:

  • (ArgumentError)


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

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

#first(pattern = //) ⇒ Object

Returns the first separation of a string. Default seperation is by character.

"Hello World".first       #=> "H"
"Hello World".first(' ')  #=> "Hello"


58
59
60
61
62
63
64
65
# File 'lib/core/facets/string/indexable.rb', line 58

def first(pattern=//)
  case pattern
  when Regexp, String
    split(pattern).at(0)
  else
    super
  end
end

#first!(pattern = //) ⇒ Object

Removes the first separation from a string. Defualt separation is by characters. – If a zero-length record separator is supplied, the string is split on /n+/. If the record separator is set to nil, then the string is split on characters. ++

a = "Hello World"
a.first!       #=> "H"
a              #=> "ello World"

a = "Hello World"
a.first!(' ')  #=> "Hello"
a              #=> "World"


100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/core/facets/string/indexable.rb', line 100

def first!(pattern=//)
  case pattern
  when Regexp, String
    a = shatter(pattern) # req. scan
    r = a.first
    a.shift
    a.shift
    replace( a.join('') )
    return r
  else
    super
  end
end

#first=(x) ⇒ Object

Prepends to a string.

"Hello World".first = "Hello,"  #=> "Hello, Hello World"


152
153
154
# File 'lib/core/facets/string/indexable.rb', line 152

def first=(x)
  insert(0, x.to_s)
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


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/core/facets/string/format.rb', line 71

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

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

CREDIT: Gavin Sinclair
CREDIT: Trans


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

def indent(n)
  if n >= 0
    gsub(/^/, ' ' * n)
  else
    gsub(/^ {0,#{-n}}/, "")
  end
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.


34
35
36
37
38
39
40
41
42
# File 'lib/core/facets/string/indexable.rb', line 34

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

#last(pattern = //) ⇒ Object

Returns the last separation of a string. Default separation is by character.

"Hello World".last(' ')  #=> "World"


73
74
75
76
77
78
79
80
# File 'lib/core/facets/string/indexable.rb', line 73

def last(pattern=//)
  case pattern
  when Regexp, String
    split(pattern).at(-1)
  else
    super
  end
end

#last!(pattern = //) ⇒ Object

Removes the last separation from a string. Default seperation is by characeter. – If a zero-length record separator is supplied, the string is split on /n+/. If the record separator is set to nil, then the string is split on characters. ++

a = "Hello World"
a.last!       #=> "d"
a             #=> "Hello Worl"

a = "Hello World"
a.last!(' ')  #=> "World"
a             #=> "Hello"


132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/core/facets/string/indexable.rb', line 132

def last!(pattern=//)
  case pattern
  when Regexp, String
    a = shatter(pattern) #req. scan
    r = a.last
    a.pop
    a.pop
    replace(a.join(''))
    return r
  else
    super
  end
end

#last=(str) ⇒ Object

Appends to a string.

"Hello World".last = ", Bye."  #=>  "Hello World, Bye."


161
162
163
# File 'lib/core/facets/string/indexable.rb', line 161

def last=(str)
  self << str
end

#lchomp(match) ⇒ Object

Left chomp.

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

CREDIT: Trans


58
59
60
61
62
63
64
# File 'lib/core/facets/string/nchar.rb', line 58

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


73
74
75
76
77
78
# File 'lib/core/facets/string/nchar.rb', line 73

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

#line_wrap(width, tabs = 4) ⇒ Object

Line wrap at width.

puts "1234567890".line_wrap(5)

produces

 12345
 67890

CREDIT: Trans


101
102
103
104
105
106
# File 'lib/core/facets/string/format.rb', line 101

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

#linesObject

Returns an array of characters.

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


17
18
19
# File 'lib/core/facets/string/partitions.rb', line 17

def lines
  self.split(/\n/)
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.

CREDIT: Trans


86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/core/facets/string/tabs.rb', line 86

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

#mscan(re) ⇒ Object

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

CREDIT: Trans


25
26
27
28
29
30
31
32
33
# File 'lib/core/facets/string/scan.rb', line 25

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

TODO: Invert case flag?

CREDIT: Alan Davies
CREDIT: Martin Pool

– Adapted from:

http://sourcefrog.net/projects/natsort/natcmp.rb

Based on Martin Pool’s “Natural Order String Comparison” originally written in C. (see sourcefrog.net/projects/natsort/)

This implementation is Copyright © 2003 by Alan Davies <cs96and_AT_yahoo_DOT_co_DOT_uk>

This software is provided ‘as-is’, without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.

Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.

  2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.

  3. This notice may not be removed or altered from any source distribution.

++



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/natcmp.rb', line 47

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

Retrns 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"

CREDIT: ?


41
42
43
44
45
46
47
48
49
# File 'lib/core/facets/string/nchar.rb', line 41

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

#outdent(n) ⇒ Object

Outdent just indents a negative number of spaces.

CREDIT: Noah Gibbs



69
70
71
# File 'lib/core/facets/string/tabs.rb', line 69

def outdent(n)
  indent(-n)
end

#peekObject

Peek at top of string.



47
48
49
# File 'lib/core/facets/string/stackable.rb', line 47

def peek
  slice(-1,1)
end

#poke(str = ' ') ⇒ Object

Like push but works from the other end of the string.



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

def poke(str=' ')
  insert(0, str)
end

#popObject

Polymorphic with Array of characters.



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

def pop
  return '' if size == 0
  r = slice(-1,1)
  self[-1] = ''
  r #self
end

#pullObject

Same as #shift.



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

def pull
  return '' if size == 0
  self[0] = ''
  self
end

#push(str = ' ') ⇒ Object

Polymorphic with Array of characters.



35
36
37
# File 'lib/core/facets/string/stackable.rb', line 35

def push(str=' ')
  concat(str)
end

#quote(type = :s) ⇒ Object

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


69
70
71
72
73
74
75
76
77
78
79
80
# 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

#rand_byteObject

Return a random byte of self.

"Ruby rules".rand_byte  #=> 121


332
333
334
# File 'lib/more/facets/random.rb', line 332

def rand_byte
  self[rand( size )]
end

#rand_byte!Object

Destructive rand_byte. Delete a random byte of self and return it.

s = "Ruby rules"
s.rand_byte!      #=> 121
s                 #=> "Rub rules"


342
343
344
345
346
347
# File 'lib/more/facets/random.rb', line 342

def rand_byte!
  i = rand( size )
  rv = self[i,1]
  self[i,1] = ''
  rv
end

#rand_indexObject

Return a random string index.

"Ruby rules".rand_index  #=> 3


353
354
355
# File 'lib/more/facets/random.rb', line 353

def rand_index
  rand( size )
end

#range(s, 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
# 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) ⇒ Object

Like #index_all but returns an array of Ranges.

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

TODO: Add offset, perhaps ?

CREDIT: Trans


24
25
26
27
28
29
30
31
32
33
34
35
36
# 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_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


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

def range_of_line
  offset=0; charmap = []
  self.each do |line|
    charmap << (offset..(offset + line.length - 1))
    offset += line.length
  end
  charmap
end

#regescObject

Escape string for Regexp use.

"H..LO".regesc  #=> "H\\.\\.LO"

CREDIT: Trans


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

def regesc
  Regexp.escape(self)
end

#restore_snapshot(snap) ⇒ Object



164
# File 'lib/more/facets/snapshot.rb', line 164

def restore_snapshot(snap) replace(snap) end

#rewrite(string, rules) ⇒ Object

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

Raises:

  • (ArgumentError)


18
19
20
21
22
23
24
25
26
27
# File 'lib/core/facets/string/filter.rb', line 18

def rewrite(string, rules)
  return nil unless string
  # gmosx: helps to find bugs
  raise ArgumentError.new('The rules parameter is nil') unless rules
  rewritten_string = string.dup
  rules.each do |match,write|
    rewritten_string.gsub!(match,write)
  end
  return (rewritten_string or string)
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


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

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

#shiftObject



61
62
63
64
65
# File 'lib/core/facets/string/stackable.rb', line 61

def shift
  return '' if size == 0
  self[0] = ''
  self
end

#shuffle(separator = //) ⇒ Object

Return the string with seperated sections arranged in a random order. The default seperation is by character.

"Ruby rules".shuffle  #=> "e lybRsuur"


362
363
364
# File 'lib/more/facets/random.rb', line 362

def shuffle(separator=//)
  split(separator).shuffle.join('')
end

#shuffle!(separator = //) ⇒ Object

In place version of shuffle.



368
369
370
# File 'lib/more/facets/random.rb', line 368

def shuffle!(separator=//)
  self.replace( shuffle(separator) )
end

#snakecaseObject

Snakecase (underscore) string based on camelcase characteristics.



80
81
82
# File 'lib/core/facets/string/case.rb', line 80

def snakecase #(camel_cased_word)
  gsub(/([A-Z]+)([A-Z])/,'\1_\2').gsub(/([a-z])([A-Z])/,'\1_\2').downcase
end

#splice(*args) ⇒ Object

Like #slice, but writes rather than reads. Like #store, but acts like slice! when given only one argument.

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

CREDIT: Trans


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

def splice(*args)
  if args.size == 1
    slice!(*args)
  else
    store(*args)
  end
end

#starts_with?(prefix) ⇒ Boolean

Does a string start with the given prefix.

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

CREDIT: Lucas Carlson
CREDIT: Blaine Cook

Returns:

  • (Boolean)


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

def starts_with?(prefix)
  self.index(prefix) == 0
end

#succ(n = 1) ⇒ Object

Allows #succ to take n step increments.

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

CREDIT Trans


51
52
53
54
55
# File 'lib/core/facets/comparable/cmp.rb', line 51

def succ(n=1)
  s = self
  n.times { s = s.next }
  s
end

#tab(n) ⇒ Object Also known as: taballto

Aligns each line n spaces.

CREDIT: Gavin Sinclair


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

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


43
44
45
46
47
48
49
# File 'lib/core/facets/string/tabs.rb', line 43

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

#take_snapshotObject



163
# File 'lib/more/facets/snapshot.rb', line 163

def take_snapshot() dup end

#titlecaseObject

Title case.

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

CREDIT: Eliazar Parra


52
53
54
# File 'lib/core/facets/string/case.rb', line 52

def titlecase
  gsub(/\b\w/){$&.upcase}
end

#to_bObject

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


48
49
50
51
52
53
54
55
56
57
# File 'lib/core/facets/boolean.rb', line 48

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_constObject

Get a constant by a given string name.

"Class".to_const   #=> Class

Note this method is not as verstile as it should be, since it can not access contants relative to the current execution context. But without a binding_of_caller that does not seem possible.



328
329
330
# File 'lib/core/facets/conversion.rb', line 328

def to_const
  split('::').inject(Object){ |namespace,name| namespace.const_get(name) }
end

#to_dateObject

Parse data from string.

CREDIT: Trans


336
337
338
339
340
# File 'lib/core/facets/conversion.rb', line 336

def to_date
  require 'date'
  require 'parsedate'
  ::Date::civil(*ParseDate.parsedate(self)[0..2])
end

#to_proc(context = nil) ⇒ Object

Evaluates a String as a Proc.

xyp = "|x,y| x + y".to_proc
xyp.class      #=> Proc
xyp.call(1,2)  #=> 3

NOTE: Sure would be nice if this could grab the caller's context!

CREDIT: Trans


384
385
386
387
388
389
390
391
392
393
394
# File 'lib/core/facets/conversion.rb', line 384

def to_proc(context=nil)
  if context
    if context.kind_of?(Binding) or context.kind_of?(Proc)
      Kernel.eval "proc { #{self} }", context
    else #context
      context.instance_eval "proc { #{self} }"
    end
  else
    Kernel.eval "proc { #{self} }"
  end
end

#to_re(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_re  #=> /\[/

CREDIT: Trans


359
360
361
# File 'lib/core/facets/conversion.rb', line 359

def to_re(esc=true)
  Regexp.new((esc ? Regexp.escape(self) : self))
end

#to_rxObject

Turns a string into a regular expression. Unlike #to_re this will not escape characters.

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

CREDIT: Trans


370
371
372
# File 'lib/core/facets/conversion.rb', line 370

def to_rx
  Regexp.new(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.



294
295
296
# File 'lib/more/facets/tuple.rb', line 294

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

#to_timeObject

Parse string to time.

CREDIT: Trans


346
347
348
349
# File 'lib/core/facets/conversion.rb', line 346

def to_time
  require 'time'
  Time.parse(self)
end

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

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


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

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

#unshift(str = ' ') ⇒ Object



69
70
71
# File 'lib/core/facets/string/stackable.rb', line 69

def unshift(str=' ')
  insert(0, str)
end

#upcase?Boolean Also known as: uppercase?

Is the string upcase/uppercase?

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

CREDIT: Phil Tomson

Returns:

  • (Boolean)


38
39
40
# File 'lib/core/facets/string/case.rb', line 38

def upcase?
  self.upcase == self
end

#word_filter(&blk) ⇒ Object

Filters out words from a string based on block test.

"a string".word_filter { |word| word =~ /^a/ }  #=> "string"

CREDIT: George Moschovitis


35
36
37
38
# File 'lib/core/facets/string/filter.rb', line 35

def word_filter( &blk )
  s = self.dup
  s.word_filter!( &blk )
end

#word_filter!Object

In place version of #word_filter.

"a string".word_filter { |word| ... }

CREDIT: George Moschovitis


46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/core/facets/string/filter.rb', line 46

def word_filter! #:yield:
  rest_of_string = self
  wordfind = /(\w+)/
  offset = 0
  while wmatch = wordfind.match(rest_of_string)
    word = wmatch[0]
    range = offset+wmatch.begin(0) ... offset+wmatch.end(0)
    rest_of_string = wmatch.post_match
    self[range] = yield( word ).to_s
    offset = self.length - rest_of_string.length
  end
  self
end

#word_wrap(col_width = 80) ⇒ Object

Word wrap a string not exceeding max width.

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

produces

this
is a
test

CREDIT: Gavin Kistner
CREDIT: Dayne Broderson


150
151
152
# File 'lib/core/facets/string/format.rb', line 150

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
CREDIT: Dayne Broderson


159
160
161
162
163
# File 'lib/core/facets/string/format.rb', line 159

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"]


52
53
54
# File 'lib/core/facets/string/partitions.rb', line 52

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