Как проверить, определена ли переменная в шаблонах Pebble? - PullRequest
0 голосов
/ 19 декабря 2018

Использование Pebble версии 3.0.6.

Мне нужно проверить, имеет ли значение 'v' определенную переменную (в переводе на Java: если объект v имеет определенное свойство).Ищете что-то вроде

{% if v instanceof test.MyClass %}
   ...
{% endif %}

или

{% if v has myProperty %}
   ...
{% endif %}

Оба не доступны, насколько я знаю.Как лучше всего добиться этого с помощью Pebble?

ОБНОВЛЕНИЕ

Контекст:

  • Использование strictVariables = true
  • свойство не является логическим значением, строкой или числом

1 Ответ

0 голосов
/ 20 декабря 2018

Не встроенный, но Pebble позволяет писать пользовательских расширений .В java instanceof есть оператор, который является чем-то, что pebble позволяет написать расширение для.

Нам нужно 3 вещи, чтобы написать собственное расширение для оператора:

  1. AКласс, который описывает оператор (implements BinaryOperator)
  2. Класс, который описывает, как оператор оценивается (extends BinaryExpression<Object>)
  3. Класс, который добавляет этот оператор к бинарным операторам pebble, этоявляется классом расширения и должен implements Extension.

Шаг 1

Мы определяем оператор как instanceof с приоритетом 30, согласно java приоритет instanceof такой же, как < > <= >=, в pebble , эти операторы имеют приоритет 30, поэтому мы используем его.Узлом, который оценивает эту операцию, является InstanceofExpression.class, который является классом, который мы создадим на шаге 2.

   public class InstanceofOperator implements BinaryOperator {

    /**
     * This precedence is set based on
     * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html">Java
     * Operators</a> 30 is the same precedence pebble has set for operators like {@code instanceof}
     * like <a href="https://github.com/PebbleTemplates/pebble/wiki/extending-pebble">Extending
     * Pebble</a>.
     */
    public int getPrecedence() {
        return 30;
    }

    public String getSymbol() {
        return "instanceof";
    }

    public Class<? extends BinaryExpression<?>> getNodeClass() {
        return InstanceofExpression.class;
    }

    public Associativity getAssociativity() {
        return Associativity.LEFT;
    }

}

Шаг 2

Теперь мы должны написать, что оператор оценивает, вВ этом случае мы вернем true , если left instanceof right.Для правой части этой оценки мы используем String, который должен содержать полное квалификационное имя для класса, например 1 instanceof "java.lang.String", который вернет false, или 1 instanceof "java.lang.Long", который вернет true.

Будет сгенерировано исключение, если класс right не может быть найден / загружен с помощью Class.forName.

public class InstanceofExpression extends BinaryExpression<Object> {

    @Override
    public Object evaluate(PebbleTemplateImpl self, EvaluationContextImpl context) {
        // The left class (left instanceof right)
        Object leftClass = getLeftExpression().evaluate(self, context);

        // The right class, this is a string with the full qualifying name of the class eg (left
        // instanceof "java.lang.String")
        String rightClassname = getRightExpression().evaluate(self, context).toString();

        // We must get the right class as Class<?> in order to check if left is an instanceof right
        Class<?> rightClass;
        try {
            rightClass = Class.forName(rightClassname);
        } catch (ClassNotFoundException e) {
            throw new PebbleException(e.getCause(),
                    String.format("Cannot find class %s", rightClassname));
        }

        // Check if the left class is an instanceof the right class
        return rightClass.isInstance(leftClass);
    }

}

Шаг 3

Теперь мы должны создать расширение для Pebble,это довольно просто.Мы создаем экземпляр нашего пользовательского InstanceofOperator и возвращаем его как бинарный оператор:

public class InstanceofExtension implements Extension {

    @Override
    public List<BinaryOperator> getBinaryOperators() {
        return Arrays.asList(new InstanceofOperator());
    }

    // ...
    // Other methods required by implementing Extension, these other methods can just return null.
    // ...
    // ...

}

В качестве альтернативы вместо всего Шаг 1 вы можете реализовать метод getBinaryOperators следующим образом:

@Override
public List<BinaryOperator> getBinaryOperators() {
  return Arrays.asList(new BinaryOperatorImpl("instanceof", 30, InstanceofExpression.class,
            Associativity.LEFT));
}

Прибыль!

Теперь мы можем добавить наше собственное расширение с помощью .extension(new InstanceofExtension()):

PebbleEngine engine =
        new PebbleEngine.Builder().strictVariables(true)
                .extension(new InstanceofExtension()).build();

PebbleTemplate compiledTemplate = engine.getTemplate("home.html");

// Test with Person as v
Writer personWriter = new StringWriter();

Map<String, Object> context = new HashMap<>();
context.put("v", new Person());
compiledTemplate.evaluate(personWriter, context);

System.out.println(personWriter.toString()); // <b>asdasdasdasds</b> is present

// Test with Fruit as v
Writer fruitWriter = new StringWriter();

context.put("v", new Fruit());
compiledTemplate.evaluate(fruitWriter, context);

System.out.println(fruitWriter.toString()); // <b>asdasdasdasds</b> is not present, but
                                            // <b>red</b> is

Класс Person, который мы обрабатываем выше, определяется кактакой, что он расширяется Entity.Чтобы доказать, что концепция работает, у нас также есть класс Fruit, который не расширяется Entity.Мы тестируем оба этих различных класса в v:

class Person extends Entity {
    public String name = "me";

}

class Entity {
    public String asd = "asdasdasdasds";

}

class Fruit {
    public String color = "red";
}

home.html . Мы проверяем, является ли v равным Person или Fruit является экземпляром com.mypackage.test.Entity или com.mypackage.test.Fruit:

<html>
    <body>
        {% if v instanceof "com.mypackage.test.Entity" %}
            <b>{{ v.asd }}</b>
        {% endif %}
        {% if v instanceof "com.mypackage.test.Fruit" %}
            <b>{{ v.color }}</b>
        {% endif %}
    </body>
</html>

Вывод:

<html>
    <body>
        <b>asdasdasdasds</b>
    </body>
</html>
<html>
    <body>
        <b>red</b>
    </body>
</html>

Комментарии

Слева не instanceof right "версия:

{% if not (v instanceof "com.mypackage.test.entity") %}
...