mirror of
https://github.com/CrowCpp/Crow.git
synced 2024-06-07 21:10:44 +00:00
mustache partial implementation
This commit is contained in:
parent
9d1d65b08c
commit
615e648260
83
mustache.h
83
mustache.h
@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <functional>
|
||||
#include "json.h"
|
||||
namespace crow
|
||||
{
|
||||
@ -8,6 +11,8 @@ namespace crow
|
||||
{
|
||||
using context = json::wvalue;
|
||||
|
||||
template_t load(const std::string& filename);
|
||||
|
||||
class invalid_template_exception : public std::exception
|
||||
{
|
||||
public:
|
||||
@ -138,19 +143,31 @@ namespace crow
|
||||
}
|
||||
}
|
||||
|
||||
void render_internal(int actionBegin, int actionEnd, std::vector<context*>& stack, std::string& out)
|
||||
void render_internal(int actionBegin, int actionEnd, std::vector<context*>& stack, std::string& out, int indent)
|
||||
{
|
||||
int current = actionBegin;
|
||||
|
||||
if (indent)
|
||||
out.insert(out.size(), indent, ' ');
|
||||
|
||||
while(current < actionEnd)
|
||||
{
|
||||
auto& fragment = fragments_[current];
|
||||
auto& action = actions_[current];
|
||||
out.insert(out.size(), body_, fragment.first, fragment.second-fragment.first);
|
||||
render_fragment(fragment, indent, out);
|
||||
switch(action.t)
|
||||
{
|
||||
case ActionType::Ignore:
|
||||
// do nothing
|
||||
break;
|
||||
case ActionType::Partial:
|
||||
{
|
||||
std::string partial_name = tag_name(action);
|
||||
auto partial_templ = load(partial_name);
|
||||
int partial_indent = action.pos;
|
||||
partial_templ.render_internal(0, partial_templ.fragments_.size()-1, stack, out, partial_indent?indent+partial_indent:0);
|
||||
}
|
||||
break;
|
||||
case ActionType::UnescapeTag:
|
||||
case ActionType::Tag:
|
||||
{
|
||||
@ -218,7 +235,7 @@ namespace crow
|
||||
for(auto it = ctx.l->begin(); it != ctx.l->end(); ++it)
|
||||
{
|
||||
stack.push_back(&*it);
|
||||
render_internal(current+1, action.pos, stack, out);
|
||||
render_internal(current+1, action.pos, stack, out, indent);
|
||||
stack.pop_back();
|
||||
}
|
||||
current = action.pos;
|
||||
@ -248,7 +265,21 @@ namespace crow
|
||||
current++;
|
||||
}
|
||||
auto& fragment = fragments_[actionEnd];
|
||||
out.insert(out.size(), body_, fragment.first, fragment.second - fragment.first);
|
||||
render_fragment(fragment, indent, out);
|
||||
}
|
||||
void render_fragment(const std::pair<int, int> fragment, int indent, std::string& out)
|
||||
{
|
||||
if (indent)
|
||||
{
|
||||
for(int i = fragment.first; i < fragment.second; i ++)
|
||||
{
|
||||
out += body_[i];
|
||||
if (body_[i] == '\n' && i+1 != (int)body_.size())
|
||||
out.insert(out.size(), indent, ' ');
|
||||
}
|
||||
}
|
||||
else
|
||||
out.insert(out.size(), body_, fragment.first, fragment.second-fragment.first);
|
||||
}
|
||||
public:
|
||||
std::string render(context& ctx)
|
||||
@ -257,7 +288,7 @@ namespace crow
|
||||
stack.emplace_back(&ctx);
|
||||
|
||||
std::string ret;
|
||||
render_internal(0, fragments_.size()-1, stack, ret);
|
||||
render_internal(0, fragments_.size()-1, stack, ret, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -337,7 +368,6 @@ namespace crow
|
||||
while(body_[idx] == ' ') idx++;
|
||||
while(body_[endIdx-1] == ' ') endIdx--;
|
||||
actions_.emplace_back(ActionType::Partial, idx, endIdx);
|
||||
throw invalid_template_exception("{{>: partial not implemented: " + body_.substr(idx+1, endIdx-idx-1));
|
||||
break;
|
||||
case '{':
|
||||
if (tag_open != "{{" || tag_close != "}}")
|
||||
@ -444,6 +474,10 @@ namespace crow
|
||||
k + 1 < (int)body_.size() &&
|
||||
body_[k+1] == '\n')))
|
||||
continue;
|
||||
if (actions_[i].t == ActionType::Partial)
|
||||
{
|
||||
actions_[i].pos = fragment_before.second - j - 1;
|
||||
}
|
||||
fragment_before.second = j+1;
|
||||
if (!all_space_after)
|
||||
{
|
||||
@ -465,5 +499,42 @@ namespace crow
|
||||
{
|
||||
return template_t(body);
|
||||
}
|
||||
namespace detail
|
||||
{
|
||||
std::string template_base_directory = "templates";
|
||||
}
|
||||
|
||||
std::string default_loader(const std::string& filename)
|
||||
{
|
||||
std::ifstream inf(detail::template_base_directory + filename);
|
||||
if (!inf)
|
||||
return {};
|
||||
return {std::istreambuf_iterator<char>(inf), std::istreambuf_iterator<char>()};
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
std::function<std::string (std::string)> loader = default_loader;
|
||||
}
|
||||
|
||||
void set_base(const std::string& path)
|
||||
{
|
||||
detail::template_base_directory = path;
|
||||
if (detail::template_base_directory.back() != '\\' &&
|
||||
detail::template_base_directory.back() != '/')
|
||||
{
|
||||
detail::template_base_directory += '/';
|
||||
}
|
||||
}
|
||||
|
||||
void set_loader(std::function<std::string(std::string)> loader)
|
||||
{
|
||||
detail::loader = std::move(loader);
|
||||
}
|
||||
|
||||
template_t load(const std::string& filename)
|
||||
{
|
||||
return compile(detail::loader(filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
all:
|
||||
$(CXX) -std=c++11 -g -o mustachetest mustachetest.cc
|
||||
$(CXX) -Wall -std=c++11 -g -o mustachetest mustachetest.cc
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f mustachetest *.o
|
||||
|
@ -11,15 +11,22 @@ using namespace crow::mustache;
|
||||
string read_all(const string& filename)
|
||||
{
|
||||
ifstream is(filename);
|
||||
string ret;
|
||||
copy(istreambuf_iterator<char>(is), istreambuf_iterator<char>(), back_inserter(ret));
|
||||
return ret;
|
||||
return {istreambuf_iterator<char>(is), istreambuf_iterator<char>()};
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
auto data = json::load(read_all("data"));
|
||||
auto templ = compile(read_all("template"));
|
||||
auto partials = json::load(read_all("partials"));
|
||||
set_loader([&](std::string name)->std::string
|
||||
{
|
||||
if (partials.count(name))
|
||||
{
|
||||
return partials[name].s();
|
||||
}
|
||||
return "";
|
||||
});
|
||||
context ctx(data);
|
||||
cout << templ.render(ctx);
|
||||
return 0;
|
||||
|
@ -8,14 +8,17 @@ for testfile in glob.glob("*.json"):
|
||||
for test in testdoc["tests"]:
|
||||
if "lambda" in test["data"]:
|
||||
continue
|
||||
if "partials" in test:
|
||||
#print testfile, test["name"]
|
||||
continue
|
||||
open('data', 'w').write(json.dumps(test["data"]))
|
||||
open('template', 'w').write(test["template"])
|
||||
if "partials" in test:
|
||||
open('partials', 'w').write(json.dumps(test["partials"]))
|
||||
else:
|
||||
open('partials', 'w').write("{}")
|
||||
ret = subprocess.check_output("./mustachetest")
|
||||
print testfile, test["name"]
|
||||
if ret != test["expected"]:
|
||||
if 'partials' in test:
|
||||
print 'partials:', json.dumps(test["partials"])
|
||||
print json.dumps(test["data"])
|
||||
print test["template"]
|
||||
print 'Expected:',repr(test["expected"])
|
||||
@ -23,3 +26,4 @@ for testfile in glob.glob("*.json"):
|
||||
assert ret == test["expected"]
|
||||
os.unlink('data')
|
||||
os.unlink('template')
|
||||
os.unlink('partials')
|
||||
|
12
unittest.cpp
12
unittest.cpp
@ -414,6 +414,18 @@ TEST(template_basic)
|
||||
//crow::mustache::load("basic.mustache");
|
||||
}
|
||||
|
||||
TEST(template_load)
|
||||
{
|
||||
crow::mustache::set_base(".");
|
||||
ofstream("test.mustache") << R"---(attack of {{name}})---";
|
||||
auto t = crow::mustache::load("test.mustache");
|
||||
crow::mustache::context ctx;
|
||||
ctx["name"] = "killer tomatoes";
|
||||
auto result = t.render(ctx);
|
||||
ASSERT_EQUAL("attack of killer tomatoes", result);
|
||||
unlink("test.mustache");
|
||||
}
|
||||
|
||||
int testmain()
|
||||
{
|
||||
bool failed = false;
|
||||
|
Loading…
Reference in New Issue
Block a user