From 2460cfe73fce317e14dc6f93632d2dbceea35302 Mon Sep 17 00:00:00 2001 From: Oliver Marks Date: Fri, 29 Jan 2021 12:32:09 +0000 Subject: [PATCH] Refactor --- .drone.yml | 8 +- reagent-reitit-demo/deps.edn | 6 +- reagent-reitit-demo/dev.cljs.edn | 2 +- reagent-reitit-demo/src/demo/core.cljs | 222 +++++++++++++++++++++++++ reagent-reitit-demo/src/org.cljs | 162 ------------------ 5 files changed, 233 insertions(+), 167 deletions(-) create mode 100644 reagent-reitit-demo/src/demo/core.cljs delete mode 100644 reagent-reitit-demo/src/org.cljs diff --git a/.drone.yml b/.drone.yml index 5571165..6cca8f4 100644 --- a/.drone.yml +++ b/.drone.yml @@ -4,7 +4,9 @@ steps: - name: Build image: clojure:tools-deps commands: - - clj -Adev -m figwheel.main -bo prod + - cd reagent-reitit-demo + - cp resources/public/rename-me-index.html resources/public/index.html + - clj -A:prod - name: deploy-site pull: True @@ -17,6 +19,8 @@ steps: from_secret: ssh_key port: 22 duration: 4m + ## strip components removes parts of path at destination + strip_components: 1 target: /var/www/clojure-demos/ source: - - resources/public/* + - reagent-reitit-demo/resources/public/* diff --git a/reagent-reitit-demo/deps.edn b/reagent-reitit-demo/deps.edn index 02b9b9f..497a6cd 100644 --- a/reagent-reitit-demo/deps.edn +++ b/reagent-reitit-demo/deps.edn @@ -1,13 +1,15 @@ {:deps {org.clojure/clojure {:mvn/version "1.10.0"} org.clojure/clojurescript {:mvn/version "1.10.764"} cljs-ajax {:mvn/version "0.8.1"} + ;; react reagent {:mvn/version "0.9.1"} reagent-utils {:mvn/version "0.3.3"} olymk2/cl-org {:git/url "https://gitlab.com/olymk2/cl-org.git" :sha "c366560dd59e16759ca24209b253f55e46ecbbe3"} - + ;;rouoting metosin/reitit {:mvn/version "0.5.10"} metosin/reitit-spec {:mvn/version "0.5.10"} metosin/reitit-frontend {:mvn/version "0.5.10"} com.bhauman/figwheel-main {:mvn/version "0.2.11"}} - :paths ["src" "resources"]} + :paths ["src" "resources"] + :aliases {:prod {:main-opts ["-m" "figwheel.main" "-bo" "dev"]}}} diff --git a/reagent-reitit-demo/dev.cljs.edn b/reagent-reitit-demo/dev.cljs.edn index 6798d2b..e4f26c2 100644 --- a/reagent-reitit-demo/dev.cljs.edn +++ b/reagent-reitit-demo/dev.cljs.edn @@ -5,4 +5,4 @@ :source-map true :source-map-timestamp true :devcards true - :main core.demo} + :main demo.core} diff --git a/reagent-reitit-demo/src/demo/core.cljs b/reagent-reitit-demo/src/demo/core.cljs new file mode 100644 index 0000000..658802c --- /dev/null +++ b/reagent-reitit-demo/src/demo/core.cljs @@ -0,0 +1,222 @@ +(ns ^:figwheel-hooks demo.core + (:require [reagent.core :as reagent] + [ajax.core :refer [GET raw-response-format]] + ;[demo.org :refer [parse->to-hiccup parse-flat]] + [cl-eorg.parser :as o :refer [parse parse-flat]] + [cl-eorg.html :refer [org->replacements]] + [reitit.frontend :as rf] + [reitit.frontend.easy :as rfe] + [reitit.coercion.spec :as rss] + [spec-tools.data-spec :as ds])) + +;; put constant data here +(def site-data + {:demos {:dsl-demo + {:file "dsl-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"} + :datalog-demo + {:file "datalog-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"} + :reagent-demo + {:file "reagent-reitit.org" :git-link "https://github.com/atomjuice/dsl-demo"}} + :homepage {:intro "Clojure tutorials examples and exploration"} + :lorem "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."}) + +;; Store site state +(defonce site-state (reagent/atom {})) + +;; for one component to render an article +(defn article [{:keys [title description tagline]}] + [:article {:data-name "article-full-bleed-background"} + [:div.cf {:style {:background "url(http://placekitten.com/g/600/300)" + :no-repeat "center center fixed" :background-size "cover"}} + [:div.fl.pa3.pa4-ns.bg-white.black-70.measure-narrow.f3.times + [:header.b--black-70.pv4 {:class (when tagline "bb")} + [:h3.f2.fw7.ttu.tracked.lh-title.mt0.mb3.avenir title] + (when tagline [:h4.f3.fw4.i.lh-title.mt0 tagline])] + [:section.pt5.pb4 [:p.times.lh-copy.measure.f4.mt0 description]]]]]) + +;; form one component to render article tiles +(defn articles [{:keys [title body articles]}] + [:section.mw7.center.avenir {:key title} + [:h2.baskerville.fw1.ph3.ph0-l title] + (when body [:p body]) + (map (fn [{:keys [title author link description img-src img-alt]}] + [:article.bt.bb.b--black-10 + [:a.db.pv4.ph3.ph0-l.no-underline.black.dim {:href link} + [:div.flex.flex-column.flex-row-ns + (when img-src + [:div.pr3-ns.mb4.mb0-ns.w-100.w-40-ns + [:img.db {:src img-src :alt img-alt}]]) + [:div.w-100.w-60-ns.pl3-ns + [:h1.f3.fw1.baskerville.mt0.lh-title title] + [:p.f6.f5-l.lh-copy description] + [:p.f6.lh-copy.mv0 author]]]]]) + articles)]) + +;; form one component to render a product +(defn product-card + [{:keys [title amount description link]}] + [:article.br2.ba.dark-gray.b--black-10.ma2.w-100.w-50-m.w-25-l.mw5 + [:img.db.w-100.br2.br--top {:src link}] + [:div.pa2.ph3-ns.pb3-ns + [:div.dt.w-100.mt1 + [:div.dtc [:h1.f5.f4-ns.mv0 title]] + [:div.dtc.tr [:h2.f5.mv0 amount]]] + [:p.f6.lh-copy.measure.mt2.mid-gray description]]]) + +(defn circle [{:keys [img alt]}] + [:div.pa4.tc [:img.br-100.ba.h3.w3.dib {:src img :alt alt}]]) + + +;; form one component ro render a nav link + + +(defn navbar-link [{:keys [href title text] :or {text nil title nil}}] + [:a.link.dim.white.dib.mr3 {:key href :href href :title title} text]) + +;; form one component to render a navbar +(defn navbar [links] + [:header.bg-black-90.w-100.ph3.pv3.pv4-ns.ph4-m.ph5-l + [:nav.f6.fw6.ttu.tracked + (map navbar-link links)]]) + +(defn my-component [title] + (let [local-state (reagent/atom true)] + (fn [] + [:h1 {:class (when @local-state "hid") + :on-click (fn [] (swap! local-state not))} title]))) + + +;; form one homepage component + + +(defn home-page [] + [:<> + [articles + {:title "Clojure Demos" + :body (-> site-data :homepage :intro) + :articles [{:title "DSL Demo" + :link (rfe/href ::demo {:page "dsl-demo"}) + :img-src "https://miro.medium.com/max/1400/1*CEYFj5R57UFyCXts2nsBqA.png"} + {:title "Datalog Demo" + :link (rfe/href ::demo {:page "datalog-demo"}) + :img-src "https://raw.githubusercontent.com/tonsky/datascript/master/extras/logo.svg"} + {:title "Reagent Demo" + :link (rfe/href ::demo {:page "reagent-demo"}) + :img-src "https://raw.githubusercontent.com/reagent-project/reagent/master/logo/logo-text.png"}]}]]) + +;; form two component render demo +(defn demo-page [route] + (let [demo-key (keyword (-> route :parameters :path :page)) + content (reagent/atom {})] + (GET (-> site-data :demos demo-key :file) + {:response-format (raw-response-format) + :handler (fn [response] + (->> response + parse + org->replacements + (reset! content)) + #_(reset! content (org->replacements (parse response))))}) + (fn [route] + [:main.mt4 + [:div.mw7.center.avenir @content]]))) + +;; form one render about page component + + +(defn about-page [] + [:main.mt4 + [:section.mw7.center.avenir + [:h1 "Clojure library examples to aid learning"] + [:p "Selection of clojure demos, rendered in reagent which itself is an example of using reagent."] + [my-component "component 1"] + [my-component "component 2"] + [circle {:alt "test"}] + [article {:title "Article" + :description (-> site-data :lorem) + :tagline "tagline here"}] + [:div.flex.flex-column.flex-row-ns + [product-card + {:title "Cat 01" + :amount "£54.59" + :description "Cat description here" + :link "http://placekitten.com/g/600/300"}] + [product-card + {:title "Cat 02" + :amount "£10.59" + :description "Cat description here" + :link "http://placekitten.com/g/600/300"}]]]]) + +;; form 3 component wrap rendering to catch errors and render them +;; or just render the rest of the page if all is good +;; this uses =create-class= and a hash map of life cycle functions +(defn err-boundary + [& children] + (let [err-state (reagent/atom nil)] + (reagent/create-class + {:display-name "ErrBoundary" + :component-did-catch (fn [err info] + (reset! err-state [err info])) + :reagent-render (fn [& children] + (if (nil? @err-state) + (into [:<>] children) + (let [[_ info] @err-state] + [:pre [:code (pr-str info)]])))}))) + +;; define our routes, just nested vectors of the route definition hash maps +(def routes + [["/" + {:name ::frontpage + :my-data "hi" + :view home-page}] + + ["/about" + {:name ::about + :view about-page}] + + ["/demo/:page" + {:name ::demo + :view demo-page + :parameters {:path {:page string?} + :query {(ds/opt :foo) keyword?}}}]]) + +;; top level component contains nav and adds in the select page into a containing element +;; we are adding in a style sheet but this will often be done in index.html +(defn current-page [] + [:<> [:link {:rel "stylesheet" :href "https://unpkg.com/tachyons@4.12.0/css/tachyons.min.css"}] + [navbar [{:href (rfe/href ::frontpage) :title "title here" :text "home"} + {:href (rfe/href ::about) :text "About"} + {:href (rfe/href ::i-do-not-exist) :text "missing"}]] + [:main.mt4 + (when-let [view (-> @site-state :current-route :data :view)] [view (-> @site-state :current-route)])]]) + + +;; This simply calls reagent render and puts the result in a div with the id of app +;; you can create your own index.html or figwheel provides one with the app id which will replace the default data +;; ^:after-load is meta data its not needed but informs figwheel to run this code after a page load + + +(defn mount-root-page [] + ;; this select the main node from the html file and injects your page content + (reagent/render + (fn [] [err-boundary [current-page]]) + (.getElementById js/document "app"))) + +(defn ^:after-load render-site [] + ;; this select the main node from the html file and injects your page content + (mount-root-page)) + +(defn startup! [] + (rfe/start! + (rf/router routes {:data {:coercion rss/coercion}}) + (fn [m] (swap! site-state assoc :current-route m)) + ;; set to false to enable HistoryAPI + {:use-fragment true}) + (render-site)) + +;; we defonce the startup so that hot reloading does not reinitialize the state of the site +(def launch (do (startup!) true)) + +(comment + @site-state + + (GET "/test.org" {:handler (fn [response] (swap! site-state assoc :content response))})) diff --git a/reagent-reitit-demo/src/org.cljs b/reagent-reitit-demo/src/org.cljs deleted file mode 100644 index 951fcd7..0000000 --- a/reagent-reitit-demo/src/org.cljs +++ /dev/null @@ -1,162 +0,0 @@ -(ns demo.org - (:require [clojure.string :as str])) - - -(def ESCAPE "\n") - -(def BLANK 0) -(def META 1) -(def META_OTHER 2) -(def HEADER 5) -(def BOLD 10) -(def ITALIC 11) -(def UNDERLINED 12) -(def VERBATIM 13) -(def LIST 20) -(def TEXT 21) -(def IMAGE 22) -(def LINK 23) -(def CAPTION 24) -(def BULLET 25) -(def SOURCE 50) -(def EXAMPLE 51) -(def RESULTS 52) -(def COMMENT 53) -(def TABLE 54) - -(def METADATA ["TITLE" - "AUTHOR" - "EMAIL" - "DESCRIPTION" - "KEYWORDS" - "FILETAGS" - "DATE" - "HTML_DOCTYPE" - "SETUPFILE"]) - -(def t_META #"^[#]\+(?:TITLE|AUTHOR|EMAIL|DESCRIPTION|KEYWORDS|FILETAGS|DATE|HTML_DOCTYPE|SETUPFILE)\:") -(def t_BLANK_LINE #"^\s*$") -(def t_COMMENT_BEGIN #"^\#\+BEGIN_COMMENT") -(def t_COMMENT_END #"^\#\+END_COMMENT") -(def t_EXAMPLE_BEGIN #"^\#\+BEGIN_EXAMPLE") -(def t_EXAMPLE_END #"^\#\+END_EXAMPLE") -(def t_SRC_BEGIN #"^\#\+BEGIN_SRC\s+") -(def t_SRC_END #"^\#\+END_SRC") -(def t_TABLE_START #"^\s*\|") -(def t_TABLE_END #"^(?!\s*\|).*$") -(def t_RESULTS_START #"^\#\+RESULTS\:") -(def t_CAPTIONS #"^\#\+CAPTION:") -(def t_NAME #"^\#\+NAME:") -; t_IMG #"^\[\[(\w|\.|-|_|/)+\]\]$" -(def t_IMG #"^\[\[") -(def t_IMG_END #"\]\]") -(def t_RESULTS_END #"^\s*$") -(def t_END_LABELS #"^(?!\[|\#).*") -(def t_BULLET_START #"^\s*[\+|\-|0-9\.]") -(def t_BULLET_END #"^(?!\s*[\+|\-|0-9\.]).*$") - -(def t_HEADER #"^\*+") -(def t_META_OTHER #"^[#]\+[A-Z\_]+\:") - -(def TYPE_SINGLE 0) -(def TYPE_BLOCK 1) -(def TYPE_ATTRIBUTE 2) - -(defn TokenStruct [values] - (merge {:start "" :end false :type TYPE_SINGLE :start_pos 2 :end_pos nil :count 0 :key ""} values)) - -(def token-map {:token nil :value nil :attrs nil}) - -(defn token - ([token-type value] (token token-type value nil)) - ([token-type value attrs] - {:token token-type :value (or value "") :attrs attrs})) - -(def TOKENS [[:META (TokenStruct {:start t_META, :end_pos -1 :key-fn #(clojure.string/join (drop 2 (butlast %)))})], - [:COMMENT (TokenStruct {:start t_COMMENT_BEGIN, :end t_COMMENT_END, :type TYPE_BLOCK, :end_pos -1})], - [:EXAMPLE (TokenStruct {:start t_EXAMPLE_BEGIN, :end t_EXAMPLE_END, :type TYPE_BLOCK, :end_pos -})], - [:IMAGE (TokenStruct {:start t_IMG, :end_pos -2})], - [:CAPTION (TokenStruct {:start t_CAPTIONS, :type TYPE_ATTRIBUTE, :key "caption"})], - [:br (TokenStruct {:start t_BLANK_LINE, :end_pos -1})], - [:SOURCE (TokenStruct {:start t_SRC_BEGIN, :end t_SRC_END})], - [:TABLE (TokenStruct {:start t_TABLE_START, :end t_TABLE_END, :start_pos 0})], - [:BULLET (TokenStruct {:start t_BULLET_START, :end t_BULLET_END, :start_pos 0})], - [:RESULTS (TokenStruct {:start t_RESULTS_START, :end t_RESULTS_END})], - [:HEADER (TokenStruct {:start t_HEADER, :start_pos 1, :count 0})], - [:META_OTHER (TokenStruct {:start t_META_OTHER, :start_pos 2, :end_pos -1})]]) - - -(def rm-ns (comp keyword name)) - -(defn mk-key [k t pos] - (case (name k) - "HEADER" (keyword (str (name k) pos)) - (rm-ns k))) - -(clojure.string/join (drop 2 (butlast "#+title:"))) -(re-find t_META "#+TITLE: abc12345def") -(re-find t_HEADER "** test") -(str/split "#+TITLE: abc12345def" t_META) -(def t - "#+TITLE: abc12345def -#+DESCRIPTION: test descriptioon - -* title -paragraph -text -** header test -hi there -") - -(defn re-split [rx line] - (let [matches (re-find rx line) - pos (if (string? matches) - (count matches) - (count (or (str (first matches)) matches))) - remainder (subs line pos)] - [matches pos remainder])) - -(defn match-token [line] - (->> TOKENS - (reduce (fn [m [k v]] - (let [s (re-find (:start v) line) - pos (if (string? s) (count s) (count (or (str (first s)) s))) - v (subs line pos)] - (when (not (nil? s)) - (reduced (remove nil? [(mk-key k v pos) (if (= "" v) nil v)]))))) []) - (#(or % [:p line])))) - - -(defn parse-line [text] - (loop [line (str/split-lines text) - r []] - (if (empty? line) - r - (recur (next line) - (if (= :HEADER1 (first (last r))) - (conj (last r) (match-token (first line))) - (conj r (match-token (first line)))))))) - -(defn parse-flat [text] - (->> (str/split-lines text) - (map (fn [a] (match-token a))))) - -(parse-line t ) -(parse-flat t ) - -(defn to->html [dsl] - (map #(apply conj [(case (first %) - :TITLE :h1 - :HEADER1 :h1 - :HEADER2 :h2 - :br :br - :SOURCE :pre - (first %))] (rest %)) dsl)) - -(defn parse->to-hiccup [org-document] - (to->html (parse-flat org-document))) - -(parse->to-hiccup t) -(parse->to-hiccup "") - -(map #(clojure.set/rename-keys (parse-line t) {:HEADER :h1}) (parse-line t ))