rewrite in d*

This commit is contained in:
Adam Jeniski 2025-09-17 11:46:54 -09:00
parent 0b63aebebd
commit eba0dbe7c2
4 changed files with 134 additions and 112 deletions

View File

@ -11,7 +11,9 @@
ring/ring-json {:mvn/version "0.5.1"} ring/ring-json {:mvn/version "0.5.1"}
ring/ring-jetty-adapter {:mvn/version "1.14.2"} ring/ring-jetty-adapter {:mvn/version "1.14.2"}
dev.data-star.clojure/sdk {:mvn/version "1.0.0-RC3"} dev.data-star.clojure/sdk {:mvn/version "1.0.0-RC3"}
dev.data-star.clojure/ring {:mvn/version "1.0.0-RC3"}} dev.data-star.clojure/ring {:mvn/version "1.0.0-RC3"}
datastar/expressions {:git/url "https://github.com/outskirtslabs/datastar-expressions/"
:git/sha "53efc7093be1ba33b331b4a27884be8925d8bdce"}}
:paths [:clj-paths :resource-paths] :paths [:clj-paths :resource-paths]
:aliases :aliases
{:repl {:extra-paths ["src/dev"] {:repl {:extra-paths ["src/dev"]

View File

@ -6,12 +6,11 @@
[compojure.core :refer [context defroutes DELETE GET let-routes PATCH POST]] [compojure.core :refer [context defroutes DELETE GET let-routes PATCH POST]]
[compojure.route :as route] [compojure.route :as route]
[hiccup2.core :as h] [hiccup2.core :as h]
[ring.middleware.content-type :refer [wrap-content-type]] [ring.middleware.json :refer [wrap-json-body]]
[ring.middleware.json :as rmjson] [starfederation.datastar.clojure.adapter.common :refer [on-open]]
[ring.middleware.not-modified :refer [wrap-not-modified]] [starfederation.datastar.clojure.adapter.ring :refer [->sse-response]]
[ring.middleware.params :refer [wrap-params]] [starfederation.datastar.clojure.api :as d*]
[ring.middleware.resource :refer [wrap-resource]] [starfederation.datastar.clojure.expressions :refer [->expr]]))
[ring.util.response :refer [response]]))
(comment (comment
(do (do
@ -20,120 +19,137 @@
(remove-tap printer)) (remove-tap printer))
(defn get-todos [_]
(let [res (db/get-all-todos)]
(response res)))
(defn get-todo [id _]
(let [res (db/get-todo id)]
(response res)))
(defn add-todo [req]
(db/add-todo! (-> req :body :title))
{:status 201})
(defn update-todo [id {{:keys [done]} :body}]
(db/set-todo-done! id done))
(defn delete-todo [id]
(db/delete-todo! id)
{:status 204})
(defn page-template [view] (defn page-template [view]
(-> [:html (str
(h/html
(h/raw "<!DOCTYPE html>")
[:html
[:head [:head
[:script {:src "https://cdn.jsdelivr.net/npm/htmx.org@2.0.7/dist/htmx.min.js"}]] [:script {:src "https://cdn.jsdelivr.net/gh/starfederation/datastar@1.0.0-RC.5/bundles/datastar.js"
[:body view]] :type "module"
h/html :defer true}]
str)) [:link {:rel "icon"
:type "imaage/x-icon"
:href "https://data-star.dev/cdn-cgi/image/format=auto,width=24/static/images/rocket-48x48-4c739bfaffe86a6ffcc3a6d77e3c5547730f03d74c11aa460209596d1811f7a3.png"}]]
[:body view]])))
(def hx-app {:hx-swap "outerHTML" (defn todos-fragment []
:hx-target "#app"}) (let [todos (db/get-all-todos)]
(h/html
(defn render-app [{:keys [show-complete] [:div {:id "todos"}
:or {show-complete true} [:div {:data-computed-show-toggle-display "($showComplete ? 'hide' : 'show') + ' done'"}]
:as opts}]
(-> (let [todos (db/get-all-todos)
complete-toggle (if show-complete "hide" "show")]
[:div {:id "app"}
[:ul [:ul
(for [{id :id (for [{id :id
done :todo/done? done :todo/done?
title :todo/title} todos title :todo/title} todos]
:when (or show-complete (not done))] [:li {:data-show (->expr (or (not ~done)
[:li $showComplete))}
[:span {:style (str "text-decoration: " (if done "line-through" "none"))} [:span {:style (str "text-decoration: " (if done "line-through" "none"))}
title] title]
[:input (merge hx-app [:input {:type "checkbox"
{:type "checkbox"
:checked done :checked done
:hx-patch (format "/htmx/todo/%s/%s?opts=%s" :data-on-click (str "@patch('/sse/todo/" id "/" (when done "un") "set')")
id :style "margin-right: 0.25rem;"}]
(if done "unset" "set") (when done
(json/write-str opts))})]])] [:button {:data-on-click (->expr (@delete ~(str "/sse/todo/" id)))}
"X"])])]
[:div [:div
[:p "settings"] [:p "settings"]
[:button (merge hx-app [:button {:data-on-click "$showComplete = !$showComplete"
{:hx-get (format "/htmx/todo/?opts=%s" :data-text "$showToggleDisplay"
(-> opts :style "margin-right: 0.25rem;"}]
(update :show-complete not) [:button {:data-on-click "$debugMode = !$debugMode"}
json/write-str))}) "toggle debug"]]])))
complete-toggle " done"]]])
h/html))
;; im gonna regert this (defn create-todo-fragment []
(defn DRY [req] (h/html
(-> req [:div {:id "create-box"
:params :style "margin-top: 2rem;"}
(get "opts" "{}") [:label "Make a todo: "]
json/read-json [:input {:name "title"
render-app :data-bind "title"
str)) :style "margin-right: 0.25rem;"}]
[:button {:data-on-click (->expr (@post "/sse/todo/create-todo"))}
"submit"]]))
(defroutes api-routes (defn app-fragment []
(h/html
[:div {:data-signals #json{:showComplete true
:debugMode false
:title ""}}]
[:pre {:data-json-signals "true"
:data-show "$debugMode"}]
[:div {:id "app"}
(todos-fragment)
(create-todo-fragment)]))
(defn refresh-todos! [sse]
(d*/patch-elements! sse
(str (todos-fragment))
#:d*.elements{:selector "#todos"
:patch-mode "replace"}))
(defroutes approutes
(GET "/" [] (GET "/" []
(wrap-params (fn [_ respond _]
#(-> % :params (-> (app-fragment)
(get "opts" "{}") page-template
json/read-json respond)))
render-app
page-template))) (wrap-json-body
(-> (context "/api/v1" [] (context "/sse/todo" []
(context "/todo" [] (GET "/" []
(GET "/" [] get-todos) (fn [req respond _]
(POST "/" [] add-todo) (respond
(->sse-response req
{on-open (fn [sse]
(d*/with-open-sse sse
(refresh-todos! sse)))}))))
(POST "/create-todo" []
(fn [req respond _]
(let [title (-> req :body :title)]
(tap> title)
(when title
(db/add-todo! title))
(respond
(->sse-response req
{on-open (fn [sse]
(d*/with-open-sse sse
(refresh-todos! sse)
(d*/patch-signals! sse
#json{:title ""})))})))))
(context ["/:id", :id #"[0-9]+"] [id] (context ["/:id", :id #"[0-9]+"] [id]
(let-routes [id (read-string id)] (let-routes [id (read-string id)]
(GET "/" [] (partial get-todo id)) (PATCH "/set" []
(PATCH "/" [] (partial update-todo id)) (fn [req respond _]
(DELETE "/" [] (delete-todo id))))) (db/set-todo-done! id true)
(respond
(GET "/example/math/:x/:y" [x y] (->sse-response req
(->> (+ (parse-long x) {on-open (fn [sse]
(parse-long y)) (d*/with-open-sse sse
(hash-map :result) (refresh-todos! sse)))}))))
response))) (PATCH "/unset" []
(rmjson/wrap-json-body {:keywords? true}) (fn [req respond _]
(rmjson/wrap-json-response)) (db/set-todo-done! id false)
(respond
(wrap-params (->sse-response
(context "/htmx/todo" [] req
(GET "/" [] DRY) {on-open (fn [sse]
(POST "/" [] add-todo) (d*/with-open-sse sse
(context ["/:id", :id #"[0-9]+"] [id] (refresh-todos! sse)))}))))
(let-routes [id (read-string id)] (DELETE "/" []
(PATCH "/set" [] (do (db/set-todo-done! id true) (db/delete-todo! id)
DRY)) (fn [req respond _]
(PATCH "/unset" [] (do (db/set-todo-done! id false) (respond
DRY)) (->sse-response
(DELETE "/" [] (do (delete-todo id) req
DRY)))))) {on-open
(fn [sse]
(d*/with-open-sse sse
(refresh-todos! sse)))})))))))
{:keywords? true})
(route/not-found (str (h/html [:h2 "Not Found"])))) (route/not-found (str (h/html [:h2 "Not Found"]))))
(def app (def app #'approutes)
(-> #'api-routes
(wrap-resource "public")
wrap-content-type
wrap-not-modified)) ;; files from resources/public are served

View File

@ -6,7 +6,10 @@
(def port 1738) (def port 1738)
(defn make-server [opts] (defn make-server [opts]
(run-jetty #'core/app (merge {:join? false, :port port} opts))) (run-jetty #'core/app (merge {:join? false,
:port port,
:async? true}
opts)))
(defn -main [& _args] (defn -main [& _args]
(make-server {})) (make-server {}))

1
src/data_readers.clj Normal file
View File

@ -0,0 +1 @@
{json clojure.data.json/write-str}