Skip to content

Commit 4854abf

Browse files
committed
Fix some bugs connected with templates extending
1 parent b0b564a commit 4854abf

File tree

6 files changed

+100
-12
lines changed

6 files changed

+100
-12
lines changed

include/jinja2cpp/reflected_value.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,15 @@ struct Reflector<const T&>
220220
}
221221
};
222222

223+
template<typename T>
224+
struct Reflector<const std::shared_ptr<T>&>
225+
{
226+
static auto Create(const std::shared_ptr<T>& val)
227+
{
228+
return Reflector<T>::CreateFromPtr(val.get());
229+
}
230+
};
231+
223232
template<typename T>
224233
struct Reflector<T&>
225234
{

include/jinja2cpp/template_env.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ class TemplateEnv
2727
{
2828
m_filesystemHandlers.push_back(FsHandler{std::move(prefix), h});
2929
}
30+
void AddFilesystemHandler(std::string prefix, IFilesystemHandler& h)
31+
{
32+
m_filesystemHandlers.push_back(FsHandler{std::move(prefix), std::shared_ptr<IFilesystemHandler>(&h, [](auto*) {})});
33+
}
3034
Template LoadTemplate(std::string fileName);
3135
TemplateW LoadTemplateW(std::string fileName);
3236

src/render_context.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class RenderContext
2424
{
2525
m_externalScope = &extValues;
2626
EnterScope();
27+
(*m_currentScope)["self"] = MapAdapter::CreateAdapter(InternalValueMap());
2728
}
2829

2930
InternalValueMap& EnterScope()

src/statements.cpp

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ void ForStatement::Render(OutStream& os, RenderContext& values)
1919
void ForStatement::RenderLoop(const InternalValue& loopVal, OutStream& os, RenderContext& values)
2020
{
2121
auto& context = values.EnterScope();
22-
22+
2323
InternalValueMap loopVar;
2424
context["loop"] = MapAdapter::CreateAdapter(&loopVar);
2525
if (m_isRecursive)
@@ -31,23 +31,23 @@ void ForStatement::RenderLoop(const InternalValue& loopVal, OutStream& os, Rende
3131
{
3232
return;
3333
}
34-
34+
3535
auto var = parsedParams["var"];
3636
if (!var)
3737
{
3838
return;
3939
}
40-
40+
4141
RenderLoop(var->Evaluate(context), stream, context);
42-
});
42+
});
4343
}
4444

4545
bool isConverted = false;
4646
auto loopItems = ConvertToList(loopVal, InternalValue(), isConverted);
4747
if (!isConverted)
4848
{
4949
values.ExitScope();
50-
return;
50+
return;
5151
}
5252

5353
if (m_ifExpr)
@@ -159,6 +159,7 @@ void SetStatement::Render(OutStream&, RenderContext& values)
159159
class BlocksRenderer : public RendererBase
160160
{
161161
public:
162+
virtual bool HasBlock(const std::string& blockName) = 0;
162163
virtual void RenderBlock(const std::string& blockName, OutStream& os, RenderContext& values) = 0;
163164
};
164165

@@ -170,17 +171,39 @@ void ParentBlockStatement::Render(OutStream& os, RenderContext& values)
170171
if (!found)
171172
return;
172173

173-
auto parentTplPtr = boost::get<RendererBase*>(&parentTplVal->second);
174-
if (parentTplPtr == nullptr)
174+
bool isConverted = false;
175+
auto parentTplsList = ConvertToList(parentTplVal->second, isConverted);
176+
if (!isConverted)
177+
return;
178+
179+
BlocksRenderer* blockRenderer = nullptr; // static_cast<BlocksRenderer*>(*parentTplPtr);
180+
for (auto& tplVal : parentTplsList)
181+
{
182+
auto ptr = boost::get<RendererBase*>(&tplVal);
183+
if (!ptr)
184+
continue;
185+
186+
auto parentTplPtr = static_cast<BlocksRenderer*>(*ptr);
187+
188+
if (parentTplPtr->HasBlock(m_name))
189+
{
190+
blockRenderer = parentTplPtr;
191+
break;
192+
}
193+
}
194+
195+
if (!blockRenderer)
175196
return;
176197

177-
BlocksRenderer* blockRenderer = static_cast<BlocksRenderer*>(*parentTplPtr);
178198

179199
auto& scope = innerContext.EnterScope();
180200
scope["$$__super_block"] = static_cast<RendererBase*>(this);
181201
scope["super"] = Callable([this](const CallParams&, OutStream& stream, RenderContext& context) {
182202
m_mainBody->Render(stream, context);
183203
});
204+
if (!m_isScoped)
205+
scope["$$__parent_template"] = parentTplsList;
206+
184207
blockRenderer->RenderBlock(m_name, os, innerContext);
185208
innerContext.ExitScope();
186209

@@ -210,7 +233,21 @@ class ParentTemplateRenderer : public BlocksRenderer
210233
void Render(OutStream& os, RenderContext& values) override
211234
{
212235
auto& scope = values.GetCurrentScope();
213-
scope["$$__parent_template"] = static_cast<RendererBase*>(this);
236+
InternalValueList parentTemplates;
237+
parentTemplates.push_back(InternalValue(static_cast<RendererBase*>(this)));
238+
bool isFound = false;
239+
auto p = values.FindValue("$$__parent_template", isFound);
240+
if (isFound)
241+
{
242+
bool isConverted = false;
243+
auto prevTplsList = ConvertToList(p->second, isConverted);
244+
if (isConverted)
245+
{
246+
for (auto& tpl : prevTplsList)
247+
parentTemplates.push_back(tpl);
248+
}
249+
}
250+
scope["$$__parent_template"] = ListAdapter::CreateAdapter(std::move(parentTemplates));
214251
m_template->GetRenderer()->Render(os, values);
215252
}
216253

@@ -223,6 +260,11 @@ class ParentTemplateRenderer : public BlocksRenderer
223260
p->second->Render(os, values);
224261
}
225262

263+
bool HasBlock(const std::string &blockName) override
264+
{
265+
return m_blocks->count(blockName) != 0;
266+
}
267+
226268
private:
227269
std::shared_ptr<TemplateImpl<CharT>> m_template;
228270
ExtendsStatement::BlocksCollection* m_blocks;

src/template_impl.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ class TemplateImpl : public ITemplateImpl
101101
InternalValueMap& InitRenderContext(RenderContext& context)
102102
{
103103
auto& curScope = context.GetCurrentScope();
104-
curScope["self"] = MapAdapter::CreateAdapter(InternalValueMap());
105104
return curScope;
106105
}
107106

test/extends_test.cpp

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ TEST_F(ExtendsTest, SimpleBlockExtends)
5555
EXPECT_STREQ(expectedResult.c_str(), result.c_str());
5656
}
5757

58-
TEST_F(ExtendsTest, DISABLED_TwoLevelBlockExtends)
58+
TEST_F(ExtendsTest, TwoLevelBlockExtends)
5959
{
6060
m_templateFs->AddFile("base.j2tpl", "Hello World! ->{% block b1 %}{% endblock %}<-");
6161
m_templateFs->AddFile("derived.j2tpl", R"({% extends "base.j2tpl" %}{%block b1%}Extended block!{%block innerB1%}=>innerB1 content<={%endblock%}{%endblock%})");
@@ -75,7 +75,7 @@ TEST_F(ExtendsTest, DISABLED_TwoLevelBlockExtends)
7575
EXPECT_STREQ(expectedResult.c_str(), result.c_str());
7676
std::string result2 = tpl2.RenderAsString(jinja2::ValuesMap{});
7777
std::cout << result2 << std::endl;
78-
expectedResult = "Hello World! ->Extended block b1!<- ->Extended block b2!<-";
78+
expectedResult = "Hello World! ->Extended block!derived2 block=>innerB1 content<=<-";
7979
EXPECT_STREQ(expectedResult.c_str(), result2.c_str());
8080
}
8181

@@ -144,6 +144,39 @@ TEST_F(ExtendsTest, SuperAndSelfBlocksExtends)
144144
EXPECT_STREQ(expectedResult.c_str(), result.c_str());
145145
}
146146

147+
TEST_F(ExtendsTest, InnerBlocksExtends)
148+
{
149+
m_templateFs->AddFile("base.j2tpl", R"(Hello World!{%set testVal='first entry' %}
150+
->{% block b1 scoped %}=>block b1 - {{testVal}}<={%block innerB1 scoped%}{%endblock%}{% endblock %}<-
151+
-->{{self.b1()}}<---->{{self.b2()}}<--{%set testVal='second entry' %}
152+
->{% block b2 %}{{self.innerB1()}}{% endblock b2%}<-
153+
-->{{self.b1()}}<---->{{self.b2()}}<--
154+
)");
155+
m_templateFs->AddFile("derived.j2tpl", R"({% extends "base.j2tpl" %}
156+
{%block b1%}Extended block b1!{{super()}}{%endblock%}
157+
{%block b2%}Extended block b2!{{super()}}{%endblock%}
158+
{%block innerB1%}###Extended innerB1 block {{testVal}}!###{%endblock%}
159+
)");
160+
161+
auto baseTpl = m_env.LoadTemplate("base.j2tpl");
162+
auto tpl = m_env.LoadTemplate("derived.j2tpl");
163+
164+
std::string baseResult = baseTpl.RenderAsString(jinja2::ValuesMap{});
165+
std::cout << baseResult << std::endl;
166+
std::string expectedResult = R"(Hello World!-><-
167+
--><----><---><-
168+
--><----><--
169+
)";
170+
EXPECT_STREQ(expectedResult.c_str(), baseResult.c_str());
171+
std::string result = tpl.RenderAsString(jinja2::ValuesMap{});
172+
std::cout << result << std::endl;
173+
expectedResult = R"(Hello World!->Extended block b1!=>block b1 - first entry<=###Extended innerB1 block first entry!###<-
174+
-->Extended block b1!=>block b1 - first entry<=###Extended innerB1 block first entry!###<----><--->Extended block b2!<-
175+
-->Extended block b1!=>block b1 - second entry<=###Extended innerB1 block second entry!###<---->Extended block b2!<--
176+
)";
177+
EXPECT_STREQ(expectedResult.c_str(), result.c_str());
178+
}
179+
147180
TEST_F(ExtendsTest, ScopedBlocksExtends)
148181
{
149182
m_templateFs->AddFile("base.j2tpl", R"(Hello World!

0 commit comments

Comments
 (0)