From 8273752ab4a22b3c05c9c1f801a8ddf07a8f66de Mon Sep 17 00:00:00 2001 From: Dennis Schridde Date: Wed, 1 Feb 2017 18:58:03 +0100 Subject: [PATCH] Support explicit iteration over arrays and maps Explicit iteration blocks are e.g. necessary to support iteration over maps/hashes, as in the following example: ```mustache Key/value pairs: {{*hashmap}} - {{k}} = {{v}} {{/hashmap}} ``` I did not include tests, because their data is included in the corresponding pull request to the Mustache spec. See-Also: https://github.com/mustache/spec/pull/23 --- src/lustache/renderer.lua | 53 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/lustache/renderer.lua b/src/lustache/renderer.lua index b190d52..8d00d59 100755 --- a/src/lustache/renderer.lua +++ b/src/lustache/renderer.lua @@ -12,7 +12,7 @@ local patterns = { nonSpace = "%S", eq = "%s*=", curly = "%s*}", - tag = "[#\\^/>{&=!]" + tag = "[#\\^/>{&=!*]" } local html_escape_characters = { @@ -27,6 +27,7 @@ local html_escape_characters = { local block_tags = { ["#"] = true, ["^"] = true, + ["*"] = true, } local function is_array(array) @@ -42,6 +43,18 @@ local function is_array(array) return n == max end +local function is_map(map) + if type(map) ~= "table" then + return false + end + for k, _ in pairs(map) do + if type(k) == "number" then + return false + end + end + return true +end + -- Low-level function that compiles the given `tokens` into a -- function that accepts two arguments: a Context and a -- Renderer. @@ -63,6 +76,9 @@ local function compile_tokens(tokens, originalTemplate) for i, token in ipairs(tokens) do local t = token.type buf[#buf+1] = + t == "*" and rnd:_iterate( + token, ctx, subrender(i, token.tokens) + ) or t == "#" and rnd:_section( token, ctx, subrender(i, token.tokens), originalTemplate ) or @@ -208,6 +224,41 @@ function renderer:render(template, view, partials) return fn(view) end +function renderer:_iterate(token, context, callback) + local value = context:lookup(token.value) + + if type(value) == "table" then + local sub_type + if is_array(value) then + sub_type = "array" + elseif is_map(value) then + sub_type = "map" + end + + if sub_type == "array" then + local buffer = "" + + for i,v in ipairs(value) do + buffer = buffer .. callback(context:push(v), self) + end + + return buffer + elseif sub_type == "map" then + local buffer = "" + + for k,v in pairs(value) do + buffer = buffer .. callback(context:push({key = k, value = v}), self) + end + + return buffer + end + + return error("Can only iterate over arrays and maps, got neither:" .. token.value) + end + + return error("Can only iterate over pure arrays and maps (tables), got " .. type(value) .. ":" .. token.value) +end + function renderer:_section(token, context, callback, originalTemplate) local value = context:lookup(token.value)