mirror of
https://github.com/pantor/inja.git
synced 2026-05-18 18:25:23 +00:00
more tests, more travis jobs, more readme
This commit is contained in:
+3
-3
@@ -60,9 +60,9 @@ matrix:
|
||||
- compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7']
|
||||
packages: clang-3.7
|
||||
env: COMPILER=clang++-3.7
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8']
|
||||
packages: clang-3.8
|
||||
env: COMPILER=clang++-3.8
|
||||
|
||||
|
||||
script:
|
||||
|
||||
@@ -23,6 +23,10 @@ Inja is headers only. Just one dependency: json by nlohmann.
|
||||
```c++
|
||||
#include "json.hpp"
|
||||
#include "inja.hpp"
|
||||
|
||||
// For convenience
|
||||
using namespace inja;
|
||||
using json = nlohmann::json;
|
||||
```
|
||||
|
||||
|
||||
@@ -34,10 +38,10 @@ Inja is headers only. Just one dependency: json by nlohmann.
|
||||
json data;
|
||||
data["name"] = "world";
|
||||
|
||||
inja::render("Hello {{ name }}!", data); // "Hello World!"
|
||||
render("Hello {{ name }}!", data); // "Hello World!"
|
||||
|
||||
// For more advanced usage, an environment is recommended
|
||||
inja::Environment env = inja::Environment();
|
||||
Environment env = Environment();
|
||||
|
||||
// Render a string with json data
|
||||
std::string result = env.render("Hello {{ name }}!", data);
|
||||
@@ -52,16 +56,32 @@ std::string result_template_2 = env.render_temlate_with_json_file("template.txt"
|
||||
The environment class can be configured.
|
||||
```c++
|
||||
// With default settings
|
||||
inja::Environment env_default = inja::Environment();
|
||||
Environment env_default = Environment();
|
||||
|
||||
// With global path to template files
|
||||
inja::Environment env_default = inja::Environment("../path/templates/");
|
||||
Environment env_default = Environment("../path/templates/");
|
||||
```
|
||||
|
||||
### Variables
|
||||
|
||||
Variables can be rendered with the `{{ ... }}` syntax.
|
||||
|
||||
```c++
|
||||
json data;
|
||||
data["name"] = "world";
|
||||
data["guests"] = {"Jeff", "Pierre", "Tom"};
|
||||
data["time"]["start"]["hour"] = 16;
|
||||
data["time"]["end"]["hour"] = 21;
|
||||
|
||||
string template = """
|
||||
{{ guests/0 }}
|
||||
|
||||
{{ time/start/hour }} to {{ time/end/hour }} or {{ 24 }}
|
||||
""";
|
||||
```
|
||||
|
||||
Valid Json -> Printed. Json Pointer.
|
||||
|
||||
|
||||
### Statements
|
||||
|
||||
@@ -106,12 +126,17 @@ Guests:
|
||||
</table>
|
||||
|
||||
In the loop, some special variables are available:
|
||||
- int index
|
||||
- bool is_first
|
||||
- bool is_last
|
||||
- `int index, index1`
|
||||
- `bool is_first`
|
||||
- `bool is_last`
|
||||
|
||||
#### Conditions
|
||||
|
||||
If, else if, else. Nested. conditions:
|
||||
- `not`
|
||||
- `==`, `>`, `<`, `>=`, `<=`, `!=`
|
||||
- `in`
|
||||
|
||||
#### Includes
|
||||
|
||||
Include other files like `(% include "footer.html" %)`. Relative from file.
|
||||
@@ -121,7 +146,7 @@ Include other files like `(% include "footer.html" %)`. Relative from file.
|
||||
Comments can be rendered with the `{# ... #}` syntax.
|
||||
|
||||
```c++
|
||||
inja::render("Hello{# Todo #}!", data); // "Hello!"
|
||||
render("Hello{# Todo #}!", data); // "Hello!"
|
||||
```
|
||||
|
||||
## Supported compilers
|
||||
@@ -129,7 +154,7 @@ inja::render("Hello{# Todo #}!", data); // "Hello!"
|
||||
Currently, the following compilers are tested:
|
||||
|
||||
- GCC 4.9 - 7.1 (and possibly later)
|
||||
- Clang 3.6 - 3.7 (and possibly later)
|
||||
- Clang 3.6 - 3.8 (and possibly later)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
+56
-44
@@ -100,6 +100,7 @@ inline SearchMatchVector search(string input, std::vector<string> regex_patterns
|
||||
}
|
||||
}
|
||||
|
||||
// TODO use search(...)
|
||||
string regex_pattern = "(" + join_strings(regex_patterns, ")|(") + ")";
|
||||
std::regex regex(regex_pattern);
|
||||
|
||||
@@ -383,56 +384,67 @@ public:
|
||||
string render_tree(json input, json data, string path) {
|
||||
string result = "";
|
||||
for (auto element: input) {
|
||||
if (element["type"] == Parser::Type::String) {
|
||||
result += element["text"];
|
||||
}
|
||||
else if (element["type"] == Parser::Type::Variable) {
|
||||
json variable = parse_variable(element["command"], data);
|
||||
result += render_json(variable);
|
||||
}
|
||||
else if (element["type"] == Parser::Type::Include) {
|
||||
result += render_template(path + element["filename"].get<string>(), data);
|
||||
}
|
||||
else if (element["type"] == Parser::Type::Loop) {
|
||||
const std::regex regex_loop_list("for (\\w+) in (.+)");
|
||||
|
||||
string command = element["command"].get<string>();
|
||||
std::smatch match_command;
|
||||
if (std::regex_match(command, match_command, regex_loop_list)) {
|
||||
string item_name = match_command.str(1);
|
||||
string list_name = match_command.str(2);
|
||||
|
||||
json list = parse_variable(list_name, data);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
json data_loop = data;
|
||||
data_loop[item_name] = list[i];
|
||||
data_loop["index"] = i;
|
||||
data_loop["index1"] = i + 1;
|
||||
data_loop["is_first"] = (i == 0);
|
||||
data_loop["is_last"] = (i == list.size() - 1);
|
||||
result += render_tree(element["children"], data_loop, path);
|
||||
}
|
||||
switch ( (Parser::Type) element["type"] ) {
|
||||
case Parser::Type::String: {
|
||||
result += element["text"];
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (element["type"] == Parser::Type::Condition) {
|
||||
const std::regex regex_condition("(if|else if|else) ?(.*)");
|
||||
|
||||
json branches = element["children"];
|
||||
for (auto branch: branches) {
|
||||
case Parser::Type::Variable: {
|
||||
json variable = parse_variable(element["command"], data);
|
||||
result += render_json(variable);
|
||||
break;
|
||||
}
|
||||
case Parser::Type::Include: {
|
||||
result += render_template(path + element["filename"].get<string>(), data);
|
||||
break;
|
||||
}
|
||||
case Parser::Type::Loop: {
|
||||
const std::regex regex_loop_list("for (\\w+) in (.+)");
|
||||
|
||||
string command = branch["command"].get<string>();
|
||||
string command = element["command"].get<string>();
|
||||
std::smatch match_command;
|
||||
if (std::regex_match(command, match_command, regex_condition)) {
|
||||
string condition_type = match_command.str(1);
|
||||
string condition = match_command.str(2);
|
||||
|
||||
if (parse_condition(condition, data) || condition_type == "else") {
|
||||
result += render_tree(branch["children"], data, path);
|
||||
break;
|
||||
if (std::regex_match(command, match_command, regex_loop_list)) {
|
||||
string item_name = match_command.str(1);
|
||||
string list_name = match_command.str(2);
|
||||
|
||||
json list = parse_variable(list_name, data);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
json data_loop = data;
|
||||
data_loop[item_name] = list[i];
|
||||
data_loop["index"] = i;
|
||||
data_loop["index1"] = i + 1;
|
||||
data_loop["is_first"] = (i == 0);
|
||||
data_loop["is_last"] = (i == list.size() - 1);
|
||||
result += render_tree(element["children"], data_loop, path);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
case Parser::Type::Condition: {
|
||||
const std::regex regex_condition("(if|else if|else) ?(.*)");
|
||||
|
||||
for (auto branch: element["children"]) {
|
||||
string command = branch["command"].get<string>();
|
||||
std::smatch match_command;
|
||||
if (std::regex_match(command, match_command, regex_condition)) {
|
||||
string condition_type = match_command.str(1);
|
||||
string condition = match_command.str(2);
|
||||
|
||||
if (parse_condition(condition, data) || condition_type == "else") {
|
||||
result += render_tree(branch["children"], data, path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Parser::Type::Comment: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw std::runtime_error("Unknown type in renderer.");
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using json = nlohmann::json;
|
||||
using Type = inja::Parser::Type;
|
||||
|
||||
|
||||
TEST_CASE("parser") {
|
||||
TEST_CASE("parse structure") {
|
||||
Environment env = Environment();
|
||||
|
||||
SECTION("basic") {
|
||||
@@ -97,7 +97,10 @@ TEST_CASE("parser") {
|
||||
CHECK( env.parse(test) == result );
|
||||
CHECK( env.parse(test2) == result2 );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("parse json") {
|
||||
Environment env = Environment();
|
||||
|
||||
json data;
|
||||
data["name"] = "Peter";
|
||||
@@ -122,4 +125,34 @@ TEST_CASE("parser") {
|
||||
CHECK( env.parse_variable("brother/daughters/0", data) == "Maria" );
|
||||
CHECK_THROWS_WITH( env.parse_variable("noelement", data), "JSON pointer found no element." );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("parse conditions") {
|
||||
Environment env = Environment();
|
||||
|
||||
json data;
|
||||
data["age"] = 29;
|
||||
data["brother"] = "Peter";
|
||||
data["father"] = "Peter";
|
||||
|
||||
SECTION("elements") {
|
||||
// CHECK( env.parse_condition("age", data) );
|
||||
CHECK_FALSE( env.parse_condition("size", data) );
|
||||
}
|
||||
|
||||
SECTION("numbers") {
|
||||
CHECK( env.parse_condition("age == 29", data) );
|
||||
CHECK( env.parse_condition("age >= 29", data) );
|
||||
CHECK( env.parse_condition("age <= 29", data) );
|
||||
CHECK( env.parse_condition("age < 100", data) );
|
||||
CHECK_FALSE( env.parse_condition("age > 29", data) );
|
||||
CHECK_FALSE( env.parse_condition("age != 29", data) );
|
||||
CHECK_FALSE( env.parse_condition("age < 28", data) );
|
||||
CHECK_FALSE( env.parse_condition("age < -100.0", data) );
|
||||
}
|
||||
|
||||
SECTION("strings") {
|
||||
CHECK( env.parse_condition("brother == father", data) );
|
||||
CHECK( env.parse_condition("brother == \"Peter\"", data) );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user