Class: Macros4Cuke::Templating::Engine

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

Overview

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



54
55
56
57
# File 'lib/macros4cuke/templating/engine.rb', line 54

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

Instance Attribute Details

#representationObject (readonly)

The internal representation of the template text



48
49
50
# File 'lib/macros4cuke/templating/engine.rb', line 48

def representation
  @representation
end

#sourceObject (readonly)

The original text of the template is kept here.



45
46
47
# File 'lib/macros4cuke/templating/engine.rb', line 45

def source
  @source
end

Class Method Details

.identify_parse_error(aTextLine) ⇒ Object

Called when the given text line could not be parsed. Raises an exception with the syntax issue identified.

Parameters:

  • aTextLine (String)

    A text line from the template.

Raises:

  • (StandardError)


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

def self.identify_parse_error(aTextLine)
  # Unsuccessful scanning: we typically have improperly balanced chevrons.
  # We will analyze the opening and closing chevrons...
  # First: replace escaped chevron(s)
  no_escaped = aTextLine.gsub(/\\[<>]/, '--')

  # var. equals count_of(<) -  count_of(>): can only be 0 or temporarily 1
  unbalance = 0

  no_escaped.each_char do |ch|
    case ch
      when '<' then unbalance += 1
      when '>' then unbalance -= 1
    end
    suffix = "opening chevron '<'."
    raise(StandardError, 'Nested ' + suffix) if unbalance > 1
    raise(StandardError, 'Missing ' + suffix) if unbalance.negative?
  end

  raise(StandardError, "Missing closing chevron '>'.") if unbalance == 1
end

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



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/macros4cuke/templating/engine.rb', line 110

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) (defaults to: {})

    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.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/macros4cuke/templating/engine.rb', line 68

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.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/macros4cuke/templating/engine.rb', line 88

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)
      end
    end

    vars.flatten.uniq
  end

  return @variables
end