Skip to content

julienrf/glitter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Glitter, an embedded Scala DSL for writing XML

Overview

Glitter is an embedded DSL allowing to write XML in a concise, type safe and composable way.

See this gist for a quick overview of what Glitter is. The first file defines some reusable HTML fragments and the two following files show the results of rendering them.

Install

Download the latest jar from the downloads section and add it to your classpath.

Using sbt

You can use it in your sbt project by adding this line in the build.sbt file (adapt it to the latest release version):

libraryDependencies += "glitter" % "glitter" % "0.1" from "file:///where/you/downloaded/glitter-0.1.jar"

Hack

Fetch the sources by cloning this repository:

git clone git://github.com/julienrf/glitter.git

Glitter is defined as a sbt project. You can run the tests using the sbt test command, or run the integrated bigtable benchmark with sbt run.

Syntax

To use Glitter you first need to import the definitions (and implicits) of the glitter package object:

import glitter._

Then write your templates, e.g. in an object:

object Templates {
  def index: Xml = …
}

Tags and text

To write a tag, use a symbol:

scala> def index: Xml = 'div
index: glitter.Xml

If you try to render this template, you will get the <div /> string:

scala> index.render
res0: String = <div />

Under the hood it uses an implicit conversion from Symbol to the EmptyTag case class. You need to explicit the return type of your template to make it work.

You can also write plain text, using a String:

scala> def index: Xml = "foo"
index: glitter.Xml
scala> index.render
res1: String = foo

Content can be nested using the :: operator:

scala> def index = 'div :: "foo"
index: glitter.Tag
scala> index.render
res2: String = <div>foo</div>

You can also write a sequence of xml content, just separate each element (tag or text) with |:

scala> def index = 'span :: "foo" | 'span :: "bar"
index: glitter.Xml
scala> index.render
res3: String = <span>foo</span><span>bar</span>

As you can see, the :: operator has a higher precedence than the | operator. If you want to nest a sequence of elements in a tag you need to enclose them in brackets:

scala> def index = 'div :: ('span :: "foo" | 'span :: "bar")
index: glitter.Tag
scala> index.render
res4: String = <div><span>foo</span><span>bar</span></div>

Actually you can also nest tags using parenthesis, so the previous example is the same as the following:

scala> def index = 'div ('span :: "foo" | 'span :: "bar")
index: glitter.Tag
scala> index.render
res5: String = <div><span>foo</span><span>bar</span></div>

Attributes

You can add attributes to the tags using the % operator:

scala> def img = 'img %('src->"/static/images/my_cat.jpg", 'alt->"picture of my cat", 'title->"My Cat")
img: glitter.EmptyTag
scala> img.render
res4: String = <img src="/static/images/my_cat.jpg" alt="picture of my cat" title="My Cat" />

The % operator takes one or more tuples as parameters (it can be tuples of (String, String) or (Symbol, String)). You can use the Scala built-in syntax to define such tuples, like in the above example, but you can also use the Glitter ~ operator to define them, so the above example is equivalent to the following definitions:

def img = 'img % 'src~"/static/images/my_cat.jpg" % 'alt~"picture of my cat" % 'title~"My Cat"
def img = 'img % ('src~"/static/images/my_cat.jpg", 'alt~"picture of my cat", 'title~"My Cat")
def img = 'img % (('src,"/static/images/my_cat.jpg"), ('alt,"picture of my cat"), ('title,"My Cat"))

For convenience, Glitter allows to add attributes with no value:

scala> def radio = 'input % 'type~"radio" % 'checked
radio: glitter.EmptyTag
scala> radio.render
res9: String = <input type="radio" checked />

It can be useful to write HTML5 markup.

Advanced Usage

Layouts and fragments

Since Glitter templates are nothing but Scala functions, you can use function composition to have reusable templates fragments:

import glitter._

object Layouts {

  // Main layout
  def main(content: Xml): Xml =
    html5dtd | 'html % 'lang~"en" :: (
        'head :: 'title :: "Title"
      | 'body (
            _header
          | _content(content)
          | _footer
      )
    )

  // Reusable header fragment
  def _header: Xml =
    'header (
        'h1 :: 'a % 'href~"/" :: "Home"
    )

  // Reusable footer fragment
  def _footer: Xml =
    'footer :: "© 2011"

  // Reusable content fragment
  def _content(content: Xml): Xml =
    'section :: content
}

object Templates {

  // A template “extending” the main layout
  def index: Xml =
    Layout.main(
        'h2 :: "Index"
      | 'p :: "…"
    )
}

I’m used to prefix templates fragments by an underscore, but that’s not mandatory at all. So, here you see how you can simulate what’s usually called template inheritance (the index template “extends” the main template layout) and template inclusion (main layout “includes” the fragments _header, _footer and _content).

Control structures

You can use the Scala if directly:

def _userInfo(user: User): Xml =
  'div :: (if (user.isAdmin) "foo" else "bar")

To iterate through a collection (or any monadic object) you can use the monad comprehension (a.k.a for comprehension) notation:

def listArticles(articles: List[Article]): Xml =
  'ul :: (
      for (article <- articles) yield ('li :: article.name)
  )

Render templates

You can render any Xml content by using its render(r: Renderer) function. By default this function will use an implicit simple renderer writing the content into a string, as HTML (without any kind of formatting, empty attributes have no value).

Misc.

Write raw content

By default string converted to xml text are HTML escaped (e.g. “<” are transformed into “&lt;”). You can disable this behavior by using the raw method:

def myHtml5dtd: Xml = "<!DOCTYPE html>\n".raw

Empty

Glitter provides an object of type Xml but doing nothing: Empty. It may be useful in some cases, e.g.:

def _adminArea(user: User): Xml =
  if (user.isAdmin)
    'a % 'href~"/admin" :: "Manage"
  else
    Empty

About

Embedded Scala DSL for HTML templates

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages