Class: Macros4Cuke::Templating::Engine

Inherits:
Object
  • Object
show all
Defined in:
lib/macros4cuke/templating/engine.rb

Overview

A very simple implementation of a templating engine.
Earlier versions of Macros4Cuke relied on the logic-less Mustache template engine.
But it was decided afterwards to replace it by a very simple template engine.
The reasons were the following:

  • Be closer to the usual Gherkin syntax (parameters of scenario outlines use chevrons <...>, while Mustache use {{...}} delimiters),
  • Feature files are meant to be simple, so should the template engine be.

Constant Summary collapse

DisallowedSigns =

The regular expression that matches a space, any punctuation sign or delimiter that is forbidden between chevrons <...> template tags.

begin 
  forbidden =  ' !"#' + "$%&'()*+,-./:;<=>?[\\]^`{|}~" # Used concatenation (+) to work around Ruby bug!
  all_escaped = [] 
  forbidden.each_char() { |ch| all_escaped << Regexp.escape(ch) }
  pattern = all_escaped.join("|")
  Regexp.new(pattern)
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(aSourceTemplate) ⇒ Engine

Builds an Engine and compiles the given template text into an internal representation.

Parameters:

  • aSourceTemplate (String)

    The template source text. It may contain zero or tags enclosed between chevrons <...>.



231
232
233
234
# File 'lib/macros4cuke/templating/engine.rb', line 231

def initialize(aSourceTemplate)
  @source = aSourceTemplate
  @representation = compile(aSourceTemplate)
end

Instance Attribute Details

#sourceObject (readonly)

The original text of the template is kept here.



227
228
229
# File 'lib/macros4cuke/templating/engine.rb', line 227

def source
  @source
end

Class Method Details

.parse(aTextLine) ⇒ Array

Class method. Parse the given line text into a raw representation. [:static, text] or [:dynamic, tag text]

Returns:

  • (Array)

    Couples of the form:



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
# File 'lib/macros4cuke/templating/engine.rb', line 280

def self.parse(aTextLine)
  scanner = StringScanner.new(aTextLine)
  result = []
  
  until scanner.eos?
    # Scan tag at current position...
    tag_literal = scanner.scan(/<(?:[^\\<>]|\\.)*>/)
    result << [:dynamic, tag_literal.gsub(/^<|>$/, '')] unless tag_literal.nil?
    
    # ... or scan plain text at current position
    text_literal = scanner.scan(/(?:[^\\<>]|\\.)+/)
    result << [:static, text_literal] unless text_literal.nil?
    
    if tag_literal.nil? && text_literal.nil?
      # Unsuccessful scanning: we have improperly balanced chevrons.
      # We will analyze the opening and closing chevrons...
      no_escaped = aTextLine.gsub(/\\[<>]/, "--")  # First: replace escaped chevron(s)
      unbalance = 0 # = count of < -  count of > (can only be 0 or -temporarily- 1)

      no_escaped.scan(/(.)/) do |match|
        case match[0]
          when '<'
            unbalance += 1 
          when '>'  
            unbalance -= 1              
        end
        
        raise StandardError, "Nested opening chevron '<'." if unbalance > 1
        raise StandardError, "Missing opening chevron '<'." if unbalance < 0
      end
      
      raise StandardError, "Missing closing chevron '>'." if unbalance == 1
      raise StandardError, "Cannot parse:\n'#{aTextLine}'"
    end
  end
  
  return result
end

Instance Method Details

#render(aContextObject = Object.new, theLocals) ⇒ String

Render the template within the given scope object and with the locals specified. The method mimicks the signature of the Tilt::Template#render method.

Parameters:

  • aContextObject (anything) (defaults to: Object.new)

    context object to get actual values (when not present in the locals Hash).

  • theLocals (Hash)

    Contains one or more pairs of the form: tag/placeholder name => actual value.

Returns:

  • (String)

    The rendition of the template given the passed argument values.



242
243
244
245
246
247
248
249
250
# File 'lib/macros4cuke/templating/engine.rb', line 242

def render(aContextObject = Object.new, theLocals)
  return '' if @representation.empty?
  
  result = @representation.each_with_object('') do |element, subResult|
    subResult << element.render(aContextObject, theLocals)
  end
  
  return result
end

#variablesArray

Retrieve all placeholder names that appear in the template.

Returns:

  • (Array)

    The list of placeholder names.



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/macros4cuke/templating/engine.rb', line 255

def variables()
  # The result will be cached/memoized...
  @variables ||= begin
    vars = @representation.each_with_object([]) do |element, subResult|
      case element
        when Placeholder          
          subResult << element.name
        
        when Section
          subResult.concat(element.variables)
        
        else
          # Do nothing
      end
    end
    
    vars.flatten.uniq
  end
  
  return @variables
end