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 
  # Use concatenation (+) to work around Ruby bug!
  forbidden =  ' !"#' + "$%&'()*+,-./:;<=>?[\\]^`{|}~" 
  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 <...>.



122
123
124
125
# File 'lib/macros4cuke/templating/engine.rb', line 122

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

Instance Attribute Details

#representationObject (readonly)

The internal representation of the template text



116
117
118
# File 'lib/macros4cuke/templating/engine.rb', line 116

def representation
  @representation
end

#sourceObject (readonly)

The original text of the template is kept here.



113
114
115
# File 'lib/macros4cuke/templating/engine.rb', line 113

def source
  @source
end

Class Method Details

.parse(aTextLine) ⇒ Array

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

Returns:

  • (Array)

    Couples of the form:



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/macros4cuke/templating/engine.rb', line 184

def self.parse(aTextLine)
  scanner = StringScanner.new(aTextLine)
  result = []

  if scanner.check(/\s*#/)  # Detect comment line
    result << [:comment, aTextLine]
  else
    until scanner.eos?
      # Scan tag at current position...
      tag_literal = scanner.scan(/<(?:[^\\<>]|\\.)*>/)
      unless tag_literal.nil?
        result << [:dynamic, tag_literal.gsub(/^<|>$/, '')] 
      end
      
      # ... or scan plain text at current position
      literal = scanner.scan(/(?:[^\\<>]|\\.)+/)
      result << [:static, literal] unless literal.nil? 
      identify_parse_error(aTextLine) if tag_literal.nil? && literal.nil?
    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.



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/macros4cuke/templating/engine.rb', line 138

def render(aContextObject = Object.new, theLocals)
  return '' if @representation.empty?

  prev = nil
  result = @representation.each_with_object('') do |element, subResult|
    # Output compaction rules:
    # -In case of consecutive eol's only one is rendered.
    # -In case of comment followed by one eol, both aren't rendered
    unless element.is_a?(EOLine) && 
      (prev.is_a?(EOLine) || prev.is_a?(Comment))
      subResult << element.render(aContextObject, theLocals)
    end
    prev = element
  end

  return result
end

#variablesArray

Retrieve all placeholder names that appear in the template.

Returns:

  • (Array)

    The list of placeholder names.



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/macros4cuke/templating/engine.rb', line 159

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