Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add variables index, index1, is_first and is_last to object loops #52

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions src/inja.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,11 +574,24 @@ class Renderer {
}
case Parsed::Loop::ForMapIn: {
const std::map<std::string, json> map = eval_expression<std::map<std::string, json>>(element_loop->list, data);
unsigned int i = 0;
for (const auto& item: map) {
json data_loop = data;
data_loop[element_loop->key] = item.first;
data_loop[element_loop->value] = item.second;
result.append( render(Template(*element_loop), data_loop) );
/* For nested loops, use parent/index */
if (data_loop.count("index") == 1 && data_loop.count("index1") == 1) {
Copy link

@flexferrum flexferrum May 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does not to follow the jinja2 specs and not to put all these variables inside the 'loop' composite variable? In this case user can refer this vars as loop.index, loop.index1 etc. and can avoid situations like this:
{% for index in indexes %} {{ index }} {% endfor %}
Which 'index' should be used here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I'm not familiar with jinja2 - I just followed the existing construct that inja uses for loops over arrays. However, your suggestion should be relatively straightforward to implement.

That said, I don't know how closely inja in intended to follow jinja2 specs - the inja readme says 'loosely inspired'. I would defer to the opinion of @pantor in this case.

Copy link

@flexferrum flexferrum May 21, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I see. So waiting for @pantor decision. But here is one more reason to use 'loop' variable instead of several different vars. In case of 'loop' var you can simplify this piece of code in the following way:

if (data.count("loop") == 1)
{
    data_loop["loop"]["parent"] = data["loop"];
}
data_loop["loop"]["index"] = ...;
// ...

And as a consequence you can access any outer loop context as {{ loop.parent.parent.index }}.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I think we should follow the jinja2 spec in this (and in general more) cases. I'll update the master branch to use the loop variable, and then feel free to update this PR, @craigpepper. Thanks so much!

data_loop["parent"]["index"] = data_loop["index"];
data_loop["parent"]["index1"] = data_loop["index1"];
data_loop["parent"]["is_first"] = data_loop["is_first"];
data_loop["parent"]["is_last"] = data_loop["is_last"];
}
data_loop[element_loop->key] = item.first;
data_loop[element_loop->value] = item.second;
data_loop["index"] = i;
data_loop["index1"] = i + 1;
data_loop["is_first"] = (i == 0);
data_loop["is_last"] = (i == map.size() - 1);
result.append( render(Template(*element_loop ), data_loop) );
i++;
}
break;
}
Expand Down
2 changes: 1 addition & 1 deletion test/src/unit-renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ TEST_CASE("types") {
CHECK( env.render("{% for name in names %}a{% endfor %}", data) == "aa" );
CHECK( env.render("Hello {% for name in names %}{{ name }} {% endfor %}!", data) == "Hello Jeff Seb !" );
CHECK( env.render("Hello {% for name in names %}{{ index }}: {{ name }}, {% endfor %}!", data) == "Hello 0: Jeff, 1: Seb, !" );
CHECK( env.render("{% for type, name in relatives %}{{ type }}: {{ name }}, {% endfor %}", data) == "brother: Chris, mother: Maria, sister: Jenny, " );
CHECK( env.render("{% for type, name in relatives %}{{index1}} - {{ type }}: {{ name }}{% if not is_last %}, {% endif %}{% endfor %}", data) == "1 - brother: Chris, 2 - mother: Maria, 3 - sister: Jenny" );
CHECK( env.render("{% for v in vars %}{% if v > 0 %}+{% endif %}{% endfor %}", data) == "+++" );
CHECK( env.render("{% for name in names %}{{ index }}: {{ name }}{% if not is_last %}, {% endif %}{% endfor %}!", data) == "0: Jeff, 1: Seb!" );
CHECK( env.render("{% for name in names %}{{ index }}: {{ name }}{% if is_last == false %}, {% endif %}{% endfor %}!", data) == "0: Jeff, 1: Seb!" );
Expand Down