squiggly is a Scala based string templating engine.
squiggly is a language, a Scala library, and a Linux command line application for doing string templating. squiggly can be compared to Mustache or Go templates or Liquid, which are all great template languages. Basically, a string template that is composed of text and tags which are instructions in squiggly, is applied to context data producing a textual output.
Unlike Mustache, squiggly is not logic-less, but allows some basic logic to be used in templates.
Curly braces or brackets are sometimes referred to as squiggly brackets, which is where the name "squiggly" comes from.
To use the library in your application, include the following in your project/plugins.sbt
:
addSbtPlugin("com.codecommit" % "sbt-github-packages" % "0.5.2")
Include the following in your build.sbt
:
resolvers += Resolver.githubPackages("edadma")
libraryDependencies += "io.github.edadma" %%% "squiggly" % "0.1.14"
Use the following import
statement in your code:
import io.github.edadma.squiggly._
To use the command line executable, download the file squiggly
from the root folder of the repository. Make it
executable by typing
chmod a+x path/to/squiggly
then copy it to your /usr/bin
folder
sudo cp path/to/scamplate /usr/bin
Here's a simple example of using squiggly to render a template in your application.
import io.github.edadma.squiggly.{Parser, Renderer}
object Main extends App {
case class Task(task: String, done: Boolean)
case class User(user: String, tasks: List[Task])
val data =
User("ed",
List(Task("Improve Parser and Renderer API", done = true),
Task("Code template example", done = false),
Task("Update README", done = false)))
val template =
"""
|<!DOCTYPE html>
|<html>
| <head>
| <title>To-Do list</title>
| </head>
| <body>
| <p>
| To-Do list for user '{{ .user }}'
| </p>
| <table>
| <tr>
| <td>Task</td>
| <td>Done</td>
| </tr>
| {{ for .tasks -}}
| <tr>
| <td>{{ .task }}</td>
| <td>{{ if .done }}Yes{{ else }}No{{ end }}</td>
| </tr>
| {{- end }}
| </table>
| </body>
|</html>
|""".trim.stripMargin
val ast = Parser.default.parse(template)
Renderer.default.render(data, ast)
}
output:
<!DOCTYPE html>
<html>
<head>
<title>To-Do list</title>
</head>
<body>
<p>
To-Do list for user 'ed'
</p>
<table>
<tr>
<td>Task</td>
<td>Done</td>
</tr>
<tr>
<td>Improve Parser and Renderer API</td>
<td>Yes</td>
</tr><tr>
<td>Code template example</td>
<td>No</td>
</tr><tr>
<td>Update README</td>
<td>No</td>
</tr>
</table>
</body>
</html>
Type
squiggly -h
to get the following usage text:
Squiggly v0.1.14
Usage: squiggly [options] [[<template>]]
-a, --ast pretty print AST
-d, --data <YAML> YAML document
-f, --template <file> template file
-h, --help prints this usage text
-v, --version prints the version
-y, --yaml <file> YAML data file
[<template>] template string
Here's a trivial example:
squiggly -d "{a: 3, b: 4}" "{{ .a }} + {{ .b }} = {{ .a + .b }}"
output:
3 + 4 = 7
Squiggly templates are written using a language that is inspired by both Hugo and Liquid. The goal is low boilerplate and readability. Hugo is compact and boilerplate free, but it is also a prefix language where operations must always precede their operands. Prefix languages tend not to be conducive to readability. Liquid has better infix syntax but more boilerplate. Both languages have good features that the other is missing.
squiggly values are pieces of data that are either computed in the template or retrieved from the context that is provided when a template is rendered. A value that is written manually inside a template is called a literal value.
null
. The null value simply represents no value, and is rendered in a template as an empty string. It corresponds to a Scalanull
.true
,false
. These correspond to the values of the Scala Boolean class.- numbers. Numbers in squiggly are all instances of one type internally: the Scala
BigDecimal
number type. This was chosen mainly because it provides exact decimal arithmetic which is desirable when working with currency values.BigDecimals
can also represent arbitrarily large integers. - strings. String literals are written between
'
and'
, or"
and"
, whichever is more convenient. Strings may contain any of the standard escapes:\n
,\uxxxx
, etc. - lists. List literals are written between
[
and]
with each item in the list separated by a,
. - maps. Map literals are written between
{
and}
with each property in the map separated by a,
, and where a property is a key (property name) and a value separated by a:
. For example:{one: 1, two: 2, three: 3}
There is a special undefined value which purposely cannot be expressed literally because it represents a property that
is missing, and is always rendered in a template as an empty string. It is not the same as null
because a property
that is not missing can contain a null
value, and one may wish to distinguish between them. The undefined value
corresponds to the Scala ()
value, the only instance of the Unit
class.
TO DO
Squiggly templates are plain text with the addition of tags that are normally between {{
and }}
delimiters. The
tag delimiter strings are configurable. Following are the currently available tags.
where value is any valid squiggly expression which results in a data value of some kind. The resulting value is converted to a string of characters if it is not already, and sent to the output stream.
For example {{ .title }}
will be replaced by the value of a context property called title
.
The with tag binds the current context to value, and is often used to simply using code that will be referring to
properties of a single data structure. For example {{ with .user }}{{ .firstName }} {{ .lastName }}{{ end }}
will be
replaced by the first and last name of .user
.
The for tag binds context to successive elements of value which must be a Scala Iterable (i.e. a Seq or Map). Optionally, e is also set to each value, and i is set to the 0-based index.
TO DO
TO DO
TO DO
Unit tests can be run quickly for the JVM platform by typing
squigglyJVM / test
Tests for all platforms can be run by typing simply
test
This project welcomes contributions from the community. Contributions are accepted using GitHub pull requests; for more information, see GitHub documentation - Creating a pull request.
For a good pull request, we ask you provide the following:
- Include a clear description of your pull request in the description with the basic "what" and "why"s for the request.
- The tests should pass as best as you can.
- The pull request should include tests for the change. A new feature should have tests for the new feature and bug fixes should include a test that fails without the corresponding code change and passes after they are applied.
- If the pull request is a new feature, please include appropriate documentation in the
README.md
file as well. - To help ensure that your code is similar in style to the existing code,
scalafmt
should be used.