Class: Grizzled::String::Template::UnixShellStringTemplate

Inherits:
TemplateBase
  • Object
show all
Defined in:
lib/grizzled/string/template.rb

Overview

A string template that uses the Unix shell-like syntax ${varname} (or $varname) for variable references. A variable’s name typically consists of alphanumerics and underscores, but is controlled by the a supplied regular expression. To include a literal “$” in a string, escape it with a backslash.

For this class, the general form of a variable reference is:

${varname?default}

The ?default suffix is optional and specifies a default value to be used if the variable has no value.

A shorthand form of a variable reference is:

$varname

The default capability is not available in the shorthand form.

Constant Summary collapse

ESCAPED_DOLLAR_PLACEHOLDER =
"\001"
ESCAPED_DOLLAR =
%r{(\\*)(\\\$)}

Instance Attribute Summary

Attributes inherited from TemplateBase

#resolver, #safe

Instance Method Summary collapse

Methods inherited from TemplateBase

#get_variable, #handle_no_value, #substitute_variable

Constructor Details

#initialize(resolver, options = {}) ⇒ UnixShellStringTemplate

Initialize a new UnixShellStringTemplate. Supports various hash options.

Parameters:

resolver

A hash-like object that can take a variable name (via the [] function) and resolve its value, returning the value (which is converted to string) or nil.

options

hash of options. See below.

Options:

:safe

true for a safe template that substitutes a blank string for a non-existent variable, instead of throwing an exception. Defaults to true.

:var_pattern

Regular expression pattern (as a string, not a Regexp object) to match a variable name. Defaults to “[A-Za-z0-9_]+”



209
210
211
212
213
214
# File 'lib/grizzled/string/template.rb', line 209

def initialize(resolver, options={})
  super(resolver, options)
  var_re = options.fetch(:var_pattern, "[A-Za-z0-9_]+")
  @long_var_regexp = %r{\$\{(#{var_re})(\?[^\}]*)?\}}
  @short_var_regexp = %r{\$(#{var_re})}
end

Instance Method Details

#find_variable_ref(s) ⇒ Object

Parse the location of the first variable in the string. Subclasses should override this method.

Parameters:

s

the string

Returns a Variable object, or nil.



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
# File 'lib/grizzled/string/template.rb', line 275

def find_variable_ref(s)

  def handle_long_match(m)
    name = m[1]
    if m[2].nil?
      default = nil
    else
      # Pull off the "?"
      default = m[2][1..-1]
    end

    Variable.new(m.begin(0), m.end(0), name, default)
  end

  def handle_no_long_match(s)
    m = @short_var_regexp.match(s)
    if m.nil?
      nil
    else
      Variable.new(m.begin(0), m.end(0), m[1], nil)
    end
  end

  m = @long_var_regexp.match(s)
  if m.nil?
    handle_no_long_match(s)
  else
    handle_long_match(m)
  end
end

#handle_long_match(m) ⇒ Object



277
278
279
280
281
282
283
284
285
286
287
# File 'lib/grizzled/string/template.rb', line 277

def handle_long_match(m)
  name = m[1]
  if m[2].nil?
    default = nil
  else
    # Pull off the "?"
    default = m[2][1..-1]
  end

  Variable.new(m.begin(0), m.end(0), name, default)
end

#handle_match(m, s) ⇒ Object



239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/grizzled/string/template.rb', line 239

def handle_match(m, s)
  if (m[1].length % 2) == 0
    # Odd number of backslashes before "$", including
    # the one with the dollar token (group 2). Valid escape.

    b = m.begin(0)
    start = (b == 0 ? "" : s[0..(b-1)])
    start + ESCAPED_DOLLAR_PLACEHOLDER + pre_sub(s[m.end(0)..-1])
  else
    # Even number of backslashes before "$", including the one
    # with the dollar token (group 2). Not an escape.
    s
  end
end

#handle_no_long_match(s) ⇒ Object



289
290
291
292
293
294
295
296
# File 'lib/grizzled/string/template.rb', line 289

def handle_no_long_match(s)
  m = @short_var_regexp.match(s)
  if m.nil?
    nil
  else
    Variable.new(m.begin(0), m.end(0), m[1], nil)
  end
end

#pre_sub(s) ⇒ Object

Kludge to handle escaped “$”. Temporarily replace it with something highly unlikely to be in the string. Then, put a single “$” in its place, after the substitution. Must be sure to handle even versus odd number of backslash characters.



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

def pre_sub(s)

  def handle_match(m, s)
    if (m[1].length % 2) == 0
      # Odd number of backslashes before "$", including
      # the one with the dollar token (group 2). Valid escape.

      b = m.begin(0)
      start = (b == 0 ? "" : s[0..(b-1)])
      start + ESCAPED_DOLLAR_PLACEHOLDER + pre_sub(s[m.end(0)..-1])
    else
      # Even number of backslashes before "$", including the one
      # with the dollar token (group 2). Not an escape.
      s
    end
  end

  # Check for an escaped "$"
  m = ESCAPED_DOLLAR.match(s)
  if (m)
    handle_match(m, s)
  else
    s
  end
end

#substitute(s) ⇒ Object

Replace all variable references in the given string. Variable references are recognized per the regular expression passed to the constructor. If a referenced variable is not found in the resolver, this method either:

  • throws a VariableNotFoundException (if safe is false).

  • substitutes an empty string (if safe is true)

Recursive references are supported (but beware of infinite recursion).

Parameters:

s

the string in which to replace variable references

Returns the substituted result.



231
232
233
234
235
236
237
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
263
264
265
# File 'lib/grizzled/string/template.rb', line 231

def substitute(s)
  # Kludge to handle escaped "$". Temporarily replace it with
  # something highly unlikely to be in the string. Then, put a single
  # "$" in its place, after the substitution. Must be sure to handle
  # even versus odd number of backslash characters.

  def pre_sub(s)

    def handle_match(m, s)
      if (m[1].length % 2) == 0
        # Odd number of backslashes before "$", including
        # the one with the dollar token (group 2). Valid escape.

        b = m.begin(0)
        start = (b == 0 ? "" : s[0..(b-1)])
        start + ESCAPED_DOLLAR_PLACEHOLDER + pre_sub(s[m.end(0)..-1])
      else
        # Even number of backslashes before "$", including the one
        # with the dollar token (group 2). Not an escape.
        s
      end
    end

    # Check for an escaped "$"
    m = ESCAPED_DOLLAR.match(s)
    if (m)
      handle_match(m, s)
    else
      s
    end
  end

  s2 = super(pre_sub(s))
  s2.gsub(ESCAPED_DOLLAR_PLACEHOLDER, '$')
end