From 6df5c94fd99b6b81772d2da4505147e39e8f2869 Mon Sep 17 00:00:00 2001 From: The-EDev Date: Sat, 3 Jul 2021 23:28:52 +0300 Subject: [PATCH 1/7] basic lambda support added (no expansion / blocks) --- include/crow/json.h | 34 ++++++++++++++++++++++++++++++---- include/crow/mustache.h | 14 ++++++++++---- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/include/crow/json.h b/include/crow/json.h index d53dee275..17e4b4aa5 100644 --- a/include/crow/json.h +++ b/include/crow/json.h @@ -88,6 +88,7 @@ namespace crow String, List, Object, + Function }; inline const char* get_type_str(type t) { @@ -98,6 +99,7 @@ namespace crow case type::List: return "List"; case type::String: return "String"; case type::Object: return "Object"; + case type::Function: return "Function"; default: return "Unknown"; } } @@ -771,6 +773,7 @@ namespace crow os << '}'; } break; + case type::Function: os << "custom function"; break; } return os; } @@ -1236,6 +1239,7 @@ namespace crow } num; ///< Value if type is a number. std::string s; ///< Value if type is a string. std::unique_ptr> l; ///< Value if type is a list. + std::function f; //Value if type is a function (C++ lambda) #ifdef CROW_JSON_USE_MAP std::unique_ptr> o; #else @@ -1264,6 +1268,7 @@ namespace crow case type::Null: case type::False: case type::True: + case type::Function: return; case type::Number: nt = r.nt(); @@ -1330,6 +1335,8 @@ namespace crow #endif o->insert(r.o->begin(), r.o->end()); return; + case type::Function: + f = r.f; } } @@ -1457,7 +1464,7 @@ namespace crow return *this; } - wvalue& operator=(const char* str) + wvalue& operator = (const char* str) { reset(); t_ = type::String; @@ -1465,7 +1472,7 @@ namespace crow return *this; } - wvalue& operator=(const std::string& str) + wvalue& operator = (const std::string& str) { reset(); t_ = type::String; @@ -1473,7 +1480,7 @@ namespace crow return *this; } - wvalue& operator=(std::vector&& v) + wvalue& operator = (std::vector&& v) { if (t_ != type::List) reset(); @@ -1491,7 +1498,7 @@ namespace crow } template - wvalue& operator=(const std::vector& v) + wvalue& operator = (const std::vector& v) { if (t_ != type::List) reset(); @@ -1508,6 +1515,14 @@ namespace crow return *this; } + wvalue& operator = (std::function&& func) + { + reset(); + t_ = type::Function; + f = std::move(func); + return *this; + } + wvalue& operator[](unsigned index) { if (t_ != type::List) @@ -1555,6 +1570,13 @@ namespace crow return result; } + std::string execute(std::string txt = "") const //Not using reference because it cannot be used with a default rvalue + { + if (t_ != type::Function) + return ""; + return f(txt); + } + /// If the wvalue is a list, it returns the length of the list, otherwise it returns 1. std::size_t size() const { @@ -1600,6 +1622,8 @@ namespace crow } return sum+2; } + case type::Function: + return 0; } return 1; } @@ -1685,6 +1709,8 @@ namespace crow out.push_back('}'); } break; + case type::Function: + out += "custom function"; break; } } diff --git a/include/crow/mustache.h b/include/crow/mustache.h index 5be8db737..54dca14e8 100644 --- a/include/crow/mustache.h +++ b/include/crow/mustache.h @@ -45,13 +45,13 @@ namespace crow int end; int pos; ActionType t; - Action(ActionType t, size_t start, size_t end, size_t pos = 0) + Action(ActionType t, size_t start, size_t end, size_t pos = 0) : start(static_cast(start)), end(static_cast(end)), pos(static_cast(pos)), t(t) {} }; /// A mustache template object. - class template_t + class template_t { public: template_t(std::string body) @@ -215,8 +215,14 @@ namespace crow else out += ctx.s; break; + case json::type::Function: + if (action.t == ActionType::Tag) + escape(ctx.execute(), out); + else + out += ctx.execute(); + break; default: - throw std::runtime_error("not implemented tag type" + boost::lexical_cast(static_cast(ctx.t()))); + throw std::runtime_error(std::string("not implemented tag type: ") + json::get_type_str(ctx.t())); } } break; @@ -341,7 +347,7 @@ namespace crow std::string tag_close = "}}"; std::vector blockPositions; - + size_t current = 0; while(1) { From 228e61d1503f5f9c560e8222d130b273f6b80cc9 Mon Sep 17 00:00:00 2001 From: The-EDev Date: Fri, 10 Dec 2021 05:17:25 +0300 Subject: [PATCH 2/7] Added mustache lambda expansion --- docs/guides/templating.md | 6 +++++- examples/example.cpp | 10 ++++++++++ include/crow/mustache.h | 28 ++++++++++++++++++++++------ tests/unittest.cpp | 10 ++++++++++ 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/docs/guides/templating.md b/docs/guides/templating.md index 92c982ecb..cdf1952b3 100644 --- a/docs/guides/templating.md +++ b/docs/guides/templating.md @@ -15,7 +15,11 @@ The HTML page (including the mustache tags). It is usually loaded into `crow::mu For more information on how to formulate a template, see [this mustache manual](http://mustache.github.io/mustache.5.html). ### Context -A JSON object containing the tags as keys and their values. `crow::mustache::context` is actually a [crow::json::wvalue](../json#wvalue). +A JSON object containing the tags as keys and their values. `crow::mustache::context` is actually a [crow::json::wvalue](../json#wvalue).

+ +!!! note + + `crow::mustache::context` can take a C++ lambda as a value. The lambda needs to take a string as an argument and return a string, such as `#!cpp ctx[lmd] = [&](std::string){return "Hello World";};`. ## Returning a template To return a mustache template, you need to load a page using `#!cpp auto page = crow::mustache::load("path/to/template.html");`, keep in mind that the path is relative to the templates directory.
diff --git a/examples/example.cpp b/examples/example.cpp index 9c3c466d5..619bb0082 100644 --- a/examples/example.cpp +++ b/examples/example.cpp @@ -123,6 +123,16 @@ int main() res.end(); }); + //same as the example above but uses mustache instead of stringstream + CROW_ROUTE(app,"/add_mustache//") + ([](int a, int b){ + auto t = crow::mustache::compile("Value is {{func}}"); + crow::mustache::context ctx; + ctx["func"] = [&](std::string){return std::to_string(a+b);}; + auto result = t.render(ctx); + return result; + }); + // Compile error with message "Handler type is mismatched with URL paramters" //CROW_ROUTE(app,"/another/") //([](int a, int b){ diff --git a/include/crow/mustache.h b/include/crow/mustache.h index a8e9cf76b..f06d64692 100644 --- a/include/crow/mustache.h +++ b/include/crow/mustache.h @@ -221,12 +221,28 @@ namespace crow else out += ctx.s; break; - case json::type::Function: - if (action.t == ActionType::Tag) - escape(ctx.execute(), out); - else - out += ctx.execute(); - break; + case json::type::Function: + if (action.t == ActionType::Tag) + { + std::string execute_result = ctx.execute(); + while (execute_result.find("{{") != std::string::npos) + { + template_t result_plug(execute_result); + execute_result = result_plug.render(*(stack[0])); + } + escape(execute_result, out); + } + else + { + std::string execute_result = ctx.execute(); + while (execute_result.find("{{") != std::string::npos) + { + template_t result_plug(execute_result); + execute_result = result_plug.render(*(stack[0])); + } + out += execute_result; + } + break; default: throw std::runtime_error("not implemented tag type" + boost::lexical_cast(static_cast(ctx.t()))); } diff --git a/tests/unittest.cpp b/tests/unittest.cpp index 5cb1eaa64..a5c1e65cb 100644 --- a/tests/unittest.cpp +++ b/tests/unittest.cpp @@ -1139,6 +1139,16 @@ TEST_CASE("template_basic") CHECK("attack of killer tomatoes" == result); } // template_basic +TEST_CASE("template_function") +{ + auto t = crow::mustache::compile("attack of {{func}}"); + crow::mustache::context ctx; + ctx["name"] = "killer tomatoes"; + ctx["func"] = [&](std::string){return std::string("{{name}}, IN SPACE!");}; + auto result = t.render(ctx); + CHECK("attack of killer tomatoes, IN SPACE!" == result); +} + TEST_CASE("template_load") { crow::mustache::set_base("."); From f6fd7dc85dd24c2e2af6ad8dcfea413b8e090bb4 Mon Sep 17 00:00:00 2001 From: The-EDev Date: Sat, 11 Dec 2021 02:38:30 +0300 Subject: [PATCH 3/7] formatting, missing newline fixed --- include/crow/json.h | 26 ++++++++++++++------------ include/crow/mustache.h | 32 ++++++++++++++++---------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/include/crow/json.h b/include/crow/json.h index 97d508bb4..006961432 100644 --- a/include/crow/json.h +++ b/include/crow/json.h @@ -768,7 +768,7 @@ namespace crow os << '}'; } break; - case type::Function: os << "custom function"; break; + case type::Function: os << "custom function"; break; } return os; } @@ -1262,10 +1262,11 @@ namespace crow si(value) {} constexpr number(double value) noexcept: d(value) {} - } num; ///< Value if type is a number. - std::string s; ///< Value if type is a string. - std::unique_ptr l; ///< Value if type is a list. - std::unique_ptr o; ///< Value if type is a JSON object. std::function f; //Value if type is a function (C++ lambda) + } num; ///< Value if type is a number. + std::string s; ///< Value if type is a string. + std::unique_ptr l; ///< Value if type is a list. + std::unique_ptr o; ///< Value if type is a JSON object. + std::function f; ///< Value if type is a function (C++ lambda) public: wvalue(): @@ -1535,7 +1536,7 @@ namespace crow return *this; } - wvalue& operator = (const char* str) + wvalue& operator=(const char* str) { reset(); t_ = type::String; @@ -1543,7 +1544,7 @@ namespace crow return *this; } - wvalue& operator = (const std::string& str) + wvalue& operator=(const std::string& str) { reset(); t_ = type::String; @@ -1639,14 +1640,14 @@ namespace crow return *this; } - wvalue& operator = (std::function&& func) + wvalue& operator=(std::function&& func) { reset(); t_ = type::Function; f = std::move(func); return *this; } - + wvalue& operator[](unsigned index) { if (t_ != type::List) @@ -1870,9 +1871,10 @@ namespace crow out.push_back('}'); } break; - - case type::Function: - out += "custom function"; break; + + case type::Function: + out += "custom function"; + break; } } diff --git a/include/crow/mustache.h b/include/crow/mustache.h index f06d64692..e858a7e8c 100644 --- a/include/crow/mustache.h +++ b/include/crow/mustache.h @@ -221,28 +221,28 @@ namespace crow else out += ctx.s; break; - case json::type::Function: - if (action.t == ActionType::Tag) + case json::type::Function: + if (action.t == ActionType::Tag) + { + std::string execute_result = ctx.execute(); + while (execute_result.find("{{") != std::string::npos) { - std::string execute_result = ctx.execute(); - while (execute_result.find("{{") != std::string::npos) - { - template_t result_plug(execute_result); - execute_result = result_plug.render(*(stack[0])); - } - escape(execute_result, out); + template_t result_plug(execute_result); + execute_result = result_plug.render(*(stack[0])); } - else + escape(execute_result, out); + } + else + { + std::string execute_result = ctx.execute(); + while (execute_result.find("{{") != std::string::npos) { - std::string execute_result = ctx.execute(); - while (execute_result.find("{{") != std::string::npos) - { template_t result_plug(execute_result); execute_result = result_plug.render(*(stack[0])); - } - out += execute_result; } - break; + out += execute_result; + } + break; default: throw std::runtime_error("not implemented tag type" + boost::lexical_cast(static_cast(ctx.t()))); } From 5c806252f06dbd5e1c8587fb0f7bff6dd6943a27 Mon Sep 17 00:00:00 2001 From: The-EDev Date: Tue, 14 Dec 2021 06:55:59 +0300 Subject: [PATCH 4/7] formatted example.cpp --- examples/example.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/example.cpp b/examples/example.cpp index 619bb0082..85be76c17 100644 --- a/examples/example.cpp +++ b/examples/example.cpp @@ -124,13 +124,15 @@ int main() }); //same as the example above but uses mustache instead of stringstream - CROW_ROUTE(app,"/add_mustache//") - ([](int a, int b){ - auto t = crow::mustache::compile("Value is {{func}}"); - crow::mustache::context ctx; - ctx["func"] = [&](std::string){return std::to_string(a+b);}; - auto result = t.render(ctx); - return result; + CROW_ROUTE(app, "/add_mustache//") + ([](int a, int b) { + auto t = crow::mustache::compile("Value is {{func}}"); + crow::mustache::context ctx; + ctx["func"] = [&](std::string) { + return std::to_string(a + b); + }; + auto result = t.render(ctx); + return result; }); // Compile error with message "Handler type is mismatched with URL paramters" From cca1d54ce5a06b3ea19f458366965e8bcb507a8b Mon Sep 17 00:00:00 2001 From: Farook Al-Sammarraie Date: Fri, 17 Dec 2021 09:07:46 +0300 Subject: [PATCH 5/7] took repeated code out of the if else statement --- include/crow/mustache.h | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/include/crow/mustache.h b/include/crow/mustache.h index e858a7e8c..d14dccddf 100644 --- a/include/crow/mustache.h +++ b/include/crow/mustache.h @@ -222,26 +222,18 @@ namespace crow out += ctx.s; break; case json::type::Function: - if (action.t == ActionType::Tag) + std::string execute_result = ctx.execute(); + while (execute_result.find("{{") != std::string::npos) { - std::string execute_result = ctx.execute(); - while (execute_result.find("{{") != std::string::npos) - { - template_t result_plug(execute_result); - execute_result = result_plug.render(*(stack[0])); - } - escape(execute_result, out); + template_t result_plug(execute_result); + execute_result = result_plug.render(*(stack[0])); } + + if (action.t == ActionType::Tag) + escape(execute_result, out); else - { - std::string execute_result = ctx.execute(); - while (execute_result.find("{{") != std::string::npos) - { - template_t result_plug(execute_result); - execute_result = result_plug.render(*(stack[0])); - } out += execute_result; - } + break; default: throw std::runtime_error("not implemented tag type" + boost::lexical_cast(static_cast(ctx.t()))); From 0ef48e82bb2238568142d19bd1b53581365bc9d6 Mon Sep 17 00:00:00 2001 From: Farook Al-Sammarraie Date: Fri, 17 Dec 2021 09:30:58 +0300 Subject: [PATCH 6/7] Fixed scoping issue introduced by initializing `execute_result` --- include/crow/mustache.h | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/include/crow/mustache.h b/include/crow/mustache.h index d14dccddf..b87bb9892 100644 --- a/include/crow/mustache.h +++ b/include/crow/mustache.h @@ -222,18 +222,19 @@ namespace crow out += ctx.s; break; case json::type::Function: - std::string execute_result = ctx.execute(); - while (execute_result.find("{{") != std::string::npos) { - template_t result_plug(execute_result); - execute_result = result_plug.render(*(stack[0])); - } - - if (action.t == ActionType::Tag) - escape(execute_result, out); - else - out += execute_result; + std::string execute_result = ctx.execute(); + while (execute_result.find("{{") != std::string::npos) + { + template_t result_plug(execute_result); + execute_result = result_plug.render(*(stack[0])); + } + if (action.t == ActionType::Tag) + escape(execute_result, out); + else + out += execute_result; + } break; default: throw std::runtime_error("not implemented tag type" + boost::lexical_cast(static_cast(ctx.t()))); From fa77468a88f572d379df8342a2f48bcfe517fc75 Mon Sep 17 00:00:00 2001 From: Farook Al-Sammarraie Date: Fri, 17 Dec 2021 09:45:50 +0300 Subject: [PATCH 7/7] Formatting --- include/crow/mustache.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/include/crow/mustache.h b/include/crow/mustache.h index b87bb9892..d5d0cb751 100644 --- a/include/crow/mustache.h +++ b/include/crow/mustache.h @@ -222,20 +222,20 @@ namespace crow out += ctx.s; break; case json::type::Function: + { + std::string execute_result = ctx.execute(); + while (execute_result.find("{{") != std::string::npos) { - std::string execute_result = ctx.execute(); - while (execute_result.find("{{") != std::string::npos) - { - template_t result_plug(execute_result); - execute_result = result_plug.render(*(stack[0])); - } - - if (action.t == ActionType::Tag) - escape(execute_result, out); - else - out += execute_result; + template_t result_plug(execute_result); + execute_result = result_plug.render(*(stack[0])); } - break; + + if (action.t == ActionType::Tag) + escape(execute_result, out); + else + out += execute_result; + } + break; default: throw std::runtime_error("not implemented tag type" + boost::lexical_cast(static_cast(ctx.t()))); }