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.

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



96
97
98
99
# File 'lib/macros4cuke/templating/engine.rb', line 96

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

Instance Attribute Details

#sourceObject (readonly)

The original text of the template is kept here.



92
93
94
# File 'lib/macros4cuke/templating/engine.rb', line 92

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:



134
135
136
137
138
139
140
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
167
168
169
170
171
# File 'lib/macros4cuke/templating/engine.rb', line 134

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.



107
108
109
110
111
112
113
114
115
# File 'lib/macros4cuke/templating/engine.rb', line 107

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.



120
121
122
123
124
125
126
127
128
# File 'lib/macros4cuke/templating/engine.rb', line 120

def variables()
  # The result will be cached/memoized...
  @variables ||= begin
    vars = @representation.select { |element| element.is_a?(Placeholder) }
    vars.map(&:name)
  end
  
  return @variables
end