Closed KingDuckZ closed 5 years ago
I was having a look at the code to see if I could figure out how to do this by myself and I spotted this dodgy line. Isn't that taking a reference to a temporary when a partial is invoked? It looks like this code is not correct to me.
I don't think I've seen support for this so I just added a quick implementation that works for me:
From 8b4e20256cc9da7272a2ccce213a286720e909db Mon Sep 17 00:00:00 2001
From: King_DuckZ <king_duckz@gmx.com>
Date: Thu, 2 May 2019 16:38:01 +0100
Subject: [PATCH] Add support for functors that load generic partials.
---
mustache.hpp | 47 ++++++++++++++++++++++++++++++++++++-----------
1 file changed, 36 insertions(+), 11 deletions(-)
diff --git a/mustache.hpp b/mustache.hpp
index ee88f9c..bb77345 100644
--- a/mustache.hpp
+++ b/mustache.hpp
@@ -38,6 +38,7 @@
#include <sstream>
#include <unordered_map>
#include <vector>
+#include <string>
namespace kainjow {
namespace mustache {
@@ -431,15 +432,22 @@ public:
virtual const basic_data<string_type>* get(const string_type& name) const = 0;
virtual const basic_data<string_type>* get_partial(const string_type& name) const = 0;
+ virtual const basic_lambda<string_type>* get_partial() const = 0;
};
template <typename string_type>
class context : public basic_context<string_type> {
public:
- context(const basic_data<string_type>* data) {
+ explicit context(const basic_data<string_type>* data) {
push(data);
}
+ context(const basic_data<string_type>* data, const basic_lambda<string_type>& general_partial) :
+ context(data)
+ {
+ general_partial_ = general_partial;
+ }
+
context() {
}
@@ -493,11 +501,16 @@ public:
return nullptr;
}
+ virtual const basic_lambda<string_type>* get_partial() const override {
+ return &general_partial_;
+ }
+
context(const context&) = delete;
context& operator= (const context&) = delete;
private:
std::vector<const basic_data<string_type>*> items_;
+ basic_lambda<string_type> general_partial_;
};
template <typename string_type>
@@ -900,21 +913,21 @@ public:
}
template <typename stream_type>
- stream_type& render(const basic_data<string_type>& data, stream_type& stream) {
- render(data, [&stream](const string_type& str) {
+ stream_type& render(const basic_data<string_type>& data, const basic_lambda<string_type>& partial, stream_type& stream) {
+ render(data, partial, [&stream](const string_type& str) {
stream << str;
});
return stream;
}
- string_type render(const basic_data<string_type>& data) {
+ string_type render(const basic_data<string_type>& data, const basic_lambda<string_type>& partial) {
std::basic_ostringstream<typename string_type::value_type> ss;
- return render(data, ss).str();
+ return render(data, partial, ss).str();
}
template <typename stream_type>
- stream_type& render(basic_context<string_type>& ctx, stream_type& stream) {
- context_internal<string_type> context{ctx};
+ stream_type& render(basic_context<string_type>& ctx, const basic_lambda<string_type>& partial, stream_type& stream) {
+ context_internal<string_type> context{ctx, partial};
render([&stream](const string_type& str) {
stream << str;
}, context);
@@ -923,15 +936,15 @@ public:
string_type render(basic_context<string_type>& ctx) {
std::basic_ostringstream<typename string_type::value_type> ss;
- return render(ctx, ss).str();
+ return render(ctx, basic_lambda<string_type>(), ss).str();
}
using render_handler = std::function<void(const string_type&)>;
- void render(const basic_data<string_type>& data, const render_handler& handler) {
+ void render(const basic_data<string_type>& data, const basic_lambda<string_type>& partial, const render_handler& handler) {
if (!is_valid()) {
return;
}
- context<string_type> ctx{&data};
+ context<string_type> ctx{&data, partial};
context_internal<string_type> context{ctx};
render(handler, context);
}
@@ -1024,8 +1037,19 @@ private:
}
return component<string_type>::walk_control::skip;
case tag_type::partial:
+ {
+ string_type partial_result;
if ((var = ctx.ctx.get_partial(tag.name)) != nullptr && (var->is_partial() || var->is_string())) {
- const auto& partial_result = var->is_partial() ? var->partial_value()() : var->string_value();
+ partial_result = var->is_partial() ? var->partial_value()() : var->string_value();
+ }
+ else {
+ auto* const p = ctx.ctx.get_partial();
+ if (p && *p) {
+ partial_result = (*p)(tag.name);
+ }
+ }
+
+ if (!partial_result.empty()) {
basic_mustache tmpl{partial_result};
tmpl.set_custom_escape(escape_);
if (!tmpl.is_valid()) {
@@ -1041,6 +1065,7 @@ private:
}
}
break;
+ }
case tag_type::set_delimiter:
ctx.delim_set = *comp.tag.delim_set;
break;
--
2.21.0
I didn't run any tests or tried any combination other than the one I needed, nor I have any idea if adding the new functor to context is the right thing to do or not. So please review it thoroughly :)
Any help with this please? @kainjow?
Sorry for the delay.
I think the existing partial support in the context class supports what you want to do? See https://github.com/kainjow/Mustache/blob/master/tests.cpp#L1174
I'm trying to use the {{>filename}} mustache syntax but it always results in a blank in the generated output. Reading the test code in this repository I seem to understand that I need to register all the filenames beforehand, for example:
While it's fine with me to provide such a function, I don't see how I could foresee all the possible filenames the end user could come up with. How can I deal with the generic case of users wanting to do {{>filename}} one day and {{>donaldduck}} the next?