added a little experimental spike of a text and markup based template library for internal DSLs for templating (which external DSLs like Jade / Razor / Velocity / Erb / JSP style) could layer on top of. Mails to follow shortly :)
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
namespace std.template
|
||||
|
||||
import std.io.*
|
||||
|
||||
/**
|
||||
* Represents a generic API to templates which should be usable
|
||||
* in JavaScript in a browser or on the server side stand alone or in a web app etc.
|
||||
*
|
||||
* To make things easier to implement in JS this namespace won't refer to any java.io or servlet
|
||||
* stuff
|
||||
*/
|
||||
trait Template {
|
||||
var printer: Printer
|
||||
|
||||
/** Renders the template to the output printer */
|
||||
fun render(): Unit
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the output of a template which is a stream of objects
|
||||
* usually strings which then write to some underlying output stream
|
||||
* such as a java.io.Writer on the server side.
|
||||
*
|
||||
* We abstract away java.io.* APIs here to make the JS implementation simpler
|
||||
*/
|
||||
trait Printer {
|
||||
fun print(value: Any): Unit
|
||||
//fun print(text: String): Unit
|
||||
}
|
||||
|
||||
class NullPrinter() : Printer {
|
||||
//override fun print(text: String) = none()
|
||||
override fun print(value: Any) {
|
||||
throw UnsupportedOperationException("No Printer defined on the Template")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for template implementations to hold any helpful behaviour
|
||||
*/
|
||||
abstract class TemplateSupport : Template {
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for templates generating text output
|
||||
* by printing values to some underlying Printer which
|
||||
* will typically output to a String, File, stream etc.
|
||||
*/
|
||||
abstract class TextTemplate() : TemplateSupport, Printer {
|
||||
override var printer: Printer = NullPrinter()
|
||||
|
||||
fun String.plus(): Unit {
|
||||
printer.print(this)
|
||||
}
|
||||
|
||||
//override fun print(value: String) = printer.print(value)
|
||||
|
||||
override fun print(value: Any) = printer.print(value)
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
namespace std.template.html
|
||||
|
||||
import std.*
|
||||
import std.template.*
|
||||
import std.io.*
|
||||
import std.util.*
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
|
||||
|
||||
trait Factory<T> {
|
||||
fun create() : T
|
||||
}
|
||||
|
||||
abstract class Element
|
||||
|
||||
class TextElement(val text : String) : Element
|
||||
|
||||
abstract class Tag(val name : String) : Element {
|
||||
val children = ArrayList<Element>()
|
||||
val attributes = HashMap<String, String>()
|
||||
|
||||
protected fun initTag<T : Element>(init : T.()-> Unit) : T
|
||||
where class object T : Factory<T> {
|
||||
val tag = T.create()
|
||||
tag.init()
|
||||
children.add(tag)
|
||||
return tag
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TagWithText(name : String) : Tag(name) {
|
||||
fun String.plus() {
|
||||
children.add(TextElement(this))
|
||||
}
|
||||
}
|
||||
|
||||
class HTML() : TagWithText("html") {
|
||||
class object : Factory<HTML> {
|
||||
override fun create() = HTML()
|
||||
}
|
||||
|
||||
fun head(init : Head.()-> Unit) = initTag(init)
|
||||
|
||||
fun body(init : Body.()-> Unit) = initTag(init)
|
||||
}
|
||||
|
||||
class Head() : TagWithText("head") {
|
||||
class object : Factory<Head> {
|
||||
override fun create() = Head()
|
||||
}
|
||||
|
||||
fun title(init : Title.()-> Unit) = initTag(init)
|
||||
}
|
||||
|
||||
class Title() : TagWithText("title")
|
||||
|
||||
abstract class BodyTag(name : String) : TagWithText(name) {
|
||||
}
|
||||
|
||||
class Body() : BodyTag("body") {
|
||||
class object : Factory<Body> {
|
||||
override fun create() = Body()
|
||||
}
|
||||
|
||||
fun b(init : B.()-> Unit) = initTag(init)
|
||||
fun p(init : P.()-> Unit) = initTag(init)
|
||||
fun h1(init : H1.()-> Unit) = initTag(init)
|
||||
|
||||
fun a(href : String) {
|
||||
a(href) {}
|
||||
}
|
||||
|
||||
fun a(href : String, init : A.()-> Unit) {
|
||||
val a = initTag(init)
|
||||
a.href = href
|
||||
}
|
||||
}
|
||||
|
||||
class B() : BodyTag("b")
|
||||
class P() : BodyTag("p")
|
||||
class H1() : BodyTag("h1")
|
||||
|
||||
class A() : BodyTag("a") {
|
||||
var href: String? = null
|
||||
/*
|
||||
var href : String
|
||||
get() = attributes["href"]
|
||||
set(value) { attributes["href"] = value }
|
||||
*/
|
||||
}
|
||||
|
||||
fun body(init: Body.()-> Unit): Body {
|
||||
val elem = Body()
|
||||
elem.init()
|
||||
return elem
|
||||
}
|
||||
|
||||
fun html(init : HTML.()-> Unit) : HTML {
|
||||
val html = HTML()
|
||||
html.init()
|
||||
return html
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for templates which generate markup (XML or HTML for example)
|
||||
* which have additional helper methods for escaping markup etc
|
||||
*/
|
||||
abstract class MarkupTemplate() : TemplateSupport {
|
||||
|
||||
override fun render() {
|
||||
val markup = markup()
|
||||
print(markup)
|
||||
}
|
||||
|
||||
/** Returns the markup to be rendered */
|
||||
abstract fun markup(): Iterable<Element>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Server side Java IO code to avoid coupling
|
||||
// the core template code to java.* for easier JS porting
|
||||
namespace std.template.io
|
||||
|
||||
import std.template.*
|
||||
import java.io.Writer
|
||||
import java.io.OutputStream
|
||||
import java.io.OutputStreamWriter
|
||||
import java.io.PrintStream
|
||||
import java.io.StringWriter
|
||||
|
||||
/**
|
||||
* A Printer implementation which uses a Writer
|
||||
*/
|
||||
class WriterPrinter(val writer: Writer) : Printer {
|
||||
|
||||
override fun print(value: Any) {
|
||||
// TODO should be using a formatter to do the conversion
|
||||
writer.write(value.toString())
|
||||
}
|
||||
}
|
||||
|
||||
fun Template.renderToText(): String {
|
||||
val buffer = StringWriter()
|
||||
renderTo(buffer)
|
||||
return buffer.toString().sure()
|
||||
}
|
||||
|
||||
fun Template.renderTo(writer: Writer): Unit {
|
||||
this.printer = WriterPrinter(writer)
|
||||
this.render()
|
||||
}
|
||||
|
||||
fun Template.renderTo(os: OutputStream): Unit = renderTo(OutputStreamWriter(os))
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="stdlib" />
|
||||
<orderEntry type="module" module-name="testlib" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
namespace std.template
|
||||
|
||||
import std.*
|
||||
import std.template.io.*
|
||||
import std.io.*
|
||||
import std.util.*
|
||||
import std.test.*
|
||||
import java.util.*
|
||||
|
||||
class EmailTemplate(var name: String = "James", var time: Date = Date()) : TextTemplate() {
|
||||
override fun render() {
|
||||
print("Hello there $name and how are you? Today is $time. Kotlin rocks")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
TODO compile error
|
||||
|
||||
class MoreDryTemplate(var name: String = "James", var time: Date = Date()) : TextTemplate() {
|
||||
override fun render() {
|
||||
+"Hey there $name and how are you? Today is $time. Kotlin rocks"
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
class TemplateCoreTest() : TestSupport() {
|
||||
fun testDefaultValues() {
|
||||
val text = EmailTemplate().renderToText()
|
||||
assert {
|
||||
println(text)
|
||||
text.startsWith("Hello there James")
|
||||
}
|
||||
}
|
||||
|
||||
fun testDifferentValues() {
|
||||
val text = EmailTemplate("Andrey").renderToText()
|
||||
assert {
|
||||
println(text)
|
||||
text.startsWith("Hello there Andrey")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
TODO compile error
|
||||
|
||||
fun testMoreDryTemplate() {
|
||||
val text = MoreDryTemplate("Alex").renderToText()
|
||||
assert {
|
||||
println(text)
|
||||
text.startsWith("Hey there Alex")
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
namespace std.template.html
|
||||
|
||||
import std.*
|
||||
import std.template.*
|
||||
import std.template.io.*
|
||||
import std.io.*
|
||||
import std.util.*
|
||||
import std.test.*
|
||||
import java.util.*
|
||||
|
||||
/*
|
||||
fun result(args : List<String>) =
|
||||
html {
|
||||
head {
|
||||
title {+"XML encoding with Kotlin"}
|
||||
}
|
||||
body {
|
||||
h1 {+"XML encoding with Kotlin"}
|
||||
p {+"this format can be used as an alternative markup to XML"}
|
||||
|
||||
// an element with attributes and text content
|
||||
a(href = "http://jetbrains.com/kotlin") {+"Kotlin"}
|
||||
|
||||
// mixed content
|
||||
p {
|
||||
+"This is some"
|
||||
b {+"mixed"}
|
||||
+"text. For more see the"
|
||||
a(href = "http://jetbrains.com/kotlin") {+"Kotlin"}
|
||||
+"project"
|
||||
}
|
||||
p {+"some text"}
|
||||
|
||||
// content generated by
|
||||
p {
|
||||
for (arg in args)
|
||||
+arg
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
val justBody = body {
|
||||
+"Hello world"
|
||||
}
|
||||
*/
|
||||
|
||||
class TemplateHtmlTest() : TestSupport() {
|
||||
fun testNoneCompileYet() {
|
||||
}
|
||||
|
||||
/*
|
||||
fun testHtmlFUnction() {
|
||||
val text = result(arrayList("a", "b", "c"))
|
||||
println(text)
|
||||
}
|
||||
|
||||
fun testJustBody() {
|
||||
println(justBody)
|
||||
}
|
||||
|
||||
fun testEmbeddedFunction() {
|
||||
val e = html {
|
||||
head {
|
||||
title {+"XML encoding with Kotlin"}
|
||||
}
|
||||
body {
|
||||
a("http://jetbrains.com/kotlin")
|
||||
}
|
||||
}
|
||||
println(e)
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package std.template;
|
||||
|
||||
import std.template.html.*;
|
||||
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class TemplateTestAll {
|
||||
public static TestSuite suite() {
|
||||
return new TestSuite(TemplateCoreTest.class, TemplateHtmlTest.class);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user