Heya,
I was hoping you could verify a couple of things for me. Please let me know if you would prefer that I make either of these be Stack Overflow questions. 1. Is it valid to rely on identity / reference-equality with env.getCurrentDirectiveCallPlace() ? Is the result stable so that I can store per-Environment data while taking advantage of this? For reference, below I've included the source for my render_once directive to clarify. 2. I was wondering about the possibility of invoking TemplateDirectiveModel's with positional parameters. I found https://issues.apache.org/jira/browse/FREEMARKER-63, so does that mean I must wait for V3? Also am I correct that this implementation makes each parameter be strictly either positional or named? This seems a shame, since I like the flexibility of being able to choose, and some teams will prefer brevity while others explicitness: <@my_directive "my arg" /> or <@my_directive param="my arg" /> Many thanks and best regards, Simon Hartley // <#macro require_jQuery> // <@render_once> // <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script><#t> // </@render_once> // </#macro> // <@require_jQuery /> <#-- Renders something --> // <@require_jQuery /> <#-- Nothing to render --> // // Inspiration: https://templ.guide/syntax-and-usage/render-once/ // // If you #include a use of this directive multiple times then each will reset whether it has rendered. // If we wanted to work around this, then this directive could be enhanced to accept an optional key, // perhaps using object-equality, rather than reference-equality, e.g. a string scalar. public class RenderOnceDirective implements TemplateDirectiveModel { private static final CustomAttribute IDENTITY_BASED_STATE = new CustomAttribute(SCOPE_ENVIRONMENT) { @Override protected Set<?> create() { return Collections.newSetFromMap(new IdentityHashMap<>()); } }; @Override public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException { if (!params.isEmpty()) { throw new TemplateModelException("This directive doesn't allow parameters."); } if (loopVars.length != 0) { throw new TemplateModelException("This directive doesn't support loop variables."); } if (body == null) { throw new TemplateModelException("missing body"); } @SuppressWarnings("unchecked") Set<Object> alreadyRun = (Set<Object>) IDENTITY_BASED_STATE.get(env); Object key = env.getCurrentDirectiveCallPlace(); boolean firstTime = alreadyRun.add(key); if (firstTime) { body.render(env.getOut()); } } }