init commit

This commit is contained in:
Adam Jeniski 2025-09-14 17:54:58 -09:00
parent d975bc3d42
commit 0c61609774
8 changed files with 299 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.clj-kondo
.nrepl-port
.lsp
.cpcache

22
deps.edn Normal file
View File

@ -0,0 +1,22 @@
{:deps
{compojure/compojure {:mvn/version "1.7.1"}
com.datomic/local {:mvn/version "1.0.291"}
org.clojure/clojure {:mvn/version "1.12.0"}
org.clojure/data.json {:mvn/version "2.5.1"}
org.slf4j/slf4j-api {:mvn/version "2.0.17"}
org.slf4j/slf4j-simple {:mvn/version "2.0.17"}
hiccup/hiccup {:mvn/version "2.0.0-RC1"}
ring-logger/ring-logger {:mvn/version "1.1.1"}
ring/ring-core {:mvn/version "1.14.2"}
ring/ring-json {:mvn/version "0.5.1"}
ring/ring-jetty-adapter {:mvn/version "1.14.2"}}
:paths [:clj-paths :resource-paths]
:aliases
{:repl {:extra-paths ["src/dev"]
:extra-deps {org.clojure/clojure {:mvn/version "1.12.1"}
nrepl/nrepl {:mvn/version "1.3.0"}
cider/cider-nrepl {:mvn/version "0.50.2"}}
:main-opts ["-m" "nrepl.cmdline",
"--middleware" "[cider.nrepl/cider-middleware]"]}
:clj-paths ["src"]
:resource-paths ["resources"]}}

3
restart.sh Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env bash
systemctl restart tbd

139
src/ajet/todo/core.clj Normal file
View File

@ -0,0 +1,139 @@
(ns ajet.todo.core
(:require
[ajet.todo.db :as db]
[clojure.data.json :as json]
[clojure.pprint :as pprint]
[compojure.core :refer [context defroutes DELETE GET let-routes PATCH POST]]
[compojure.route :as route]
[hiccup2.core :as h]
[ring.middleware.content-type :refer [wrap-content-type]]
[ring.middleware.json :as rmjson]
[ring.middleware.not-modified :refer [wrap-not-modified]]
[ring.middleware.params :refer [wrap-params]]
[ring.middleware.resource :refer [wrap-resource]]
[ring.util.response :refer [response]]))
(comment
(do
(defonce printer (bound-fn* pprint/pprint))
(add-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]
(-> [:html
[:head
[:script {:src "https://cdn.jsdelivr.net/npm/htmx.org@2.0.7/dist/htmx.min.js"}]]
[:body view]]
h/html
str))
(def hx-app {:hx-swap "outerHTML"
:hx-target "#app"})
(defn render-app [{:keys [show-complete]
:or {show-complete true}
:as opts}]
(-> (let [todos (db/get-all-todos)
complete-toggle (if show-complete "hide" "show")]
[:div {:id "app"}
[:ul
(for [{id :id
done :todo/done?
title :todo/title} todos
:when (or show-complete (not done))]
[:li
[:span {:style (str "text-decoration: " (if done "line-through" "none"))}
title]
[:input (merge hx-app
{:type "checkbox"
:checked done
:hx-patch (format "/htmx/todo/%s/%s?opts=%s"
id
(if done "unset" "set")
(json/write-str opts))})]])]
[:div
[:p "settings"]
[:button (merge hx-app
{:hx-get (format "/htmx/todo/?opts=%s"
(-> opts
(update :show-complete not)
json/write-str))})
complete-toggle " done"]]])
h/html))
;; im gonna regert this
(defn DRY [req]
(-> req
:params
(get "opts" "{}")
json/read-json
render-app
str))
(defroutes api-routes
(GET "/" []
(wrap-params
#(-> % :params
(get "opts" "{}")
json/read-json
render-app
page-template)))
(-> (context "/api/v1" []
(context "/todo" []
(GET "/" [] get-todos)
(POST "/" [] add-todo)
(context ["/:id", :id #"[0-9]+"] [id]
(let-routes [id (read-string id)]
(GET "/" [] (partial get-todo id))
(PATCH "/" [] (partial update-todo id))
(DELETE "/" [] (delete-todo id)))))
(GET "/example/math/:x/:y" [x y]
(->> (+ (parse-long x)
(parse-long y))
(hash-map :result)
response)))
(rmjson/wrap-json-body {:keywords? true})
(rmjson/wrap-json-response))
(wrap-params
(context "/htmx/todo" []
(GET "/" [] DRY)
(POST "/" [] add-todo)
(context ["/:id", :id #"[0-9]+"] [id]
(let-routes [id (read-string id)]
(PATCH "/set" [] (do (db/set-todo-done! id true)
DRY))
(PATCH "/unset" [] (do (db/set-todo-done! id false)
DRY))
(DELETE "/" [] (do (delete-todo id)
DRY))))))
(route/not-found (str (h/html [:h2 "Not Found"]))))
(def app
(-> #'api-routes
(wrap-resource "public")
wrap-content-type
wrap-not-modified)) ;; files from resources/public are served

103
src/ajet/todo/db.clj Normal file
View File

@ -0,0 +1,103 @@
(ns ajet.todo.db
(:require [datomic.client.api :as d]))
(def client (d/client {:server-type :datomic-local
:system "dev"}))
(d/create-database client {:db-name "todos"})
(def conn (d/connect client {:db-name "todos"}))
(comment
;; to reset local dev db
(try
(d/delete-database client {:db-name "todos"})
(catch Exception _))
(do
(def todo-schema [{:db/ident :todo/title
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/unique :db.unique/identity
:db/doc "The title of the todo"}
{:db/ident :todo/done?
:db/valueType :db.type/boolean
:db/cardinality :db.cardinality/one
:db/doc "The completedness of the todo"}])
(d/transact conn {:tx-data todo-schema})
(def first-todos [{:todo/title "set up git repo"
:todo/done? true}
{:todo/title "teach jon lisp"
:todo/done? false}
{:todo/title "finish the todo app"
:todo/done? false}
{:todo/title "profit?"
:todo/done? false}])
(d/transact conn {:tx-data first-todos})))
(defn normalize-todo [[id title done]]
{:id id
:todo/title title
:todo/done? done})
(defn get-unfinished-todos []
(let [db (d/db conn)]
(->> db
(d/q '[:find ?e ?title
:where [?e :todo/title ?title]
[?e :todo/done? false]])
(map normalize-todo)
(map #(assoc % :todo?/done false)))))
(defn get-all-todos []
(let [db (d/db conn)]
(->> db
(d/q '[:find ?e ?title ?done
:where [?e :todo/title ?title]
[?e :todo/done? ?done]])
(sort-by first)
(map normalize-todo))))
(defn get-todo [id]
(let [db (d/db conn)]
(some->> (d/q '[:find ?e ?title ?done
:in $ ?e
:where [?e :todo/title ?title]
[?e :todo/done? ?done]]
db
id)
first
normalize-todo)))
(defn get-todo-by-title [title]
(let [db (d/db conn)]
(some->> (d/q '[:find ?e ?title ?done
:in $ ?title
:where [?e :todo/title ?title]
[?e :todo/done? ?done]]
db
title)
first
normalize-todo)))
(defn add-todo! [title]
(d/transact conn {:tx-data [{:todo/title title
:todo/done? false}]}))
(defn set-todo-done! [eid done]
(d/transact conn {:tx-data [{:db/id eid
:todo/done? done}]}))
(defn delete-todo! [id]
(d/transact conn {:tx-data [[:db/retractEntity id]]}))
(comment
(get-all-todos)
(get-unfinished-todos)
(add-todo! "foo")
(def foo-id (-> (get-todo-by-title "foo")
:id))
(set-todo-done! foo-id true)
(delete-todo! foo-id))

13
src/ajet/todo/server.clj Normal file
View File

@ -0,0 +1,13 @@
(ns ajet.todo.server
(:require
[ajet.todo.core :as core]
[ring.adapter.jetty :refer [run-jetty]]))
(def port 1738)
(defn make-server [opts]
(run-jetty #'core/app (merge {:join? false, :port port} opts)))
(defn -main [& _args]
(make-server {}))

11
src/dev/user.clj Normal file
View File

@ -0,0 +1,11 @@
(ns user
(:require [ajet.todo.server :as server]))
(defonce server (server/make-server {}))
(comment
server
(. server stop)
(. server start))

3
start.sh Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/env sh
cd ~/repos/tbd
clj -M -m ajet.todo.server