diff --git a/README.md b/README.md index fe344315..ca5d0317 100644 --- a/README.md +++ b/README.md @@ -1130,6 +1130,13 @@ Your command line args: (1 2 3) (into-array CopyOption []))) ``` +### Notes webapp + +See +[examples/notes.clj](https://github.com/borkdude/babashka/blob/master/examples/notes.clj). This +is a variation on the +[http-server](https://github.com/borkdude/babashka/#tiny-http-server) example. + ## Thanks - [adgoji](https://www.adgoji.com/) for financial support diff --git a/examples/notes.clj b/examples/notes.clj new file mode 100755 index 00000000..977f8e30 --- /dev/null +++ b/examples/notes.clj @@ -0,0 +1,86 @@ +#!/usr/bin/env bb + +(import (java.net ServerSocket)) +(require '[clojure.java.io :as io] + '[clojure.java.shell :refer [sh]] + '[clojure.string :as str]) + +(def debug? false) + +(def notes-file (io/file (System/getProperty "user.home") ".notes" "notes.txt")) +(io/make-parents notes-file) + +;; ensure notes file exists +(spit notes-file "" :append true) + +;; we wait for the server to accept connections and then open a browser +(def accepting (promise)) +(future + @accepting + (sh "open" "http://localhost:8080")) + +;; hiccup-like +(defn html [v] + (cond (vector? v) + (let [tag (first v) + attrs (second v) + attrs (when (map? attrs) attrs) + elts (if attrs (nnext v) (next v)) + tag-name (name tag)] + (format "<%s%s>%s\n" tag-name (html attrs) (html elts) tag-name)) + (map? v) + (str/join "" + (map (fn [[k v]] + (format " %s=\"%s\"" (name k) v)) v)) + (seq? v) + (str/join " " (map html v)) + :else (str v))) + +;; the home page +(defn home [] + (str + "\n" + (html + (list [:head + [:title "Notes"]] + [:body + [:h1 "Notes"] + [:pre (slurp notes-file)]] + [:form {:action "/" :method "post"} + [:input {:type "text" :name "note"}] + [:input {:type "submit" :value "Submit"}]])))) + +;; run the server +(with-open [server-socket (let [s (new ServerSocket 8080)] + (deliver accepting true) + s) + client-socket (.accept server-socket)] + (loop [] + (let [out (io/writer (.getOutputStream client-socket)) + is (.getInputStream client-socket) + in (io/reader is) + response (loop [headers []] + (let [line (.readLine in)] + (if (str/blank? line) + headers + (recur (conj headers line))))) + data (let [sb (StringBuilder.)] + (loop [] + (when (.ready in) + (.append sb (char (.read in))) + (recur))) + (-> (str sb) + (java.net.URLDecoder/decode))) + _ (when debug? (println (str/join "\n" response))) + _ (when-not (str/blank? data) + (when debug? (println data)) + (let [note (str/replace data "note=" "")] + (spit notes-file (str note "\n") :append true))) + _ (when debug? (println)) + body (home)] + (.write out (format "HTTP/1.1 %s OK\r\nContent-Length: %s\r\n\r\n%s" + 200 + (count body) + body)) + (.flush out)) + (recur)))