Git Product home page Git Product logo

jinjava's Introduction

jinjava

Build Status Coverage status Maven Central Join the chat at https://gitter.im/HubSpot/jinjava

jinjava

Java-based template engine based on django template syntax, adapted to render jinja templates (at least the subset of jinja in use in HubSpot content). Currently used in production to render thousands of websites with hundreds of millions of page views per month on the HubSpot CMS.

Note: Requires Java >= 8. Originally forked from jangod.

Get it:

  <dependency>
    <groupId>com.hubspot.jinjava</groupId>
    <artifactId>jinjava</artifactId>
    <version>{ LATEST_VERSION }</version>
  </dependency>

where LATEST_VERSION is the latest version from CHANGES.

or if you're stuck on java 7:

  <dependency>
    <groupId>com.hubspot.jinjava</groupId>
    <artifactId>jinjava</artifactId>
    <version>2.0.11-java7</version>
</dependency>

Example usage:

my-template.html:

<div>Hello, {{ name }}!</div>

java code:

Jinjava jinjava = new Jinjava();
Map<String, Object> context = Maps.newHashMap();
context.put("name", "Jared");

String template = Resources.toString(Resources.getResource("my-template.html"), Charsets.UTF_8);

String renderedTemplate = jinjava.render(template, context);

result:

<div>Hello, Jared!</div>

Voila!

Advanced Topics

Template loading

Jinjava needs to know how to interpret template paths, so it can properly handle tags like:

{% extends "foo/bar/base.html" %}

By default, it will load only a ClasspathResourceLocator which will allow loading from ANY file in the classpath inclusing class files. If you want to allow Jinjava to load any file from the file system, you can add a FileResourceLocator. Be aware the security risks of allowing user input to prevent a user from adding code such as {% include '/etc/password' %}.

You will likely want to provide your own implementation of ResourceLoader to hook into your application's template repository, and then tell jinjava about it:

JinjavaConfig config = new JinjavaConfig();

Jinjava jinjava = new Jinjava(config);
jinjava.setResourceLocator(new MyCustomResourceLocator());

To use more than one ResourceLocator, use a CascadingResourceLocator.

JinjavaConfig config = new JinjavaConfig();

Jinjava jinjava = new Jinjava(config);
jinjava.setResourceLocator(new MyCustomResourceLocator(), new FileResourceLocator());

Custom tags, filters and functions

You can provide custom jinja tags, filters, and static functions to the template engine.

// define a custom tag implementing com.hubspot.jinjava.lib.Tag
jinjava.getGlobalContext().registerTag(new MyCustomTag());
// define a custom filter implementing com.hubspot.jinjava.lib.Filter
jinjava.getGlobalContext().registerFilter(new MyAwesomeFilter());
// define a custom public static function (this one will bind to myfn:my_func('foo', 42))
jinjava.getGlobalContext().registerFunction(new ELFunctionDefinition("myfn", "my_func", 
    MyFuncsClass.class, "myFunc", String.class, Integer.class);

// define any number of classes which extend Importable
jinjava.getGlobalContext().registerClasses(Class<? extends Importable>... classes);

See also

jinjava's People

Contributors

alprielse avatar anthmatic avatar asegal-hs avatar bkrainer avatar boulter avatar cubeton avatar dependabot[bot] avatar gabru-md avatar hs-jenkins-bot avatar hs-lsong avatar jaredstehler avatar jasmith-hs avatar jboulter avatar jessbrandi avatar jhaber avatar jmaroeder avatar jmp3833 avatar joeoh avatar julia-uy avatar lh-hubs-test avatar liamrharwood avatar maple-buice avatar mattcoley avatar michaelpro1 avatar npharate avatar phalaphone avatar roobun avatar samukce avatar stevie400 avatar tkindy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jinjava's Issues

Support for enumerate()

Currently, the following statement fails even though I tried defining the enumerate method in java to return my own "Tuple" class and a "Map" object.

{% for index, object in enumerate(list) %}

I was able to write and define an ELFunction which was accepted and runs without failing. However, after the template is rendered, the replacement for "index" and "object" are just null.

What should/can I return as a result of "enumerate" for the above statement to work with jinjava?

jinjava.getGlobalContext().registerFunction(
new ELFunctionDefinition("", "enumerate", JinjavaHelpers.class, "enumerate", List.class));

public static List<Map<Integer,X>> enumerate(List list_in) {
List nums = new ArrayList(list_in.size());
for (int x = 0; x < list_in.size(); x++) {
nums.add(Integer.valueOf(x));
}

    return zip(nums, list_in);
}

public static <X,Y> List<Map<X,Y>> zip(List<X> list_a, List<Y> list_b) {
    Iterator<X> xiter = list_a.iterator();
    Iterator<Y> yiter = list_b.iterator();

    List<Map<X,Y>> result = new LinkedList<Map<X,Y>>();

    while (xiter.hasNext() && yiter.hasNext()) {
        Map<X,Y> myMap = new HashMap<X,Y>();
        myMap.put(xiter.next(), yiter.next());
        result.add(myMap);
    }

    return result;
}

Bug in range function

example:

{% set l = 2 %}
{% for i in range(1, l + 1) %}
{{i}}
{% endfor %}

the parser does not understand the expression l + 1 in args range function

Documentation is incorrect

JinjavaConfig config = new JinjavaConfig();
config.setResourceLocator(new MyCustomResourceLocator());

Jinjava jinjava = new Jinjava(config);

Should read

Jinjava jinjava = new Jinjava(config);
jinjava.setResourceLocator(new MyCustomResourceLocator());

Add range function to HubL

There is a Python function called "range", which is also available in Jinja2, that allows you to create an incrementing loop without the need to declare a scope. Can we have this added to our HubL accessible functions? This would make it a lot easier for us to create simple loops for stuff like numbered blog pagination.

Thanks!

  • Colby

Add bool filter

I'm using this library to template files from an api where all values are strings and I wonder if you would be open to add a bool filter that would convert a true/false string to a boolean. I'm happy to submit a PR if you think it's suiting. This is what ansible does in their playbook filters.

Documentation for registering a new filter is wrong

The steps mentioned in the README.md to add a custom filter dont seem to be valid anymore.

The following line:
jinjava.getFilterLibrary().addFilter(new MyAwesomeFilter());
should be
jinjava.getGlobalContext().registerFilter(new MyAwesomeFilter());

Macro recursion broken in versions 2.1.8 and later

Consider the following code:

{% macro example(prefix, items) %}
{{ prefix }} {{ items | first }}
{% if (items | count > 1) %}{{ example( prefix ~'-', items[1:items|count]) }}{% endif %}
{% endmacro %}

{{  example('-',['lorem', 'ipsum', 'dolor']) }}

With jinjava 2.1.7 or earlier this correctly outputs:

- lorem

-- ipsum

--- dolor

Starting with version 2.1.8 this no longer works and instead just outputs:

- lorem

datetimeformat filter on NULL dates

I am not sure if this is a bug or by design, but I thought I would point it out. The datetimeformat is returning the format of today's date if the date the filter was ran on is null. I looked at the doc here and it does not give any indication of the intended functionality.

The filter is using the Functions.getDateTimeArg() which is causing this behavior

private static ZonedDateTime getDateTimeArg(Object var) {

    ZonedDateTime d = null;

    if (var == null) {
      d = ZonedDateTime.now(ZoneOffset.UTC);
    } else if (var instanceof Number) {
      d = ZonedDateTime.ofInstant(Instant.ofEpochMilli(((Number) var).longValue()), ZoneOffset.UTC);
    } else if (var instanceof PyishDate) {
      d = ((PyishDate) var).toDateTime();
    } else if (var instanceof ZonedDateTime) {
      d = (ZonedDateTime) var;
    } else if (!ZonedDateTime.class.isAssignableFrom(var.getClass())) {
      throw new InterpretException("Input to function must be a date object, was: " + var.getClass());
    }

    return d;
  }

Questions about this project

  1. Is there a Gradle plug-in, so I can process templates from my Gradle builds
  2. How compatible is jinjava with the jinja2 processor in Ansible

Thanks

how to create a parsedTemplate in advance and reuse it?

It is same issue with #4

I need custom filter and add my filters using below code

        // create a node in advance and reuse it each time
        String template = "{{some thing here}}";
        TokenParser t =  new TokenParser(null,template);
        Node parsedTemplate =TreeParser.parseTree(t);

        Jinjava jinjava = new Jinjava();

        Context c  = jinjava.getGlobalContext();
        c.registerFilter(f);

        c.putAll(event);

        JinjavaInterpreter interpreter = new JinjavaInterpreter(jinjava, c, null);

        interpreter.render(parsedTemplate);

However , the event , for example ** event = {'name': "jia.liu"} ** always varies, and I must put the event to Contest c each time.

I don't want to create a Context c and register all filters to it .
But If I create Context c in advance and reuse it , will the content in the context increase continually?

In the python jinja2 , Context(named env in jinja2) is apart from event.

Is withFailOnUnknownTokens working?

I've been trying to use the withFailOnUnknownTokens, but I when I active it, it seems not change anything. Do you guys know if it is working? Is there a example?

I looked into some tests here on Git, but even doing the same, it is not working for me.

Thanks

the custom functions don`t work

   public static boolean test(String str){
        return true;
    }

    public static void main(String[] args) throws IOException {
        Jinjava jinjava = new Jinjava();
        jinjava.getGlobalContext().registerFunction(
                new ELFunctionDefinition("x", "test", App.class,"test",String.class));

        Map<String, Object> context = Maps.newHashMap();
        context.put("name", "Jared");

        String template = "| {% if x.test(name) %} test {% endif %}|";

        String renderedTemplate = jinjava.render(template, context);
        System.out.println(renderedTemplate);
    }

whether the test function return true or false, no any effect
what can I do

help~

Add support for converting string variables to timestamps

While you can use the datetimeformat filter to format timestamps, there is currently no way to take a convert variables into datetime objects. For example, if you wanted to calculate 30 days before local_dt, there isn't any way to take a string or numeric variable and convert it to a datetime object.

It would be great if a filter or function could be added to support string/int to datetime object conversion.

Contribution guidelines request

This is a great open source library! Sounds like it is exactly what we might want to use for our company.

Could you add some documentation for contribution guidelines and how to get involved in this project? Or do you think that HubSpot won't be keeping this open source for very long?

TemplateError: could not resolve function 'range', why?

when I used jinjava, i have a template file. but the template have "for i in range(2) ....",but when running this, failed. why? donn't support "function range" ,help me...
I used jar version: 2.0.11-java7, is this merged the range function code ???
i am waiting, please .thank you

Bug dict default

CODE:

{% set a="1" %}
{% set b=b | default("2") %}
{% set c={"key": "value"} %}
{% set d=d | default({"key": "value"}) %}

Type: {{ type(a) }} - Value: {{ a }}
Type: {{ type(b) }} - Value: {{ b }}
Type: {{ type(c) }} - Value: {{ c }}
Type: {{ type(d) }} - Value: {{ d }}
Type: {{ type(c.key) }} - Value: {{ c.key }}
Type: {{ type(d.key) }} - Value: {{ d.key }}

OUTPUT:

Type: str - Value: 1
Type: str - Value: 2
Type: dict - Value: {key=value}
Type: str - Value: {key=value}
Type: str - Value: value
Type: null - Value: 

What the problem with the last output?

Too many dependencies?

Here are the dependency 2.1.7 inflicts on your project:
+--- com.hubspot.jinjava:jinjava:2.1.7
| +--- org.slf4j:slf4j-api:1.7.12
| +--- org.javassist:javassist:3.18.2-GA
| +--- org.jsoup:jsoup:1.8.1
| +--- de.odysseus.juel:juel-api:2.2.7
| +--- de.odysseus.juel:juel-impl:2.2.7
| +--- de.odysseus.juel:juel-spi:2.2.7
| +--- org.apache.commons:commons-lang3:3.4
| --- com.google.code.findbugs:annotations:3.0.0

That is a bit too much! Especially problematic are the findbugs "shutup" annotation, but are these jars all that necessary? The most I can put up with is Guava dependence, if at all... Can you try to make it a "single jar" beauty like the freemarker or jsoup projects?

... Staying away from jinjava for now, given no compelling benefits over freemarker for email templates (Unless you can point some?) and all the extra baggage.

PS: I use ansible a lot and I am well aware of jinja2's power.

add support for trim_blocks, lstrip_blocks

Jinja itself provides two useful options trim_blocks and lstrip_blocks for changing the behavior of whitespace preservation in the output. Quoting from the documentation:

If an application configures Jinja to trim_blocks, the first newline after a template tag is removed automatically (like in PHP). The lstrip_blocks option can also be set to strip tabs and spaces from the beginning of a line to the start of a block. (Nothing will be stripped if there are other characters before the start of the block.)

With both trim_blocks and lstrip_blocks enabled, you can put block tags on their own lines, and the entire block line will be removed when rendered, preserving the whitespace of the contents.

These options are particularly important when using this to render things other than HTML where whitespace itself can be significant (for example, Markdown).

select with equalto filter is not working

Almost identical to #78, but the issue is with the select() filter. Seeing the following error: TemplateSyntaxException: com.hubspot.jinjava.interpret.InterpretException: equalto test requires 1 argument

VariableChain does not use SimpleResolver

Due to VariableChain doing the resolving (instead of SimpleResolver) ResourceBundles are not supported. The reflection stuff for beans, maps, collections etc. has been duplicated. Is there a reason for this or is it by accident?

ArrayIndexOutOfBoundsException on parsing

Hi,

I am parsing the following jinja template:

{% import 'library.jinja' as lib %}
{{ lib.echo('test') }}

library.jinja is defined as this:

{% macro echo(what) -%}
echo {{ what }}
{%- endmacro %}

For some reason that I can not figure out I am ending with the following Exeception when calling

JinjavaConfig.Builder configBuilder = JinjavaConfig.newBuilder().withLstripBlocks(true).withTrimBlocks(true);
Jinjava jinjava = new Jinjava(configBuilder.build());

String template = "{% import 'library.jinja' as lib %}\n{{ lib.echo('test') }}";
String renderedTemplate = jinjava.render(template, null);

Exception:

java.lang.ArrayIndexOutOfBoundsException: 55
    at com.hubspot.jinjava.tree.parse.TokenScanner.newToken(TokenScanner.java:247)
    at com.hubspot.jinjava.tree.parse.TokenScanner.getNextToken(TokenScanner.java:172)
    at com.hubspot.jinjava.tree.parse.TokenScanner.computeNext(TokenScanner.java:281)
    at com.hubspot.jinjava.tree.parse.TokenScanner.computeNext(TokenScanner.java:31)
    at com.google.common.collect.AbstractIterator.tryToComputeNext(AbstractIterator.java:143)
    at com.google.common.collect.AbstractIterator.hasNext(AbstractIterator.java:138)
    at com.google.common.collect.Iterators$PeekingImpl.hasNext(Iterators.java:1144)
    at com.hubspot.jinjava.tree.TreeParser.text(TreeParser.java:101)
    at com.hubspot.jinjava.tree.TreeParser.nextNode(TreeParser.java:81)
    at com.hubspot.jinjava.tree.TreeParser.buildTree(TreeParser.java:59)
    at com.hubspot.jinjava.interpret.JinjavaInterpreter.parse(JinjavaInterpreter.java:116)
    at com.hubspot.jinjava.lib.tag.ImportTag.interpret(ImportTag.java:86)
    at com.hubspot.jinjava.lib.tag.Tag.interpretOutput(Tag.java:29)
    at com.hubspot.jinjava.tree.TagNode.render(TagNode.java:50)
    at com.hubspot.jinjava.interpret.JinjavaInterpreter.render(JinjavaInterpreter.java:182)
    at com.hubspot.jinjava.interpret.JinjavaInterpreter.render(JinjavaInterpreter.java:155)
    at com.hubspot.jinjava.Jinjava.renderForResult(Jinjava.java:204)
    at com.hubspot.jinjava.Jinjava.renderForResult(Jinjava.java:177)
    at com.hubspot.jinjava.Jinjava.render(Jinjava.java:150)

I do not know if this is a bug in Jinjava or in my code. But I can not figure out what I would be doing wrong.

I just figured out that I do not experience this error when I do not use

JinjavaConfig.newBuilder().withLstripBlocks(true).withTrimBlocks(true);

Just using the following works out fine:

JinjavaConfig.Builder configBuilder = JinjavaConfig.newBuilder().build();

Forloop???

Hello, how could I render a for loop?
I mean actually my Map("list" -> List())
will always behave strangely if i do it like that:

{% for item in list %}
{{ item }}
{% endfor %}

Item will still always be List() instead of the inner variables..

Edit:
I sent a HashMap with a "list" -> List("1", "2", "3") object to the template the result will always be:
List(1, 2, 3)
But it should be:
1
2
3

Edit: it looks that the scala List is making problems..

Problem with xmlattr filter

There seems to be a problem with the xmlattr and the way it binds itself to the filter method of the XmlAtttrFilter class. Using this in my template:

{{ boot_attributes_map|xmlattr }}

Produces this error:

com.hubspot.jinjava.interpret.FatalTemplateErrorsException: TemplateError{severity=FATAL, reason=SYNTAX_ERROR, message=TemplateSyntaxException: Method not found: class com.hubspot.jinjava.lib.filter.XmlAttrFilter.filter(com.hubspot.jinjava.objects.collections.PyMap, com.hubspot.jinjava.interpret.JinjavaInterpreter), fieldName=null, lineno=5}

It seems like it is ignoring the String... on the method when trying to find it. I can work around the error by sending a parameter to the xmlattr so that it finds the method:

{{ boot_attributes_map|xmlattr('') }}

I will see if I can identify where the problem is in the code and submit a pull request.

May I create a Template instance first and the use it to render context?

In http://product.hubspot.com/blog/jinjava-a-jinja-for-your-java the author says speed of jinja2 in python is 1387.3108 runs/s which is a bit slower than jinjava.

But I compared the 2 projects:
jinja2 is much faster if a Template is prepared in advance.

python code:

from jinja2 import Template

n = 70000
event = {'name': "jia.liu"}
t = Template('Hello, {% if name is defined %} {{name}} {% else %} world {% endif %}')
msg = []
for i in range(n):
    msg.append(t.render(**event))

jinjava code:

Jinjava jinjava = new Jinjava();

Map<String, Object> context = new HashMap<>();      
context.put("name", "Jared");
String template = "Hello, {% if name is defined %} {{name}} {% else %} world {% endif %}";

for (int i = 0; i < 70000; i++) {

    jinjava.renderForResult(template, context);

}       
System.out.println(System.currentTimeMillis()-s);

I really hope i use jinjava in a wrong way. Is there any method I could first create a Template instance like jinja2?

selectattr with equalto filter is not working

When I have a list of items and I want to filter them using selectattr like this

items | selectattr('property', 'equalto', 'fixed-value')

I'm getting the following error:

generate failed: TemplateError{severity=FATAL, reason=SYNTAX_ERROR, message=TemplateSyntaxException: com.hubspot.jinjava.interpret.InterpretException: equalto test requires 1 argument, fieldName=null, lineno=79}

But that is exactly the syntax mentioned in https://github.com/HubSpot/jinjava/blob/5e6411216131e5405af8259bfddaef61a6550ac3/src/main/java/com/hubspot/jinjava/lib/exptest/IsEqualToExpTest.java. Am I using this the wrong way or is this a bug?

Filters don't seem to work

Hi, trying to use filters I get the following error:

com.hubspot.jinjava.interpret.FatalTemplateErrorsException: TemplateError{severity=FATAL, reason=EXCEPTION, message=InterpretException: Error resolving expression [testvar | upper]: IllegalArgumentException: wrong number of arguments, fieldName=null, lineno=1}

This is running:

Jinjava jinjava = new Jinjava(new JinjavaConfig());
String template = "hello {{ testvar | upper }}";
jinjava.render(template, ImmutableMap.of("testvar", "stranger")));

Running with no filters works fine.

I've checked with a colleague that's using jinjava in another project and he seems to be getting the same error.

TemplateSyntaxException: filter method not found

Any time I attempt to use filters I get the following error:

Request processing failed; nested exception is com.hubspot.jinjava.interpret.FatalTemplateErrorsException: TemplateError{severity=FATAL, reason=SYNTAX_ERROR, message=TemplateSyntaxException: Method not found: class com.hubspot.jinjava.lib.filter.TitleFilter.filter(java.lang.String, com.hubspot.jinjava.interpret.JinjavaInterpreter), fieldName=null, lineno=2}

My template for that example is simply:
{{ "hello world" | title }}

At this point I'm not doing much so wondering if its just something I missed that's required for filter support? I'm assuming the default ones are already registered.

I was originally attempting to add my own filter but had the same problem, my filter implemented the Jinjava Filter interface as I'm sure the TitleFilter does. The method its looking for in the filter has the following footprint:

public Object filter(Object o, JinjavaInterpreter jinjavaInterpreter, String... strings)

I'm running on Java 1.8.0_101 with Jinjava 2.1.0.

list filter on strings

According to the list filter file: https://github.com/HubSpot/jinjava/blob/master/src/main/java/com/hubspot/jinjava/lib/filter/ListFilter.java
Applying a list filter to a string variable should return a sequence of characters.

{% set my_test = 'Test'%}
{{ my_test|list }}

prints something like

[[C@45a31e18]

Is this expected behavior for this filter, when used with strings? If so is there any practical application for this or should the filter really only be applied to numeric variables?

Disable default registered tags

I'm looking for a way to remove some of the default registered tags. For security reasons, we don't want end-users to be able to use tags like extends, import or include.

Would you accept a PR that adds this capability?

Nested variable support

Nested variables don't seem to be working as expected ie: in my template

{{ date.from }}

and rendering with something like this (using Scala)

val m = immutable.HashMap(
      "date" -> immutable.HashMap(
        "from" -> "1451865600000",
        "to" -> "1452038400000"
      )
  )
jinjava.render(template, mapAsJavaMap(m))

Renders fine, but the value is not being printed into the output.

Is this just not part of your implementation of Jinja?

Why is expression result reinterpreted?

Please consider the following code:

final String tpl = "The question is: {{ obj.text }}!";
final Map<String, Object> ctx = new HashMap<>();
final Map<String, Object> obj = new HashMap<>();
obj.put("text", "{{ obj.val }}");
obj.put("val", "is it a bug?");
ctx.put("obj", obj);
Jinjava jj = new Jinjava();
System.out.println(jj.render(tpl, ctx));

The expected output is: The question is {{ obj.val }}!, but The question is: is it a bug?! appears on my console.

I have created similar code in Jinja2 (python):

from jinja2 import Template
t = Template('The question is {{ obj.text }}!')
ctx = {"obj": {"text": "{{ obj.val }}", "val": "is it a bug?"}}
print t.render(ctx)

The render result is as expected: The question is {{ obj.val }}!

I would like to know if it is an intended behavior, because in my application I have some templates where expressions insert google-encoded polylines which may occasionally contain {{ }} characters, which in turn are interpreted as another expression with unresolvable name.

Can not get the key in map in for loop

TestCase:
{ "zkhost":{ "aaa":null, "bbb":null, "ccc":null }
{%for zk in zkhost%}{{zk}}{%if not loop.last%},{%endif%}{%endfor%}

the result we want:

aaa,bbb,ccc

in fact

,,

String splitting does not work

When I try to get a part of a string like this (currently using jinjava 2.1.7):

{{ repository[0:4] }}

I get the following Exception thrown:

com.hubspot.jinjava.interpret.TemplateSyntaxException: Property repository is not a sequence.
    at com.hubspot.jinjava.el.ExpressionResolver.resolveExpression(ExpressionResolver.java:74)
    at com.hubspot.jinjava.interpret.JinjavaInterpreter.resolveELExpression(JinjavaInterpreter.java:323)
    at com.hubspot.jinjava.tree.ExpressionNode.render(ExpressionNode.java:41)
    at com.hubspot.jinjava.lib.tag.ForTag.interpret(ForTag.java:137)
    at com.hubspot.jinjava.lib.tag.Tag.interpretOutput(Tag.java:29)
    at com.hubspot.jinjava.tree.TagNode.render(TagNode.java:50)
    at com.hubspot.jinjava.lib.fn.MacroFunction.doEvaluate(MacroFunction.java:69)
    at com.hubspot.jinjava.el.ext.AbstractCallableMethod.evaluate(AbstractCallableMethod.java:72)
    at com.hubspot.jinjava.el.JinjavaInterpreterResolver.invoke(JinjavaInterpreterResolver.java:82)
    at de.odysseus.el.tree.impl.ast.AstMethod.eval(AstMethod.java:91)
    at de.odysseus.el.tree.impl.ast.AstMethod.eval(AstMethod.java:100)
    at de.odysseus.el.tree.impl.ast.AstEval.eval(AstEval.java:51)
    at de.odysseus.el.tree.impl.ast.AstNode.getValue(AstNode.java:31)
    at de.odysseus.el.TreeValueExpression.getValue(TreeValueExpression.java:122)
    at com.hubspot.jinjava.el.ExpressionResolver.resolveExpression(ExpressionResolver.java:62)
    at com.hubspot.jinjava.interpret.JinjavaInterpreter.resolveELExpression(JinjavaInterpreter.java:323)
    at com.hubspot.jinjava.tree.ExpressionNode.render(ExpressionNode.java:41)
    at com.hubspot.jinjava.interpret.JinjavaInterpreter.render(JinjavaInterpreter.java:181)
    at com.hubspot.jinjava.interpret.JinjavaInterpreter.render(JinjavaInterpreter.java:153)
    at com.hubspot.jinjava.Jinjava.renderForResult(Jinjava.java:200)
    at com.hubspot.jinjava.Jinjava.renderForResult(Jinjava.java:173)
    at com.hubspot.jinjava.Jinjava.render(Jinjava.java:149)
    at com.automic.bob.service.translate.parser.jinja.JinjaParser.getParsedString(JinjaParser.java:34)
    at com.automic.bob.service.translate.parser.build.BuildFileParser.parseAction(BuildFileParser.java:186)
    at com.automic.bob.service.translate.parser.build.BuildFileParser.generateScriptForBuildActions(BuildFileParser.java:148)
    at com.automic.bob.service.translate.parser.build.BuildFileParser.parse(BuildFileParser.java:78)
    at com.automic.bob.service.translate.TranslateFileMessageHandler.sendReplyMessage(TranslateFileMessageHandler.java:77)
    at com.automic.bob.service.translate.TranslateFileMessageHandler.handle(TranslateFileMessageHandler.java:42)
    at com.automic.bob.service.translate.TranslateFileMessageHandler.handle(TranslateFileMessageHandler.java:19)
    at com.automic.bob.lib.comm.RabbitMQ$4.handleDelivery(RabbitMQ.java:290)
    at com.rabbitmq.client.impl.ConsumerDispatcher$5.run(ConsumerDispatcher.java:144)
    at com.rabbitmq.client.impl.ConsumerWorkService$WorkPoolRunnable.run(ConsumerWorkService.java:99)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: javax.el.ELException: Property repository is not a sequence.
    at com.hubspot.jinjava.el.ext.AstRangeBracket.eval(AstRangeBracket.java:35)
    at de.odysseus.el.tree.impl.ast.AstEval.eval(AstEval.java:51)
    at de.odysseus.el.tree.impl.ast.AstNode.getValue(AstNode.java:31)
    at de.odysseus.el.TreeValueExpression.getValue(TreeValueExpression.java:122)
    at com.hubspot.jinjava.el.ExpressionResolver.resolveExpression(ExpressionResolver.java:62)
    ... 34 more

rejectattr with equalto filter is not working

I believe this issue is the same as issue #78, except that rejectattr is concerned.

topics|rejectattr("name", "equalto", "News")

TemplateSyntaxException: com.hubspot.jinjava.interpret.InterpretException: equalto test requires 1 argument

Should a for-loop fail if `failOnUnknownTokens` is set?

From the name of the config flag failOnUnknownTokens, I expected rendering
{% for zone in zones %} ... {% endfor %}
to fail if zones is not defined in the bindings.

I can implement the behavior if that's a reasonable expectation. If it is, are there other tags that should respect that flag?

Unable to use forloop.counter and list.count

When I try to use those variables in a for loop, those variables are replaced by nothing.

My syntax is :

{% for fo in foo %}
{% for ba in bar %}
...
{% endfor %}
send_user "Completed ({{ forloop.counter }}/{{ foo.count }})\n"
{% endfor %}

output is :
Completed (/)

incorrect whitespacing

example template that reveals error:

{%- macro do_thing() -%}
  {%- set x = 1 -%}
  token
{%- endmacro -%}
xx{{ do_thing() }}xx

I would expect output to be: (as confirmed by http://jinja2test.tk/ )

xxtokenxxx

but instead jinjava outputs:

xx
  tokenxxx

Add support for disabling features

It would be nice to be able to allow users to disable certain template features. One use case would be to disable looping, extensions, etc. to offer an environment similar to what Ansible does for its YAML files. From http://docs.ansible.com/ansible/playbooks_variables.html:

ansible allows Jinja2 loops and conditionals in templates, but in playbooks, we do not use them. Ansible playbooks are pure machine-parseable YAML. This is a rather important feature as it means it is possible to code-generate pieces of files, or to have other ecosystem tools read Ansible files. Not everyone will need this but it can unlock possibilities.

It looks like this should be possible by allowing on Context that allows setting the following:

https://github.com/HubSpot/jinjava/blob/master/src/main/java/com/hubspot/jinjava/interpret/Context.java#L69-L72

And then allowing a constructor on Jinjava that takes a Context:

https://github.com/HubSpot/jinjava/blob/master/src/main/java/com/hubspot/jinjava/Jinjava.java#L82

Alternatively, the JinjavaConfig could encapsulate the Context dependencies`.

I would be happy to add PR if this sounds reasonable.

Importing template by name breaks macro references within that template

I’ve found that if you define a macro like this:
(within valid-macro-usage.jinja)

{%- macro fun(val) %}{{ val }}{% endmacro -%}
{{ fun("called within template in which it was defined") }}

And then import it with a name like this:

{% import "valid-macro-usage.jinja" as m %}
{{ m.fun("called within template in which it was imported") }}

you’ll get a TemplateError like this:
[TemplateError{severity=FATAL, reason=SYNTAX_ERROR, item=OTHER, message='TemplateSyntaxException: Could not resolve function 'fun'', fieldName='null', lineno=3, category=UNKNOWN, categoryErrors=null}]

Whereas if you modify valid-macro-usage.jinja to call m.fun (which shouldn’t be necessary) it works as expected.

This branch adds a breaking test to illustrate the problem: https://github.com/HubSpot/jinjava/tree/fix_macro_reference_error_in_template_imported_by_name

Unable to pass arguments back into call blocks

While you can pass arguments into macros and a call block back into a macro using {{ caller }}, you are not able to pass an argument into the call block. For example the following example does not work, despite being seemingly possible based on Jinja and the CallTag.java.

{% macro foo(class) %}

{{ caller('a message from me') }}

{% endmacro %}
{% call(message) foo('my-message') %}
Print this call block back into macro with {{ message }}
{% endcall %}

jinjava // either buggy or unimplemented

from jinja math docs:

//
Divide two numbers and return the truncated integer result. {{ 20 // 7 }} is 2.

However attempting this with jinjava throws an exception. For example, I would expect this template:

20 // 7 = {{ 20 // 7 }}

to return 20 // 7 = 2, but instead it throws this:

com.hubspot.jinjava.interpret.InterpretException: Error resolving expression [20 // 7]: NullPointerException: 
	at com.hubspot.jinjava.el.ExpressionResolver.resolveExpression(ExpressionResolver.java:89)
	at com.hubspot.jinjava.interpret.JinjavaInterpreter.resolveELExpression(JinjavaInterpreter.java:397)
Caused by: java.lang.NullPointerException
	at jinjava.de.odysseus.el.tree.impl.Parser.mul(Parser.java:547)
	at com.hubspot.jinjava.el.ext.ExtendedParser.add(ExtendedParser.java:149)
	at jinjava.de.odysseus.el.tree.impl.Parser.cmp(Parser.java:462)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.