{"id":15286905,"url":"https://github.com/flamefork/fleet","last_synced_at":"2025-04-09T20:05:48.375Z","repository":{"id":62432678,"uuid":"450912","full_name":"Flamefork/fleet","owner":"Flamefork","description":"Templating System for Clojure","archived":false,"fork":false,"pushed_at":"2019-01-08T10:53:39.000Z","size":310,"stargazers_count":151,"open_issues_count":0,"forks_count":10,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-09T20:05:43.765Z","etag":null,"topics":["clojure","template-language"],"latest_commit_sha":null,"homepage":"http://clojars.org/fleet","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Flamefork.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2009-12-28T11:27:17.000Z","updated_at":"2024-05-31T07:44:33.000Z","dependencies_parsed_at":"2022-11-01T21:15:31.377Z","dependency_job_id":null,"html_url":"https://github.com/Flamefork/fleet","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Flamefork%2Ffleet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Flamefork%2Ffleet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Flamefork%2Ffleet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Flamefork%2Ffleet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Flamefork","download_url":"https://codeload.github.com/Flamefork/fleet/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248103872,"owners_count":21048245,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","template-language"],"created_at":"2024-09-30T15:18:55.142Z","updated_at":"2025-04-09T20:05:48.336Z","avatar_url":"https://github.com/Flamefork.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"### Fleet\n\n[![Build Status](https://travis-ci.org/Flamefork/fleet.png?branch=master)](https://travis-ci.org/Flamefork/fleet)\n\n[![Clojars Project](http://clojars.org/fleet/latest-version.svg)](http://clojars.org/fleet)\n\nTemplating System for Clojure\n\n## Gist\n\n0. Template is function of its arguments.\n0. HTML is better for HTML than some host language DSL (just cause HTML *is* DSL).\n0. DOM manipulation tools and XSLT are good for transforming, not for templating (yes, opinionated).\n0. Clojure is good :)\n0. HTML isn't the only language that needs templating.\n\n## Brief\n\nWrite\n```clojure\n\u003cp\u003e\u003c(post :body)\u003e\u003c/p\u003e\n```\ninstead of\n```clojure\n\u003cp\u003e\u003c%= (escape-html (post :body)) %\u003e\u003c/p\u003e\n```\nRead on for more goodness.\n\n## Template Language\n\n### Main Fleet construction is Spaceship `\u003c()\u003e`.\n\n...just because (star)fleet consists of many spaceships.\n\n`\u003c()\u003e` is almost equivalent to Clojure's `()`, so\n`\u003ch1\u003e\u003c(body)\u003e\u003c/h1\u003e` in Fleet is nearly the same as `(str \"\u003ch1\u003e\" (body) \"\u003c/h1\u003e\")` in Clojure.\n\nThe only difference is that `(body)` output gets escaped (e.g. html-encoded to prevent XSS).  \nUse `raw` function to prevent escaping: `\u003c(raw \"\u003cbr/\u003e\")\u003e`.  \nUse `str` function to place value `\u003c(str posts-count)\u003e` instead of calling a function.\n\nThis is almost all we need, with one issue: writing something like\n```clojure\n\u003c(raw (for [p posts]\n  (str \"\u003cli class=\\\"post\\\"\u003e\" (p :title) \"\u003c/li\u003e\")))\u003e\n```\nis too ugly, and defining `\u003cli class=\"post\"\u003e\u003c(p :title)\u003e\u003c/li\u003e` as separate template\ncan be overkill in many cases. So there should be the good way of embedding strings and anonymous templates.\n\n### Slipway construction `\"\u003e\u003c\"` is for embedding strings.\n\nThe previous example could be rewritten using Slipway as\n```clojure\n    \u003c(for [p posts] \"\u003e\n      \u003cli class=\"post\"\u003e\u003c(p :title)\u003e\u003c/li\u003e\n    \u003c\")\u003e\n```\n\nThis example has two points worth mentioning.\nResult of `\"\u003e\u003c\"` construction processing is an expression of String type.\nStrings in Slipway considered `raw` by default.\n\nNext case is something like this:\n```clojure\n    \u003c(raw (map (fn [post]\n      (str \"\u003cli class=\\\"post\\\"\u003e\" (post :title) \"\u003c/li\u003e\")) posts))\u003e\n```\n\nWith Slipway it can be replaced with\n```clojure\n    \u003c(map (fn [post] \"\u003e\n      \u003cli class=\"post\"\u003e\u003c(post :title)\u003e\u003c/li\u003e\n    \u003c\") posts)\u003e\n```\n\nNeed to mention that all this supports lexical scoping and other Clojure features just like reference (previous) expression.\n\n## Functions\n\n### Single anonymous template: `fleet`\n\n```clojure\n(fleet [\u0026 args] template-str options)\n```\n\nCreates anonymous function from `template-str` using provided `options` map. Intended to use just like `(fn` construct.\n\nExample:\n\n```clojure\n(def footer (fleet \"\u003cp\u003e\u0026copy; \u003c(year (now))\u003e Your Company\u003c/p\u003e\"))\n(println (footer))\n\n(def header (fleet [title] \"\u003chead\u003e\u003ctitle\u003e\u003c(str title)\u003e\u003c/title\u003e\u003c/head\u003e\"))\n(println (header \"Main Page\"))\n```\n\nMain option is `:escaping`. It can be function of one String argument or keyword specifying one of predefined functions:  \n`:bypass` — default, no escaping;  \n`:xml` — XML (or HTML) rules;  \n`:str` — Java-compatible string escaping;  \n`:clj-str` — Clojure string escaping (`\\n` is allowed);  \n`:regex` — Escaping of Regex special symbols.\n\nOptions `:file-name` and `:file-path` (both String) are in place for better stack traces.\n\n### Template namespace: `fleet-ns`\n\n```clojure\n(fleet-ns root-ns root-path filters)\n```\n\nTreats `root-path` as root of template directory tree, maps it to namespace with prefix `root-ns.`, creates template functions\nfor each file in it with name and samespace according to relative path.\n\nExample:\n\n```clojure\n(fleet-ns view \"path/to/view_dir\" [:fleet :xml])\n```\n\nTemplate functions are created by the following rules:\n\n— 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`.\n\nThis 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`,\nwhile and if you have only one `posts.html.fleet` you could call it `posts` conviniently.\n\n— 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`.\n\nWhen it's called with one arguments both symbols (fn-name and data) are bound to same value of this argument.  \nWhen it's called with no arguments both symbols (fn-name and data) are bound to nil.  \nThis is also for convinience: you could use name appropriate to usage: e.g. if your template renders post, you could use `post` param name,\nand if template renders some complex data you could use `data`.\nAlso you can mix\u0026match, for example `post` as main rendered entity and `data` as some render options.\n\nFilters argument is vector of `file-filter escaping-fn` pairs used to filter which files to process and with which escaping function.\nFile filters could be defined as function, string, regex, `:fleet` or `:all`.  \n— Function should have Boolean type and one File argument.  \n— String filter definition treated as `*.string.fleet` mask, e.g. `\"js\"` mask will match `update.js.fleet`.  \n— Regex filter matches whole filename, e.g. `#\".*.html\"` will match `posts.html`.  \n— `:fleet` filter is treated as \"others\". If it is set all `*.fleet` files will be processed.  \n— `:all` means, literally, all.\n\n### More on escaping\n\nIf you need to insert Fleet constructions into text you can escape them using backslash.  \nYou only need escaping to remove ambiguity,\nso use `\\\u003c(` and `\\\u003c\"` only outside embedded clojure code,  `\\\"\u003e` and `\\)\u003e` only inside embedded clojure code.\n\n## Examples\n\nThis is not intended to work out-of-box, only to show some bits of a language / system.\n\n### Language\n\nTemplate file (`post_dedicated.fleet`):\n\n```clojure\n\u003chead\u003e\n  \u003ctitle\u003e\u003c(post :title)\u003e\u003c/title\u003e\n\n  \u003c(stylesheet :main)\u003e\n  \u003c(raw \"\u003cscript\u003ealert('Hello!')\u003c/script\u003e\")\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\n\u003cp\u003e\u003c(str notice)\u003e\u003c/p\u003e\n\n\u003cp\u003eSpaceship \\\u003c()\u003e is landing.\u003c/p\u003e\n\n\u003c(\n; Begin of post\n)\u003e\n\u003c(inside-frame (let [p post] \"\u003e\n  Author: \u003c(p :author)\u003e\u003cbr/\u003e\n  Date: \u003c(p :date)\u003e\u003cbr/\u003e\n\u003c\"))\u003e\n\n\u003cp\u003e\u003c(post :body)\u003e\u003c/p\u003e\n\u003cul\u003e\n  \u003c(for [tag (post :tags] \"\u003e\n    \u003cli\u003e\u003c(str tag)\u003e\u003c/li\u003e\n  \u003c\")\u003e\n\u003c/ul\u003e\n\u003c(\n; End of post\n)\u003e\n\n\u003c(footer)\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nClojure:\n\n```clojure\n(def post-page (fleet [post] (slurp \"post_dedicated.fleet\")))\n\n(post-page p)\n\n(footer)\n```\n\n### API\n\nLow-level:\n\n```clojure\n(def footer (fleet \"\u003cp\u003e\u0026copy; \u003c(year (now))\u003e Flamefork\u003c/p\u003e\"))\n```\n\nHigh-level:\n\nDirectory tree\n\n```\nroot_dir/\n  first_subdir/\n    file_a.html.fleet\n    file_b.html.fleet\n  second_subdir/\n    file_c.html.fleet\n```\n        \nwill be treated and processed by `(fleet-ns templates \"path/to/root_dir\" [:fleet :xml])` as functions\n\n```\ntemplates.first-subdir/file-a\ntemplates.first-subdir/file-b\ntemplates.second-subdir/file-c\n```\n    \nand (for example) first function will be like\n\n```clojure\n(defn file-a\n  ([file-a data] ...)\n  ([file-a] (recur file-a file-a)))\n  ([] (recur nil nil)))\n```\n\n## Compatibility\n\nUse 0.9.x for Clojure 1.2, 1.3  \nUse 0.10.x for Clojure 1.4+\n\n## Roadmap\n\n- update Fleet with latest Clojure goodness [in progress]\n- support ClojureScript\n\n## License\n\nCopyright (c) 2010 Ilia Ablamonov, released under the [MIT license](http://www.opensource.org/licenses/mit-license.php).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflamefork%2Ffleet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fflamefork%2Ffleet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflamefork%2Ffleet/lists"}