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:
James Strachan
2011-12-23 13:40:03 +00:00
parent f64c02c80a
commit db76e28c6d
7 changed files with 369 additions and 0 deletions
+59
View File
@@ -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)
}
+119
View File
@@ -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>
}
+34
View File
@@ -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))