On Tue, Sep 3, 2024 at 9:35 PM Simon Hartley <[email protected]> wrote:
> I've also discovered that DirectiveCallPlace.getOrCreateCustomData doesn't > guarantee that it will be stable. > > "the object will be usually created only once, even if multiple threads > request the value when it's still null. > It doesn't stand though when providerIdentity mismatches occur" > But this only matters if the render_once variable, when you call it as directive on a given call place, is not always your single TempalteDirectiveModel implementation. Given the function of that directive, if that can happen, then all bets are off anyway. > "if multiple directives that use the custom data feature use the same call > place, the caching of the custom data can be inefficient, as they will keep > overwriting each other's custom data" > Same applies as above. This is not a problem for you. On Tuesday 3 September 2024 at 19:04:46 BST, Daniel Dekany < > [email protected]> wrote: > > > > > > DirectiveCallPlace doesn't make any promises regarding equality (even if in > the current implementation it happens to work), but you can instead use > DirectiveCallPlace.getOrCreateCustomData, which was exactly made for things > like what you try to do. > > TemplateDirectiveModel doesn't currently support positionals. But that > feature actually can be implemented in 2.x too. (The 3 branch is not too > relevant, as it's questionable at best if it will ever get enough time, and > even then the point of it is breaking backward compatibility, so it's not > an answer for most current users.) > > Yes, directives defined with macros can be called either as > fully positional, or as fully by-name. There's an ambiguity issue with it > otherwise. Like if you have <@m x y=2 />, then currently means that 1st > parameter is the value of x, and 2nd parameter is the boolean result of y > == 2. Because, in FreeMarker, unfortunately, you can write = instead == of > =. > > On Tue, Sep 3, 2024 at 5:54 PM Simon Hartley <[email protected] > .invalid> > wrote: > > > 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()); > > > } > > } > > > > } > > > > > -- > Best regards, > Daniel Dekany > > -- Best regards, Daniel Dekany
