#hamplate
Insanely productive template engine for scala projects.
Hamplate is in early alpha, so syntax rules might change.
There are quite a few great template engines out there, like jade, slim or haml. But those are not statically typed.
The only statically typed template engine I know of is the playframework2 template engine. But the Play! engine does not help you deal with the HTML/XML hazzle. You still have to write stuff like this:
<ul id="dummies" class="sorted">
<li>
<a class="dummy-link" href="...">
<span class="hidden dummy-name">a dummy name</a>
<img src="..." />
</a>
</li>
</ul>
I'm used to write (and more important read) extremly concise code in scala and want to be able to just write:
%ul#dummies.sorted
%li
%a.dummy-link href="..."
%span.hidden.dummy-name
a dummy name
%img src="..."
This is roughly two thirds of the original code and avoids most unnecessary duplication.
So in summary Hamplate tries to make writing view code fun again (like haml does).
And since it's not actually a full template engine but more a template transformer, it compiles down to statically typed Play! templates.
In the future it might also give you a safety net in form of the compiler and maybe some automated checks.
There will also be some sensible defaults for dealing with forms, helpers and CSS/JS includes, and embedded scala code.
Just copy this in your "project/Project.scala" file
import sbt._
import sbt.Keys._
object MyBuild extends Build
{
override lazy val projects = Seq(root)
lazy val root = Project("hamplate-test", file(".")).settings( Hamplate.hamplateSettings : _*).
settings(
// These folders need to be the same folder or completely disjunct (meaning no folder is a subfolder of the other)
Hamplate.HamplateKeys.sourceDir := "app/views", // Folder containing your *.hpt files
Hamplate.HamplateKeys.outputDir := "app/views", // Folder which will contain your *.scala.html files
unmanagedResourceDirectories in Compile <+= (baseDirectory) { _ / "app/views/" }
)
}
And this in "project/project/Build.scala"
import sbt._
object PluginDef extends Build {
override lazy val projects = Seq(root)
lazy val root = Project("plugins", file(".")).dependsOn( hamplatePlugin )
lazy val hamplatePlugin = uri("git://github.com/syrix/hamplate")
}
After doing this just type
sbt ~hpt
and all your *.hpt files in app/views will be automatically compiled to *.scala.html files everytime you change a file.
*Be carefull not to overwrite your .scala.html files by accident! Happend to me. Really hurts...
If you want to update to a new version and sbt doesn't pull the new version try to clear out "~/.sbt/staging/".
Hamplate borrows major parts from haml. So it's basic syntax rules are the same. So this:
%html
%head
%body
%h1
Headline
%p
%em
Lorem ipsum ...
renders to
<html>
<head>
</head>
<body>
<h1>
Headline
</h1>
<p>
<em>
Lorem ipsum ...
</em>
</p>
</body>
</html>
There should only be a very limited set of concepts
You should only need to type as few characters as possible
The compiler should be as simple as possible
To achieve those goals there are some basic concept used in hamplates
- The first symbol in a line (or the absence of such) determines how this line is interpreted
- Besides that there are no special symbols
- You should not need to use brackets ever. Not even in scala constructs.
- You should be able to bypass those rules when needed and just write plain html
There are only four kinds of special symbols at the moment: '%','.','#',':'.
All lines not starting with one of this symbols (after the intendation) is passed through the compiler as it was. So this html snippet does not change through hamplate:
<h3> Header </h3>
<p> Lorem ipsum <p>
<p>
@content
</p>
Lines starting with % are the most complex to parse and indicate an html tag.
As example this:
%a.myclass1#myid.myclass2 href="..." data-toggle="..."
random link
Get's parsed to
<a class="myclass1 myclass2" id="myid" href="..." data-toggle="...">
random link
</a>
The parsing rules are
Everything from the %-sign to the first occurrence of '#', '.' or whitespace is parsed as the tag name.
This name is used in the opening and closing tag. The closing tag is put after the last line which has a higher intendation than the line where the tag starts.
Everything from the first occurrecne of '#' or '.' is parsed as class and id attributes.
A dot indicates a class and a hash indicates an id. If you don't need to assign classes or ids (or want to do it the traditional way, using id="myid"), you don't need to use class or id shorthands. But the name of the tag must be present after the %-Symbol. So this is not valid hamplate:
%
The rest of the line is simply moved into the starting tag.
There is no magic going on, so this.
%a.class1 class="class2" some random stuff
renders to
<a class="class1" class="class2" some random stuff>
</a>
So as you see the parser is really really dumb and you can't write certain things like
%h1 Lorem ipsum
Which will render to
<h1 Lorem ipsum>
</h1>
But must write
%h1
Lorem ipsum
This may seem silly, but it's way easier to understand, what the compiler does. It just onfolds the class and id shortcuts and copies the remaining line in the starting tag.
Braindead simple and way easier to read than HTML.
There is a shorthand for divs, where a tag without a tagname is parsed as a div-tag. So lines starting with a dot or hash are exactly interpreted as lines starting with an additional '%div'
So this
#navbar.myclass
.item
Item one
.item
Item two
renders exactly like this
%div#navbar.myclass
%div.item
Item one
%div.item
Item two
which renders to
<div id="navbar" class="myclass">
<div class="item">
Item one
</div>
<div class="item">
Item two
</div>
</div>
Filters are used to make writing embedded code (like javascript or markdown) easier.
At the moment there is only one filter. It is used for javascript and works like this:
:javascript
alert('hello world!");
renders to
<script type="text/javascript">
alert('hello world!");
</script>
Closing rules of filters are the same as for tags.
Everything inside a filter is still parsed as hamplate code
So you should never use '%', '.', ':' or '#' at the start of a line inside of a filter. At the moment the compiler will just happily transform your code and mess up your script code. Always remember: The compiler is really really dumb.
In the future there might be compiler errors or warnings to indicate the use of special symbols inside of a filter.
Lines starting with '{', '@' or '}' will not be evaluated by the parser (they are normal text-lines). So you can just write normal Play! template code and the Play!-Compiler will do the heavy lifting and compile your code.
Example:
%ul
@for(u <- user) {
%li
%h2
@u.name <small>@u.email</small>
%p
@u.description
}
renders to
<ul>
@for(u <- user) {
<li>
<h2>
@u.name <small>@u.email</small>
</h2>
<p>
@u.description
</p>
</li>
}
</ul>
In the future there might be some shorthands to support common scala constructs. Like '-', which puts an @sign at the front of the line, a opening '{' at the end of the line and a closing '}' after the last line with higher intendation.
So this
-for(u <- user)
@u.name
might render to
@for(u<-user){
@u.name
}
There might also be stuff like autoimports or better support of pattern matching.
But that's for some later version. For now I'm just happy to never have to write HTML-tags again ;-)