From 7af519c78854719ef1ca851bae673289672b649a Mon Sep 17 00:00:00 2001 From: ajet Date: Mon, 21 Jul 2025 08:37:33 -0900 Subject: [PATCH] frameworkify 2/x --- deps.edn | 2 ++ src/main/example/core.clj | 68 ++++++++++++++++--------------------- src/main/example/server.clj | 3 ++ src/main/example/utils.clj | 67 +++++++++++++++++++++++------------- 4 files changed, 77 insertions(+), 63 deletions(-) diff --git a/deps.edn b/deps.edn index 4e96647..d158ffc 100644 --- a/deps.edn +++ b/deps.edn @@ -7,6 +7,8 @@ starfederation.datastar/ring {:git/url "https://github.com/starfederation/datastar/" :git/sha "376c4e2411706b942ea0ab937e4c6218d24fb30f" :deps/root "sdk/clojure/adapter-ring"} + datastar/expressions {:git/url "https://github.com/ramblurr/datastar-expressions/" + :git/sha "8db9d4bf5a178912ca173f67671fd9dba6b14f90"} ring/ring-jetty-adapter {:mvn/version "1.13.0"} metosin/reitit {:mvn/version "0.7.2"} dev.onionpancakes/chassis {:mvn/version "1.0.365"} diff --git a/src/main/example/core.clj b/src/main/example/core.clj index 4571e68..19050bd 100644 --- a/src/main/example/core.clj +++ b/src/main/example/core.clj @@ -1,57 +1,47 @@ (ns example.core (:require - [example.utils :refer [html->str defpage defaction defaction-async] :as u] + [example.utils :refer [html->str defpage defaction defaction-async keep-open] :as u] + [starfederation.datastar.clojure.expressions :refer [->expr]] [reitit.ring :as rr] [ring.util.response :as ruresp])) (declare home-page page-2 hello-world) -;; views -(def home-page - {:url "/" - :view - (fn home-view [_] - (html->str - [:main - [:div - [:input {:data-bind "msg"}] - [:div {:data-text "$msg"}] - [:button {:data-on-click (u/at-get hello-world)} - "click for text animation"]] - [:button {:data-on-click (u/at-get page-2)} "go to page 2"]]))}) - -(def page-2 - {:url "/page2" - :view - (fn page2-view [_] - (html->str - [:main - [:p "this is page2"] - [:button {:data-on-click (u/at-get home-page)} - "go back to home"]]))}) - -;; hello world animation (def message "Hello, world!") - (def msg-count (count message)) -(def hello-world - {:url "/hello-world" - :fn (fn hello-world [sse] - (dotimes [i msg-count] - (u/patch-signals-edn! sse {:msg (subs message 0 (inc i))}) - (Thread/sleep 500)))}) +;; actions +(defaction hello-world [sse] + (dotimes [i msg-count] + (u/patch-signals-edn! sse {:msg (subs message 0 (inc i))}) + (Thread/sleep 500))) -;; http stuff +;; pages +(defpage home-page + (html->str + [:main + [:div + [:input {:data-bind "msg"}] + [:div {:data-text "$msg"}] + [:button {:data-on-click (->expr (@post ~hello-world ~keep-open))} + "click for text animation"]] + [:button {:data-on-click (->expr (@post ~page-2))} + "go to page 2"]])) + +(defpage page-2 + (html->str + [:main + [:p "this is page2"] + [:button {:data-on-click (->expr (@post ~home-page))} + "go back to home"]])) + +;; http app server (def routes (concat - (defpage home-page) - (defpage page-2) - (defaction hello-world) + (u/app-routes home-page) + ;; todo frameworkify the aysnc & broadcast stuffs then all the http things (defaction-async "/connect" u/connect-sse! u/disconnect-sse!))) - (def router (rr/router routes)) - (def handler (rr/ring-handler router)) ;; repl it up ;P diff --git a/src/main/example/server.clj b/src/main/example/server.clj index 4ad931d..1d3bcd5 100644 --- a/src/main/example/server.clj +++ b/src/main/example/server.clj @@ -25,5 +25,8 @@ (stop! server)) (start! handler opts)))) +(defn -main [& _args] + (reboot-jetty-server! #'c/handler {:async? true})) + (comment (reboot-jetty-server! #'c/handler {:async? true})) diff --git a/src/main/example/utils.clj b/src/main/example/utils.clj index 4a9184a..154a286 100644 --- a/src/main/example/utils.clj +++ b/src/main/example/utils.clj @@ -11,7 +11,7 @@ [starfederation.datastar.clojure.adapter.ring :refer [->sse-response on-open on-close] :as dr] [clojure.data.json :as json])) -(declare conns add-elements! sse-navigate!) +(declare conns add-elements! sse-navigate! disconnect-sse!) ;; global utils (defn tap! [x] @@ -30,12 +30,14 @@ (string/split-lines) (->> (drop 3) (apply str)) - (str "
") + (str "
") (string/replace "%%content%%" content))) (defn at-get [page-or-action] (format "@get('%s')" (:url page-or-action))) +(def keep-open {:openWhenHidden true}) + ;; fix. use replaceState api? or push some real state? back button doesn't work yet (defn update-url-frag [url] (html->str [:script (format "history.pushState({page:1}, 'Title', '%s')" @@ -117,29 +119,46 @@ (swap! conns disj conn))) ;; http stuffs -(defn defpage - ([page] - (defpage (:url page) (:view page))) - ([endpoint view] - [[endpoint - {:handler (fn [request respond _] - (page request respond endpoint view))}] - [(str "/sse" endpoint) - {:handler (fn [request respond _] - (page request respond endpoint view))}]])) +(defonce !ma-routes (atom {})) -(defn defaction - ([action] - (defaction (:url action) (:fn action))) - ([url f] - (let [sse-handler - (fn [request respond raise_] - (respond - (->sse-response - request - {on-open #(d*/with-open-sse % (f %))})))] - [[url sse-handler] - [(str "/sse" url) sse-handler]]))) +(defn add-route! [url f] + (swap! !ma-routes assoc url f)) + +;; todo, custom url support +(defmacro defpage + [name-sym & body] + (let [route-str (str "/" name-sym) + route-str-sse (str "/sse" route-str)] + `(let [view# (fn [_#] + ~@body)] + + (add-route! ~route-str + (fn [request# respond# _#] + (page request# respond# ~route-str view#))) + (add-route! ~route-str-sse + (fn [request# respond# _#] + (sse-page request# respond# view# ~route-str-sse))) + (def ~name-sym ~route-str)))) + +(defn app-routes + ([] + (->> @!ma-routes + vec)) + ([index-route] + (concat (app-routes) + [["/" (@!ma-routes index-route)]]))) + +(defmacro defaction [action-sym args & body] + (let [url (str "/sse/" action-sym)] + `(let [sse-handler# + (fn [request# respond# raise_#] + (respond# + (->sse-response + request# + {on-open (fn ~args (d*/with-open-sse ~(get args 0) + ~@body))})))] + (add-route! ~url sse-handler#) + (def ~action-sym ~url)))) (defn defaction-async [url on-open-f on-close-f] (let [sse-handler