PoC demonstrating a dynamic filtering form with HTMX for a SIP/Mail-Server applicance.
The backend is implemented in Common Lisp, using the 3rd party systems cl-who (HTML generation), esrap (Packrat parsing), hunchentoot (HTTP server), cl-sqlite (SQL database).
- Install SBCL (Other Common Lisp implementations such as Clozure or ECL may work, but are untested).
- Install the ocicl package manager.
- Run
ocicl install
to install all dependencies.
Run the following command:
sbcl --disable-debugger \
--eval '(pushnew (uiop:getcwd) asdf:*central-registry*)' \
--eval '(asdf:load-system :xfiltertree-server)' \
--eval '(hunchentoot:start xfiltertree-server:*acceptor*)' \
--eval '(sleep #xffffffff)'
Then access the server at http://localhost:8080
Provides webstr:escape
that hex-escapes strings for safe usage in HTML/CSS.
All non-alphanumeric characters C are translated to _XX_
where XX = hex(C)
.
webstr:unescape
unescapes any string produced by webstr:escape
so that (webstr:unescape (webstr:escape) S) ≣ S
.
Parser & grammar definition for a simple equivalency expression language. (fql:parse-filter string)
parses string into a structured list.
table.column=x
is parsed into a structured list of the form ((:eq "table.column" "x"))
.
Subtyping clauses are also supported as a special case. table[type=t].column=x
is parsed into ((:eq "table.column" "x") (:strict-eq "table.type" "t"))
.
Defines the classes node
and aggregation
where aggregation extends node.
Each node has a string name and a list of children nodes.
An aggregation node additional has an associative list bin which associates bin specification string to fql filter strings.
Uses xfiltertree, fql, webstr.
A clause is a list of pairs of the form ((filter . bin) …)
where filter is a fql filter string and bin is an opaque bin designator. The HTML form values are received by the server as such pairs.
(xfiltertree-html:parse-filter-clauses clauses)
parses ((filter . bin) …)
(where filter is unescaped) into structured lists using fql:parse-filter
.
The resulting structured list is converted into a hierarchical tree of the form ((column (bin (operator arg0 arg1 …) …) …) …)
using (xfiltertree-html:sort-filter-clauses structured-clauses)
.
(xfiltertree-html:htmlize node)
returns a string containing a htmx-enabled hypermedia HTML form given a xfiltertree:node
instance.
xfiltertree:aggregation
nodes are transformed into <fieldset>
nodes containing checkbox inputs, allowing for selection of aggregation bin. One checkbox input is generated for each bin. The name=…
attribute is set to be equivalent to the webstr:escape-ed bin fql filter string, and the value=…
attribute is set to the bin specification string.
xfiltertree-html:*form-post*
should be bound to the form submission POST endpoint, and defaults to /
.
If xfiltertree-html:*form-update*
is bound to t (or any true value), xfiltertree-html:htmlize
will generate a DOM update payload, where tags may have an additional hx-oob-swap
property facilitating htmx-driven updates of bin counts with minimal rerendering.
Defines factories for creating xfiltertree
nodes.
(xfiltertree-bom:consume-aggregation-tree consume)
is a pull-style function which calls consume without arguments for every required aggregation count.
Uses xfiltertree-bom.
Generates a filter node tree from a sqlite3 database using xfiltertree-bom
.
Provides (xfiltertree-sql:query-aggregation-tree hier-clauses)
where hier-clauses is a hierarchical clause tree as produced by xfiltertree-html:sort-filter-clauses
.
xfiltertree-sql:*db*
should be bound to the sqlite3 database file (this defaults to db.sqlite3
).
Uses xfiltertree-html, xfiltertree-sql.
Integrates the systems xfiltertree-html, xfiltertree-sql and hunchentoot to provide a filter tree server.
xfiltertree-server:*acceptor*
is the hunchentoot acceptor, with all routes defined. By default, it's configured to listen on localhost:8080.
The server may be interactively started with (hunchentoot:start xfiltertree-server:*acceptor*)
, and stopped with (hunchentoot:stop xfiltertree-server:*acceptor*)
.