mirror of https://github.com/shadowfacts/ekt.git
Add template environments for includes
This commit is contained in:
parent
f1e9e7266c
commit
21d7cb841c
|
@ -25,8 +25,13 @@ object EKT {
|
||||||
private val endStringRegex = Regex("\\[([:=#])")
|
private val endStringRegex = Regex("\\[([:=#])")
|
||||||
|
|
||||||
private val scriptPrefix = """
|
private val scriptPrefix = """
|
||||||
|
val _env = bindings["_env"] as net.shadowfacts.ekt.EKT.TemplateEnvironment
|
||||||
val _result = StringBuilder()
|
val _result = StringBuilder()
|
||||||
fun echo(s: Any) { _result.append(s) }
|
fun echo(it: Any) { _result.append(it) }
|
||||||
|
fun include(include: String) {
|
||||||
|
val env = net.shadowfacts.ekt.EKT.TemplateEnvironment(include, _env)
|
||||||
|
echo(net.shadowfacts.ekt.EKT.render(env, env.include))
|
||||||
|
}
|
||||||
"""
|
"""
|
||||||
private val scriptSuffix = """
|
private val scriptSuffix = """
|
||||||
_result.toString()
|
_result.toString()
|
||||||
|
@ -40,7 +45,11 @@ _result.toString()
|
||||||
manager.getEngineByExtension("kts")
|
manager.getEngineByExtension("kts")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun render(template: String, scriptCache: File? = null, data: Map<String, TypedValue>): String {
|
fun render(env: TemplateEnvironment, template: String = env.template): String {
|
||||||
|
if (env.cacheDir != null && env.cacheFile.exists()) {
|
||||||
|
return eval(env.cacheFile.readText(Charsets.UTF_8), env) as String
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
var template = template
|
var template = template
|
||||||
template = template.replace("$", "\${'$'}")
|
template = template.replace("$", "\${'$'}")
|
||||||
|
@ -63,57 +72,89 @@ _result.toString()
|
||||||
})
|
})
|
||||||
|
|
||||||
// Hack to allow data to be accessed by name from template instead of via bindings map
|
// Hack to allow data to be accessed by name from template instead of via bindings map
|
||||||
val unwrapBindings = data.keys.map {
|
val unwrapBindings = env.data.keys.map {
|
||||||
val type = data[it]!!.type
|
val type = env.data[it]!!.type
|
||||||
"val $it = (bindings[\"$it\"] as net.shadowfacts.ekt.EKT.TypedValue).value as $type"
|
"val $it = (bindings[\"$it\"] as net.shadowfacts.ekt.EKT.TypedValue).value as $type"
|
||||||
}.joinToString("\n")
|
}.joinToString("\n")
|
||||||
|
|
||||||
val script = unwrapBindings + scriptPrefix + template + scriptSuffix
|
val script = unwrapBindings + scriptPrefix + template + scriptSuffix
|
||||||
|
|
||||||
scriptCache?.apply {
|
if (env.cacheDir != null) {
|
||||||
if (!parentFile.exists()) parentFile.mkdirs()
|
env.cacheFile.apply {
|
||||||
if (!exists()) createNewFile()
|
if (!parentFile.exists()) parentFile.mkdirs()
|
||||||
writeText(script)
|
if (!exists()) createNewFile()
|
||||||
|
writeText(script, Charsets.UTF_8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return eval(script, data) as String
|
return eval(script, env) as String
|
||||||
}
|
}
|
||||||
|
|
||||||
fun render(template: String, scriptCache: File? = null, dataProvider: DataProviderContext.() -> Unit): String {
|
fun render(name: String, templateDir: File, includeDir: File, cacheDir: File? = null, data: Map<String, TypedValue>): String {
|
||||||
val ctx = DataProviderContext()
|
return render(TemplateEnvironment(name, templateDir, includeDir, cacheDir, data))
|
||||||
ctx.dataProvider()
|
|
||||||
return render(template, scriptCache, ctx.map)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun render(template: File, scriptCacheDir: File? = null, data: Map<String, TypedValue>): String {
|
fun render(name: String, templateDir: File, includeDir: File, cacheDir: File? = null, init: DataProvider.() -> Unit): String {
|
||||||
val cacheFile = if (scriptCacheDir != null) {
|
return render(TemplateEnvironment(name, templateDir, includeDir, cacheDir, init))
|
||||||
File(scriptCacheDir, template.nameWithoutExtension + ".kts")
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cacheFile != null && cacheFile.exists()) {
|
|
||||||
return eval(cacheFile.readText(), data) as String
|
|
||||||
} else {
|
|
||||||
return render(template.readText(), cacheFile, data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun render(template: File, scriptCacheDir: File? = null, dataProvider: DataProviderContext.() -> Unit): String {
|
fun render(name: String, dir: File, cacheScripts: Boolean = false, data: Map<String, TypedValue>): String {
|
||||||
val ctx = DataProviderContext()
|
return render(name, dir, File(dir, "includes"), if (cacheScripts) File(dir, "cache") else null, data)
|
||||||
ctx.dataProvider()
|
|
||||||
return render(template, scriptCacheDir, ctx.map)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun eval(script: String, data: Map<String, TypedValue> = mapOf()): Any? {
|
fun render(name: String, dir: File, cacheScripts: Boolean = false, init: DataProvider.() -> Unit): String {
|
||||||
|
return render(name, dir, File(dir, "includes"), if (cacheScripts) File(dir, "cache") else null, init)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun eval(script: String, env: TemplateEnvironment): Any? {
|
||||||
engine.context = SimpleScriptContext()
|
engine.context = SimpleScriptContext()
|
||||||
val bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE)
|
val bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE)
|
||||||
bindings.putAll(data)
|
bindings.putAll(env.data)
|
||||||
|
bindings.put("_env", env)
|
||||||
|
|
||||||
return engine.eval(script)
|
return engine.eval(script)
|
||||||
}
|
}
|
||||||
|
|
||||||
class DataProviderContext {
|
class TemplateEnvironment {
|
||||||
|
|
||||||
|
val rootName: String
|
||||||
|
val name: String
|
||||||
|
val templateDir: File
|
||||||
|
val includeDir: File
|
||||||
|
val cacheDir: File?
|
||||||
|
val data: Map<String, TypedValue>
|
||||||
|
|
||||||
|
val template: String
|
||||||
|
get() = File(templateDir, "$name.ekt").readText(Charsets.UTF_8)
|
||||||
|
val include: String
|
||||||
|
get() = File(includeDir, "$name.ekt").readText(Charsets.UTF_8)
|
||||||
|
val cacheFile: File
|
||||||
|
get() = File(cacheDir!!, "$name.kts")
|
||||||
|
|
||||||
|
constructor(name: String, templateDir: File, includeDir: File, cacheDir: File?, data: Map<String, TypedValue>) {
|
||||||
|
this.rootName = name
|
||||||
|
this.name = name
|
||||||
|
this.templateDir = templateDir
|
||||||
|
this.includeDir = includeDir
|
||||||
|
this.cacheDir = cacheDir
|
||||||
|
this.data = data
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(name: String, templateDir: File, includeDir: File, cacheDir: File?, init: DataProvider.() -> Unit):
|
||||||
|
this(name, templateDir, includeDir, cacheDir, DataProvider.init(init))
|
||||||
|
|
||||||
|
constructor(name: String, parent: TemplateEnvironment) {
|
||||||
|
this.rootName = parent.rootName
|
||||||
|
this.name = name
|
||||||
|
this.templateDir = parent.templateDir
|
||||||
|
this.includeDir = parent.includeDir
|
||||||
|
this.cacheDir = parent.cacheDir
|
||||||
|
this.data = parent.data
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataProvider {
|
||||||
internal val map = mutableMapOf<String, TypedValue>()
|
internal val map = mutableMapOf<String, TypedValue>()
|
||||||
|
|
||||||
infix fun String.to(value: Any) {
|
infix fun String.to(value: Any) {
|
||||||
|
@ -127,6 +168,14 @@ _result.toString()
|
||||||
infix fun Any.asType(type: String): TypedValue {
|
infix fun Any.asType(type: String): TypedValue {
|
||||||
return TypedValue(this, type)
|
return TypedValue(this, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun init(init: DataProvider.() -> Unit): Map<String, TypedValue> {
|
||||||
|
val ctx = DataProvider()
|
||||||
|
ctx.init()
|
||||||
|
return ctx.map
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class TypedValue(val value: Any, val type: String)
|
data class TypedValue(val value: Any, val type: String)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import java.io.File
|
||||||
* @author shadowfacts
|
* @author shadowfacts
|
||||||
*/
|
*/
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
val res = EKT.render(File("template.ekt"), scriptCacheDir = File("cache/")) {
|
val res = EKT.render("template", File("."), cacheScripts = true) {
|
||||||
"list" to (listOf(1, 2, 3) asType "List<Int>")
|
"list" to (listOf(1, 2, 3) asType "List<Int>")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue