Scalate is a template engine based on the Scala language.
Supports multiple template syntaxes
Support for layouts of templates and wiki markup
Scalate supports a number of different template languages as template languages have various different sweet spots depending on your requirements.
If you know Velocity, JSP or Erb from Rails then hopefully the syntax of Ssp is familiar; only using Scala as the language of expressions and method invocations.
<%@ var user: User %>
<p>Hi ${user.name},</p>
#for (i <- 1 to 3)
<p>${i}</p>
#end
<p>See, I can count!</p>
<p>Hi James,</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>See, I can count!</p>
For full documentation of the Ssp syntax see the Ssp Reference Guide
Scaml is a markup language that's used to cleanly and simply describe the XHTML of any web document, without the use of inline code. It is Scala version of Haml. Scaml functions as a replacement for inline page templating systems such as PHP, ERB, and ASP. However, Scaml avoids the need for explicitly coding XHTML into the template, because it is actually an abstract description of the XHTML, with some code to generate dynamic content.
-@ var user: User
%p Hi #{user.name},
- for(i <- 1 to 3)
%p= i
%p See, I can count!
<p>Hi James,</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>See, I can count!</p>
For full documentation of the Scaml syntax see the Scaml Reference Guide
The Jade syntax is similar to Scaml, its a modified dialect of Haml where element names do not require a leading % symbol which can make it a little easier to read.
-@ var user: User
p Hi #{user.name},
- for(i <- 1 to 3)
p= i
p See, I can count!
<p>Hi James,</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>See, I can count!</p>
For more details see the Jade reference
The Scalate Mustache template language is a Scala dialect of cross-language Mustache template engine for logic-less templates which also work inside the browser using mustache.js.
Mustache is logic-less, using simple tags which can be used to represent loops, expressions or logical branching.
Given the following attributes:
Map(
"name" -> "Chris",
"value" -> 10000,
"taxed_value" -> 10000 - (10000 * 0.4),
"in_ca" -> true
)
Then the following mustache file will generate
Hello {{name}}
You have just won ${{value}}!
{{#in_ca}}
Well, ${{taxed_value}}, after taxes.
{{/in_ca}}
Hello Chris
You have just won $10000!
Well, $6000.0, after taxes.
For more detail see the Mustache Reference
Its very simple to invoke any scala function inside Scalate. By default if the function you call returns NodeSeq then the output will already be properly XML encoded; so things output nicely without any possible cross scripting hacks etc.
For example the following function creates a hypertext link using Scala's XML support
object Cheese {
def foo(productId: Int) =
<a href={"/products/" + productId} title="Product link">My Product</a>
}
This function can be invoked in your Ssp code as
<% import Cheese._ %>
${foo(123)}
If your template is in the same package as the Cheese class then the import is not required.
The Scaml version is
- import Cheese._
= foo(123)
As you write snippet functions for use in your templates you might find yourself needing to access the current HttpServletRequest or HttpServletResponse.
There is a simple helper import you can use…
import org.fusesource.scalate.servlet.ServletRenderContext._
object MySnippets {
def foo = {
// thanks to the import I now have access to the renderContext
// along with the standard servlet objects:
// request, response, servletContext, servletConfig
request.getParameter("foo")
}
}
This helps you keep your snippet functions nice and small.
To use the JSP concept of custom tags, you might want to pass a block of template to a function for further processing or transformation.
This can be done by just adding a parameter list of the form (body: => Unit) to your method. For example
import org.fusesource.scalate.RenderContext.capture
object Cheese {
def foo(productId: Int)(body: => Unit) =
<a href={"/products/" + productId} title="Product link">capture(body)</a>
}
See how the body is captured using the capture(body) function. Now the text of the hypertext link can be specified as a block of template in Ssp
<%@ val id: Int = 123 %>
<% import Cheese._ %>
<%= foo(id) { %>
product ${id}
<% } %>
This should generate something like
<a href="/products/123" title="Product link">product 123</a>
Or using Velocity style directives this might look like this
#do(foo(id))
product ${id}
#end
The Scaml version is
-@ val id: Int = 123
- import Cheese._
= foo(id)
product #{id}
Notice the Scaml version is simpler, not requiring the open and close { } tokens as it uses indentation.
From within your Scala code or inside a template you often want to render an object or collection of objects. Scalate uses a convention over configuration mechanism so that you can render any object using a simple method call in your template. e.g. in SSP
<% val user = new User("foo") %>
<p>Something...</p>
<% view(user) %>
<p>... more stuff </p>
The view method takes a model object and an optional view name. The view name defaults to "index" if you do not specify one. For exmaple you could have various views for an object such as "index”, “edit”, “detail”, etc. Then you might want to show the edit view of an object via
<% val user = new User("foo") %>
${view(user, "edit")}
Scalate will then look for the template called packageDirectory.ClassName.viewName.(jade|mustache|ssp|scaml) and render that. For example in the sample web application to render an org.fusesource.scalate.sample.Person object Scalate uses the org/fusesource/scalate/sample/Person.index.ssp template.
Notice that since the view is defined within the package of the model object, there is no need to import the org.fusesource.scalate.sample.Person, instead you can just refer to the model type directly as Person.
If a template is not found for the exact class name then the class and interface (trait) hierarchies are walked until one is found.
So for example you could provide a template for a generic trait you have - such as a template to render any scala.Product which will then render any case class; then you can customise the view on a class by class basis as required.
By default we use the variable named it to refer to the model parameter. This convention means that when working with JAXRS and Jersey's implicit views the model object is implicitly available to any templates using this naming convention.
Given how common object views are and templates where you pass in an object to render, the default behaviour of Scalate Packages is to automatically import a value based on the type of the template if you follow the object naming convention shown above.
For example if your template matches the name package/className.viewName.extension then the className is implicitly imported into your template as a typesafe attribute.
For example if you're template is org/fusesource/scalate/sample/Person.index.ssp then it can look like this
name: ${firstName} ${surname}
Which has an implicit import of the 'it' variable so the above template is equivalent to the more explicit (but less DRY) alternative:
<%@ import val it: Person %>
name: ${firstName} ${surname}
Or the even less DRY
<%@ val it: Person %>
name: ${it.firstName} ${it.surname}
If you have a collection of objects you wish to view then you can use a simple helper method called collection which works like the view method described above.
<% val people = List(Person("James", "Strachan"), Person("Hiram", "Chirino")) %>
<% collection(people) %>
As with the view method you can specify an optional view name if you won't want to use the "index" default.
Also you can specify a separator to use between views of objects. The following example shows a horizontal line between views…
<% val people = List(Person("James", "Strachan"), Person("Hiram", "Chirino")) %>
<% collection(people, separator = "<hr/>") %>
Or without using named arguments
<% collection(people, "index", "<hr/>") %>
If a collection contains different types of objects then the correct view will be used for each element in the collection.
You can also supply a function for the separator if you want it to be dynamic
<% var x = 1 %>
collection(people, separator = {x += 1; <h3>Person {x}</h3>})
It is common to want to refactor large templates into smaller reusable pieces. Its easy to render a template from inside another template with the render method as follows
<% render("foo.ssp") %>
This will render a template called foo.ssp relative to the current template. You can use absolute names if you prefer
<% render("/customers/contact.ssp") %>
You can also pass parameters into the template if it takes any
<% render("/customers/contact.ssp", Map("customer" -> c, "title" -> "Customer")) %>
When passing attributes you can use the Scala symbol notation for keys if you prefer…
<% render("/customers/contact.ssp", Map('customer -> c, 'title -> "Customer")) %>
If you prefer you can pass in a body to the template using the layout method as described in using explicit layouts inside a template.
Its quite common to want to style all pages in a similar way; such as adding a header and footer, a common navigation bar or including a common set of CSS stylesheets.
You can achieve this using the layout support in Scalate.
All you need to do is create a layout template in /WEB-INF/scalate/layouts/default.ssp (or /WEB-INF/scalate/layouts/default.scaml if you prefer). Here is a simple example layout which lays out the body and lets the title be customized on a per page basis.
<%@ var body: String %>
<%@ var title: String = "Some Default Title" %>
<html>
<head>
<title>${title}</title>
</head>
<body>
<p>layout header goes here...</p>
${unescape(body)}
<p>layout footer goes here...</p>
</body>
</html>
Then all pages will be wrapped in this layout by default.
This means your templates don't need to include the whole html/head/body stuff, typically you'll just want the actual content to be displayed in the part of the layout you need. So a typical page might look like this…
<h3>My Page</h3>
<p>This is some text</p>
To set parameters on a layout or to change the layout template used, just output attribute values in your template.
<% attributes("layout") = "/WEB-INF/layouts/custom.ssp" %>
<% attributes("title") = "This is the custom title" %>
<h3>Custom page</h3>
<p>This is some text</p>
If you wish to disable the use of the layout on a template, just set the layout attribute to “" the empty string.
<% attributes("layout") = "" %>
<html>
<body>
<h1>No Layout</h1>
<p>This page does not use the layout</p>
</body>
</html>
To see examples of layouts in use, try running the sample web application and looking at the layout related example pages.
You may want to layout some content within part of your template explicitly rather than just applying a layout to an entire page.
For example you may want to create a layout as follows in file foo.ssp
<%@ val body: String = "Bar" %>
<table>
<tr>
<th>Some Header</th>
</tr>
<tr>
<td><%= body %></td>
</tr>
</table>
Then we can invoke this template passing in the body as follows in Ssp
<% render("foo.ssp", Map("body" -> "Foo")) %>
However if you want to pass in the body as a block of template you can use the layout method as follows
<% layout("foo.ssp") {%>
Foo
<%}%>
Or using Velocity style directives this might look like this
#do( layout("foo.ssp") )
Foo
#end
Both will generate the same response.
Using the above mechanism via either the render or layout methods is quite like creating a JSP custom tag inside a .tag file if you come from a JSP background.
The nice thing is there's really no difference technically between a regular template, a layout or a 'tag' template or a 'partial' (to use Rails terminology), they are all just templates which can have parameters which can be mandatory or optional.
Sometimes you may wish to capture the result of rendering a block of template, assign it to a variable and then pass it as an argument to some method. For this the capture method can be used.
For example
<% val foo = capture { %>
hello there ${user.name} how are you?
<%}%>
...
${foo}
...
${foo}
We capture the block which generates a greeting, assign it to the foo variable which we can then render or pass into methods etc.
The Scaml version of this is a bit more concise
- var foo = capture
hello there #{user.name} how are you?
...
= foo
...
= foo
When you create a number of templates in a directory you might find you are repeating the same sets of imports across many templates. This doesn't feel terribly DRY. Scala 2.8 supports package objects which allows you to define types, variables and methods at the package scope to be reused inside classes and traits defined inside the package.
So Scalate supports a similar feature for templates which are code generated like SSP, Scaml and Jade.
The basic idea is Scalate will look in the same package as the template for a Scala/Java class called ScalatePackage which must extend TemplatePackage. If there is no ScalatePackage in the package, its parent package is searched all the way to the root package (i.e. no package name).
If a ScalatePackage class is found then its header method is invoked to generate any shared imports, variables or methods across templates.
For example you could add the following Scala code to a file called src/main/scala/foo/ScalatePackage.scala to add some default imports you want to share across a number of templates in a the foo directory and its descendants…
package foo
import org.fusesource.scalate.TemplateSource
import org.fusesource.scalate.support.TemplatePackage
/**
* Defines some common imports, attributes and methods across templates in package foo and below
*/
class ScalatePackage extends TemplatePackage {
/** Returns the Scala code to add to the top of the generated template method */
def header(source: TemplateSource, bindings: List[Binding]) = """
// some shared imports
import com.acme._
import com.acme.something.MyHelper._
// some helper methods
// would be better being imported from a helper class like MyHelper above
def time = new java.util.Date()
"""
}
You can then use the usual expressive composition features of Scala to use inheritance, traits, delegation and so forth to decide how to spread this code across your templates and decide how to combine these things at the package level to be inherited by all child packages and templates. You might find moving templates into functional directories makes it easier to reuse common boilerplate imports, values and methods across templates.
The easiest way to get started is to try the Getting Started Guide
The source code comes with a sample web application called scalate-sample which includes a number of exmample templates you can play around with>
Scalate can be built either using Maven or SBT
General Requirements:
Web Container Requirements:
Install Maven version 2.0.9 or later. Then type
mvn install
To run the sample web application
cd scalate-sample
mvn jetty:run
Then open the sample home page
You can also use sbt to build Scalate.
To setup your sbt environment and import the dependencies from the maven pom files type
./sbt
update
Then to build the code type
compile
to run the tests
test
For more information see the sbt building instructions
You might want to refer to the Frameworks Documentation to see if there is some specific instructions on using Scalate with your favourite web framework.
<filter>
<filter-name>TemplateEngineFilter</filter-name>
<filter-class>org.fusesource.scalate.servlet.TemplateEngineFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TemplateEngineFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
You could add one or more of the above to your servlet container's server-wide configuration if you prefer.
The Scalate template filter looks for templates in several locations to satisfy a request.
For example, if requested URI is /path/file.html
, then it will for templates in this order:
/path/file.html.${ext}
/WEB-INF/path/file.html.${ext}
/path/file.${ext}
/WEB-INF/path/file.${ext}
Where ${ext}
gets replaced with all the template extensions supported by the Template Engine. If the requested
URI already ends with template extension then it would get looked up in the root and under the /WEB-INF
directory.
Our recommendation is to start with JOG (Jersey on Guice).
To get up to speed quickly with JOG try the Getting Started Guide which uses the Scalate Tool and WAR Overlay to include the Console in your web application.
Scalate does not have any hard dependencies on a web framework or even HTTP. It can be used as a standalone rendering engine in your application. For more information on how to embed in your application, please reference the Scalate Embedding Guide
Scalate uses a working directory to store the generated scala source files and the compiled JVM bytecode for templates. This can be configured on a TemplateEngine using the workingDirectory property. If no configuration is made Scalate will use the scalate.workdir system property by default.
The archetypes or projects created by the scalate tool or the modules in the scalate source all set the scalate.workdir to be the maven property of the same name; which defaults to target/_scalate
If you wanted to run a web application using a different directory, such as /tmp you could do
mvn -Dscalate.workdir=/tmp jetty:run
In production settings you can disable the caching and reloading of templates if you wish using the allowCaching and allowReload properties on TemplateEngine which default to scalate.allowCaching and scalate.allowReload respectively.
Scalate supports a standard bootstrap mechanism which tries to be framework agnostic so it can work in servlets or using the servlet filter or works when generating static websites and is easy to plugin to other frameworks.
Just add a class called scalate.Boot which should be a class with a method called run() which can then do whatever you need to configure things before the template engine starts to render your templates.
If you need access to the TemplateEngine then just add a constructor argument. (You can also pass in ServletContext as a constructor parameter if you are inside a web application and other values which may come from your framework).
e.g.
package scalate
import org.fusesource.scalate.TemplateEngine
import java.io.File
class Boot(engine: TemplateEngine) extends Logging {
def run: Unit = {
// lets change the workingDirectory
engine.workingDirectory = new File("myScalateWorkDir")
}
}
Scalate currently lazily compiles templates on the fly, then it will cache the compiled template and only recompile it if it detects the source template has changed.
In production you probably want all your templates to be precompiled so that
you can detect at build time any typos in your templates, particularly if you are using Scaml or Ssp which use static type checking for all expressions in your template.
all your templates are immediately available as a fast, compiled JVM .class file rather than taking the overhead of the first request causing a compile phase
To do this you just need to include the maven-scalate-plugin into your project. The plugin only precompiles when you are packaging into a war to avoid slowing down your development mode cycles.
The archetypes created by the scalate tool come with this plugin enabled already.
Otherwise you can just add this to your pom.xml
<build>
<plugins>
<plugin>
<groupId>org.fusesource.scalate</groupId>
<artifactId>maven-scalate-plugin_#{scala_compat_tag}</artifactId>
<version>1.6.1</version>
<executions>
<execution>
<goals>
<goal>precompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
The archetypes created by the scalate tool have their SBT build configuration setup so that the templates are precompiled before they are packaged. If you need to add the precompiler to an existing sbt project then you need to first add the plugin dependency:
lazy val scalate_plugin = "org.fusesource.scalate" % "sbt-scalate-plugin_2.10" % "1.6.1"
And then in your WebProject, you will need to add the
org.fusesource.scalate.sbt.PrecompilerWebProject
trait. And then make sure
the Scalate dependencies are added to the project. For example:
class Project(info: ProjectInfo) extends
DefaultWebProject(info) with
PrecompilerWebProject {
lazy val scalate_core = "org.fusesource.scalate" % "scalate-core_2.10" % "1.6.1"
lazy val servlet = "javax.servlet" % "servlet-api"% "2.5"
lazy val logback = "ch.qos.logback" % "logback-classic" % "0.9.26"
}
If you are using Scalate on Google AppEngine (GAE) then you will probably want to precompile all your templates before you deploy them; so that each request is processed very quickly - so app engine won't kill your thread midway through.
To see an example of a Scalate project already setup using GAE try the the hello-scalate-appengine project by Yasushi Abe.
Scalate can sometimes struggle with ClassLoaders. This is due to the Scala compiler requiring an explicit class path to be specified as a String of names rather than taking an actual ClassLoader object.
So Scalate works fine with expanded WARs, or servlet containers who's ClassLoader implements URLClassLoader or an AntClassLoader like thing or the Play Framework. But you might be able to find some application server that doens't provide an easy-to-diagnose ClassLoader object which may require an explicitly configured class path for compiling.
A work around is to precompile your templates with the maven plugin (see the section above).
Scalate currently assumes that
template source files are all UTF-8-encoded.
template output is all UTF-8-encoded.
Using an IDE plugin can make it much easier to view and edit Scalate templates.
If you use TextMate (which on OS X is a great text editor) you can install the Scalate plugin as follows:
cd ~/Library/Application\ Support/TextMate/Bundles/
git clone git://github.com/scalate/Scalate.tmbundle.git
If you have not already done so you will also need a Scala plugin for TextMate which the Ssp and Scaml languages uses for the Scala code blocks.
We like the version by Dean Wampler though there's a few around github and one included in sbaz in the Scala distro too.
cd ~/Library/Application\ Support/TextMate/Bundles/
git clone git://github.com/deanwampler/Scala.tmbundle.git
When you restart TextMate you should now get syntax highlighting and more when you open up either a Ssp or Scaml file.
The current plugin does not highlight Scala expressions terribly well with the default Mac Classic colour scheme in TextMate. We found that it helps to add an extra colour to your scheme.
We created Scalate specifically to be IDE friendly and we'd love to help create more and better IDE plugins (plus we love contributions!). It should be easy to reuse any JSP / Erb / HAML IDE plugins but just swizzle them a little to use the Scala language instead - then get all the benefits of smart completion from Scala's static type system.
We've a page on writing IDE plugins for Scalate which has more details on how an IDE plugin should ideally work for Scalate.