Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/flamefork/fleet

Templating System for Clojure
https://github.com/flamefork/fleet

clojure template-language

Last synced: 4 days ago
JSON representation

Templating System for Clojure

Awesome Lists containing this project

README

        

### Fleet

[![Build Status](https://travis-ci.org/Flamefork/fleet.png?branch=master)](https://travis-ci.org/Flamefork/fleet)

[![Clojars Project](http://clojars.org/fleet/latest-version.svg)](http://clojars.org/fleet)

Templating System for Clojure

## Gist

0. Template is function of its arguments.
0. HTML is better for HTML than some host language DSL (just cause HTML *is* DSL).
0. DOM manipulation tools and XSLT are good for transforming, not for templating (yes, opinionated).
0. Clojure is good :)
0. HTML isn't the only language that needs templating.

## Brief

Write
```clojure

<(post :body)>


```
instead of
```clojure

<%= (escape-html (post :body)) %>


```
Read on for more goodness.

## Template Language

### Main Fleet construction is Spaceship `<()>`.

...just because (star)fleet consists of many spaceships.

`<()>` is almost equivalent to Clojure's `()`, so
`

<(body)>

` in Fleet is nearly the same as `(str "

" (body) "

")` in Clojure.

The only difference is that `(body)` output gets escaped (e.g. html-encoded to prevent XSS).
Use `raw` function to prevent escaping: `<(raw "
")>`.
Use `str` function to place value `<(str posts-count)>` instead of calling a function.

This is almost all we need, with one issue: writing something like
```clojure
<(raw (for [p posts]
(str "

  • " (p :title) "
  • ")))>
    ```
    is too ugly, and defining `
  • <(p :title)>
  • ` as separate template
    can be overkill in many cases. So there should be the good way of embedding strings and anonymous templates.

    ### Slipway construction `"><"` is for embedding strings.

    The previous example could be rewritten using Slipway as
    ```clojure
    <(for [p posts] ">

  • <(p :title)>

  • <")>
    ```

    This example has two points worth mentioning.
    Result of `"><"` construction processing is an expression of String type.
    Strings in Slipway considered `raw` by default.

    Next case is something like this:
    ```clojure
    <(raw (map (fn [post]
    (str "

  • " (post :title) "
  • ")) posts))>
    ```

    With Slipway it can be replaced with
    ```clojure
    <(map (fn [post] ">

  • <(post :title)>

  • <") posts)>
    ```

    Need to mention that all this supports lexical scoping and other Clojure features just like reference (previous) expression.

    ## Functions

    ### Single anonymous template: `fleet`

    ```clojure
    (fleet [& args] template-str options)
    ```

    Creates anonymous function from `template-str` using provided `options` map. Intended to use just like `(fn` construct.

    Example:

    ```clojure
    (def footer (fleet "

    © <(year (now))> Your Company

    "))
    (println (footer))

    (def header (fleet [title] "<(str title)>"))
    (println (header "Main Page"))
    ```

    Main option is `:escaping`. It can be function of one String argument or keyword specifying one of predefined functions:
    `:bypass` — default, no escaping;
    `:xml` — XML (or HTML) rules;
    `:str` — Java-compatible string escaping;
    `:clj-str` — Clojure string escaping (`\n` is allowed);
    `:regex` — Escaping of Regex special symbols.

    Options `:file-name` and `:file-path` (both String) are in place for better stack traces.

    ### Template namespace: `fleet-ns`

    ```clojure
    (fleet-ns root-ns root-path filters)
    ```

    Treats `root-path` as root of template directory tree, maps it to namespace with prefix `root-ns.`, creates template functions
    for each file in it with name and samespace according to relative path.

    Example:

    ```clojure
    (fleet-ns view "path/to/view_dir" [:fleet :xml])
    ```

    Template functions are created by the following rules:

    — Several equal functions will be created for each file. E.g. file `posts.html.fleet` will produce 3 functions: `posts`, `posts-html` and `posts-html-fleet`.

    This is useful for cases where you have `posts.html.fleet` and `posts.json.fleet`, so you may access distinct templates as `posts-html` and `posts-json`,
    while and if you have only one `posts.html.fleet` you could call it `posts` conviniently.

    — Template function will take one or two arguments: first named same as shortest function name for file (`posts` in previous example) and second named `data`.

    When it's called with one arguments both symbols (fn-name and data) are bound to same value of this argument.
    When it's called with no arguments both symbols (fn-name and data) are bound to nil.
    This is also for convinience: you could use name appropriate to usage: e.g. if your template renders post, you could use `post` param name,
    and if template renders some complex data you could use `data`.
    Also you can mix&match, for example `post` as main rendered entity and `data` as some render options.

    Filters argument is vector of `file-filter escaping-fn` pairs used to filter which files to process and with which escaping function.
    File filters could be defined as function, string, regex, `:fleet` or `:all`.
    — Function should have Boolean type and one File argument.
    — String filter definition treated as `*.string.fleet` mask, e.g. `"js"` mask will match `update.js.fleet`.
    — Regex filter matches whole filename, e.g. `#".*.html"` will match `posts.html`.
    — `:fleet` filter is treated as "others". If it is set all `*.fleet` files will be processed.
    — `:all` means, literally, all.

    ### More on escaping

    If you need to insert Fleet constructions into text you can escape them using backslash.
    You only need escaping to remove ambiguity,
    so use `\<(` and `\<"` only outside embedded clojure code, `\">` and `\)>` only inside embedded clojure code.

    ## Examples

    This is not intended to work out-of-box, only to show some bits of a language / system.

    ### Language

    Template file (`post_dedicated.fleet`):

    ```clojure

    <(post :title)>

    <(stylesheet :main)>
    <(raw "alert('Hello!')")>

    <(str notice)>

    Spaceship \<()> is landing.

    <(
    ; Begin of post
    )>
    <(inside-frame (let [p post] ">
    Author: <(p :author)>

    Date: <(p :date)>

    <"))>

    <(post :body)>



      <(for [tag (post :tags] ">
    • <(str tag)>

    • <")>

    <(
    ; End of post
    )>

    <(footer)>