mirror of
https://github.com/pantor/inja.git
synced 2026-05-04 20:35:22 +00:00
Add support for setting subobjects via JSON pointer in set statements. (#202)
E.g. `{% set x.y = 1 %}` sets the `y` member of `x` to 1.
This commit is contained in:
committed by
GitHub
parent
86f38f05d7
commit
798a0b92b1
@@ -198,6 +198,13 @@ Variables can also be defined within the template using the set statment.
|
||||
render("{% set new_hour=23 %}{{ new_hour }}pm", data); // "23pm"
|
||||
```
|
||||
|
||||
json pointers can be used to set sub-objects:
|
||||
```.cpp
|
||||
render("{% set time.start=18 %}{{ time.start }}pm", data); // "18pm"
|
||||
```
|
||||
|
||||
Assignments only set the value within the rendering context; they do not modify the json object passed into the `render` call.
|
||||
|
||||
### Functions
|
||||
|
||||
A few functions are implemented within the inja template syntax. They can be called with
|
||||
|
||||
@@ -184,10 +184,10 @@ class Renderer : public NodeVisitor {
|
||||
void visit(const JsonNode& node) {
|
||||
if (json_additional_data.contains(node.ptr)) {
|
||||
json_eval_stack.push(&(json_additional_data[node.ptr]));
|
||||
|
||||
|
||||
} else if (json_input->contains(node.ptr)) {
|
||||
json_eval_stack.push(&(*json_input)[node.ptr]);
|
||||
|
||||
|
||||
} else {
|
||||
// Try to evaluate as a no-argument callback
|
||||
const auto function_data = function_storage.find_function(node.name, 0);
|
||||
@@ -662,7 +662,10 @@ class Renderer : public NodeVisitor {
|
||||
}
|
||||
|
||||
void visit(const SetStatementNode& node) {
|
||||
json_additional_data[node.key] = *eval_expression_list(node.expression);
|
||||
std::string ptr = node.key;
|
||||
replace_substring(ptr, ".", "/");
|
||||
ptr = "/" + ptr;
|
||||
json_additional_data[nlohmann::json::json_pointer(ptr)] = *eval_expression_list(node.expression);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -69,6 +69,17 @@ inline SourceLocation get_source_location(nonstd::string_view content, size_t po
|
||||
return {count_lines + 1, sliced.length() - last_newline};
|
||||
}
|
||||
|
||||
inline void replace_substring(std::string& s, const std::string& f,
|
||||
const std::string& t)
|
||||
{
|
||||
if (f.empty()) return;
|
||||
for (auto pos = s.find(f); // find first occurrence of f
|
||||
pos != std::string::npos; // make sure f was found
|
||||
s.replace(pos, f.size(), t), // replace with t, and
|
||||
pos = s.find(f, pos + t.size())) // find next occurrence of f
|
||||
{}
|
||||
}
|
||||
|
||||
} // namespace inja
|
||||
|
||||
#endif // INCLUDE_INJA_UTILS_HPP_
|
||||
|
||||
@@ -1907,6 +1907,17 @@ inline SourceLocation get_source_location(nonstd::string_view content, size_t po
|
||||
return {count_lines + 1, sliced.length() - last_newline};
|
||||
}
|
||||
|
||||
inline void replace_substring(std::string& s, const std::string& f,
|
||||
const std::string& t)
|
||||
{
|
||||
if (f.empty()) return;
|
||||
for (auto pos = s.find(f); // find first occurrence of f
|
||||
pos != std::string::npos; // make sure f was found
|
||||
s.replace(pos, f.size(), t), // replace with t, and
|
||||
pos = s.find(f, pos + t.size())) // find next occurrence of f
|
||||
{}
|
||||
}
|
||||
|
||||
} // namespace inja
|
||||
|
||||
#endif // INCLUDE_INJA_UTILS_HPP_
|
||||
@@ -3663,10 +3674,10 @@ class Renderer : public NodeVisitor {
|
||||
void visit(const JsonNode& node) {
|
||||
if (json_additional_data.contains(node.ptr)) {
|
||||
json_eval_stack.push(&(json_additional_data[node.ptr]));
|
||||
|
||||
|
||||
} else if (json_input->contains(node.ptr)) {
|
||||
json_eval_stack.push(&(*json_input)[node.ptr]);
|
||||
|
||||
|
||||
} else {
|
||||
// Try to evaluate as a no-argument callback
|
||||
const auto function_data = function_storage.find_function(node.name, 0);
|
||||
@@ -4141,7 +4152,10 @@ class Renderer : public NodeVisitor {
|
||||
}
|
||||
|
||||
void visit(const SetStatementNode& node) {
|
||||
json_additional_data[node.key] = *eval_expression_list(node.expression);
|
||||
std::string ptr = node.key;
|
||||
replace_substring(ptr, ".", "/");
|
||||
ptr = "/" + ptr;
|
||||
json_additional_data[nlohmann::json::json_pointer(ptr)] = *eval_expression_list(node.expression);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -125,8 +125,13 @@ TEST_CASE("types") {
|
||||
SUBCASE("set statements") {
|
||||
CHECK(env.render("{% set predefined=true %}{% if predefined %}a{% endif %}", data) == "a");
|
||||
CHECK(env.render("{% set predefined=false %}{% if predefined %}a{% endif %}", data) == "");
|
||||
CHECK_THROWS_WITH(env.render("{% if predefined %}{% endif %}", data),
|
||||
CHECK(env.render("{% set age=30 %}{{age}}", data) == "30");
|
||||
CHECK(env.render("{% set predefined.value=1 %}{% if existsIn(predefined, \"value\") %}{{predefined.value}}{% endif %}", data) == "1");
|
||||
CHECK(env.render("{% set brother.name=\"Bob\" %}{{brother.name}}", data) == "Bob");
|
||||
CHECK_THROWS_WITH(env.render("{% if predefined %}{% endif %}", data),
|
||||
"[inja.exception.render_error] (at 1:7) variable 'predefined' not found");
|
||||
CHECK(env.render("{{age}}", data) == "29");
|
||||
CHECK(env.render("{{brother.name}}", data) == "Chris");
|
||||
}
|
||||
|
||||
SUBCASE("short circuit evaluation") {
|
||||
@@ -210,7 +215,7 @@ TEST_CASE("templates") {
|
||||
CHECK(env.render("Test\n {%- if is_happy %}{{ name }}{% endif %} ", data) == "Test\nPeter ");
|
||||
CHECK(env.render(" {%+ if is_happy %}{{ name }}{% endif %}", data) == " Peter");
|
||||
CHECK(env.render(" {%- if is_happy %}{{ name }}{% endif -%} \n ", data) == "Peter");
|
||||
|
||||
|
||||
CHECK(env.render(" {{- name -}} \n ", data) == "Peter");
|
||||
CHECK(env.render("Test\n {{- name }} ", data) == "Test\nPeter ");
|
||||
CHECK(env.render(" {{ name }}\n ", data) == " Peter\n ");
|
||||
|
||||
Reference in New Issue
Block a user