Skip to content

Commit

Permalink
Merge pull request #299 from CrowCpp/mustache_lambdas
Browse files Browse the repository at this point in the history
Mustache lambda support
  • Loading branch information
The-EDev authored Dec 17, 2021
2 parents 51d6d05 + fa77468 commit 680fb4c
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 5 deletions.
6 changes: 5 additions & 1 deletion docs/guides/templating.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).<br><br>

!!! 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.<br>
Expand Down
12 changes: 12 additions & 0 deletions examples/example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,18 @@ int main()
res.end();
});

//same as the example above but uses mustache instead of stringstream
CROW_ROUTE(app, "/add_mustache/<int>/<int>")
([](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>")
//([](int a, int b){
Expand Down
36 changes: 32 additions & 4 deletions include/crow/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ namespace crow
String,
List,
Object,
Function
};

inline const char* get_type_str(type t)
Expand All @@ -99,6 +100,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";
}
}
Expand Down Expand Up @@ -766,6 +768,7 @@ namespace crow
os << '}';
}
break;
case type::Function: os << "custom function"; break;
}
return os;
}
Expand Down Expand Up @@ -1259,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<list> l; ///< Value if type is a list.
std::unique_ptr<object> o; ///< Value if type is a JSON object.
} num; ///< Value if type is a number.
std::string s; ///< Value if type is a string.
std::unique_ptr<list> l; ///< Value if type is a list.
std::unique_ptr<object> o; ///< Value if type is a JSON object.
std::function<std::string(std::string&)> f; ///< Value if type is a function (C++ lambda)

public:
wvalue():
Expand Down Expand Up @@ -1342,6 +1346,7 @@ namespace crow
case type::Null:
case type::False:
case type::True:
case type::Function:
return;
case type::Number:
nt = r.nt();
Expand Down Expand Up @@ -1401,6 +1406,8 @@ namespace crow
o = std::unique_ptr<object>(new object{});
o->insert(r.o->begin(), r.o->end());
return;
case type::Function:
f = r.f;
}
}

Expand Down Expand Up @@ -1633,6 +1640,14 @@ namespace crow
return *this;
}

wvalue& operator=(std::function<std::string(std::string&)>&& func)
{
reset();
t_ = type::Function;
f = std::move(func);
return *this;
}

wvalue& operator[](unsigned index)
{
if (t_ != type::List)
Expand Down Expand Up @@ -1676,6 +1691,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
{
Expand Down Expand Up @@ -1721,6 +1743,8 @@ namespace crow
}
return sum + 2;
}
case type::Function:
return 0;
}
return 1;
}
Expand Down Expand Up @@ -1847,6 +1871,10 @@ namespace crow
out.push_back('}');
}
break;

case type::Function:
out += "custom function";
break;
}
}

Expand Down
15 changes: 15 additions & 0 deletions include/crow/mustache.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,21 @@ namespace crow
else
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;
}
break;
default:
throw std::runtime_error("not implemented tag type" + boost::lexical_cast<std::string>(static_cast<int>(ctx.t())));
}
Expand Down
10 changes: 10 additions & 0 deletions tests/unittest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(".");
Expand Down

0 comments on commit 680fb4c

Please sign in to comment.