Introdução ao Bigode

Introdução ao Bigode

1. Visão geral

Neste artigo, focaremos nos Mustache modelos e usaremos uma de suas Java APIs para produzir conteúdo HTML dinâmico .

O Bigode é um mecanismo de modelo sem lógica para criar conteúdo dinâmico como HTML, arquivos de configuração entre outras coisas.

*2. Introdução *

Simplificando, o mecanismo é classificado como logicless porque não possui construções que suportam instruções if-else e loops.

Os modelos de bigode consistem em nomes de tags cercados por* \ {\ {}} *(que se assemelham a bigodes - daí o nome) e são apoiados por um objeto de modelo que contém os dados para o modelo.

===* 3. Dependência Maven *

A compilação e execução dos modelos são suportadas por vários idiomas - do lado do cliente e do servidor.

Para poder processar os modelos do Java, usamos sua biblioteca Java, que pode ser adicionada como uma dependência do Maven.

Java 8+:

<dependency>
    <groupId>com.github.spullara.mustache.java</groupId>
    <artifactId>compiler</artifactId>
    <version>0.9.4</version>
</dependency>

Java 6/7:

<dependency>
    <groupId>com.github.spullara.mustache.java</groupId>
    <artifactId>compiler</artifactId>
    <version>0.8.18</version>
</dependency>

Podemos verificar as versões mais recentes da biblioteca em https://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22com.github.spullara.mustache.java%22%20AND%20a% 3A% 22compiler% 22 [Repositório Central do Maven].

===* 4. Uso *

Vejamos um cenário simples que mostra como:

  1. Escreva um modelo simples

  2. Compile o modelo usando a API Java

  3. Execute-o fornecendo os dados necessários

====* 4.1 Um modelo simples de bigode *

Vamos criar um modelo simples para exibir os detalhes de uma tarefa de tarefas:

<h2>{{title}}</h2>
<small>Created on {{createdOn}}</small>
<p>{{text}}</p>

No modelo acima, os campos entre chaves (\ {\ {}}) podem ser:

  • métodos e propriedades de uma classe Java *chaves de um objeto Map

====* 4.2 Compilando o modelo de bigode *

Podemos compilar o modelo como mostrado abaixo:

MustacheFactory mf = new DefaultMustacheFactory();
Mustache m = mf.compile("todo.mustache");

MustacheFactory procura o modelo fornecido no caminho de classe. Em nosso exemplo, colocamos todo.mustache em src/main/resources.

====* 4.3 Executando o modelo de bigode *

Os dados fornecidos ao modelo serão uma instância da classe Todo cuja definição é:

public class Todo {
    private String title;
    private String text;
    private boolean done;
    private Date createdOn;
    private Date completedOn;

   //constructors, getters and setters
}

O modelo compilado pode ser executado para obter HTML, como mostrado abaixo:

Todo todo = new Todo("Todo 1", "Description");
StringWriter writer = new StringWriter();
m.execute(writer, todo).flush();
String html = writer.toString();

===* 5. Seções e Iterações de Bigode *

Vamos agora dar uma olhada em como listar todos.* Para iterar sobre os dados de uma lista, usamos as seções Bigode. *

Uma seção é um bloco de código que é repetido uma ou mais vezes, dependendo do valor da chave no contexto atual.

Parece algo como:

{{#todo}}
<!-- Other code -->
{{/todo}}

Uma seção começa com uma libra (#) e termina com uma barra (/), onde cada um dos sinais é seguido pela chave cujo valor é usado como base para renderizar a seção.

A seguir, estão os cenários que podem ocorrer, dependendo do valor da chave:

====* 5.1. Seção com lista não vazia ou valor não falso *

Vamos criar um modelo todo-section.mustache que usa uma seção:

{{#todo}}
<h2>{{title}}</h2>
<small>Created on {{createdOn}}</small>
<p>{{text}}</p>
{{/todo}}

Vejamos este modelo em ação:

@Test
public void givenTodoObject_whenGetHtml_thenSuccess()
  throws IOException {

    Todo todo = new Todo("Todo 1", "Todo description");
    Mustache m = MustacheUtil.getMustacheFactory()
      .compile("todo.mustache");
    Map<String, Object> context = new HashMap<>();
    context.put("todo", todo);

    String expected = "<h2>Todo 1</h2>";
    assertThat(executeTemplate(m, todo)).contains(expected);
}

Vamos criar outro modelo todos.mustache para listar todos:

{{#todos}}
<h2>{{title}}</h2>
{{/todos}}

E crie uma lista de todos usando-o:

@Test
public void givenTodoList_whenGetHtml_thenSuccess()
  throws IOException {

    Mustache m = MustacheUtil.getMustacheFactory()
      .compile("todos.mustache");

    List<Todo> todos = Arrays.asList(
      new Todo("Todo 1", "Todo description"),
      new Todo("Todo 2", "Todo description another"),
      new Todo("Todo 3", "Todo description another")
    );
    Map<String, Object> context = new HashMap<>();
    context.put("todos", todos);

    assertThat(executeTemplate(m, context))
      .contains("<h2>Todo 1</h2>")
      .contains("<h2>Todo 2</h2>")
      .contains("<h2>Todo 3</h2>");
}

====* 5.2 Seção com List ou False vazio ou Null Value *

Vamos testar o todo-section.mustache com um valor null:

@Test
public void givenNullTodoObject_whenGetHtml_thenEmptyHtml()
  throws IOException {
    Mustache m = MustacheUtil.getMustacheFactory()
      .compile("todo-section.mustache");
    Map<String, Object> context = new HashMap<>();
    assertThat(executeTemplate(m, context)).isEmpty();
}

Da mesma forma, teste todos.mustache com uma lista vazia:

@Test
public void givenEmptyList_whenGetHtml_thenEmptyHtml()
  throws IOException {
    Mustache m = MustacheUtil.getMustacheFactory()
      .compile("todos.mustache");

    Map<String, Object> context = new HashMap<>();
    assertThat(executeTemplate(m, context)).isEmpty();;
}

===* 6. Seções invertidas *

*Seções invertidas são aquelas que são renderizadas apenas uma vez com base na inexistência* da chave ou no valor _false_ ou _null_ ou em uma lista vazia. Em outras palavras, elas são renderizadas quando uma seção não é renderizada.

Eles começam com um sinal de intercalação (^) e terminam com uma barra (/), como mostrado abaixo:

{{#todos}}
<h2>{{title}}</h2>
{{/todos}}
{{^todos}}
<p>No todos!</p>
{{/todos}}

O modelo acima, quando fornecido com uma lista vazia:

@Test
public void givenEmptyList_whenGetHtmlUsingInvertedSection_thenHtml()
  throws IOException {

    Mustache m = MustacheUtil.getMustacheFactory()
      .compile("todos-inverted-section.mustache");

    Map<String, Object> context = new HashMap<>();
    assertThat(executeTemplate(m, context).trim())
      .isEqualTo("<p>No todos!</p>");
}

*7. Lambdas *

Os valores* para chaves de uma seção de bigode podem ser uma função ou uma expressão lambda *. Nesse caso, a expressão lambda completa é invocada passando o texto dentro da seção como um parâmetro para a expressão lambda.

Vejamos um modelo todos-lambda.mustache:

{{#todos}}
<h2>{{title}}{{#handleDone}}{{doneSince}}{{/handleDone}}</h2>
{{/todos}}

A chave handleDone resolve para uma expressão lambda do Java 8, como mostrado abaixo:

public Function<Object, Object> handleDone() {
    return (obj) -> done ?
      String.format("<small>Done %s minutes ago<small>", obj) : "";
}

O HTML gerado pela execução do modelo acima é:

<h2>Todo 1</h2>
<h2>Todo 2</h2>
<h2>Todo 3<small>Done 5 minutes ago<small></h2>

8. Conclusão

Neste artigo introdutório, analisamos a criação de modelos de bigode com seções, seções invertidas e lambdas. E usamos a API Java para compilar e executar os modelos, fornecendo dados relevantes.

Existem alguns recursos mais avançados do Bigode que merecem ser explorados - como:

  • fornecer um valor exigível como um valor que resulta em uma avaliação simultânea

  • usando DecoratedCollection para obter o primeiro, o último e o índice dos elementos da coleção

  • API invert que fornece aos dados o texto e o modelo

E, como sempre, o código fonte completo está disponível over no Github.