Annotation Interface JStacheLambda


Tag a method to be used as a mustache lambda section for custom logic.

Lambda sections look like regular mustache sections but execute code and is one of the only ways to add custom logic to mustache templates. Lambda sections look something like:


 {{#context}}
 {{#lambda}}body{{/lambda}}
 {{/context}}
 
Where in the above example a lambda is named "lambda" and optionally has access to the object called "context" and the raw body passed to the lambda is "body". A nonexhaustive example of the above lambda in Java assuming the context type is SomeType might look like:

 @JStacheLambda
 @JStacheLambda.Raw
 public String lambda(SomeType context, @JStacheLambda.Raw String body) {
     return "Hello" + body "!";
 }
  
The lambda could also return a new model and use the section body as a template instead of raw content:

 {{#context}}
 {{#lambda}}{{message}}{{/lambda}}
 {{/context}}
  

 record Model(String message){}

 @JStacheLambda
 public Model lambda(SomeType context) {
     return new Model("Hello " + context.name() + "!");
 }
  
TIP: A nice feature of mustache when using lambdas is leveraging dotted path support for reduced syntax noise:

 {{#context.lambda}}{{message}}{{/context.lambda}}
  
Notice how this sort of resembles OOP method calls. We can even pass virtual keys like -index.

 {{#someList}}
 {{.}} is {{#-index.isEven}}{{#.}}even{{/.}}{{^.}}odd{{/.}}{{/-index.isEven}}
 {{/someList}}
  

 @JStacheLambda
 public boolean isEven(int index) {
     return index % 2 == 0;
 }
  
Output of someList = List.of("a", "b", "c"):
 a is odd
 b is even
 c is odd
 
If we want to duplicate or wrap the results of the lambda we can use lambda templates:

 @JStacheLambda(template="<span class="{{>@section}}">{{>@section}}<span>")
 public boolean isEven(int index) {
     return index % 2 == 0;
 }
  
Output of someList = List.of("a", "b", "c"):
 a is <span class="odd">odd<span>
 b is <span class="even">even<span>
 c is <span class="odd">odd<span>
 

JStachio lambdas just like normal method calls do not have to be directly enclosed on the context objects but can be on implemented interfaces or inherited and thus models can be "mixed in" with interfaces to achieve sharing of lambdas. However there is currently no support for static methods to be used as lambdas.

JStachio lambdas work in basically two modes for parameters:

  1. Context aware: The default. The top of the stack is passed if an argument is present and is not annotated with JStacheLambda.Raw.
  2. Raw: If a string parameter is annoated with JStacheLambda.Raw it will be passed the contents of the lambda section call. Some caveats:
    • While this mode appears to be the default for the spec it is not for JStachio.
    • The contents may not be valid mustache as the spec does not define that it has to be.
    • If the lambda start tag is standalone the space and newline following the tag will not be passed to the lambda.

Similarly JStachio works in two modes for return types:

  1. Model: The default. The returned model is pushed onto the context stack and the contents of the lambda section call are used as an inline template and rendered against it.
  2. Raw: If the return type is a String and the method is annotated with JStacheLambda.Raw the contents of the string are directly written unescaped.
Regardless of parameter and return annotations the method must always be annotated with this annotation to be discovered.

Due to the static nature of JStachio, JStachio does not support returning truly dynamic templates which is the optional lambda spec default if a String is returned. That is you cannot construct a string as a template at runtime.

That being said the lambda can ostensibly return a template (and a model that the template uses) that then references the section body as a partial by using template() and then referencing the section body with the partial named "@section". This allows repeating or wrapping the passed in section body. In some other mustache implementations this accomplished with a render callback but because templates are compiled statically this is a powerful declaritive workaround.

For those that are coming from other Mustache implementations the JStachio's lambda model is very similar to the JMustache model and does not have a direct analog to mustache.java of returning Function<String,String> where the function will automatically be called.

Author:
agentgt
See Also:
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static @interface 
    Tag a method return type of String or parameter of String to be used as a raw unprocessed string.
  • Optional Element Summary

    Optional Elements
    Modifier and Type
    Optional Element
    Description
    The logical name of the lambda.
    An inline template used for rendering the returned model that has access to the lambda section body as a partial.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final String
    Name of the partial to render the section body of a lambda call.
  • Field Details

  • Element Details

    • name

      The logical name of the lambda. If blank the method name will be used.
      Returns:
      lambda name
      Default:
      ""
    • template

      An inline template used for rendering the returned model that has access to the lambda section body as a partial. The section body contents can be accessed as a partial with the name @section. This effectively allows you render the section body and wrap or repeat it. Below is an example:
      
       {{! template call lambda }}
       {{#context}}
       {{#lambda}}Use the force {{name}}{{/lambda}} {{! "name" will come from the returned model }}
       {{/context}}
        
      
       record Model(String name){}
      
       public record LambdaModel(List<Model> list) {
       }
      
       @JStacheLambda(template="""
            {{#list}}
               {{>@section}}
            {{/list}}
            """)
       public LambdaModel lambda(SomeType context) {
           return new LambdaModel(List.of(new Model("Luke"), new Model("Leia"), new Model("Anakin")));
       }
        
      Output:
       Use the force Luke
       Use the force Leia
       Use the force Anakin
       
      Returns:
      the inline template and if empty is ignored. By default it is empty and ignored.
      Default:
      ""