This commit is contained in:
Jonathan Cook
2019-10-23 15:01:44 +02:00
parent db85c8f275
commit 684ec0d2e3
20486 changed files with 1642483 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
## Mustache
This module contains articles about Mustache.
### Relevant Articles:
- [Introduction to Mustache](https://www.baeldung.com/mustache)
- [Guide to Mustache with Spring Boot](https://www.baeldung.com/spring-boot-mustache)
+80
View File
@@ -0,0 +1,80 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>mustache</artifactId>
<name>mustache</name>
<packaging>jar</packaging>
<parent>
<artifactId>parent-boot-2</artifactId>
<groupId>com.baeldung</groupId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../parent-boot-2</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>${mustache.compiler.api.version}</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mustache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>${webjars.bootstrap.version}</version>
</dependency>
<dependency>
<groupId>org.fluttercode.datafactory</groupId>
<artifactId>datafactory</artifactId>
<version>${datafactory.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<properties>
<mustache.compiler.api.version>0.9.2</mustache.compiler.api.version>
<log4j.version>1.2.16</log4j.version>
<!-- maven plugins -->
<datafactory.version>0.8</datafactory.version>
<webjars.bootstrap.version>3.3.7</webjars.bootstrap.version>
</properties>
</project>
@@ -0,0 +1,15 @@
package com.baeldung.mustache;
import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.MustacheFactory;
public class MustacheUtil {
private static final MustacheFactory mf = new DefaultMustacheFactory();
public static MustacheFactory getMustacheFactory(){
return mf;
}
}
@@ -0,0 +1,77 @@
package com.baeldung.mustache.model;
import java.time.Duration;
import java.util.Date;
import java.util.function.Function;
public class Todo {
public Todo() {
}
public Todo(String title, String text) {
this.title = title;
this.text = text;
createdOn = new Date();
}
private String title;
private String text;
private boolean done = false;
private Date createdOn;
private Date completedOn;
public void markAsDone() {
done = true;
completedOn = new Date();
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public boolean getDone() {
return done;
}
public Date getCreatedOn() {
return createdOn;
}
public Date getCompletedOn() {
return completedOn;
}
public void setDone(boolean done) {
this.done = done;
}
public void setCompletedOn(Date completedOn) {
this.completedOn = completedOn;
}
public long doneSince() {
return done ? Duration
.between(createdOn.toInstant(), completedOn.toInstant())
.toMinutes() : 0;
}
public Function<Object, Object> handleDone() {
return (obj) -> done ? String.format("<small>Done %s minutes ago<small>", obj) : "";
}
}
@@ -0,0 +1,32 @@
package com.baeldung.springmustache;
import com.samskivert.mustache.Mustache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.mustache.MustacheEnvironmentCollector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.env.Environment;
@SpringBootApplication
@ComponentScan(basePackages = {"com.baeldung"})
public class SpringMustacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringMustacheApplication.class, args);
}
@Bean
public Mustache.Compiler mustacheCompiler(Mustache.TemplateLoader templateLoader, Environment environment) {
MustacheEnvironmentCollector collector = new MustacheEnvironmentCollector();
collector.setEnvironment(environment);
return Mustache.compiler()
.defaultValue("Some Default Value")
.withLoader(templateLoader)
.withCollector(collector);
}
}
@@ -0,0 +1,42 @@
package com.baeldung.springmustache.controller;
import com.baeldung.springmustache.model.Article;
import org.fluttercode.datafactory.impl.DataFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@Controller
public class ArticleController {
@GetMapping("/article")
public ModelAndView displayArticle(Map<String, Object> model) {
List<Article> articles = IntStream.range(0, 10)
.mapToObj(i -> generateArticle("Article Title " + i))
.collect(Collectors.toList());
model.put("articles", articles);
return new ModelAndView("index", model);
}
private Article generateArticle(String title) {
Article article = new Article();
DataFactory factory = new DataFactory();
article.setTitle(title);
article.setBody(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur faucibus tempor diam. In molestie arcu eget ante facilisis sodales. Maecenas porta tellus sapien, eget rutrum nisi blandit in. Mauris tempor auctor ante, ut blandit velit venenatis id. Ut varius, augue aliquet feugiat congue, arcu ipsum finibus purus, dapibus semper velit sapien venenatis magna. Nunc quam ex, aliquet at rutrum sed, vestibulum quis libero. In laoreet libero cursus maximus vulputate. Nullam in fermentum sem. Duis aliquam ullamcorper dui, et dictum justo placerat id. Aliquam pretium orci quis sapien convallis, non blandit est tempus.");
article.setPublishDate(factory.getBirthDate().toString());
article.setAuthor(factory.getName());
return article;
}
}
@@ -0,0 +1,41 @@
package com.baeldung.springmustache.model;
public class Article {
private String title;
private String body;
private String author;
private String publishDate;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getPublishDate() {
return publishDate;
}
public void setPublishDate(String publishDate) {
this.publishDate = publishDate;
}
}
@@ -0,0 +1 @@
spring.mustache.suffix:.html
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<body>
Something went wrong: {{status}} {{error}}
</body>
</html>
@@ -0,0 +1,9 @@
{{>layout/header}}
<body>
<div class="container">{{>layout/article}}</div>
<script type="text/javascript"
src="webjars/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
{{>layout/footer}}
@@ -0,0 +1,8 @@
<div class="starter-template">
{{#articles}}
<h1>{{title}}</h1>
<h3>{{publishDate}}</h3>
<h3>{{author}}</h3>
<p>{{body}}</p>
{{/articles}}
</div>
@@ -0,0 +1,2 @@
<!-- this is footer -->
</html>
@@ -0,0 +1,11 @@
<!DOCTYPE HTML>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="webjars/bootstrap/3.3.7/css/bootstrap.min.css" />
<link rel="stylesheet" href="css/main.css" />
</head>
<!-- this is header -->
@@ -0,0 +1,5 @@
{{#todo}}
<h2>{{title}}</h2>
<small>Created on {{createdOn}}</small>
<p>{{text}}</p>
{{/todo}}
@@ -0,0 +1,3 @@
<h2>{{title}}</h2>
<small>Created on {{createdOn}}</small>
<p>{{text}}</p>
@@ -0,0 +1,6 @@
{{#todos}}
<h2>{{title}}</h2>
{{/todos}}
{{^todos}}
<p>No todos!</p>
{{/todos}}
@@ -0,0 +1,3 @@
{{#todos}}
<h2>{{title}}{{#handleDone}}{{doneSince}}{{/handleDone}}</h2>
{{/todos}}
@@ -0,0 +1,3 @@
{{#todos}}
<h2>{{title}}</h2>
{{/todos}}
@@ -0,0 +1,100 @@
package com.baeldung.mustache;
import com.baeldung.mustache.model.Todo;
import com.github.mustachejava.Mustache;
import org.junit.Test;
import java.io.IOException;
import java.io.StringWriter;
import java.time.Instant;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
public class TodoMustacheServiceUnitTest {
private String executeTemplate(Mustache m, Map<String, Object> context) throws IOException {
StringWriter writer = new StringWriter();
m.execute(writer, context).flush();
return writer.toString();
}
private String executeTemplate(Mustache m, Todo todo) throws IOException {
StringWriter writer = new StringWriter();
m.execute(writer, todo).flush();
return writer.toString();
}
@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);
}
@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();
}
@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>");
}
@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();
;
}
@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>");
}
@Test
public void givenTodoList_whenGetHtmlUsingLamdba_thenHtml() throws IOException {
Mustache m = MustacheUtil.getMustacheFactory().compile("todos-lambda.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")
);
todos.get(2).setDone(true);
todos.get(2).setCompletedOn(Date.from(Instant.now().plusSeconds(300)));
Map<String, Object> context = new HashMap<>();
context.put("todos", todos);
assertThat(executeTemplate(m, context).trim()).contains("Done 5 minutes ago");
}
}
@@ -0,0 +1,30 @@
package com.baeldung.springmustache;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SpringMustacheApplicationIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void givenIndexPageWhenContainsArticleThenTrue() {
ResponseEntity<String> entity = this.restTemplate.getForEntity("/article", String.class);
Assert.assertTrue(entity.getStatusCode().equals(HttpStatus.OK));
Assert.assertTrue(entity.getBody().contains("Article Title 0"));
}
}