Skip to content

Commit

Permalink
Compiler: add support for local sub templates
Browse files Browse the repository at this point in the history
- Sub templates stored in local vars are compiled on the fly, and the compiled version is cached.
  • Loading branch information
noteflakes committed Feb 19, 2024
1 parent deb9913 commit 6e836d3
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 10 deletions.
52 changes: 42 additions & 10 deletions lib/papercraft/compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,26 @@
require 'escape_utils'

module Papercraft
def self.__cache_compiled_template__(value)
@compiled_template_cache ||= {}

if !(compiled = @compiled_template_cache[value])
value = Papercraft.html(&value) if value.is_a?(Proc)
compiled = value.compile.to_proc
@compiled_template_cache[value] = compiled
end
end

def self.__emit__(value, __buf__, *args)
case value
when Proc, Papercraft::Template
compiled = __cache_compiled_template__(value)
compiled.(__buf__, *args)
else
__buf__ << CGI.escapeHTML(value.to_s)
end
end

# The Compiler class compiles Papercraft templates
class Compiler
DEFAULT_CODE_BUFFER_CAPACITY = 8192
Expand Down Expand Up @@ -210,7 +230,7 @@ def parse_fcall(node, block = nil)
when :html5
return emit_html5(node, block)
when :emit
return emit_emit(args.first, block)
return emit_emit(node.children[1], block)
when :text
return emit_text_fcall(args.first)
end
Expand Down Expand Up @@ -293,15 +313,23 @@ def emit_tag(tag, atts, &block)
end
end

def emit_emit(node, block)
case node.type
def emit_emit(args, block)
value, *rest = args.children.compact
case value.type
when :STR, :LIT, :SYM
value = node.children.first.to_s
value = value.children.first.to_s
emit_output { emit_literal(value) }
when :VCALL
emit_code("__buf__ << #{node.children.first}\n")
emit_code("__buf__ << #{value.children.first}\n")
when :DVAR
emit_code("Papercraft.__emit__(#{value.children.first}, __buf__")
if !rest.empty?
emit_code(", ")
parse_list(args, false, 1..-1)
end
emit_code(")\n")
when :CONST
name = node.children.first.to_s
name = value.children.first.to_s
value = get_const(name)
case value
when Papercraft::Template
Expand Down Expand Up @@ -430,17 +458,21 @@ def parse_false(node)
emit_expression { emit_literal('true') }
end

def parse_list(node)
emit_literal('[')
items = node.children.compact
def parse_list(node, emit_array = true, range = nil)
emit_literal('[') if emit_array
if range
items = node.children[range].compact
else
items = node.children.compact
end
while true
item = items.shift
break unless item

parse(item)
emit_literal(', ') if !items.empty?
end
emit_literal(']')
emit_literal(']') if emit_array
end

def parse_vcall(node)
Expand Down
19 changes: 19 additions & 0 deletions test/test_compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -426,4 +426,23 @@ def test_ivar
assert_equal template_body(expected), t.code_buffer.chomp
assert_equal '<p>bar</p>', t.to_proc.render
end

def test_sub_template_with_args
sub = ->(x) {
p x
}
t = c {
div {
emit sub, 40 + 2
}
}

expected = <<~RUBY
__buf__ << "<div>"
Papercraft.__emit__(sub, __buf__, 40 + 2)
__buf__ << "</div>"
RUBY
assert_equal template_body(expected), t.code_buffer.chomp
assert_equal '<div><p>42</p></div>', t.to_proc.render
end
end

0 comments on commit 6e836d3

Please sign in to comment.