Module: VV::StringMethods

Included in:
String
Defined in:
lib/vv/string_methods.rb

Defined Under Namespace

Modules: ClassMethods, SharedInstanceAndClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
# File 'lib/vv/string_methods.rb', line 4

def self.included(base)
  base.instance_eval do
    extend(ClassMethods)
    extend(SharedInstanceAndClassMethods)
    include(SharedInstanceAndClassMethods)

    alias_method :starts_with?, :start_with?
    alias_method :ends_with?,   :end_with?
    alias_method :includes?,    :include?
  end
end

Instance Method Details

#_ensure_pluralize_available!Object

Raises:

  • (NotImplementedError)


466
467
468
469
470
# File 'lib/vv/string_methods.rb', line 466

def _ensure_pluralize_available!
  message = "String does not define pluralize."
  pluralize_available = self.respond_to? :pluralize
  raise NotImplementedError, message unless pluralize_available
end

#_ensure_singularize_available!Object

Raises:

  • (NotImplementedError)


472
473
474
475
476
# File 'lib/vv/string_methods.rb', line 472

def _ensure_singularize_available!
  message = "String does not define singularize."
  singularize_available = self.respond_to? :singularize
  raise NotImplementedError, message unless singularize_available
end

#_numeral_postfixesObject



379
380
381
382
383
384
# File 'lib/vv/string_methods.rb', line 379

def _numeral_postfixes
  { k: 1000,
    m: 1000_000,
    b: 1000_000_000,
    t: 1000_000_000_000 }.stringify_keys
end

#after(string, safe: true) ⇒ Object



122
123
124
125
126
127
128
129
130
131
# File 'lib/vv/string_methods.rb', line 122

def after(string, safe: true)
  if safe && ! self.starts_with?(string)
    message = "String does not start with #{string}"
    raise RuntimeError, message
  elsif not self.starts_with?(string)
    return self
  end

  self[string.size..-1]
end

#cli_print(width: nil, padding: nil, position: nil, hard_wrap: false) ⇒ Object

Raises:

  • (NotImplemented)


592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
# File 'lib/vv/string_methods.rb', line 592

def cli_print width: nil,
              padding: nil,
              position: nil,
              hard_wrap: false

  width    ||= 80
  position ||= 0
  padding  ||= 0

  raise NotImplemented if hard_wrap
  raise NotImplemented if self.includes? newline

  pad_length = padding - position
  position += pad_length
  print pad_length.spaces

  unstyled_length = self.unstyled.length
  remaining_length = width - position
  if unstyled_length <= remaining_length
    print self
    position += unstyled_length
    return position
  end

  space_index   = self[0..remaining_length].rindex(" ")
  space_index ||= self.index(" ")

  if space_index
    sub = self[0..space_index]
    print sub
    puts
    position = 0
    start = space_index + 1
    return self[start..-1].cli_print width: width,
                                     padding: padding,
                                     position: position,
                                     hard_wrap: hard_wrap
  else
    print self
    puts
    position = 0
  end

  return position
end

#cli_puts(**kwargs) ⇒ Object



588
589
590
# File 'lib/vv/string_methods.rb', line 588

def cli_puts **kwargs
  puts String.get_stdout { self.cli_print( **kwargs ) }
end

#eighthObject



576
577
578
# File 'lib/vv/string_methods.rb', line 576

def eighth
  self[7]
end

#fifthObject



564
565
566
# File 'lib/vv/string_methods.rb', line 564

def fifth
  self[4]
end

#file_join(*args) ⇒ Object



274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/vv/string_methods.rb', line 274

def file_join *args
  unsafe = args.reject(&:safe_path?)

  return File.join self, *args if unsafe.blank?

  frags = unsafe.first(3).stringify_collection grave: true
  count = unsafe.count

  message = \
  "#{count} unsafe path fragments including: #{frags}"

  fail ArgumentError, message
end

#first(limit = 1) ⇒ Object



542
543
544
545
546
547
548
549
550
# File 'lib/vv/string_methods.rb', line 542

def first limit=1
  if limit == 0
    ""
  elsif limit >= size
    dup
  else
    to(limit - 1)
  end
end

#format!(other) ⇒ Object



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/vv/string_methods.rb', line 191

def format!(other)
  mappings = {}
  self.split('#{')[1..-1].map do | token_fragment |
    format_string = token_fragment.split("}").first
    token = format_string.squish

    value = other.instance_variable_get(token).to_s
    wrapped_format_string = '#{' + format_string + "}"
    mappings[wrapped_format_string] = value
  end

  response = self.dup

  mappings.each do |key, value|
    response.gsub! key, value
  end

  response
end

#fourthObject



560
561
562
# File 'lib/vv/string_methods.rb', line 560

def fourth
  self[3]
end

#from(position) ⇒ Object



462
463
464
# File 'lib/vv/string_methods.rb', line 462

def from position
  self[position..-1]
end

#hex?Boolean

Returns:

  • (Boolean)


288
289
290
291
292
# File 'lib/vv/string_methods.rb', line 288

def hex?
  return false if self.blank?
  match_non_hex_digits = /\H/
  !self[match_non_hex_digits]
end

#instaObject



440
441
442
443
# File 'lib/vv/string_methods.rb', line 440

def insta
  return self if self.starts_with?("@")
  "@#{self}"
end

#insta_symObject



445
446
447
# File 'lib/vv/string_methods.rb', line 445

def insta_sym
  self.insta.to_sym
end

#is_directory_path?Boolean

Returns:

  • (Boolean)


266
267
268
# File 'lib/vv/string_methods.rb', line 266

def is_directory_path?
  File.directory? self
end

#is_file_path?Boolean

Returns:

  • (Boolean)


270
271
272
# File 'lib/vv/string_methods.rb', line 270

def is_file_path?
  File.file? self
end

#last(limit = 1) ⇒ Object



532
533
534
535
536
537
538
539
540
# File 'lib/vv/string_methods.rb', line 532

def last(limit = 1)
  if limit == 0
    ""
  elsif limit >= size
    self.dup
  else
    self.from(-limit)
  end
end

#matches_glob(pattern) ⇒ Object



211
212
213
# File 'lib/vv/string_methods.rb', line 211

def matches_glob pattern
  File.fnmatch(pattern, self)
end

#ninthObject



580
581
582
# File 'lib/vv/string_methods.rb', line 580

def ninth
  self[8]
end

#number?Boolean

Returns:

  • (Boolean)


294
295
296
297
# File 'lib/vv/string_methods.rb', line 294

def number?
  return false if self.blank?
  self.gsub(/[^0-9]/, '') == self
end

#parse(notation: :json) ⇒ Object



324
325
326
327
328
329
# File 'lib/vv/string_methods.rb', line 324

def parse notation: :json
  message = "Only JSON support at this time."
  fail NotImplementedError, message unless notation == :json

  ::JSON.parse self
end

#parse_jsonObject



331
332
333
# File 'lib/vv/string_methods.rb', line 331

def parse_json
  self.parse notation: :json
end

#plural?(coward: true) ⇒ Boolean

Returns:

  • (Boolean)

Raises:

  • (RuntimeError)


478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
# File 'lib/vv/string_methods.rb', line 478

def plural?(coward: true)
  self._ensure_pluralize_available!
  self._ensure_singularize_available!

  plural = self == self.pluralize
  return plural if !coward || !plural

  non_ambiguous = self.pluralize != self.singularize

  message = \
  "String is ambiguously plural. Cowardly exiting."
  raise RuntimeError, message unless non_ambiguous

  true
end

#popObject Also known as: pop!



117
118
119
# File 'lib/vv/string_methods.rb', line 117

def pop
  self.slice!(-1)
end

#queryObject



510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
# File 'lib/vv/string_methods.rb', line 510

def query
  string_fragments = self.split("#")[0].split("?")
  message = "Query string contains multiple question marks"

  fail message if string_fragments.count > 2
  substring = string_fragments[-1]
  response = {}

  substring.split("&").each do |key_value_pair|
    key, value = key_value_pair.split(String.equals_sign)
    if key.ends_with? "[]"
      _key = key[0..-3]
      response[_key] ||= Array.new
      response[_key] << value
    else
      response[key] = value
    end
  end

  response
end

#readable_number?Boolean

Returns:

  • (Boolean)


299
300
301
302
303
304
305
# File 'lib/vv/string_methods.rb', line 299

def readable_number?
  self.readable_to_i
  true
rescue StandardError => e
  return false if e.message == "String is not a number"
  raise
end

#readable_to_iObject

Raises:

  • (StandardError)


366
367
368
369
370
371
372
373
374
375
376
377
# File 'lib/vv/string_methods.rb', line 366

def readable_to_i
  return self.to_i if self.number?

  valid_postfix = self._numeral_postfixes.include? self.last
  valid_body    = self[0...-1].number?
  valid = valid_body && valid_postfix

  message = "String is not a number"
  raise StandardError, message unless valid

  self[0...-1].to_i * self._numeral_postfixes[self.last]
end

#safe_dir_path?(allow_hidden: false, allow_absolute: true) ⇒ Boolean

Returns:

  • (Boolean)


252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/vv/string_methods.rb', line 252

def safe_dir_path? allow_hidden: false,
                   allow_absolute: true
  separator = File::SEPARATOR

  unsafe   = false
  unsafe ||= self.starts_with?(separator) unless allow_absolute
  unsafe ||= self.after(separator, safe: false)
               .split(separator).map do |fragment|
    fragment.safe_filename? allow_hidden: allow_hidden
  end.map(&:!).any?

  ! unsafe
end

#safe_filename?(allow_hidden: false) ⇒ Boolean

Returns:

  • (Boolean)


228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/vv/string_methods.rb', line 228

def safe_filename?( allow_hidden: false )
  unsafe   = self.blank?

  unsafe ||= self.starts_with?(period) unless allow_hidden
  unsafe ||= self.starts_with? dash

  unsafe ||= self.end_with? period
  unsafe ||= self.end_with? dash

  unsafe ||= self =~ self.safe_filename_characters.to_regex_filter

  ! unsafe
end

#safe_path?(allow_hidden: false, allow_absolute: false) ⇒ Boolean

Returns:

  • (Boolean)


242
243
244
245
246
247
248
249
250
# File 'lib/vv/string_methods.rb', line 242

def safe_path?( allow_hidden: false, allow_absolute: false )
  safe = self.safe_dir_path? allow_hidden: allow_hidden,
                             allow_absolute: allow_absolute

  unsafe   = ( ! safe )
  unsafe ||= self.ends_with? File::SEPARATOR

  ! unsafe
end

#secondObject



552
553
554
# File 'lib/vv/string_methods.rb', line 552

def second
  self[1]
end

#setterObject



449
450
451
452
# File 'lib/vv/string_methods.rb', line 449

def setter
  return self if self.ends_with?(String.equals_sign)
  "#{self}="
end

#setter_symObject



454
455
456
# File 'lib/vv/string_methods.rb', line 454

def setter_sym
  self.setter.to_sym
end

#seventhObject



572
573
574
# File 'lib/vv/string_methods.rb', line 572

def seventh
  self[6]
end

#shiftObject Also known as: shift!

Instance methods start



112
113
114
# File 'lib/vv/string_methods.rb', line 112

def shift
  self.slice!(0)
end

#singular?(coward: true) ⇒ Boolean

Returns:

  • (Boolean)

Raises:

  • (RuntimeError)


494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
# File 'lib/vv/string_methods.rb', line 494

def singular?(coward: true)
  self._ensure_pluralize_available!
  self._ensure_singularize_available!

  singular = self == self.singularize
  return singular if !coward || !singular

  non_ambiguous = self.pluralize != self.singularize

  message = \
  "String is ambiguously singular. Cowardly exiting."
  raise RuntimeError, message unless non_ambiguous

  true
end

#sixthObject



568
569
570
# File 'lib/vv/string_methods.rb', line 568

def sixth
  self[5]
end

#split_english(ignore_newlines: false) ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
# File 'lib/vv/string_methods.rb', line 168

def split_english ignore_newlines: false

  options = [ ", ",
              ", and ",
              ", or ",
              " and ",
              " or "]
  self.split_via(options,
                 ignore_newlines: ignore_newlines)
    .map(&:strip)
end

#split_via(*arguments, ignore_newlines: false) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/vv/string_methods.rb', line 141

def split_via *arguments, ignore_newlines: false
  newlines_encountered = self.include? String.newline
  newlines_ok   = ( not newlines_encountered )
  newlines_ok ||= ignore_newlines

  message  = "Newlines encountered, but disallowed. "
  message += \
  "Set `ignore_newlines` to true to treat as spaces."

  fail message unless newlines_ok

  args = arguments.flatten.sort_by(&:length).reverse

  response = [self.gsub(String.newline, String.space)]

  args.map do |arg|

    response.map! do |fragment|
      fragment.split arg
    end

    response.flatten!
  end

  response
end

#squishObject



187
188
189
# File 'lib/vv/string_methods.rb', line 187

def squish
  self.dup.squish!
end

#squish!Object



180
181
182
183
184
185
# File 'lib/vv/string_methods.rb', line 180

def squish!
  self.gsub!(/\A[[:space:]]+/, "")
  self.gsub!(/[[:space:]]+\z/, "")
  self.gsub!(/[[:space:]]+/, " ")
  self
end

#style(*args) ⇒ Object



397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
# File 'lib/vv/string_methods.rb', line 397

def style *args
  color = bold = underline = italic = nil

  args.flatten!
  args.map! { |arg| arg.to_sym }

  args.each do |arg|
    if Color.known_color? arg
      if color.present?
        raise "Color already set"
      else
        color = Color.new arg
      end
    elsif arg == :bold
      bold = Bold.new
    elsif arg == :underline
      underline = Underline.new
    elsif ( arg == :italic ) or ( arg == :italics )
      italic = Italic.new
    else
      raise NotImplemented
    end
  end

  reset = Format.reset_code
  response = self.chomp(reset) + reset

  response.prepend italic.code    if italic
  response.prepend underline.code if underline
  response.prepend bold.code      if bold

  if color
    start  = response.index color.start_code
    if start
      finish = response[start..-1].index("m") + start
      response.slice! start..finish
    end
    response.prepend color.code
  end

  response
end

#tenthObject



584
585
586
# File 'lib/vv/string_methods.rb', line 584

def tenth
  self[9]
end

#thirdObject



556
557
558
# File 'lib/vv/string_methods.rb', line 556

def third
  self[2]
end

#to(position) ⇒ Object



458
459
460
# File 'lib/vv/string_methods.rb', line 458

def to position
  self[0..position]
end

#to_booleanObject

Raises:

  • (RuntimeError)


307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/vv/string_methods.rb', line 307

def to_boolean
  _true = self == "true"
  return true if _true

  _false = self == "false"
  return false if _false

  message = %w[ Unable to cast supplied string to boolean,
                only `"true"` and `"false"` can be coerced into
                boolean. ].spaced
  raise RuntimeError, message
end

#to_h(parse: :json, symbolize_keys: false) ⇒ Object



347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/vv/string_methods.rb', line 347

def to_h parse: :json, symbolize_keys: false
  message = "Only JSON support at this time."
  fail NotImplementedError, message unless parse == :json

  response = self.parse_json

  response.symbolize_keys! if symbolize_keys

  return response if response.to_h == response

  message = \
  "Parse string was #{response.class}, instead of Hash."
  fail message
end

#to_i!Object



362
363
364
# File 'lib/vv/string_methods.rb', line 362

def to_i!
  Integer(self)
end

#to_regexObject



215
216
217
# File 'lib/vv/string_methods.rb', line 215

def to_regex
  Regexp.new self
end

#to_regex_filterObject



219
220
221
222
# File 'lib/vv/string_methods.rb', line 219

def to_regex_filter
  regex_string = '[^' + self + ']'
  regex_string.to_regex
end

#to_regexp_filterObject



224
225
226
# File 'lib/vv/string_methods.rb', line 224

def to_regexp_filter
  self.to_regex_filter
end

#to_timeObject



335
336
337
338
339
340
341
342
343
344
345
# File 'lib/vv/string_methods.rb', line 335

def to_time
  message = "Incorrect time string formatting `#{self}`, " + \
            "must be of the form `2020-02-20T11:22:33.4567Z`"

  fail message unless self.ends_with? "Z"
  fail message unless self.split(String.dash).count == 3
  fail message unless self.split(String.colon).count == 3
  fail message unless self[10] == "T"

  Time.parse self
end

#unstyleObject Also known as: unstyled



386
387
388
389
# File 'lib/vv/string_methods.rb', line 386

def unstyle
  self.gsub( /\e\[+\d+m/, empty_string )
    .gsub( /\e\[((\d+)+\;)+\d+m/, empty_string )
end

#unstyle!Object



392
393
394
395
# File 'lib/vv/string_methods.rb', line 392

def unstyle!
  self.gsub!( /\e\[+\d+m/,          empty_string )
  self.gsub!( /\e\[((\d+)+\;)+\dm/, empty_string )
end

#vv_jsonObject



320
321
322
# File 'lib/vv/string_methods.rb', line 320

def vv_json
  VV::JSON.generate self
end

#with_ending(string) ⇒ Object



133
134
135
# File 'lib/vv/string_methods.rb', line 133

def with_ending(string)
  self.chomp(string) + string
end

#with_newlineObject



137
138
139
# File 'lib/vv/string_methods.rb', line 137

def with_newline
  self.with_ending newline
end