Class: Serbea::TemplateEngine

Inherits:
Erubi::CaptureEndEngine
  • Object
show all
Defined in:
lib/serbea/template_engine.rb

Constant Summary collapse

FRONT_MATTER_REGEXP =
%r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m.freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input, properties = {}) ⇒ TemplateEngine



23
24
25
26
27
# File 'lib/serbea/template_engine.rb', line 23

def initialize(input, properties={})
  properties[:regexp] = /{%(\:?={1,2}|-|\#|%|\:)?(.*?)([-=])?%}([ \t]*\r?\n)?/m
  properties[:strip_front_matter] = true unless properties.key?(:strip_front_matter)
  super process_serbea_input(input, properties), properties
end

Class Method Details

.front_matter_preambleObject



15
16
17
# File 'lib/serbea/template_engine.rb', line 15

def self.front_matter_preamble
  @front_matter_preamble ||= "frontmatter = YAML.load"
end

.front_matter_preamble=(varname) ⇒ Object



12
13
14
# File 'lib/serbea/template_engine.rb', line 12

def self.front_matter_preamble=(varname)
  @front_matter_preamble = varname
end

.has_yaml_header?(template) ⇒ Boolean



19
20
21
# File 'lib/serbea/template_engine.rb', line 19

def self.has_yaml_header?(template)
  template.lines.first&.match? %r!\A---\s*\r?\n!
end

.render_directiveObject



8
9
10
# File 'lib/serbea/template_engine.rb', line 8

def self.render_directive
  @render_directive ||= "render"
end

.render_directive=(directive) ⇒ Object



5
6
7
# File 'lib/serbea/template_engine.rb', line 5

def self.render_directive=(directive)
  @render_directive = directive
end

Instance Method Details

#add_postamble(postamble) ⇒ Object



29
30
31
32
33
34
35
36
37
# File 'lib/serbea/template_engine.rb', line 29

def add_postamble(postamble)
  src << postamble
  src << "@_erbout.html_safe" if postamble.respond_to?(:html_safe)
  
  src.gsub!("__RAW_START_PRINT__", "{{")
  src.gsub!("__RAW_END_PRINT__", "}}")
  src.gsub!("__RAW_START_EVAL__", "{%")
  src.gsub!("__RAW_END_EVAL__", "%}")
end

#process_serbea_input(template, properties) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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
# File 'lib/serbea/template_engine.rb', line 39

def process_serbea_input(template, properties)
  buff = ""
  
  string = template.dup
  if properties[:strip_front_matter] && self.class.has_yaml_header?(string)
    if string = string.match(FRONT_MATTER_REGEXP)
      require "yaml" if self.class.front_matter_preamble.include?(" = YAML.load")

      string = "{% #{self.class.front_matter_preamble} <<~YAMLDATA\n" + 
        string[1].sub(/^---\n/,'') +
        "YAMLDATA\n%}" +
        string[2].sub(/^---\n/, '') +
        string.post_match
    end
  end
  
  # Ensure the raw "tag" will strip out all ERB-style processing
  until string.empty?
    text, code, string = string.partition(/{% raw %}.*?{% endraw %}/m)
  
    buff << text
    if code.length > 0
      buff << code.
        sub("{% raw %}", "").
        sub("{% endraw %}", "").
        gsub("{{", "__RAW_START_PRINT__").
        gsub("}}", "__RAW_END_PRINT__").
        gsub("{%", "__RAW_START_EVAL__").
        gsub("%}", "__RAW_END_EVAL__")
    end
  end

  # Process any pipelines
  string = buff
  buff = ""
  until string.empty?
    text, code, string = string.partition(/{{.*?}}/m)
  
    buff << text
    if code.length > 0
      original_line_length = code.lines.size
      processed_filters = false
  
      code = code.gsub('\|', "__PIPE_C__")
  
      subs = code.gsub(/\s*\|>?\s+(.*?)\s([^|}]*)/) do
        args = $2
        args = nil if args.strip == ""
        prefix = processed_filters ? ")" : "))"
        processed_filters = true
        "#{prefix}.filter(:#{$1.chomp(":")}" + (args ? ", #{args}" : "")
      end
  
      pipeline_suffix = processed_filters ? ") %}" : ")) %}"
  
      subs = subs.sub("{{", "{%= pipeline(self, (").sub("}}", pipeline_suffix).gsub("__PIPE_C__", '\|')

      buff << subs

      (original_line_length - subs.lines.size).times do
        buff << "\n{% %}" # preserve original line length
      end
    end
  end

  # Process any directives
  #
  # TODO: allow custom directives! aka
  # {%@something whatever %}
  # {%@script
  #   const foo = "really? #{really}!"
  #   alert(foo.toLowerCase())
  # %}
  # {%@preact AwesomeChart data={#{ruby_data.to_json}} %}
  string = buff
  buff = ""
  until string.empty?
    text, code, string = string.partition(/{%@.*?%}/m)
  
    buff << text
    if code.length > 0
      code.sub! /^\{%@/, ""
      code.sub! /%}$/, ""
      unless ["end", ""].include? code.strip
        original_line_length = code.lines.size

        pieces = code.split(" ")
        if pieces[0].start_with?(/[A-Z]/) # Ruby class name
          pieces[0].prepend " "
          pieces[0] << ".new("
        else # string or something else
          pieces[0].prepend "("
        end

        includes_block = false
        pieces.reverse.each do |piece|
          if piece == "do" && (pieces.last == "do" || pieces.last.end_with?("|"))
            piece.prepend(") ")
            includes_block = true
            break
          end
        end

        if includes_block
          buff << "{%:= #{self.class.render_directive}#{pieces.join(" ")} %}"
        else
          pieces.last << ")"
          buff << "{%= #{self.class.render_directive}#{pieces.join(" ")} %}"
        end
        (original_line_length - 1).times do
          buff << "\n{% %}" # preserve original directive line length
        end
      else
        buff << "{%: end %}"
      end
    end
  end

  buff
end