mirror of
https://gitee.com/dcren/initializr.git
synced 2026-02-26 13:43:15 +08:00
Review TemplateRenderer abstraction
Closes gh-818
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.generator.io.template;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.samskivert.mustache.Mustache;
|
||||
import com.samskivert.mustache.Mustache.Compiler;
|
||||
import com.samskivert.mustache.Mustache.TemplateLoader;
|
||||
import com.samskivert.mustache.Template;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.Cache.ValueRetrievalException;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
/**
|
||||
* A {@link TemplateRenderer} using Mustache.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class MustacheTemplateRenderer implements TemplateRenderer {
|
||||
|
||||
private final Compiler mustache;
|
||||
|
||||
private final Function<String, String> keyGenerator;
|
||||
|
||||
private final Cache templateCache;
|
||||
|
||||
public MustacheTemplateRenderer(String resourcePrefix, Cache templateCache) {
|
||||
String prefix = (resourcePrefix.endsWith("/") ? resourcePrefix
|
||||
: resourcePrefix + "/");
|
||||
this.mustache = Mustache.compiler().withLoader(mustacheTemplateLoader(prefix));
|
||||
this.keyGenerator = (name) -> String.format("%s%s", prefix, name);
|
||||
this.templateCache = templateCache;
|
||||
}
|
||||
|
||||
public MustacheTemplateRenderer(String resourcePrefix) {
|
||||
this(resourcePrefix, null);
|
||||
}
|
||||
|
||||
private static TemplateLoader mustacheTemplateLoader(String prefix) {
|
||||
ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
return (name) -> {
|
||||
String location = prefix + name + ".mustache";
|
||||
return new InputStreamReader(
|
||||
resourceLoader.getResource(location).getInputStream(),
|
||||
StandardCharsets.UTF_8);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String render(String templateName, Map<String, ?> model) throws IOException {
|
||||
Template template = getTemplate(templateName);
|
||||
return template.execute(model);
|
||||
}
|
||||
|
||||
private Template getTemplate(String name) {
|
||||
try {
|
||||
if (this.templateCache != null) {
|
||||
try {
|
||||
return this.templateCache.get(this.keyGenerator.apply(name),
|
||||
() -> loadTemplate(name));
|
||||
}
|
||||
catch (ValueRetrievalException ex) {
|
||||
throw ex.getCause();
|
||||
}
|
||||
}
|
||||
return loadTemplate(name);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Cannot load template " + name, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Template loadTemplate(String name) throws Exception {
|
||||
Reader template = this.mustache.loader.getTemplate(name);
|
||||
return this.mustache.compile(template);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.generator.io.template;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Template rendering abstraction.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface TemplateRenderer {
|
||||
|
||||
/**
|
||||
* Render the template with the specified name and the specified model.
|
||||
* @param templateName the name of the template
|
||||
* @param model the model to use
|
||||
* @return the rendering result
|
||||
* @throws IOException if rendering the template failed
|
||||
*/
|
||||
String render(String templateName, Map<String, ?> model) throws IOException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Template rendering abstraction.
|
||||
*/
|
||||
package io.spring.initializr.generator.io.template;
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.util;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import com.samskivert.mustache.Mustache;
|
||||
import com.samskivert.mustache.Mustache.Compiler;
|
||||
import com.samskivert.mustache.Mustache.TemplateLoader;
|
||||
import com.samskivert.mustache.Template;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
/**
|
||||
* A template renderer backed by Mustache.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class TemplateRenderer {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TemplateRenderer.class);
|
||||
|
||||
private boolean cache = true;
|
||||
|
||||
private final Compiler mustache;
|
||||
|
||||
private final ConcurrentMap<String, Template> templateCaches = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
public TemplateRenderer() {
|
||||
this(mustacheCompiler());
|
||||
}
|
||||
|
||||
public TemplateRenderer(Compiler mustache) {
|
||||
this.mustache = mustache;
|
||||
}
|
||||
|
||||
public boolean isCache() {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
public void setCache(boolean cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public String process(String name, Map<String, ?> model) {
|
||||
try {
|
||||
Template template = getTemplate(name);
|
||||
return template.execute(model);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
log.error("Cannot render: " + name, ex);
|
||||
throw new IllegalStateException("Cannot render template", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Template getTemplate(String name) {
|
||||
if (this.cache) {
|
||||
return this.templateCaches.computeIfAbsent(name, this::loadTemplate);
|
||||
}
|
||||
return loadTemplate(name);
|
||||
}
|
||||
|
||||
protected Template loadTemplate(String name) {
|
||||
try {
|
||||
Reader template;
|
||||
template = this.mustache.loader.getTemplate(name);
|
||||
return this.mustache.compile(template);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Cannot load template " + name, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static Compiler mustacheCompiler() {
|
||||
return Mustache.compiler().withLoader(mustacheTemplateLoader());
|
||||
}
|
||||
|
||||
private static TemplateLoader mustacheTemplateLoader() {
|
||||
ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
String prefix = "classpath:/templates/";
|
||||
Charset charset = Charset.forName("UTF-8");
|
||||
return (name) -> new InputStreamReader(
|
||||
resourceLoader.getResource(prefix + name).getInputStream(), charset);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.generator.io.template;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link MustacheTemplateRenderer}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class MustacheTemplateRendererTests {
|
||||
|
||||
private final Cache templatesCache = new ConcurrentMapCache("test");
|
||||
|
||||
@Test
|
||||
void renderTemplate() throws IOException {
|
||||
MustacheTemplateRenderer render = new MustacheTemplateRenderer(
|
||||
"classpath:/templates/mustache", this.templatesCache);
|
||||
assertThat(this.templatesCache.get("classpath:/templates/mustache/test"))
|
||||
.isNull();
|
||||
assertThat(render.render("test", Collections.singletonMap("key", "value")))
|
||||
.isEqualTo("value");
|
||||
assertThat(this.templatesCache.get("classpath:/templates/mustache/test"))
|
||||
.isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void renderTemplateWithoutCache() throws IOException {
|
||||
MustacheTemplateRenderer render = new MustacheTemplateRenderer(
|
||||
"classpath:/templates/mustache");
|
||||
assertThat(render.render("test", Collections.singletonMap("key", "value")))
|
||||
.isEqualTo("value");
|
||||
}
|
||||
|
||||
@Test
|
||||
void renderUnknownTemplate() {
|
||||
MustacheTemplateRenderer render = new MustacheTemplateRenderer(
|
||||
"classpath:/templates/mustache", this.templatesCache);
|
||||
assertThatExceptionOfType(IllegalStateException.class)
|
||||
.isThrownBy(() -> render.render("does-not-exist", Collections.emptyMap()))
|
||||
.withMessageContaining("Cannot load template")
|
||||
.withMessageContaining("does-not-exist");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{{key}}
|
||||
Reference in New Issue
Block a user