Closed sdedovic closed 1 year ago
Just now seeing the discussion here: https://github.com/Keats/tera/issues/370#issuecomment-455483920
My opinion is that forcing separation of the macro can make things less ergonomic. Additionally, it was be trivial to enforce macros only at the top level.
In my case, I have a template for rending a recipe and a macro that renders the list of ingredients. I want to call the macro multiple times, because my recipe has a few different sections (e.g. dough, topping, filling). This template uses some shared CSS and only makes sense in my recipe template. I don't want to create a set of files, one for templates, and one for macros, just to achieve this.
I have updated the grammer, parser, and docs to force macros at the top-level only. This should remove any concern of a macro in a for, if, etc.
Result of running cargo test -- --format=terse
running 423 tests
........................................................................................ 88/423
........................................................................................ 176/423
........................................................................................ 264/423
........................................................................................ 352/423
.......................................................................
test result: ok. 423 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.07s
Running tests/render_fails.rs (target/debug/deps/render_fails-1e0a12e06effa9e9)
running 11 tests
...........
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
Doc-tests tera
running 4 tests
....
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.06s```
That looks ok. I'm not sure what behaviour I want for Tera v2 concerning that.
I'll leave my reply here and also in the #637 thread:
I believe macro definition should only be done:
See the next section to understand why.
With jinja2 templates you can do the following:
{% if some_val %}
{% macro foo() %}42{% endmacro %}
{% endif %}
{{ foo() }}
The macro may or may not be defined during rendering based on the truthiness of some_val
. This is because Jinja takes the template AST and converts it into a Python module (using string concatenation) and the "rendering" is more or less eval
ing the Python at runtime.
This can't work easily with Rust, for obvious reasons. (Maybe if you turn the Tera template AST into rust code, compile it, and load it dynamically...) anyway,
This becomes a point of divergence for Tera macros. The current code will load all the macro definitions into one table as a first pass during rendering. Then they are looked up from this table when traversing the AST, and thus we cannot have dynamic, conditional macro definitions. This can be fixed by defining macros during AST traversal with Context in scope, or by extending the multi-pass approach to optimize the AST and generate macro definitions prior to rendering.
In any case, I personally believe this is likely an anti-pattern. Rarely is there a valid use case to define globals dynamically, and even less so when these act like pure functions, or "template snippets" as I see it.
Hey @Keats I think the CI is failing due to some kind of configuration issues. Haven't looked into it. Is there a way to re-trigger a build or should I look to fix the problem?
I think it was just a spurious issue, i've restarted the jobs
Rebased on top of #825 to fix failing builds
This change refactors the
MacroDefinition
rendering to no-op so that a template may contain a macro definition and still render. The previous behaviour was to error.This means I may define a macro in a template and use it with the
self
namespace, as the test shows. It's useful for me to be able to extract some common behaviour in my templates out without having multiple files.Related issues:
821
644