Support i18n and nested templates in ScriptTemplateView

This commit changes the 3rd parameter passed to the rendering
function from String url to RenderingContext renderingContext.

RenderingContext contains 4 properties:
 - ApplicationContext applicationContext
 - Locale locale
 - Function<String, String> templateLoader
 - String url

Issue: SPR-15064
This commit is contained in:
Sebastien Deleuze
2017-01-23 18:37:27 +01:00
parent 8038fb9c8b
commit 2d95199466
29 changed files with 320 additions and 28 deletions

View File

@@ -17,6 +17,7 @@
package org.springframework.web.servlet.view.script;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.servlet.ServletContext;
@@ -28,6 +29,7 @@ import static org.mockito.Mockito.mock;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
import org.springframework.mock.web.test.MockServletContext;
@@ -53,18 +55,28 @@ public class KotlinScriptTemplateTests {
}
@Test
public void renderTemplate() throws Exception {
public void renderTemplateWithFrenchLocale() throws Exception {
Map<String, Object> model = new HashMap<>();
model.put("title", "Layout example");
model.put("body", "This is the body");
model.put("foo", "Foo");
MockHttpServletResponse response = renderViewWithModel("org/springframework/web/servlet/view/script/kotlin/template.kts",
model, ScriptTemplatingConfiguration.class);
assertEquals("<html><head><title>Layout example</title></head><body><p>This is the body</p></body></html>",
model, Locale.FRENCH, ScriptTemplatingConfiguration.class);
assertEquals("<html><body>\n<p>Bonjour Foo</p>\n</body></html>",
response.getContentAsString());
}
private MockHttpServletResponse renderViewWithModel(String viewUrl, Map<String, Object> model, Class<?> configuration) throws Exception {
@Test
public void renderTemplateWithEnglishLocale() throws Exception {
Map<String, Object> model = new HashMap<>();
model.put("foo", "Foo");
MockHttpServletResponse response = renderViewWithModel("org/springframework/web/servlet/view/script/kotlin/template.kts",
model, Locale.ENGLISH, ScriptTemplatingConfiguration.class);
assertEquals("<html><body>\n<p>Hello Foo</p>\n</body></html>",
response.getContentAsString());
}
private MockHttpServletResponse renderViewWithModel(String viewUrl, Map<String, Object> model, Locale locale, Class<?> configuration) throws Exception {
ScriptTemplateView view = createViewWithUrl(viewUrl, configuration);
view.setLocale(locale);
MockHttpServletResponse response = new MockHttpServletResponse();
MockHttpServletRequest request = new MockHttpServletRequest();
view.renderMergedOutputModel(model, request, response);
@@ -95,6 +107,13 @@ public class KotlinScriptTemplateTests {
configurer.setRenderFunction("render");
return configurer;
}
@Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("org/springframework/web/servlet/view/script/messages");
return messageSource;
}
}
}

View File

@@ -0,0 +1,14 @@
package org.springframework.web.reactive.result.view.script
import kotlin.script.templates.standard.ScriptTemplateWithBindings
fun ScriptTemplateWithBindings.include(path: String) =
(bindings["include"] as (String) -> String).invoke(path)
fun ScriptTemplateWithBindings.i18n(code: String) =
(bindings["i18n"] as (String) -> String).invoke(code)
var ScriptTemplateWithBindings.foo: String
get() = bindings["foo"] as String
set(value) { throw UnsupportedOperationException()}

View File

@@ -3,7 +3,7 @@ require 'ostruct'
require 'java'
# Renders an ERB template against a hashmap of variables.
def render(template, variables, url)
def render(template, variables, renderingContext)
context = OpenStruct.new(variables).instance_eval do
variables.each do |k, v|
instance_variable_set(k, v) if k[0] == '@'

View File

@@ -1,5 +1,5 @@
from string import Template
def render(template, model, url):
def render(template, model, renderingContext):
s = Template(template)
return s.substitute(model)

View File

@@ -1,10 +1,17 @@
import javax.script.*
import org.springframework.web.servlet.view.script.RenderingContext
import org.springframework.context.support.ResourceBundleMessageSource
import org.springframework.beans.factory.getBean
// TODO Use engine.eval(String, Bindings) when https://youtrack.jetbrains.com/issue/KT-15450 will be fixed
fun render(template: String, model: Map<String, Any>, url: String): String {
fun render(template: String, model: Map<String, Any>, renderingContext: RenderingContext): String {
val engine = ScriptEngineManager().getEngineByName("kotlin")
val bindings = SimpleBindings()
bindings.putAll(model)
var messageSource = renderingContext.applicationContext.getBean<ResourceBundleMessageSource>()
bindings.put("i18n", { code: String -> messageSource.getMessage(code, null, renderingContext.locale) })
bindings.put("include", { path: String -> renderingContext.templateLoader.apply("org/springframework/web/servlet/view/script/kotlin/$path.html") })
engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE)
return engine.eval(template) as String
}

View File

@@ -1 +1,5 @@
"""<html><head><title>${bindings["title"]}</title></head><body><p>${bindings["body"]}</p></body></html>"""
import org.springframework.web.reactive.result.view.script.*
"""${include("header") }
<p>${i18n("hello")} $foo</p>
${include("footer")}"""

View File

@@ -2,6 +2,6 @@ function render(template, model) {
return template.replace("{{title}}", model.title).replace("{{body}}", model.body);
}
function renderWithUrl(template, model, url) {
return template.replace("{{title}}", "Check url parameter").replace("{{body}}", url);
function renderWithUrl(template, model, renderingContext) {
return template.replace("{{title}}", "Check url parameter").replace("{{body}}", renderingContext.url);
}