Class: String

Inherits:
Object
  • Object
show all
Defined in:
lib/planter/color.rb,
lib/planter/string.rb

Overview

String helpers

Constant Summary collapse

MOD_RX =

Returns Regular expression for matching variable modifiers.

Returns:

  • (String)

    Regular expression for matching variable modifiers

'(?<mod>
  (?::
    (
      l(?:ow(?:er(case)?)?)?)?|
      d(?:own(?:case)?)?|
      u(?:p(?:per(case)?)?)?|upcase|
      c(?:ap(?:ital(?:ize)?)?)?|
      t(?:itle)?|
      snake|camel|slug|
      fl|first_letter|
      fw|first_word|
      f(?:ile(?:name)?
    )?
  )*
)'
DEFAULT_RX =

Returns regular expression string for default values.

Returns:

  • (String)

    regular expression string for default values

'(?:%(?<default>[^%]+))?'

Instance Method Summary collapse

Instance Method Details

#apply_allObject

Apply all logic, variables, and regexes to a string



429
430
431
# File 'lib/planter/string.rb', line 429

def apply_all
  apply_logic.apply_variables.apply_regexes
end

#apply_conditions(conditions, variables, output) ⇒ String

Apply conditions

Parameters:

  • conditions (Array<MatchData>)

    Array of conditions ['statement', 'condition', 'content']

  • variables (Hash)

    Hash of variables

  • output (String)

    Output string

Returns:



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/planter/string.rb', line 273

def apply_conditions(conditions, variables, output)
  res = false
  conditions.each do |condition|
    variable, operator, value = condition['condition'].split(/ +/, 3)
    value.strip_quotes!
    variable = variable.to_var
    negate = false
    if operator =~ /^!/
      operator = operator[1..-1]
      negate = true
    end
    operator = case operator
               when /^={1,2}/
                 :equal
               when /^=~/
                 :matches_regex
               when /\*=/
                 :contains
               when /\^=/
                 :starts_with
               when /\$=/
                 :ends_with
               when />/
                 :greater_than
               when /</
                 :less_than
               when />=/
                 :greater_than_or_equal
               when /<=/
                 :less_than_or_equal
               else
                 :equal
               end

    comp = variables[variable.to_var].to_s

    res = case operator
          when :equal
            comp =~ /^#{value}$/i
          when :matches_regex
            comp =~ Regexp.new(value.gsub(%r{^/|/$}, ''))
          when :contains
            comp =~ /#{value}/i
          when :starts_with
            comp =~ /^#{value}/i
          when :ends_with
            comp =~ /#{value}$/i
          when :greater_than
            comp > value.to_f
          when :less_than
            comp < value.to_f
          when :greater_than_or_equal
            comp >= value.to_f
          when :less_than_or_equal
            comp <= value.to_f
          else
            false
          end
    res = res ? true : false
    res = !res if negate

    next unless res

    Planter.notify("Condition matched: #{comp} #{negate ? 'not ' : ''}#{operator} #{value}", :debug)
    output = condition['content']
    break
  end
  output
end

#apply_defaults(variables) ⇒ String

Apply default values to a string

Default values are applied to variables that are not present in the variables hash, or whose value matches the default value

Parameters:

  • variables (Hash)

    Hash of variable values

Returns:

  • (String)

    string with default values applied



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/planter/string.rb', line 166

def apply_defaults(variables)
  # Perform an in-place substitution on the content string for default values
  gsub(/%%(?<varname>[^%:]+)(?<mods>(?::[^%]+)*)%(?<default>[^%]+)%%/) do
    # Capture the last match object
    m = Regexp.last_match

    # Check if the variable is not present in the variables hash
    if !variables.key?(m['varname'].to_var)
      # If the variable is not present, use the default value from the match
      m['default'].apply_var_names
    else
      # Retrieve the default value for the variable from the configuration
      vars = Planter.config.variables.filter { |v| v[:key] == m['varname'] }
      default = vars.first[:default] if vars.count.positive?
      if default.nil?
        m[0]
      elsif variables[m['varname'].to_var] == default
        # If the variable's value matches the default value, use the default value from the match
        m['default'].apply_var_names
      else
        m[0]
      end
    end
  end
end

#apply_defaults!(variables) ⇒ String

Destructive version of #apply_defaults

Parameters:

  • variables (Hash)

    hash of variables to apply

Returns:

  • (String)

    string with defaults applied



199
200
201
# File 'lib/planter/string.rb', line 199

def apply_defaults!(variables)
  replace apply_defaults(variables)
end

#apply_logic(variables = nil) ⇒ Object

Apply logic to a string

Parameters:

  • variables (Hash) (defaults to: nil)

    Hash of variables to apply



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/planter/string.rb', line 207

def apply_logic(variables = nil)
  variables = variables.nil? ? Planter.variables : variables

  gsub(/%%if .*?%%.*?%%end( ?if)?%%/mi) do |construct|
    # Get the condition and the content
    output = construct.match(/%%else%%(.*?)%%end/m) ? Regexp.last_match(1) : ''

    conditions = construct.to_enum(:scan,
                                   /%%(?<statement>(?:els(?:e )?)?if) (?<condition>.*?)%%(?<content>.*?)(?=%%)/mi).map do
      Regexp.last_match
    end

    apply_conditions(conditions, variables, output)
  end
end

#apply_logic!(variables) ⇒ Object

Destructive version of #apply_logic



224
225
226
# File 'lib/planter/string.rb', line 224

def apply_logic!(variables)
  replace apply_logic(variables)
end

#apply_mod(mod) ⇒ String

Apply a modification to string

Parameters:

  • mod (Symbol)

    The modifier to apply

Returns:

  • (String)

    modified string



506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
# File 'lib/planter/string.rb', line 506

def apply_mod(mod)
  case mod
  when :slug
    to_slug
  when :title_case
    title_case
  when :lowercase
    downcase
  when :uppercase
    upcase
  when :snake_case
    snake_case
  when :camel_case
    camel_case
  when :first_letter
    split('')[0]
  when :first_word
    split(/[ !,?;:]+/)[0]
  else
    self
  end
end

#apply_mod!(mod) ⇒ <Type>

Destructive version of #apply_mod

Parameters:

  • mod (String)

    modified string

Returns:

  • (<Type>)



536
537
538
# File 'lib/planter/string.rb', line 536

def apply_mod!(mod)
  replace apply_mod(mod)
end

#apply_mods(mods) ⇒ String

Apply modifiers to a string

Parameters:

  • mods (String)

    Colon separated list of modifiers to apply

Returns:

  • (String)

    string with modifiers applied



418
419
420
421
422
423
424
# File 'lib/planter/string.rb', line 418

def apply_mods(mods)
  content = dup
  mods.split(/:/).each do |mod|
    content.apply_mod!(mod.normalize_mod)
  end
  content
end

#apply_operator_logic(variables = nil) ⇒ Object

Apply operator logic to a string. Operators are defined as :copy, :overwrite, :ignore, or :merge. Logic can be if/else constructs or inline operators.

Examples:

"var = 1; if var == 1:copy; else: ignore" #=> :copy

"var = 2; copy if var == 1 else ignore" #=> :ignore

Parameters:

  • variables (Hash) (defaults to: nil)

    Hash of variables (default: Planter.variables)



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/planter/string.rb', line 238

def apply_operator_logic(variables = nil)
  variables = variables.nil? ? Planter.variables : variables
  op_rx = ' *(?<content>c(?:opy)?|o(?:ver(?:write)?)?|i(?:gnore)?|m(?:erge)?)? *'

  output = strip.gsub(/^if .*?(?:end(?: ?if)?|$)/mi) do |construct|
    # Get the condition and the content
    output = construct.match(/else:#{op_rx}/m) ? Regexp.last_match(1) : ''

    conditions = construct.to_enum(:scan,
                                   /(?<statement>(?:els(?:e )?)?if) +(?<condition>.*?):#{op_rx}(?=;|$)/mi).map do
      Regexp.last_match
    end

    apply_conditions(conditions, variables, output)
  end
  output = output.gsub(/^#{op_rx} +if .*?(end( ?if)?|$)/mi) do |construct|
    # Get the condition and the content
    output = construct.match(/else[; ]+(#{op_rx})/m) ? Regexp.last_match(1) : :ignore
    condition = construct.match(/^#{op_rx}(?<statement>if) +(?<condition>.*?)(?=;|$)/mi)

    apply_conditions([condition], variables, output)
  end

  output.normalize_operator
end

#apply_regexes(regexes = nil) ⇒ String

Apply regex replacements from Planter.config[:replacements]

Returns:

  • (String)

    string with regexes applied



438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'lib/planter/string.rb', line 438

def apply_regexes(regexes = nil)
  content = dup.clean_encode
  regexes = regexes.nil? && Planter.config.key?(:replacements) ? Planter.config.replacements : regexes

  return self unless regexes

  regexes.stringify_keys.each do |pattern, replacement|
    pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp)
    replacement = replacement.gsub(/\$(\d)/, '\\\1').apply_variables
    content.gsub!(pattern, replacement)
  end
  content
end

#apply_regexes!(regexes = nil) ⇒ String

Destructive version of #apply_regexes

Returns:

  • (String)

    string with variables substituted



468
469
470
# File 'lib/planter/string.rb', line 468

def apply_regexes!(regexes = nil)
  replace apply_regexes(regexes)
end

#apply_var_namesString

Handle $varname and $varname variable substitutions

Returns:

  • (String)

    String with variables substituted



400
401
402
403
404
405
406
407
408
409
# File 'lib/planter/string.rb', line 400

def apply_var_names
  sub(/\$\{?(?<varname>\w+)(?<mods>(?::\w+)+)?\}?/) do
    m = Regexp.last_match
    if Planter.variables.key?(m['varname'].to_var)
      Planter.variables[m['varname'].to_var].apply_mods(m['mods'])
    else
      m
    end
  end
end

#apply_variables(variables: nil, last_only: false) ⇒ String

Apply key/value substitutions to a string. Variables are represented as %%key%%, and the hash passed to the function is { key: value }

Parameters:

  • last_only (Boolean) (defaults to: false)

    Only replace the last instance of %%key%%

Returns:

  • (String)

    string with variables substituted



351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
# File 'lib/planter/string.rb', line 351

def apply_variables(variables: nil, last_only: false)
  variables = variables.nil? ? Planter.variables : variables

  content = dup.clean_encode

  content = content.apply_defaults(variables)

  content = content.apply_logic(variables)

  variables.each do |k, v|
    if last_only
      pattern = "%%#{k.to_var}"
      content = content.reverse.sub(/(?mix)%%(?:(?<mod>.*?):)*(?<key>#{pattern.reverse})/i) do
        m = Regexp.last_match
        if m['mod']
          m['mod'].reverse.split(/:/).each do |mod|
            v = v.apply_mod(mod.normalize_mod)
          end
        end

        v.reverse
      end.reverse
    else
      rx = /(?mix)%%(?<key>#{k.to_var})#{MOD_RX}#{DEFAULT_RX}%%/

      content.gsub!(rx) do
        m = Regexp.last_match

        if m['mod']
          mods = m['mod']&.split(/:/)
          mods&.each do |mod|
            next if mod.nil? || mod.empty?

            v = v.apply_mod(mod.normalize_mod)
          end
        end
        v
      end
    end
  end

  content
end

#apply_variables!(variables: nil, last_only: false) ⇒ String

Destructive version of #apply_variables

Parameters:

  • last_only (Boolean) (defaults to: false)

    Only replace the last instance of %%key%%

Returns:

  • (String)

    string with variables substituted



459
460
461
# File 'lib/planter/string.rb', line 459

def apply_variables!(variables: nil, last_only: false)
  replace apply_variables(variables: variables, last_only: last_only)
end

#camel_caseString

Convert a string to camel case, handling spaces or snake_casing

Examples:

"class_name".camel_case #=> className

"A title string".camel_case #=> aTitleString

Returns:

  • (String)

    Snake-cased version of string



119
120
121
122
123
# File 'lib/planter/string.rb', line 119

def camel_case
  strip.gsub(/(?<=[^a-z0-9])(\S)/) { Regexp.last_match(1).upcase }
       .gsub(/[^a-z0-9]+/i, '')
       .sub(/^(\w)/) { Regexp.last_match(1).downcase }
end

#clean_encodeString

Get a clean UTF-8 string by forcing an ISO encoding and then re-encoding

Returns:



661
662
663
# File 'lib/planter/string.rb', line 661

def clean_encode
  force_encoding('ISO-8859-1').encode('utf-8', replace: nil)
end

#clean_encode!String

Destructive version of #clean_encode

Returns:

  • (String)

    UTF-8 string, in place



670
671
672
# File 'lib/planter/string.rb', line 670

def clean_encode!
  replace clean_encode
end

#clean_valueString

Clean up a string by removing leading numbers and parentheticalse

Returns:



678
679
680
# File 'lib/planter/string.rb', line 678

def clean_value
  sub(/^\(?\d+\.\)? +/, '').sub(/\((.*?)\)/, '\1')
end

#coerce(type) ⇒ Object

Coerce a variable to a type

Parameters:



639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
# File 'lib/planter/string.rb', line 639

def coerce(type)
  type = type.normalize_type

  case type
  when :date
    Chronic.parse(self).strftime('%Y-%m-%d %H:%M')
  when :integer || :number
    to_i
  when :float
    to_f
  when :class || :module
    to_class_name
  else
    to_s
  end
end

#ext(extension) ⇒ String

Add an extension to the string, replacing existing extension if needed

Examples:

"planter-string".ext('rb') #=> planter-string.rb

"planter-string.rb".ext('erb') #=> planter-string.erb

Parameters:

  • extension (String)

    The extension to add

Returns:

  • (String)

    string with new extension



494
495
496
497
# File 'lib/planter/string.rb', line 494

def ext(extension)
  extension = extension.sub(/^\./, '')
  sub(/(\.\w+)?$/, ".#{extension}")
end

#glob_to_rxString

Convert a,b,c to (?:a|b|c)

Returns:

  • (String)

    Converted string



36
37
38
39
40
41
# File 'lib/planter/string.rb', line 36

def glob_to_rx
  gsub(/\\?\{(.*?)\\?\}/) do
    m = Regexp.last_match
    "(?:#{m[1].split(/,/).map { |c| Regexp.escape(c) }.join('|')})"
  end
end

#highlight_character(default: nil) ⇒ Object

Highlight characters in parenthesis, with special color for default if provided. Output is color templated string, unprocessed.

Parameters:

  • default (String) (defaults to: nil)

    The default



688
689
690
691
692
693
694
# File 'lib/planter/string.rb', line 688

def highlight_character(default: nil)
  if default
    gsub(/\((#{default})\)/, '{dw}({xbc}\1{dw}){xw}').gsub(/\((.)\)/, '{dw}({xbw}\1{dw}){xw}')
  else
    gsub(/\((.)\)/, '{dw}({xbw}\1{dw}){xw}')
  end
end

#last_color_codeObject

Get the calculated ANSI color at the end of the string

Returns:

  • ANSI escape sequence to match color



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/planter/color.rb', line 153

def last_color_code
  m = scan(ESCAPE_REGEX)

  em = ['0']
  fg = nil
  bg = nil
  rgbf = nil
  rgbb = nil

  m.each do |c|
    case c
    when '0'
      em = ['0']
      fg, bg, rgbf, rgbb = nil
    when /^[34]8/
      case c
      when /^3/
        fg = nil
        rgbf = c
      when /^4/
        bg = nil
        rgbb = c
      end
    else
      c.split(';').each do |i|
        x = i.to_i
        if x <= 9
          em << x
        elsif x >= 30 && x <= 39
          rgbf = nil
          fg = x
        elsif x >= 40 && x <= 49
          rgbb = nil
          bg = x
        elsif x >= 90 && x <= 97
          rgbf = nil
          fg = x
        elsif x >= 100 && x <= 107
          rgbb = nil
          bg = x
        end
      end
    end
  end

  escape = "\e[#{em.join(';')}m"
  escape += "\e[#{rgbb}m" if rgbb
  escape += "\e[#{rgbf}m" if rgbf
  escape + "\e[#{[fg, bg].delete_if(&:nil?).join(';')}m"
end

#no_extString

Remove any file extension

Examples:

"planter-string.rb".no_ext #=> planter-string

Returns:

  • (String)

    string with no extension



479
480
481
# File 'lib/planter/string.rb', line 479

def no_ext
  sub(/\.\w{2,4}$/, '')
end

#normalize_colorString

Normalize a color name, removing underscores, replacing "bright" with "bold", and converting bgbold to boldbg

Returns:

  • (String)

    Normalized color name



144
145
146
# File 'lib/planter/color.rb', line 144

def normalize_color
  delete('_').sub(/bright/i, 'bold').sub(/bgbold/, 'boldbg')
end

#normalize_modSymbol

Convert mod string to symbol

Examples:

"snake" => :snake_case

"cap" => :title_case

Returns:

  • (Symbol)

    symbolized modifier



548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
# File 'lib/planter/string.rb', line 548

def normalize_mod
  case self
  when /^(file|slug)/
    :slug
  when /^cam/
    :camel_case
  when /^s/
    :snake_case
  when /^u/
    :uppercase
  when /^[ld]/
    :lowercase
  when /^[ct]/
    :title_case
  when /^(fl|first_letter)/
    :first_letter
  when /^(fw|first_word)/
    :first_word
  end
end

#normalize_operatorSymbol

Convert operator string to symbol

Examples:

"ignore" => :ignore

"m" => :merge

Returns:

  • (Symbol)

    symbolized operator



577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
# File 'lib/planter/string.rb', line 577

def normalize_operator
  case self
  # merge or append
  when /^i/
    :ignore
  when /^(m|ap)/
    :merge
  # ask or optional
  when /^(a|op)/
    :ask
  # overwrite
  when /^o/
    :overwrite
  else
    :copy
  end
end

#normalize_typeSymbol

Convert type string to symbol

Examples:

"string".coerce #=> :string

"date".coerce #=> :date

"num".coerce #=> :number

Returns:



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
# File 'lib/planter/string.rb', line 604

def normalize_type
  case self
  # date
  when /^da/
    :date
  # integer
  when /^i/
    :integer
  # number or float
  when /^[nf]/
    :float
  # paragraph
  when /^p/
    :multiline
  # class
  when /^cl/
    :class
  # module
  when /^mod/
    :module
  # multiple choice
  when /^(ch|mu)/
    :choice
  # string
  else
    :string
  end
end

#selector?Boolean

Test if a string has a parenthetical selector

Returns:

  • (Boolean)

    has selector



701
702
703
# File 'lib/planter/string.rb', line 701

def selector?
  self =~ /\(.\)/ ? true : false
end

#slugifyString

Convert some characters to text

Returns:

  • (String)

    slugified character or empty string



82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/planter/string.rb', line 82

def slugify
  char = to_s
  slug_version = {
    '.' => 'dot',
    '/' => 'slash',
    ':' => 'colon',
    ',' => 'comma',
    '!' => 'bang',
    '#' => 'hash'
  }
  slug_version[char] ? "-#{slug_version[char]}-" : ''
end

#snake_caseString

Convert a string to snake case, handling spaces or CamelCasing

Examples:

"ClassName".snake_case #=> class-name

"A title string".snake_case #=> a-title-string

Returns:

  • (String)

    Snake-cased version of string



103
104
105
106
107
108
109
# File 'lib/planter/string.rb', line 103

def snake_case
  strip.gsub(/\S(?=[A-Z])/, '\0_')
       .gsub(/[ -]+/, '_')
       .gsub(/[^a-z0-9_]+/i, '')
       .gsub(/_+/, '_')
       .gsub(/(^_|_$)/, '').downcase
end

#strip_quotesString

Strip quotes from a string

Returns:

  • (String)

    string with quotes stripped



22
23
24
# File 'lib/planter/string.rb', line 22

def strip_quotes
  sub(/^(["'])(.*)\1$/, '\2')
end

#strip_quotes!Object

Destructive version of #strip_quotes



27
28
29
# File 'lib/planter/string.rb', line 27

def strip_quotes!
  replace strip_quotes
end

#strip_templateString

Remove template codes from a string

Returns:

  • (String)

    uncolored string



113
114
115
# File 'lib/planter/color.rb', line 113

def strip_template
  gsub(/(?<!\\)\{(\w+)\}/i, '')
end

#title_caseString

Capitalize the first character after a word border. Prevents downcasing intercaps.

Examples:

"a title string".title_case #=> A Title String

Returns:

  • (String)

    title cased string



133
134
135
# File 'lib/planter/string.rb', line 133

def title_case
  split(/\b(\w+)/).map(&:capitalize).join('')
end

#to_class_nameObject

Convert a slug into a class name

Examples:

"planter-string".to_class_name #=> PlanterString

Returns:

  • Class name representation of the object.



60
61
62
# File 'lib/planter/string.rb', line 60

def to_class_name
  strip.no_ext.title_case.gsub(/[^a-z0-9]/i, '').sub(/^\S/, &:upcase)
end

#to_rxString

Convert a string to a regular expression by escaping special characters and converting wildcards (*,?) to regex wildcards

Returns:

  • (String)

    String with wildcards converted (not Regexp)



49
50
51
# File 'lib/planter/string.rb', line 49

def to_rx
  gsub(/([.()])/, '\\\\\1').gsub(/\?/, '.').gsub(/\*/, '.*?').glob_to_rx
end

#to_slugObject

Convert a class name to a file slug

Examples:

"PlanterString".to_class_name #=> planter-string

Returns:

  • Filename representation of the object.



71
72
73
74
75
76
# File 'lib/planter/string.rb', line 71

def to_slug
  strip.split(/(?=[A-Z ])/).map(&:downcase).join('-')
       .gsub(/[^a-z0-9_-]/i, &:slugify)
       .gsub(/-+/, '-')
       .gsub(/(^-|-$)/, '')
end

#to_varSymbol

Convert string to snake-cased variable name

Examples:

"Planter String" #=> planter_string

"Planter-String" #=> planter_string

Returns:

  • (Symbol)

    string as variable key



14
15
16
# File 'lib/planter/string.rb', line 14

def to_var
  strip_quotes.snake_case.to_sym
end

#validate_colorString

Extract the longest valid %color name from a string.

Allows %colors to bleed into other text and still be recognized, e.g. %greensomething still finds %green.

Returns:

  • (String)

    a valid color name



126
127
128
129
130
131
132
133
134
135
# File 'lib/planter/color.rb', line 126

def validate_color
  valid_color = nil
  compiled = ''
  normalize_color.chars.each do |char|
    compiled += char
    valid_color = compiled if Color.attributes.include?(compiled.to_sym) || compiled =~ /^([fb]g?)?#([a-f0-9]{6})$/i
  end

  valid_color
end

#xString

Shortcut for #template

Returns:

  • (String)

    colorized string



107
108
109
# File 'lib/planter/color.rb', line 107

def x
  Color.template(self)
end