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:
@@ -870,6 +870,7 @@ project("spring-web-reactive") {
|
||||
// the case yet, so we depend on kotlin-script-util and exclude these
|
||||
// dependencies only used for artifact retrieval. Point raised to Kotlin team.
|
||||
testRuntime("org.jetbrains.kotlin:kotlin-compiler:${kotlinVersion}")
|
||||
testCompile("org.jetbrains.kotlin:kotlin-script-runtime:${kotlinVersion}")
|
||||
testRuntime("org.jetbrains.kotlin:kotlin-script-util:${kotlinVersion}") {
|
||||
exclude group: "com.jcabi", module: "jcabi-aether"
|
||||
exclude group: "org.apache.maven", module: "maven-core"
|
||||
@@ -983,6 +984,7 @@ project("spring-webmvc") {
|
||||
// the case yet, so we depend on kotlin-script-util and exclude these
|
||||
// dependencies only used for artifact retrieval. Point raised to Kotlin team.
|
||||
testRuntime("org.jetbrains.kotlin:kotlin-compiler:${kotlinVersion}")
|
||||
testCompile("org.jetbrains.kotlin:kotlin-script-runtime:${kotlinVersion}")
|
||||
testRuntime("org.jetbrains.kotlin:kotlin-script-util:${kotlinVersion}") {
|
||||
exclude group: "com.jcabi", module: "jcabi-aether"
|
||||
exclude group: "org.apache.maven", module: "maven-core"
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2002-2017 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 org.springframework.web.reactive.result.view.script;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
* Context passed to {@link ScriptTemplateView} render function.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.0
|
||||
*/
|
||||
public class RenderingContext {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final Locale locale;
|
||||
|
||||
private final Function<String, String> templateLoader;
|
||||
|
||||
private final String url;
|
||||
|
||||
|
||||
public RenderingContext(ApplicationContext applicationContext, Locale locale,
|
||||
Function<String, String> templateLoader, String url) {
|
||||
this.applicationContext = applicationContext;
|
||||
this.locale = locale;
|
||||
this.templateLoader = templateLoader;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
|
||||
public ApplicationContext getApplicationContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
public Function<String, String> getTemplateLoader() {
|
||||
return templateLoader;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
@@ -74,6 +75,8 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
|
||||
|
||||
private String engineName;
|
||||
|
||||
private Locale locale;
|
||||
|
||||
private Boolean sharedEngine;
|
||||
|
||||
private String[] scripts;
|
||||
@@ -119,6 +122,13 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
|
||||
this.engineName = engineName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link Locale} to pass to the render function.
|
||||
*/
|
||||
public void setLocale(Locale locale) {
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link ScriptTemplateConfigurer#setSharedEngine(Boolean)} documentation.
|
||||
*/
|
||||
@@ -284,14 +294,23 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
|
||||
Invocable invocable = (Invocable) engine;
|
||||
String url = getUrl();
|
||||
String template = getTemplate(url);
|
||||
Function<String, String> templateLoader = path -> {
|
||||
try {
|
||||
return getTemplate(path);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
};
|
||||
RenderingContext context = new RenderingContext(this.getApplicationContext(), this.locale, templateLoader, url);
|
||||
|
||||
Object html;
|
||||
if (this.renderObject != null) {
|
||||
Object thiz = engine.eval(this.renderObject);
|
||||
html = invocable.invokeMethod(thiz, this.renderFunction, template, model, url);
|
||||
html = invocable.invokeMethod(thiz, this.renderFunction, template, model, context);
|
||||
}
|
||||
else {
|
||||
html = invocable.invokeFunction(this.renderFunction, template, model, url);
|
||||
html = invocable.invokeFunction(this.renderFunction, template, model, context);
|
||||
}
|
||||
|
||||
byte[] bytes = String.valueOf(html).getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
@@ -16,7 +16,13 @@
|
||||
|
||||
package org.springframework.web.reactive.result.view.script;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.web.reactive.result.view.AbstractUrlBasedView;
|
||||
import org.springframework.web.reactive.result.view.UrlBasedViewResolver;
|
||||
import org.springframework.web.reactive.result.view.View;
|
||||
|
||||
/**
|
||||
* Convenience subclass of {@link UrlBasedViewResolver} that supports
|
||||
@@ -55,6 +61,14 @@ public class ScriptTemplateViewResolver extends UrlBasedViewResolver {
|
||||
setSuffix(suffix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<View> resolveViewName(String viewName, Locale locale) {
|
||||
return super.resolveViewName(viewName, locale).map(view -> {
|
||||
((ScriptTemplateView)view).setLocale(locale);
|
||||
return view;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> requiredViewClass() {
|
||||
return ScriptTemplateView.class;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.web.reactive.result.view.script;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -26,6 +27,7 @@ import org.junit.Test;
|
||||
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.context.support.StaticApplicationContext;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
@@ -50,18 +52,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");
|
||||
MockServerHttpResponse response = renderViewWithModel("org/springframework/web/reactive/result/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.getBodyAsString().block());
|
||||
}
|
||||
|
||||
private MockServerHttpResponse 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");
|
||||
MockServerHttpResponse response = renderViewWithModel("org/springframework/web/reactive/result/view/script/kotlin/template.kts",
|
||||
model, Locale.ENGLISH, ScriptTemplatingConfiguration.class);
|
||||
assertEquals("<html><body>\n<p>Hello Foo</p>\n</body></html>",
|
||||
response.getBodyAsString().block());
|
||||
}
|
||||
|
||||
private MockServerHttpResponse renderViewWithModel(String viewUrl, Map<String, Object> model, Locale locale, Class<?> configuration) throws Exception {
|
||||
ScriptTemplateView view = createViewWithUrl(viewUrl, configuration);
|
||||
view.setLocale(locale);
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/").build();
|
||||
MockServerHttpResponse response = new MockServerHttpResponse();
|
||||
WebSessionManager manager = new DefaultWebSessionManager();
|
||||
@@ -94,6 +106,13 @@ public class KotlinScriptTemplateTests {
|
||||
configurer.setRenderFunction("render");
|
||||
return configurer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResourceBundleMessageSource messageSource() {
|
||||
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
|
||||
messageSource.setBasename("org/springframework/web/reactive/result/view/script/messages");
|
||||
return messageSource;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()}
|
||||
@@ -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] == '@'
|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
</body></html>
|
||||
@@ -0,0 +1 @@
|
||||
<html><body>
|
||||
@@ -1,10 +1,17 @@
|
||||
import javax.script.*
|
||||
import org.springframework.web.reactive.result.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/reactive/result/view/script/kotlin/$path.html") })
|
||||
engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE)
|
||||
return engine.eval(template) as String
|
||||
}
|
||||
|
||||
|
||||
@@ -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")}"""
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
hello = Hello
|
||||
@@ -0,0 +1 @@
|
||||
hello = Bonjour
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2002-2017 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 org.springframework.web.servlet.view.script;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
* Context passed to {@link ScriptTemplateView} render function.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.0
|
||||
*/
|
||||
public class RenderingContext {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final Locale locale;
|
||||
|
||||
private final Function<String, String> templateLoader;
|
||||
|
||||
private final String url;
|
||||
|
||||
|
||||
public RenderingContext(ApplicationContext applicationContext, Locale locale,
|
||||
Function<String, String> templateLoader, String url) {
|
||||
this.applicationContext = applicationContext;
|
||||
this.locale = locale;
|
||||
this.templateLoader = templateLoader;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
|
||||
public ApplicationContext getApplicationContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
public Function<String, String> getTemplateLoader() {
|
||||
return templateLoader;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
@@ -83,6 +84,8 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
|
||||
|
||||
private String engineName;
|
||||
|
||||
private Locale locale;
|
||||
|
||||
private Boolean sharedEngine;
|
||||
|
||||
private String[] scripts;
|
||||
@@ -126,6 +129,14 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link Locale} to pass to the render function.
|
||||
* @since 5.0
|
||||
*/
|
||||
public void setLocale(Locale locale) {
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link ScriptTemplateConfigurer#setEngineName(String)} documentation.
|
||||
*/
|
||||
@@ -345,14 +356,23 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
|
||||
Invocable invocable = (Invocable) engine;
|
||||
String url = getUrl();
|
||||
String template = getTemplate(url);
|
||||
Function<String, String> templateLoader = path -> {
|
||||
try {
|
||||
return getTemplate(path);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
};
|
||||
RenderingContext context = new RenderingContext(this.getApplicationContext(), this.locale, templateLoader, url);
|
||||
|
||||
Object html;
|
||||
if (this.renderObject != null) {
|
||||
Object thiz = engine.eval(this.renderObject);
|
||||
html = invocable.invokeMethod(thiz, this.renderFunction, template, model, url);
|
||||
html = invocable.invokeMethod(thiz, this.renderFunction, template, model, context);
|
||||
}
|
||||
else {
|
||||
html = invocable.invokeFunction(this.renderFunction, template, model, url);
|
||||
html = invocable.invokeFunction(this.renderFunction, template, model, context);
|
||||
}
|
||||
|
||||
response.getWriter().write(String.valueOf(html));
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.web.servlet.view.script;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.web.servlet.view.UrlBasedViewResolver;
|
||||
|
||||
/**
|
||||
@@ -56,6 +59,12 @@ public class ScriptTemplateViewResolver extends UrlBasedViewResolver {
|
||||
setSuffix(suffix);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View createView(String viewName, Locale locale) throws Exception {
|
||||
ScriptTemplateView view = (ScriptTemplateView)super.createView(viewName, locale);
|
||||
view.setLocale(locale);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> requiredViewClass() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()}
|
||||
@@ -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] == '@'
|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
</body></html>
|
||||
@@ -0,0 +1 @@
|
||||
<html><body>
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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")}"""
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
hello = Hello
|
||||
@@ -0,0 +1 @@
|
||||
hello = Bonjour
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user